tesseract-agent 0.2.0 → 0.2.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.
Files changed (2) hide show
  1. package/dist-bundle/cli.js +233 -9
  2. package/package.json +4 -1
@@ -6031,6 +6031,20 @@ function resolveUniqueName(dir, name) {
6031
6031
  }
6032
6032
  var OUTSIDE = (verb) => ({ ok: false, error: `Refusing to ${verb} outside the shared root.` });
6033
6033
  var validName = (n) => !!n && !n.includes("/") && !n.includes("\\") && n !== "." && n !== "..";
6034
+ async function readRemoteBytes(root, path) {
6035
+ if (!isInsideRoot(path, root))
6036
+ return { error: "Path is outside the shared root." };
6037
+ try {
6038
+ const s = statSync(path);
6039
+ if (!s.isFile())
6040
+ return { error: "Not a file" };
6041
+ if (s.size > REMOTE_READ_CAP)
6042
+ return { error: `File is too large to read whole over the network (${(s.size / 1024 / 1024).toFixed(1)} MB) \u2014 open it locally.` };
6043
+ return { bytes: await readFile(path), size: s.size };
6044
+ } catch (err) {
6045
+ return { error: String(err.message) };
6046
+ }
6047
+ }
6034
6048
  async function runRemoteFsOp(root, op, args) {
6035
6049
  const a = args ?? [];
6036
6050
  switch (op) {
@@ -6295,12 +6309,202 @@ function unwatchAllRemote() {
6295
6309
  remoteWatches.clear();
6296
6310
  }
6297
6311
 
6312
+ // ../../packages/host-core/dist/fs-datachannel-host.js
6313
+ var ndcPromise = null;
6314
+ function loadNdc() {
6315
+ if (!ndcPromise) {
6316
+ ndcPromise = (async () => {
6317
+ try {
6318
+ const m = await import("node-datachannel");
6319
+ return m.default ?? m;
6320
+ } catch {
6321
+ return null;
6322
+ }
6323
+ })();
6324
+ }
6325
+ return ndcPromise;
6326
+ }
6327
+ function toNdcIceServers(configs) {
6328
+ const out = [];
6329
+ for (const c of configs ?? []) {
6330
+ const urls = Array.isArray(c.urls) ? c.urls : [c.urls];
6331
+ for (const url of urls) {
6332
+ const m = /^(stun|stuns|turn|turns):([^:?]+)(?::(\d+))?(?:\?transport=(udp|tcp))?/i.exec(url);
6333
+ if (!m)
6334
+ continue;
6335
+ const scheme = m[1].toLowerCase();
6336
+ const hostname3 = m[2];
6337
+ const port = m[3] ? parseInt(m[3], 10) : scheme.endsWith("s") ? 5349 : 3478;
6338
+ if (scheme.startsWith("stun")) {
6339
+ out.push({ hostname: hostname3, port });
6340
+ continue;
6341
+ }
6342
+ const transport = (m[4] ?? "udp").toLowerCase();
6343
+ const relayType = scheme === "turns" ? "TurnTls" : transport === "tcp" ? "TurnTcp" : "TurnUdp";
6344
+ out.push({ hostname: hostname3, port, username: c.username, password: c.credential, relayType });
6345
+ }
6346
+ }
6347
+ return out;
6348
+ }
6349
+ var peers = /* @__PURE__ */ new Map();
6350
+ var delay = (ms) => new Promise((r) => setTimeout(r, ms));
6351
+ var CHUNK = 16 * 1024;
6352
+ var HIGH_WATER = 4 * 1024 * 1024;
6353
+ async function serveGet(dc, id, path, root) {
6354
+ const res = await readRemoteBytes(root, path);
6355
+ if (!dc.isOpen())
6356
+ return;
6357
+ if (res.error || !res.bytes) {
6358
+ try {
6359
+ dc.sendMessage(JSON.stringify({ t: "err", id, error: res.error ?? "read failed" }));
6360
+ } catch {
6361
+ }
6362
+ return;
6363
+ }
6364
+ const bytes = res.bytes;
6365
+ try {
6366
+ dc.sendMessage(JSON.stringify({ t: "meta", id, size: bytes.length }));
6367
+ } catch {
6368
+ return;
6369
+ }
6370
+ for (let off = 0, seq = 0; off < bytes.length; off += CHUNK, seq++) {
6371
+ let guard = 0;
6372
+ while (dc.isOpen() && dc.bufferedAmount() > HIGH_WATER && guard++ < 2e3)
6373
+ await delay(15);
6374
+ if (!dc.isOpen())
6375
+ return;
6376
+ const end = Math.min(off + CHUNK, bytes.length);
6377
+ const frame = Buffer.allocUnsafe(8 + (end - off));
6378
+ frame.writeUInt32LE(id >>> 0, 0);
6379
+ frame.writeUInt32LE(seq >>> 0, 4);
6380
+ bytes.copy(frame, 8, off, end);
6381
+ try {
6382
+ dc.sendMessageBinary(frame);
6383
+ } catch {
6384
+ return;
6385
+ }
6386
+ }
6387
+ if (dc.isOpen()) {
6388
+ try {
6389
+ dc.sendMessage(JSON.stringify({ t: "end", id }));
6390
+ } catch {
6391
+ }
6392
+ }
6393
+ }
6394
+ function wireServer(dc, root) {
6395
+ try {
6396
+ dc.setBufferedAmountLowThreshold(HIGH_WATER / 4);
6397
+ } catch {
6398
+ }
6399
+ dc.onMessage((msg) => {
6400
+ if (typeof msg !== "string")
6401
+ return;
6402
+ let req;
6403
+ try {
6404
+ req = JSON.parse(msg);
6405
+ } catch {
6406
+ return;
6407
+ }
6408
+ if (req.t === "get" && typeof req.id === "number" && typeof req.path === "string")
6409
+ void serveGet(dc, req.id, req.path, root);
6410
+ });
6411
+ }
6412
+ async function handleDcSignal(clientId, msg, root, ownHostId2, emit) {
6413
+ if (msg.type === "signal:dc-ice") {
6414
+ const peer2 = peers.get(clientId);
6415
+ if (peer2 && peer2.remoteSet) {
6416
+ try {
6417
+ peer2.pc.addRemoteCandidate(msg.candidate, msg.mid);
6418
+ } catch {
6419
+ }
6420
+ } else if (peer2)
6421
+ peer2.pendingIce.push({ candidate: msg.candidate, mid: msg.mid });
6422
+ return;
6423
+ }
6424
+ if (msg.type !== "signal:dc-offer")
6425
+ return;
6426
+ const ndc = await loadNdc();
6427
+ if (!ndc) {
6428
+ emit({ type: "signal:dc-unavailable", hostId: ownHostId2 });
6429
+ return;
6430
+ }
6431
+ releaseDcClient(clientId);
6432
+ let pc;
6433
+ try {
6434
+ pc = new ndc.PeerConnection("fsdc", { iceServers: toNdcIceServers(msg.iceServers) });
6435
+ } catch {
6436
+ emit({ type: "signal:dc-unavailable", hostId: ownHostId2 });
6437
+ return;
6438
+ }
6439
+ const peer = { pc, pendingIce: [], remoteSet: false };
6440
+ peers.set(clientId, peer);
6441
+ pc.onLocalDescription((sdp, type) => emit({ type: "signal:dc-answer", sdp, sdpType: type, hostId: ownHostId2 }));
6442
+ pc.onLocalCandidate((candidate, mid) => emit({ type: "signal:dc-ice", candidate, mid, hostId: ownHostId2 }));
6443
+ pc.onDataChannel((dc) => wireServer(dc, root));
6444
+ try {
6445
+ pc.setRemoteDescription(msg.sdp, "offer");
6446
+ peer.remoteSet = true;
6447
+ for (const c of peer.pendingIce) {
6448
+ try {
6449
+ pc.addRemoteCandidate(c.candidate, c.mid);
6450
+ } catch {
6451
+ }
6452
+ }
6453
+ peer.pendingIce = [];
6454
+ } catch {
6455
+ releaseDcClient(clientId);
6456
+ emit({ type: "signal:dc-unavailable", hostId: ownHostId2 });
6457
+ }
6458
+ }
6459
+ function releaseDcClient(clientId) {
6460
+ const peer = peers.get(clientId);
6461
+ if (!peer)
6462
+ return;
6463
+ peers.delete(clientId);
6464
+ try {
6465
+ peer.pc.close();
6466
+ } catch {
6467
+ }
6468
+ }
6469
+
6470
+ // ../../packages/host-core/dist/host-metrics.js
6471
+ import os from "node:os";
6472
+ function primaryIpv4(ifaces = os.networkInterfaces()) {
6473
+ for (const name of Object.keys(ifaces)) {
6474
+ for (const ni of ifaces[name] ?? []) {
6475
+ if (ni.family === "IPv4" && !ni.internal)
6476
+ return ni.address;
6477
+ }
6478
+ }
6479
+ return void 0;
6480
+ }
6481
+ function staticMetrics(version) {
6482
+ const cpus = os.cpus();
6483
+ return {
6484
+ version: version || void 0,
6485
+ arch: os.arch(),
6486
+ cpuModel: cpus[0]?.model?.trim() || void 0,
6487
+ cpuCount: cpus.length || void 0,
6488
+ totalMem: os.totalmem() || void 0,
6489
+ ip: primaryIpv4()
6490
+ };
6491
+ }
6492
+ function dynamicMetrics() {
6493
+ const la = os.loadavg();
6494
+ return {
6495
+ load: la[0] > 0 ? Number(la[0].toFixed(2)) : void 0,
6496
+ // loadavg is [0,0,0] on Windows
6497
+ freeMem: os.freemem() || void 0,
6498
+ uptime: Math.round(os.uptime()) || void 0
6499
+ };
6500
+ }
6501
+
6298
6502
  // ../../packages/host-core/dist/fs-transfer.js
6299
6503
  import { readdir as readdir2, lstat, mkdir as mkdir3, chmod, rm as rm2, readlink, symlink, stat as stat2 } from "node:fs/promises";
6300
6504
  import { openSync as openSync2, readSync as readSync2, closeSync as closeSync2, existsSync as existsSync3, createWriteStream } from "node:fs";
6301
6505
  import { dirname as dirname4, join as join4, relative as relative2, sep as sep2 } from "node:path";
6302
- var CHUNK = 1024 * 1024;
6303
- var MAX_INFLIGHT = 8 * CHUNK;
6506
+ var CHUNK2 = 1024 * 1024;
6507
+ var MAX_INFLIGHT = 8 * CHUNK2;
6304
6508
  var IDLE_MS = 6e4;
6305
6509
  var HEAVY_DIRS = /* @__PURE__ */ new Set([".git", "node_modules", "dist", "build", ".next", ".turbo", "out", "target", ".cache", "coverage"]);
6306
6510
  var ALWAYS_SKIP = /* @__PURE__ */ new Set([".DS_Store"]);
@@ -6433,11 +6637,11 @@ async function startOutboundTransfer(msg, controllerClientId, ownHostId2, hostRo
6433
6637
  if (f.size === 0) {
6434
6638
  send2({ type: "fs:transferChunk", transferId, toHost, fileIx, rel: f.rel, mode: f.mode, kind: "file", offset: 0, base64: "", eof: true }, { hostId: toHost });
6435
6639
  } else {
6436
- for (let offset = 0; offset < f.size && !state.cancelled; offset += CHUNK) {
6640
+ for (let offset = 0; offset < f.size && !state.cancelled; offset += CHUNK2) {
6437
6641
  await waitWindow();
6438
6642
  if (state.cancelled)
6439
6643
  break;
6440
- const len = Math.min(CHUNK, f.size - offset);
6644
+ const len = Math.min(CHUNK2, f.size - offset);
6441
6645
  const buf = Buffer.alloc(len);
6442
6646
  const read = readSync2(fd, buf, 0, len, offset);
6443
6647
  send2({ type: "fs:transferChunk", transferId, toHost, fileIx, rel: f.rel, mode: f.mode, kind: "file", offset, base64: buf.subarray(0, read).toString("base64"), eof: offset + len >= f.size }, { hostId: toHost });
@@ -6681,6 +6885,7 @@ var SERVER_HOST_ID = "server";
6681
6885
  var cfg = null;
6682
6886
  var deviceId = "";
6683
6887
  var backendOrigin = "";
6888
+ var hostVersion;
6684
6889
  function getToken() {
6685
6890
  return cfg ? cfg.tokenSource.getToken() : null;
6686
6891
  }
@@ -6750,7 +6955,17 @@ function ownHostId() {
6750
6955
  function presentedHosts() {
6751
6956
  const out = serverHosts.slice();
6752
6957
  if (!out.some((h) => h.hostId.endsWith(`:${deviceId}`))) {
6753
- out.unshift({ hostId: "local", name: hostname(), kind: "electron", platform: process.platform, online: true, caps: ["agent", "fs"] });
6958
+ out.unshift({
6959
+ hostId: "local",
6960
+ name: hostname(),
6961
+ kind: "electron",
6962
+ platform: process.platform,
6963
+ online: true,
6964
+ caps: ["agent", "fs"],
6965
+ ...staticMetrics(hostVersion),
6966
+ ...dynamicMetrics(),
6967
+ lastSeen: Date.now()
6968
+ });
6754
6969
  }
6755
6970
  return out;
6756
6971
  }
@@ -6896,6 +7111,7 @@ async function serveInboundFs(requestId, op, args, driverClientId) {
6896
7111
  }
6897
7112
  function releaseDriver(clientId) {
6898
7113
  unwatchRemoteClient(clientId);
7114
+ releaseDcClient(clientId);
6899
7115
  for (const [sid, cid] of [...driverBySession]) {
6900
7116
  if (cid !== clientId)
6901
7117
  continue;
@@ -6973,9 +7189,9 @@ function stopHeartbeat() {
6973
7189
  }
6974
7190
  }
6975
7191
  function registerAsHost() {
6976
- send("host", { type: "host:register", name: hostname(), platform: process.platform, deviceId, caps: ["agent", "fs"] });
7192
+ send("host", { type: "host:register", name: hostname(), platform: process.platform, deviceId, caps: ["agent", "fs"], ...staticMetrics(hostVersion) });
6977
7193
  if (!heartbeat)
6978
- heartbeat = setInterval(() => send("host", { type: "host:heartbeat" }), 2e4);
7194
+ heartbeat = setInterval(() => send("host", { type: "host:heartbeat", ...dynamicMetrics() }), 2e4);
6979
7195
  }
6980
7196
  function cleanup() {
6981
7197
  stopHeartbeat();
@@ -7093,6 +7309,13 @@ function connect() {
7093
7309
  }
7094
7310
  return;
7095
7311
  }
7312
+ if (channel === "signal") {
7313
+ const p = env.payload;
7314
+ if (inbound && clientId && typeof p?.type === "string" && p.type.startsWith("signal:dc-")) {
7315
+ void handleDcSignal(clientId, p, HOST_ROOT, ownHostId(), (payload) => send("signal", payload, { clientId, browserId: "fsdc" }));
7316
+ }
7317
+ return;
7318
+ }
7096
7319
  };
7097
7320
  socket.onclose = () => {
7098
7321
  cleanup();
@@ -7106,6 +7329,7 @@ function startControlLink(config, h) {
7106
7329
  cfg = config;
7107
7330
  deviceId = config.deviceId;
7108
7331
  backendOrigin = config.tokenSource.backendOrigin;
7332
+ hostVersion = config.version;
7109
7333
  hooks = h;
7110
7334
  stopped = false;
7111
7335
  hooks.pushHosts(presentedHosts());
@@ -7519,7 +7743,7 @@ async function runDaemon(opts = {}) {
7519
7743
  log2("local listener failed to start", e.message);
7520
7744
  }
7521
7745
  writeStatus({ deviceId: c.deviceId, connected: false });
7522
- startControlLink({ deviceId: c.deviceId, power: createNodePowerController(), tokenSource }, hooks2);
7746
+ startControlLink({ deviceId: c.deviceId, power: createNodePowerController(), tokenSource, version: VERSION }, hooks2);
7523
7747
  log2(`started \u2014 device ${c.deviceId}, user ${c.userId}, relay ${c.backendOrigin}, local :${localInfo?.localPort ?? "off"}, version ${VERSION}`);
7524
7748
  await refresh();
7525
7749
  timer = setInterval(() => {
@@ -8079,7 +8303,7 @@ async function logs(instanceId, follow) {
8079
8303
  }
8080
8304
 
8081
8305
  // src/cli.ts
8082
- var CLI_VERSION = process.env.CCB_DAEMON_VERSION ?? "0.2.0";
8306
+ var CLI_VERSION = process.env.CCB_DAEMON_VERSION ?? "0.2.1";
8083
8307
  function parseFlags(argv) {
8084
8308
  const out = {};
8085
8309
  for (let i = 0; i < argv.length; i++) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tesseract-agent",
3
- "version": "0.2.0",
3
+ "version": "0.2.2",
4
4
  "license": "UNLICENSED",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -33,6 +33,9 @@
33
33
  "dependencies": {
34
34
  "@anthropic-ai/claude-agent-sdk": "0.2.112"
35
35
  },
36
+ "optionalDependencies": {
37
+ "node-datachannel": "^0.32.0"
38
+ },
36
39
  "devDependencies": {
37
40
  "@fineguide-tesseract/agent-core": "*",
38
41
  "@fineguide-tesseract/host-core": "*",