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.
- package/dist-bundle/cli.js +233 -9
- package/package.json +4 -1
package/dist-bundle/cli.js
CHANGED
|
@@ -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
|
|
6303
|
-
var MAX_INFLIGHT = 8 *
|
|
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 +=
|
|
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(
|
|
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({
|
|
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.
|
|
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.
|
|
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": "*",
|