@hasna/computer 0.1.1 → 0.1.2

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.
@@ -1,5 +1,6 @@
1
1
  #!/usr/bin/env bun
2
2
  // @bun
3
+ var __require = import.meta.require;
3
4
 
4
5
  // src/agent/loop.ts
5
6
  import { randomUUID } from "crypto";
@@ -9,10 +10,15 @@ import { mkdir } from "fs/promises";
9
10
  import { tmpdir } from "os";
10
11
  import { join } from "path";
11
12
  import { readFile, unlink } from "fs/promises";
12
- async function captureScreenshot() {
13
+ async function captureScreenshot(displayNumber) {
13
14
  const timestamp = Date.now();
14
15
  const tmpPath = join(tmpdir(), `computer-screenshot-${timestamp}.png`);
15
- const proc = Bun.spawn(["screencapture", "-x", "-C", "-t", "png", tmpPath], {
16
+ const args = ["screencapture", "-x", "-C", "-t", "png"];
17
+ if (displayNumber) {
18
+ args.push(`-D${displayNumber}`);
19
+ }
20
+ args.push(tmpPath);
21
+ const proc = Bun.spawn(args, {
16
22
  stdout: "pipe",
17
23
  stderr: "pipe"
18
24
  });
@@ -235,19 +241,23 @@ function getScrollHelperPath() {
235
241
 
236
242
  // src/drivers/mac/index.ts
237
243
  class MacDriver {
244
+ displayNumber;
245
+ constructor(opts) {
246
+ this.displayNumber = opts?.displayNumber;
247
+ }
238
248
  async getScreenSize() {
239
249
  return getScreenSize();
240
250
  }
241
251
  async screenshot() {
242
- return captureScreenshot();
252
+ return captureScreenshot(this.displayNumber);
243
253
  }
244
254
  async execute(action) {
245
255
  return executeAction(action);
246
256
  }
247
257
  async dispose() {}
248
258
  }
249
- function createMacDriver() {
250
- return new MacDriver;
259
+ function createMacDriver(opts) {
260
+ return new MacDriver(opts);
251
261
  }
252
262
 
253
263
  // node_modules/@anthropic-ai/sdk/version.mjs
@@ -9537,7 +9547,7 @@ var __export = (target, all) => {
9537
9547
  set: __exportSetter.bind(all, name)
9538
9548
  });
9539
9549
  };
9540
- var __require = /* @__PURE__ */ createRequire(import.meta.url);
9550
+ var __require2 = /* @__PURE__ */ createRequire(import.meta.url);
9541
9551
  var require_postgres_array = __commonJS((exports) => {
9542
9552
  exports.parse = function(source, transform) {
9543
9553
  return new ArrayParser(source, transform).parse();
@@ -10477,7 +10487,7 @@ var require_defaults = __commonJS((exports, module) => {
10477
10487
  });
10478
10488
  var require_utils = __commonJS((exports, module) => {
10479
10489
  var defaults2 = require_defaults();
10480
- var util = __require("util");
10490
+ var util = __require2("util");
10481
10491
  var { isDate } = util.types || util;
10482
10492
  function escapeElement(elementRepresentation) {
10483
10493
  const escaped = elementRepresentation.replace(/\\/g, "\\\\").replace(/"/g, "\\\"");
@@ -10633,7 +10643,7 @@ var require_utils = __commonJS((exports, module) => {
10633
10643
  };
10634
10644
  });
10635
10645
  var require_utils_legacy = __commonJS((exports, module) => {
10636
- var nodeCrypto = __require("crypto");
10646
+ var nodeCrypto = __require2("crypto");
10637
10647
  function md5(string) {
10638
10648
  return nodeCrypto.createHash("md5").update(string, "utf-8").digest("hex");
10639
10649
  }
@@ -10666,7 +10676,7 @@ var require_utils_legacy = __commonJS((exports, module) => {
10666
10676
  };
10667
10677
  });
10668
10678
  var require_utils_webcrypto = __commonJS((exports, module) => {
10669
- var nodeCrypto = __require("crypto");
10679
+ var nodeCrypto = __require2("crypto");
10670
10680
  module.exports = {
10671
10681
  postgresMd5PasswordHash,
10672
10682
  randomBytes,
@@ -11076,7 +11086,7 @@ var require_pg_connection_string = __commonJS((exports, module) => {
11076
11086
  if (config.sslcert || config.sslkey || config.sslrootcert || config.sslmode) {
11077
11087
  config.ssl = {};
11078
11088
  }
11079
- const fs = config.sslcert || config.sslkey || config.sslrootcert ? __require("fs") : null;
11089
+ const fs = config.sslcert || config.sslkey || config.sslrootcert ? __require2("fs") : null;
11080
11090
  if (config.sslcert) {
11081
11091
  config.ssl.cert = fs.readFileSync(config.sslcert).toString();
11082
11092
  }
@@ -11199,7 +11209,7 @@ See https://www.postgresql.org/docs/current/libpq-ssl.html for libpq SSL mode de
11199
11209
  parse.parseIntoClientConfig = parseIntoClientConfig;
11200
11210
  });
11201
11211
  var require_connection_parameters = __commonJS((exports, module) => {
11202
- var dns = __require("dns");
11212
+ var dns = __require2("dns");
11203
11213
  var defaults2 = require_defaults();
11204
11214
  var parse = require_pg_connection_string().parse;
11205
11215
  var val = function(key, config, envVar) {
@@ -11422,7 +11432,7 @@ var require_result = __commonJS((exports, module) => {
11422
11432
  module.exports = Result;
11423
11433
  });
11424
11434
  var require_query = __commonJS((exports, module) => {
11425
- var { EventEmitter } = __require("events");
11435
+ var { EventEmitter } = __require2("events");
11426
11436
  var Result = require_result();
11427
11437
  var utils = require_utils();
11428
11438
 
@@ -12405,11 +12415,11 @@ var require_stream = __commonJS((exports, module) => {
12405
12415
  };
12406
12416
  function getNodejsStreamFuncs() {
12407
12417
  function getStream2(ssl) {
12408
- const net = __require("net");
12418
+ const net = __require2("net");
12409
12419
  return new net.Socket;
12410
12420
  }
12411
12421
  function getSecureStream2(options) {
12412
- const tls = __require("tls");
12422
+ const tls = __require2("tls");
12413
12423
  return tls.connect(options);
12414
12424
  }
12415
12425
  return {
@@ -12451,7 +12461,7 @@ var require_stream = __commonJS((exports, module) => {
12451
12461
  }
12452
12462
  });
12453
12463
  var require_connection = __commonJS((exports, module) => {
12454
- var EventEmitter = __require("events").EventEmitter;
12464
+ var EventEmitter = __require2("events").EventEmitter;
12455
12465
  var { parse, serialize } = require_dist();
12456
12466
  var { getStream, getSecureStream } = require_stream();
12457
12467
  var flushBuffer = serialize.flush();
@@ -12524,7 +12534,7 @@ var require_connection = __commonJS((exports, module) => {
12524
12534
  options.key = self.ssl.key;
12525
12535
  }
12526
12536
  }
12527
- const net = __require("net");
12537
+ const net = __require2("net");
12528
12538
  if (net.isIP && net.isIP(host) === 0) {
12529
12539
  options.servername = host;
12530
12540
  }
@@ -12627,8 +12637,8 @@ var require_connection = __commonJS((exports, module) => {
12627
12637
  module.exports = Connection;
12628
12638
  });
12629
12639
  var require_split2 = __commonJS((exports, module) => {
12630
- var { Transform } = __require("stream");
12631
- var { StringDecoder } = __require("string_decoder");
12640
+ var { Transform } = __require2("stream");
12641
+ var { StringDecoder } = __require2("string_decoder");
12632
12642
  var kLast = Symbol("last");
12633
12643
  var kDecoder = Symbol("decoder");
12634
12644
  function transform(chunk, enc, cb) {
@@ -12724,10 +12734,10 @@ var require_split2 = __commonJS((exports, module) => {
12724
12734
  module.exports = split;
12725
12735
  });
12726
12736
  var require_helper = __commonJS((exports, module) => {
12727
- var path = __require("path");
12728
- var Stream3 = __require("stream").Stream;
12737
+ var path = __require2("path");
12738
+ var Stream3 = __require2("stream").Stream;
12729
12739
  var split = require_split2();
12730
- var util = __require("util");
12740
+ var util = __require2("util");
12731
12741
  var defaultPort = 5432;
12732
12742
  var isWin = process.platform === "win32";
12733
12743
  var warnStream = process.stderr;
@@ -12886,8 +12896,8 @@ var require_helper = __commonJS((exports, module) => {
12886
12896
  };
12887
12897
  });
12888
12898
  var require_lib = __commonJS((exports, module) => {
12889
- var path = __require("path");
12890
- var fs = __require("fs");
12899
+ var path = __require2("path");
12900
+ var fs = __require2("fs");
12891
12901
  var helper = require_helper();
12892
12902
  module.exports = function(connInfo, cb) {
12893
12903
  var file = helper.getFileName();
@@ -12902,9 +12912,9 @@ var require_lib = __commonJS((exports, module) => {
12902
12912
  module.exports.warnTo = helper.warnTo;
12903
12913
  });
12904
12914
  var require_client = __commonJS((exports, module) => {
12905
- var EventEmitter = __require("events").EventEmitter;
12915
+ var EventEmitter = __require2("events").EventEmitter;
12906
12916
  var utils = require_utils();
12907
- var nodeUtils = __require("util");
12917
+ var nodeUtils = __require2("util");
12908
12918
  var sasl = require_sasl();
12909
12919
  var TypeOverrides = require_type_overrides();
12910
12920
  var ConnectionParameters = require_connection_parameters();
@@ -13481,7 +13491,7 @@ var require_client = __commonJS((exports, module) => {
13481
13491
  module.exports = Client;
13482
13492
  });
13483
13493
  var require_pg_pool = __commonJS((exports, module) => {
13484
- var EventEmitter = __require("events").EventEmitter;
13494
+ var EventEmitter = __require2("events").EventEmitter;
13485
13495
  var NOOP = function() {};
13486
13496
  var removeWhere = (list, predicate) => {
13487
13497
  const i = list.findIndex(predicate);
@@ -13891,8 +13901,8 @@ var require_pg_pool = __commonJS((exports, module) => {
13891
13901
  module.exports = Pool;
13892
13902
  });
13893
13903
  var require_query2 = __commonJS((exports, module) => {
13894
- var EventEmitter = __require("events").EventEmitter;
13895
- var util = __require("util");
13904
+ var EventEmitter = __require2("events").EventEmitter;
13905
+ var util = __require2("util");
13896
13906
  var utils = require_utils();
13897
13907
  var NativeQuery = module.exports = function(config, values, callback) {
13898
13908
  EventEmitter.call(this);
@@ -14025,7 +14035,7 @@ var require_query2 = __commonJS((exports, module) => {
14025
14035
  };
14026
14036
  });
14027
14037
  var require_client2 = __commonJS((exports, module) => {
14028
- var nodeUtils = __require("util");
14038
+ var nodeUtils = __require2("util");
14029
14039
  var Native;
14030
14040
  try {
14031
14041
  Native = (() => {
@@ -14035,8 +14045,8 @@ var require_client2 = __commonJS((exports, module) => {
14035
14045
  throw e;
14036
14046
  }
14037
14047
  var TypeOverrides = require_type_overrides();
14038
- var EventEmitter = __require("events").EventEmitter;
14039
- var util = __require("util");
14048
+ var EventEmitter = __require2("events").EventEmitter;
14049
+ var util = __require2("util");
14040
14050
  var ConnectionParameters = require_connection_parameters();
14041
14051
  var NativeQuery = require_query2();
14042
14052
  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 +18540,7 @@ function getDb() {
18530
18540
  total_tokens_in INTEGER NOT NULL DEFAULT 0,
18531
18541
  total_tokens_out INTEGER NOT NULL DEFAULT 0,
18532
18542
  total_duration_ms INTEGER NOT NULL DEFAULT 0,
18543
+ tags TEXT,
18533
18544
  error TEXT,
18534
18545
  created_at TEXT NOT NULL,
18535
18546
  completed_at TEXT
@@ -18585,9 +18596,9 @@ function getDb() {
18585
18596
  async function createSession(session) {
18586
18597
  const d = getDb();
18587
18598
  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);
18599
+ INSERT INTO sessions (id, task, provider, model, status, steps, total_tokens_in, total_tokens_out, total_duration_ms, tags, error, created_at, completed_at)
18600
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
18601
+ `).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
18602
  }
18592
18603
  async function updateSession(session) {
18593
18604
  const d = getDb();
@@ -18615,10 +18626,18 @@ function listSessions(opts) {
18615
18626
  const d = getDb();
18616
18627
  let sql = "SELECT * FROM sessions";
18617
18628
  const params = [];
18629
+ const conditions = [];
18618
18630
  if (opts?.status) {
18619
- sql += " WHERE status = ?";
18631
+ conditions.push("status = ?");
18620
18632
  params.push(opts.status);
18621
18633
  }
18634
+ if (opts?.tag) {
18635
+ conditions.push("tags LIKE ?");
18636
+ params.push(`%"${opts.tag}"%`);
18637
+ }
18638
+ if (conditions.length > 0) {
18639
+ sql += " WHERE " + conditions.join(" AND ");
18640
+ }
18622
18641
  sql += " ORDER BY created_at DESC";
18623
18642
  if (opts?.limit) {
18624
18643
  sql += " LIMIT ?";
@@ -18684,12 +18703,104 @@ function rowToSession(row) {
18684
18703
  total_tokens_in: row.total_tokens_in,
18685
18704
  total_tokens_out: row.total_tokens_out,
18686
18705
  total_duration_ms: row.total_duration_ms,
18706
+ tags: row.tags ? JSON.parse(row.tags) : undefined,
18687
18707
  error: row.error,
18688
18708
  created_at: row.created_at,
18689
18709
  completed_at: row.completed_at
18690
18710
  };
18691
18711
  }
18692
18712
 
18713
+ // src/lib/integrations.ts
18714
+ async function saveToRecordings(session, logs) {
18715
+ try {
18716
+ const { saveRecording } = await import("@hasna/recordings");
18717
+ await saveRecording({
18718
+ title: `Computer Use: ${session.task.slice(0, 100)}`,
18719
+ type: "computer-use",
18720
+ source: "computer",
18721
+ duration_ms: session.total_duration_ms,
18722
+ metadata: {
18723
+ session_id: session.id,
18724
+ provider: session.provider,
18725
+ model: session.model,
18726
+ steps: session.steps,
18727
+ tokens_in: session.total_tokens_in,
18728
+ tokens_out: session.total_tokens_out,
18729
+ status: session.status,
18730
+ tags: session.tags
18731
+ },
18732
+ transcript: logs.map((l) => ({
18733
+ step: l.step,
18734
+ action: l.action.type,
18735
+ reasoning: l.reasoning?.slice(0, 200),
18736
+ success: l.success,
18737
+ timestamp: l.created_at
18738
+ }))
18739
+ });
18740
+ return true;
18741
+ } catch {
18742
+ return false;
18743
+ }
18744
+ }
18745
+ async function registerWithSessions(session) {
18746
+ try {
18747
+ const mod = await import("@hasna/sessions");
18748
+ const registerSession = mod.registerSession ?? mod.createSession ?? mod.saveSession;
18749
+ if (typeof registerSession !== "function")
18750
+ return false;
18751
+ await registerSession({
18752
+ id: session.id,
18753
+ type: "computer-use",
18754
+ source: "computer",
18755
+ status: session.status,
18756
+ metadata: {
18757
+ task: session.task,
18758
+ provider: session.provider,
18759
+ model: session.model,
18760
+ steps: session.steps
18761
+ },
18762
+ started_at: session.created_at,
18763
+ ended_at: session.completed_at
18764
+ });
18765
+ return true;
18766
+ } catch {
18767
+ return false;
18768
+ }
18769
+ }
18770
+ async function pushToLogs(session, logs) {
18771
+ try {
18772
+ const mod = await import("@hasna/logs");
18773
+ const pushBatch = mod.logPushBatch ?? mod.pushBatch;
18774
+ if (typeof pushBatch !== "function")
18775
+ return false;
18776
+ await pushBatch(logs.map((l) => ({
18777
+ level: l.success ? "info" : "error",
18778
+ source: "computer",
18779
+ message: `[${l.action.type}] ${l.reasoning?.slice(0, 100) ?? ""}`,
18780
+ metadata: {
18781
+ session_id: session.id,
18782
+ step: l.step,
18783
+ action_type: l.action.type,
18784
+ success: l.success,
18785
+ error: l.error,
18786
+ duration_ms: l.duration_ms
18787
+ },
18788
+ timestamp: l.created_at
18789
+ })));
18790
+ return true;
18791
+ } catch {
18792
+ return false;
18793
+ }
18794
+ }
18795
+ async function runPostSessionIntegrations(session, logs) {
18796
+ const [recordings, sessions, logsPushed] = await Promise.all([
18797
+ saveToRecordings(session, logs),
18798
+ registerWithSessions(session),
18799
+ pushToLogs(session, logs)
18800
+ ]);
18801
+ return { recordings, sessions, logs: logsPushed };
18802
+ }
18803
+
18693
18804
  // src/agent/loop.ts
18694
18805
  var DEFAULT_MAX_STEPS = 50;
18695
18806
  async function runTask(options) {
@@ -18703,10 +18814,12 @@ async function runTask(options) {
18703
18814
  systemPrompt,
18704
18815
  screenshotMaxWidth,
18705
18816
  dryRun = false,
18817
+ tags,
18818
+ displayNumber,
18706
18819
  onStep,
18707
18820
  onDone
18708
18821
  } = options;
18709
- const driver = createMacDriver();
18822
+ const driver = createMacDriver({ displayNumber });
18710
18823
  const provider = createProvider(providerName, { model });
18711
18824
  const config = loadConfig();
18712
18825
  const safetyConfig = config.safety;
@@ -18717,6 +18830,7 @@ async function runTask(options) {
18717
18830
  provider: providerName,
18718
18831
  model: model ?? (providerName === "anthropic" ? "claude-sonnet-4-5-20250514" : "computer-use-preview"),
18719
18832
  status: "running",
18833
+ tags,
18720
18834
  steps: 0,
18721
18835
  total_tokens_in: 0,
18722
18836
  total_tokens_out: 0,
@@ -18769,6 +18883,8 @@ async function runTask(options) {
18769
18883
  });
18770
18884
  await updateSession(session);
18771
18885
  onStep?.(step, response, { success: true, duration_ms: 0 });
18886
+ const logs = getActionLogs(sessionId);
18887
+ await runPostSessionIntegrations(session, logs).catch(() => {});
18772
18888
  onDone?.(session);
18773
18889
  await driver.dispose();
18774
18890
  return session;
@@ -18834,6 +18950,8 @@ async function runTask(options) {
18834
18950
  session.completed_at = new Date().toISOString();
18835
18951
  session.error = `Reached max steps (${maxSteps})`;
18836
18952
  await updateSession(session);
18953
+ const endLogs = getActionLogs(sessionId);
18954
+ await runPostSessionIntegrations(session, endLogs).catch(() => {});
18837
18955
  onDone?.(session);
18838
18956
  await driver.dispose();
18839
18957
  return session;
@@ -18843,6 +18961,8 @@ async function runTask(options) {
18843
18961
  session.total_duration_ms = Date.now() - startTime;
18844
18962
  session.completed_at = new Date().toISOString();
18845
18963
  await updateSession(session);
18964
+ const errLogs = getActionLogs(sessionId);
18965
+ await runPostSessionIntegrations(session, errLogs).catch(() => {});
18846
18966
  onDone?.(session);
18847
18967
  await driver.dispose();
18848
18968
  return session;
@@ -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.1",
3
+ "version": "0.1.2",
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",