tesseract-agent 0.4.16 → 0.4.18
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 +1646 -432
- package/package.json +3 -2
package/dist-bundle/cli.js
CHANGED
|
@@ -70,7 +70,7 @@ var require_buffer_util = __commonJS({
|
|
|
70
70
|
"use strict";
|
|
71
71
|
var { EMPTY_BUFFER } = require_constants();
|
|
72
72
|
var FastBuffer = Buffer[Symbol.species];
|
|
73
|
-
function
|
|
73
|
+
function concat2(list, totalLength) {
|
|
74
74
|
if (list.length === 0) return EMPTY_BUFFER;
|
|
75
75
|
if (list.length === 1) return list[0];
|
|
76
76
|
const target = Buffer.allocUnsafe(totalLength);
|
|
@@ -116,7 +116,7 @@ var require_buffer_util = __commonJS({
|
|
|
116
116
|
return buf;
|
|
117
117
|
}
|
|
118
118
|
module.exports = {
|
|
119
|
-
concat,
|
|
119
|
+
concat: concat2,
|
|
120
120
|
mask: _mask,
|
|
121
121
|
toArrayBuffer,
|
|
122
122
|
toBuffer,
|
|
@@ -785,7 +785,7 @@ var require_receiver = __commonJS({
|
|
|
785
785
|
kStatusCode,
|
|
786
786
|
kWebSocket
|
|
787
787
|
} = require_constants();
|
|
788
|
-
var { concat, toArrayBuffer, unmask } = require_buffer_util();
|
|
788
|
+
var { concat: concat2, toArrayBuffer, unmask } = require_buffer_util();
|
|
789
789
|
var { isValidStatusCode, isValidUTF8 } = require_validation();
|
|
790
790
|
var FastBuffer = Buffer[Symbol.species];
|
|
791
791
|
var GET_INFO = 0;
|
|
@@ -1271,9 +1271,9 @@ var require_receiver = __commonJS({
|
|
|
1271
1271
|
if (this._opcode === 2) {
|
|
1272
1272
|
let data;
|
|
1273
1273
|
if (this._binaryType === "nodebuffer") {
|
|
1274
|
-
data =
|
|
1274
|
+
data = concat2(fragments, messageLength);
|
|
1275
1275
|
} else if (this._binaryType === "arraybuffer") {
|
|
1276
|
-
data = toArrayBuffer(
|
|
1276
|
+
data = toArrayBuffer(concat2(fragments, messageLength));
|
|
1277
1277
|
} else if (this._binaryType === "blob") {
|
|
1278
1278
|
data = new Blob(fragments);
|
|
1279
1279
|
} else {
|
|
@@ -1291,7 +1291,7 @@ var require_receiver = __commonJS({
|
|
|
1291
1291
|
});
|
|
1292
1292
|
}
|
|
1293
1293
|
} else {
|
|
1294
|
-
const buf =
|
|
1294
|
+
const buf = concat2(fragments, messageLength);
|
|
1295
1295
|
if (!this._skipUTF8Validation && !isValidUTF8(buf)) {
|
|
1296
1296
|
const error51 = this.createError(
|
|
1297
1297
|
Error,
|
|
@@ -2289,7 +2289,7 @@ var require_websocket = __commonJS({
|
|
|
2289
2289
|
var http = __require("http");
|
|
2290
2290
|
var net = __require("net");
|
|
2291
2291
|
var tls2 = __require("tls");
|
|
2292
|
-
var { randomBytes:
|
|
2292
|
+
var { randomBytes: randomBytes5, createHash: createHash6 } = __require("crypto");
|
|
2293
2293
|
var { Duplex, Readable: Readable2 } = __require("stream");
|
|
2294
2294
|
var { URL: URL2 } = __require("url");
|
|
2295
2295
|
var PerMessageDeflate2 = require_permessage_deflate();
|
|
@@ -2827,7 +2827,7 @@ var require_websocket = __commonJS({
|
|
|
2827
2827
|
}
|
|
2828
2828
|
}
|
|
2829
2829
|
const defaultPort = isSecure ? 443 : 80;
|
|
2830
|
-
const key2 =
|
|
2830
|
+
const key2 = randomBytes5(16).toString("base64");
|
|
2831
2831
|
const request = isSecure ? https.request : http.request;
|
|
2832
2832
|
const protocolSet = /* @__PURE__ */ new Set();
|
|
2833
2833
|
let perMessageDeflate;
|
|
@@ -4129,8 +4129,8 @@ function createSchedulerLock(lockDir, ownerId, now = () => Date.now()) {
|
|
|
4129
4129
|
}
|
|
4130
4130
|
};
|
|
4131
4131
|
const writeLease = () => {
|
|
4132
|
-
const
|
|
4133
|
-
const rec = { ownerId, pid: process.pid, refreshedAt: now(), nonce };
|
|
4132
|
+
const nonce2 = `${process.pid}-${now()}-${++nonceSeq}`;
|
|
4133
|
+
const rec = { ownerId, pid: process.pid, refreshedAt: now(), nonce: nonce2 };
|
|
4134
4134
|
const tmp = `${lockFile}.${process.pid}.tmp`;
|
|
4135
4135
|
try {
|
|
4136
4136
|
writeFileSync2(tmp, JSON.stringify(rec));
|
|
@@ -4144,7 +4144,7 @@ function createSchedulerLock(lockDir, ownerId, now = () => Date.now()) {
|
|
|
4144
4144
|
return false;
|
|
4145
4145
|
}
|
|
4146
4146
|
const after = read();
|
|
4147
|
-
held = !!after && after.nonce ===
|
|
4147
|
+
held = !!after && after.nonce === nonce2;
|
|
4148
4148
|
return held;
|
|
4149
4149
|
};
|
|
4150
4150
|
return {
|
|
@@ -19350,6 +19350,76 @@ var init_cron_tools = __esm({
|
|
|
19350
19350
|
}
|
|
19351
19351
|
});
|
|
19352
19352
|
|
|
19353
|
+
// ../../packages/shared/dist/permissions.js
|
|
19354
|
+
var PERMISSIONS, PERMISSION_BY_ID;
|
|
19355
|
+
var init_permissions = __esm({
|
|
19356
|
+
"../../packages/shared/dist/permissions.js"() {
|
|
19357
|
+
"use strict";
|
|
19358
|
+
PERMISSIONS = [
|
|
19359
|
+
{
|
|
19360
|
+
id: "microphone",
|
|
19361
|
+
label: "Microphone",
|
|
19362
|
+
why: "Talk to the agent in voice mode and be heard on group calls.",
|
|
19363
|
+
icon: "Mic",
|
|
19364
|
+
category: "media",
|
|
19365
|
+
applicablePlatforms: ["darwin", "win32"],
|
|
19366
|
+
requestKind: "in-app"
|
|
19367
|
+
},
|
|
19368
|
+
{
|
|
19369
|
+
id: "camera",
|
|
19370
|
+
label: "Camera",
|
|
19371
|
+
why: "Share your video on group calls.",
|
|
19372
|
+
icon: "Camera",
|
|
19373
|
+
category: "media",
|
|
19374
|
+
applicablePlatforms: ["darwin", "win32"],
|
|
19375
|
+
requestKind: "in-app"
|
|
19376
|
+
},
|
|
19377
|
+
{
|
|
19378
|
+
id: "screen-recording",
|
|
19379
|
+
label: "Screen Recording",
|
|
19380
|
+
why: "Capture a screenshot region or share your screen on calls.",
|
|
19381
|
+
icon: "MonitorPlay",
|
|
19382
|
+
category: "media",
|
|
19383
|
+
applicablePlatforms: ["darwin"],
|
|
19384
|
+
requestKind: "settings-only",
|
|
19385
|
+
requiresRelaunchAfterGrant: true,
|
|
19386
|
+
settingsHint: "Enable Tesseract under Screen Recording, then relaunch."
|
|
19387
|
+
},
|
|
19388
|
+
{
|
|
19389
|
+
id: "full-disk-access",
|
|
19390
|
+
label: "Full Disk Access",
|
|
19391
|
+
why: "Show the folders you pinned in Finder\u2019s sidebar.",
|
|
19392
|
+
icon: "FolderLock",
|
|
19393
|
+
category: "system",
|
|
19394
|
+
applicablePlatforms: ["darwin"],
|
|
19395
|
+
requestKind: "settings-only",
|
|
19396
|
+
requiresRelaunchAfterGrant: true,
|
|
19397
|
+
settingsHint: "Enable Tesseract under Full Disk Access, then relaunch."
|
|
19398
|
+
},
|
|
19399
|
+
{
|
|
19400
|
+
id: "notifications",
|
|
19401
|
+
label: "Notifications",
|
|
19402
|
+
why: "Tell you when a background task or agent turn finishes while you\u2019re away.",
|
|
19403
|
+
icon: "Bell",
|
|
19404
|
+
category: "notifications",
|
|
19405
|
+
applicablePlatforms: ["darwin", "win32", "linux"],
|
|
19406
|
+
requestKind: "in-app"
|
|
19407
|
+
},
|
|
19408
|
+
{
|
|
19409
|
+
id: "local-network",
|
|
19410
|
+
label: "Local Network",
|
|
19411
|
+
why: "Connect to your other machines on the same network.",
|
|
19412
|
+
icon: "Wifi",
|
|
19413
|
+
category: "network",
|
|
19414
|
+
applicablePlatforms: ["darwin"],
|
|
19415
|
+
requestKind: "info-only",
|
|
19416
|
+
settingsHint: "Managed by macOS \u2014 allow Tesseract under Local Network in System Settings."
|
|
19417
|
+
}
|
|
19418
|
+
];
|
|
19419
|
+
PERMISSION_BY_ID = Object.fromEntries(PERMISSIONS.map((p) => [p.id, p]));
|
|
19420
|
+
}
|
|
19421
|
+
});
|
|
19422
|
+
|
|
19353
19423
|
// ../../packages/shared/dist/ws-protocol.js
|
|
19354
19424
|
var MIRRORED_AGENT_EVENTS;
|
|
19355
19425
|
var init_ws_protocol = __esm({
|
|
@@ -20477,6 +20547,128 @@ var init_design_tokens = __esm({
|
|
|
20477
20547
|
}
|
|
20478
20548
|
});
|
|
20479
20549
|
|
|
20550
|
+
// ../../packages/shared/dist/file-langs.js
|
|
20551
|
+
function langForFilename(name) {
|
|
20552
|
+
const lower = name.toLowerCase();
|
|
20553
|
+
if (SPECIAL_NAMES[lower])
|
|
20554
|
+
return SPECIAL_NAMES[lower];
|
|
20555
|
+
const dot = lower.lastIndexOf(".");
|
|
20556
|
+
if (dot > 0) {
|
|
20557
|
+
const ext = lower.slice(dot + 1);
|
|
20558
|
+
if (EXT_TO_LANG[ext])
|
|
20559
|
+
return EXT_TO_LANG[ext];
|
|
20560
|
+
}
|
|
20561
|
+
return null;
|
|
20562
|
+
}
|
|
20563
|
+
var EXT_TO_LANG, SPECIAL_NAMES, STATS_HEAVY_DIRS, STATS_SKIP_FILES;
|
|
20564
|
+
var init_file_langs = __esm({
|
|
20565
|
+
"../../packages/shared/dist/file-langs.js"() {
|
|
20566
|
+
"use strict";
|
|
20567
|
+
EXT_TO_LANG = {
|
|
20568
|
+
ts: "TypeScript",
|
|
20569
|
+
tsx: "TypeScript",
|
|
20570
|
+
mts: "TypeScript",
|
|
20571
|
+
cts: "TypeScript",
|
|
20572
|
+
js: "JavaScript",
|
|
20573
|
+
jsx: "JavaScript",
|
|
20574
|
+
mjs: "JavaScript",
|
|
20575
|
+
cjs: "JavaScript",
|
|
20576
|
+
py: "Python",
|
|
20577
|
+
pyi: "Python",
|
|
20578
|
+
rb: "Ruby",
|
|
20579
|
+
go: "Go",
|
|
20580
|
+
rs: "Rust",
|
|
20581
|
+
java: "Java",
|
|
20582
|
+
kt: "Kotlin",
|
|
20583
|
+
kts: "Kotlin",
|
|
20584
|
+
c: "C",
|
|
20585
|
+
h: "C",
|
|
20586
|
+
cpp: "C++",
|
|
20587
|
+
cc: "C++",
|
|
20588
|
+
cxx: "C++",
|
|
20589
|
+
hpp: "C++",
|
|
20590
|
+
hh: "C++",
|
|
20591
|
+
cs: "C#",
|
|
20592
|
+
swift: "Swift",
|
|
20593
|
+
m: "Objective-C",
|
|
20594
|
+
mm: "Objective-C++",
|
|
20595
|
+
php: "PHP",
|
|
20596
|
+
dart: "Dart",
|
|
20597
|
+
scala: "Scala",
|
|
20598
|
+
sh: "Shell",
|
|
20599
|
+
bash: "Shell",
|
|
20600
|
+
zsh: "Shell",
|
|
20601
|
+
fish: "Shell",
|
|
20602
|
+
sql: "SQL",
|
|
20603
|
+
html: "HTML",
|
|
20604
|
+
htm: "HTML",
|
|
20605
|
+
css: "CSS",
|
|
20606
|
+
scss: "SCSS",
|
|
20607
|
+
sass: "Sass",
|
|
20608
|
+
less: "Less",
|
|
20609
|
+
vue: "Vue",
|
|
20610
|
+
svelte: "Svelte",
|
|
20611
|
+
json: "JSON",
|
|
20612
|
+
json5: "JSON",
|
|
20613
|
+
jsonc: "JSON",
|
|
20614
|
+
yaml: "YAML",
|
|
20615
|
+
yml: "YAML",
|
|
20616
|
+
toml: "TOML",
|
|
20617
|
+
ini: "INI",
|
|
20618
|
+
xml: "XML",
|
|
20619
|
+
md: "Markdown",
|
|
20620
|
+
markdown: "Markdown",
|
|
20621
|
+
mdx: "MDX",
|
|
20622
|
+
rst: "reStructuredText",
|
|
20623
|
+
tex: "TeX",
|
|
20624
|
+
r: "R",
|
|
20625
|
+
lua: "Lua",
|
|
20626
|
+
pl: "Perl",
|
|
20627
|
+
pm: "Perl",
|
|
20628
|
+
ex: "Elixir",
|
|
20629
|
+
exs: "Elixir",
|
|
20630
|
+
erl: "Erlang",
|
|
20631
|
+
clj: "Clojure",
|
|
20632
|
+
cljs: "Clojure",
|
|
20633
|
+
hs: "Haskell",
|
|
20634
|
+
proto: "Protobuf",
|
|
20635
|
+
gradle: "Gradle",
|
|
20636
|
+
groovy: "Groovy",
|
|
20637
|
+
ps1: "PowerShell",
|
|
20638
|
+
bat: "Batch",
|
|
20639
|
+
cmd: "Batch",
|
|
20640
|
+
vim: "Vim Script",
|
|
20641
|
+
graphql: "GraphQL",
|
|
20642
|
+
gql: "GraphQL"
|
|
20643
|
+
};
|
|
20644
|
+
SPECIAL_NAMES = {
|
|
20645
|
+
dockerfile: "Dockerfile",
|
|
20646
|
+
makefile: "Makefile",
|
|
20647
|
+
cmakelists: "CMake",
|
|
20648
|
+
rakefile: "Ruby",
|
|
20649
|
+
gemfile: "Ruby"
|
|
20650
|
+
};
|
|
20651
|
+
STATS_HEAVY_DIRS = /* @__PURE__ */ new Set([
|
|
20652
|
+
".git",
|
|
20653
|
+
"node_modules",
|
|
20654
|
+
"dist",
|
|
20655
|
+
"build",
|
|
20656
|
+
".next",
|
|
20657
|
+
".turbo",
|
|
20658
|
+
"out",
|
|
20659
|
+
"target",
|
|
20660
|
+
".cache",
|
|
20661
|
+
"coverage",
|
|
20662
|
+
".venv",
|
|
20663
|
+
"venv",
|
|
20664
|
+
"__pycache__",
|
|
20665
|
+
".gradle",
|
|
20666
|
+
"vendor"
|
|
20667
|
+
]);
|
|
20668
|
+
STATS_SKIP_FILES = /* @__PURE__ */ new Set([".DS_Store", "Thumbs.db"]);
|
|
20669
|
+
}
|
|
20670
|
+
});
|
|
20671
|
+
|
|
20480
20672
|
// ../../packages/shared/dist/snapshot.js
|
|
20481
20673
|
var SNAPSHOT_MAX_TEXT_BYTES, SNAPSHOT_MAX_BINARY_BYTES, SNAPSHOT_MAX_TOTAL_BYTES;
|
|
20482
20674
|
var init_snapshot = __esm({
|
|
@@ -20492,6 +20684,7 @@ var init_snapshot = __esm({
|
|
|
20492
20684
|
var init_dist2 = __esm({
|
|
20493
20685
|
"../../packages/shared/dist/index.js"() {
|
|
20494
20686
|
"use strict";
|
|
20687
|
+
init_permissions();
|
|
20495
20688
|
init_ws_protocol();
|
|
20496
20689
|
init_frame();
|
|
20497
20690
|
init_plans();
|
|
@@ -20505,6 +20698,7 @@ var init_dist2 = __esm({
|
|
|
20505
20698
|
init_rotation();
|
|
20506
20699
|
init_settings();
|
|
20507
20700
|
init_design_tokens();
|
|
20701
|
+
init_file_langs();
|
|
20508
20702
|
init_snapshot();
|
|
20509
20703
|
}
|
|
20510
20704
|
});
|
|
@@ -21269,7 +21463,7 @@ var init_canvas_tools = __esm({
|
|
|
21269
21463
|
});
|
|
21270
21464
|
|
|
21271
21465
|
// ../../packages/agent-core/dist/session-transfer.js
|
|
21272
|
-
import { homedir } from "node:os";
|
|
21466
|
+
import { homedir as homedir2 } from "node:os";
|
|
21273
21467
|
import { join as join6, dirname as dirname3, sep, resolve as resolve3 } from "node:path";
|
|
21274
21468
|
import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2, readdir } from "node:fs/promises";
|
|
21275
21469
|
import { existsSync } from "node:fs";
|
|
@@ -21289,7 +21483,7 @@ function sanitizeProjectDir(cwd) {
|
|
|
21289
21483
|
return s.length <= MAX_PROJECT_DIR_LEN ? s : `${s.slice(0, MAX_PROJECT_DIR_LEN)}-${cwdHash(cwd)}`;
|
|
21290
21484
|
}
|
|
21291
21485
|
function projectsRoot() {
|
|
21292
|
-
return join6(
|
|
21486
|
+
return join6(homedir2(), ".claude", "projects");
|
|
21293
21487
|
}
|
|
21294
21488
|
function sessionDir(cwd) {
|
|
21295
21489
|
return join6(projectsRoot(), sanitizeProjectDir(cwd));
|
|
@@ -21802,7 +21996,7 @@ ${input.command}
|
|
|
21802
21996
|
}
|
|
21803
21997
|
return blocks;
|
|
21804
21998
|
}
|
|
21805
|
-
var SEARCH_SCAN_LIMIT, SEARCH_RESULT_LIMIT, SEARCH_MAX_FILE_BYTES, SessionStore;
|
|
21999
|
+
var SEARCH_SCAN_LIMIT, SEARCH_RESULT_LIMIT, SEARCH_MAX_FILE_BYTES, FORK_RETRY_ATTEMPTS, FORK_RETRY_DELAY_MS, delay, SessionStore;
|
|
21806
22000
|
var init_session_store = __esm({
|
|
21807
22001
|
"../../packages/agent-core/dist/session-store.js"() {
|
|
21808
22002
|
"use strict";
|
|
@@ -21810,6 +22004,9 @@ var init_session_store = __esm({
|
|
|
21810
22004
|
SEARCH_SCAN_LIMIT = 200;
|
|
21811
22005
|
SEARCH_RESULT_LIMIT = 50;
|
|
21812
22006
|
SEARCH_MAX_FILE_BYTES = 8 * 1024 * 1024;
|
|
22007
|
+
FORK_RETRY_ATTEMPTS = 2;
|
|
22008
|
+
FORK_RETRY_DELAY_MS = 300;
|
|
22009
|
+
delay = (ms) => new Promise((resolve10) => setTimeout(resolve10, ms));
|
|
21813
22010
|
SessionStore = class {
|
|
21814
22011
|
/** List resumable sessions. When `projectDir` is given, only sessions whose
|
|
21815
22012
|
* cwd matches that project are returned, so each window's Resume list is
|
|
@@ -21890,16 +22087,20 @@ var init_session_store = __esm({
|
|
|
21890
22087
|
* Returns the new session id, or null on failure. NOTE: forked sessions start
|
|
21891
22088
|
* WITHOUT file-history/undo snapshots (SDK behavior). */
|
|
21892
22089
|
async forkSession(sessionId, opts) {
|
|
21893
|
-
|
|
21894
|
-
|
|
21895
|
-
|
|
21896
|
-
|
|
21897
|
-
|
|
21898
|
-
|
|
21899
|
-
|
|
21900
|
-
|
|
21901
|
-
|
|
21902
|
-
|
|
22090
|
+
for (let attempt = 0; ; attempt++) {
|
|
22091
|
+
try {
|
|
22092
|
+
const upToMessageId = opts?.upToMessageId ?? await this.lastResumableMessageId(sessionId, opts?.projectDir);
|
|
22093
|
+
const result = await forkSession(sessionId, {
|
|
22094
|
+
...opts?.projectDir ? { dir: opts.projectDir } : {},
|
|
22095
|
+
...upToMessageId ? { upToMessageId } : {},
|
|
22096
|
+
...opts?.title ? { title: opts.title } : {}
|
|
22097
|
+
});
|
|
22098
|
+
return result.sessionId;
|
|
22099
|
+
} catch {
|
|
22100
|
+
if (attempt >= FORK_RETRY_ATTEMPTS)
|
|
22101
|
+
return null;
|
|
22102
|
+
await delay(FORK_RETRY_DELAY_MS);
|
|
22103
|
+
}
|
|
21903
22104
|
}
|
|
21904
22105
|
}
|
|
21905
22106
|
/** The uuid of the last transcript message after which NO tool_use is left without
|
|
@@ -21965,7 +22166,7 @@ var init_log = __esm({
|
|
|
21965
22166
|
import { readFile as readFile5 } from "node:fs/promises";
|
|
21966
22167
|
import { execFile } from "node:child_process";
|
|
21967
22168
|
import { promisify } from "node:util";
|
|
21968
|
-
import { homedir as
|
|
22169
|
+
import { homedir as homedir3 } from "node:os";
|
|
21969
22170
|
import { join as join8 } from "node:path";
|
|
21970
22171
|
function tokenFromCredsBlob(raw) {
|
|
21971
22172
|
try {
|
|
@@ -21995,7 +22196,7 @@ async function resolveOauthToken(env) {
|
|
|
21995
22196
|
const ambientTok = process.env.CLAUDE_CODE_OAUTH_TOKEN?.trim();
|
|
21996
22197
|
if (ambientTok)
|
|
21997
22198
|
return ambientTok;
|
|
21998
|
-
const fromAmbientDir = await tokenFromConfigDir(process.env.CLAUDE_CONFIG_DIR ?? join8(
|
|
22199
|
+
const fromAmbientDir = await tokenFromConfigDir(process.env.CLAUDE_CONFIG_DIR ?? join8(homedir3(), ".claude"));
|
|
21999
22200
|
if (fromAmbientDir)
|
|
22000
22201
|
return fromAmbientDir;
|
|
22001
22202
|
if (process.platform === "darwin") {
|
|
@@ -22111,17 +22312,45 @@ var init_session_usage = __esm({
|
|
|
22111
22312
|
}
|
|
22112
22313
|
});
|
|
22113
22314
|
|
|
22315
|
+
// ../../packages/agent-core/dist/kill-tree.js
|
|
22316
|
+
import { spawn } from "node:child_process";
|
|
22317
|
+
function killProcessTree(child, signal = "SIGKILL") {
|
|
22318
|
+
if (!child || child.pid == null || child.exitCode !== null)
|
|
22319
|
+
return;
|
|
22320
|
+
const pid = child.pid;
|
|
22321
|
+
if (process.platform === "win32") {
|
|
22322
|
+
try {
|
|
22323
|
+
spawn("taskkill", ["/pid", String(pid), "/T", "/F"], { stdio: "ignore" });
|
|
22324
|
+
} catch {
|
|
22325
|
+
}
|
|
22326
|
+
return;
|
|
22327
|
+
}
|
|
22328
|
+
try {
|
|
22329
|
+
process.kill(-pid, signal);
|
|
22330
|
+
} catch {
|
|
22331
|
+
try {
|
|
22332
|
+
child.kill(signal);
|
|
22333
|
+
} catch {
|
|
22334
|
+
}
|
|
22335
|
+
}
|
|
22336
|
+
}
|
|
22337
|
+
var init_kill_tree = __esm({
|
|
22338
|
+
"../../packages/agent-core/dist/kill-tree.js"() {
|
|
22339
|
+
"use strict";
|
|
22340
|
+
}
|
|
22341
|
+
});
|
|
22342
|
+
|
|
22114
22343
|
// ../../packages/agent-core/dist/agent-manager.js
|
|
22115
22344
|
import { query, createSdkMcpServer } from "@anthropic-ai/claude-agent-sdk";
|
|
22116
22345
|
import { writeFile as writeFile4, mkdir as mkdir4, readFile as readFile6 } from "node:fs/promises";
|
|
22117
22346
|
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
22118
|
-
import { tmpdir, homedir as
|
|
22347
|
+
import { tmpdir, homedir as homedir4 } from "node:os";
|
|
22119
22348
|
import { join as join9, dirname as dirname5 } from "node:path";
|
|
22120
22349
|
import { existsSync as existsSync2, readFileSync as readFileSync3 } from "node:fs";
|
|
22121
22350
|
import { fileURLToPath } from "node:url";
|
|
22122
22351
|
import { createRequire } from "node:module";
|
|
22123
22352
|
import { createServer } from "node:net";
|
|
22124
|
-
import { spawn, execSync } from "node:child_process";
|
|
22353
|
+
import { spawn as spawn2, execSync } from "node:child_process";
|
|
22125
22354
|
function findClaudeCliJs() {
|
|
22126
22355
|
const require2 = createRequire(import.meta.url);
|
|
22127
22356
|
const sdkMain = require2.resolve("@anthropic-ai/claude-agent-sdk");
|
|
@@ -22294,9 +22523,9 @@ function sourcesEqual(a, b) {
|
|
|
22294
22523
|
if (a.length !== b.length)
|
|
22295
22524
|
return false;
|
|
22296
22525
|
const as = [...a].sort();
|
|
22297
|
-
const
|
|
22526
|
+
const bs2 = [...b].sort();
|
|
22298
22527
|
for (let i = 0; i < as.length; i++)
|
|
22299
|
-
if (as[i] !==
|
|
22528
|
+
if (as[i] !== bs2[i])
|
|
22300
22529
|
return false;
|
|
22301
22530
|
return true;
|
|
22302
22531
|
}
|
|
@@ -22328,7 +22557,7 @@ MOBILE (${fw}):
|
|
|
22328
22557
|
return "";
|
|
22329
22558
|
}
|
|
22330
22559
|
}
|
|
22331
|
-
var cachedNodeExe, HUMAN_READABLE_FIELDS, TOOL_RESULT_CAP, MAX_RESULT_IMAGES, MAX_IMAGE_BASE64, PERMISSION_MODES, EDIT_PREVIEW_CAP, AsyncMessagePump, IDLE_REAP_MS, MODERATE_IDLE_MS, STEER_COALESCE_MS, AgentManager;
|
|
22560
|
+
var cachedNodeExe, HUMAN_READABLE_FIELDS, TOOL_RESULT_CAP, MAX_RESULT_IMAGES, MAX_IMAGE_BASE64, PERMISSION_MODES, EDIT_PREVIEW_CAP, AsyncMessagePump, IDLE_REAP_MS, MODERATE_IDLE_MS, STEER_COALESCE_MS, STOP_HARD_KILL_AFTER_MS, AgentManager;
|
|
22332
22561
|
var init_agent_manager = __esm({
|
|
22333
22562
|
"../../packages/agent-core/dist/agent-manager.js"() {
|
|
22334
22563
|
"use strict";
|
|
@@ -22344,6 +22573,7 @@ var init_agent_manager = __esm({
|
|
|
22344
22573
|
init_trace_store();
|
|
22345
22574
|
init_session_store();
|
|
22346
22575
|
init_session_usage();
|
|
22576
|
+
init_kill_tree();
|
|
22347
22577
|
init_log();
|
|
22348
22578
|
HUMAN_READABLE_FIELDS = ["message", "text", "content", "summary", "description", "output", "stdout", "error", "reason", "name"];
|
|
22349
22579
|
TOOL_RESULT_CAP = 16e3;
|
|
@@ -22393,6 +22623,7 @@ var init_agent_manager = __esm({
|
|
|
22393
22623
|
IDLE_REAP_MS = 5 * 60 * 1e3;
|
|
22394
22624
|
MODERATE_IDLE_MS = 60 * 1e3;
|
|
22395
22625
|
STEER_COALESCE_MS = 500;
|
|
22626
|
+
STOP_HARD_KILL_AFTER_MS = 1200;
|
|
22396
22627
|
AgentManager = class {
|
|
22397
22628
|
send;
|
|
22398
22629
|
onBrowserRequest;
|
|
@@ -23145,7 +23376,7 @@ CRITICAL RULES:
|
|
|
23145
23376
|
// fall back to ~/.tesseract/canvas when the session isn't on disk yet.
|
|
23146
23377
|
canvasDir: async () => {
|
|
23147
23378
|
const dir = await sessionSidecarDir(live.sessionId, projectDir);
|
|
23148
|
-
return dir ? join9(dir, "canvas") : join9(process.env.CCB_TESSERACT_DIR ?? join9(
|
|
23379
|
+
return dir ? join9(dir, "canvas") : join9(process.env.CCB_TESSERACT_DIR ?? join9(homedir4(), ".tesseract"), "canvas");
|
|
23149
23380
|
},
|
|
23150
23381
|
emitCanvasReady: (p) => {
|
|
23151
23382
|
const ts = Date.now();
|
|
@@ -23298,12 +23529,18 @@ CRITICAL RULES:
|
|
|
23298
23529
|
const exe = nodeExe2 ?? process.execPath;
|
|
23299
23530
|
const runAsNode = exe === process.execPath && !!process.versions.electron;
|
|
23300
23531
|
const spawnEnv = { ...options.env, ...params.env ?? {}, ...params.engineEnv ?? {}, CCB_IPC_PORT: String(live.ipcPort), ...runAsNode ? { ELECTRON_RUN_AS_NODE: "1" } : {} };
|
|
23301
|
-
const child =
|
|
23532
|
+
const child = spawn2(exe, [cliJs, ...options.args], {
|
|
23302
23533
|
cwd: options.cwd,
|
|
23303
23534
|
env: spawnEnv,
|
|
23304
23535
|
signal: options.signal,
|
|
23305
|
-
stdio: ["pipe", "pipe", "pipe"]
|
|
23536
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
23537
|
+
// Own process group (POSIX) so Stop can SIGKILL the WHOLE tree — the
|
|
23538
|
+
// CLI re-execs node and its Bash tool spawns grandchildren (builds,
|
|
23539
|
+
// dev servers, sleeps) that a single-PID kill would orphan. Pipes keep
|
|
23540
|
+
// it attached; we never unref, so the parent still tracks it.
|
|
23541
|
+
detached: process.platform !== "win32"
|
|
23306
23542
|
});
|
|
23543
|
+
live.child = child;
|
|
23307
23544
|
return child;
|
|
23308
23545
|
},
|
|
23309
23546
|
// Claude Code preset + our browser instructions appended. The preset
|
|
@@ -23556,6 +23793,7 @@ CRITICAL RULES:
|
|
|
23556
23793
|
refreshSessionUsage(turnSid);
|
|
23557
23794
|
} else if (live.interrupted) {
|
|
23558
23795
|
live.interrupted = false;
|
|
23796
|
+
this.clearStopWatchdog(live);
|
|
23559
23797
|
log("Session interrupted by user:", turnSid);
|
|
23560
23798
|
emit({ type: "chat:error", sessionId: turnSid, error: "Interrupted." });
|
|
23561
23799
|
emit({ type: "agent:status", sessionId: turnSid, status: "idle" });
|
|
@@ -23779,6 +24017,7 @@ ${text3}` : header;
|
|
|
23779
24017
|
const sid = live.sessionId;
|
|
23780
24018
|
if (live.interrupted || live.abort.signal.aborted) {
|
|
23781
24019
|
live.interrupted = false;
|
|
24020
|
+
this.clearStopWatchdog(live);
|
|
23782
24021
|
log("Session interrupted by user (caught from iterator):", sid);
|
|
23783
24022
|
emit({ type: "chat:error", sessionId: sid, error: "Interrupted." });
|
|
23784
24023
|
emit({ type: "agent:status", sessionId: sid, status: "idle" });
|
|
@@ -23798,6 +24037,7 @@ ${err.stack}` : ""}` : String(err);
|
|
|
23798
24037
|
clearTimeout(live.steerFlushTimer);
|
|
23799
24038
|
live.steerFlushTimer = void 0;
|
|
23800
24039
|
}
|
|
24040
|
+
this.clearStopWatchdog(live);
|
|
23801
24041
|
if (live.sessionId)
|
|
23802
24042
|
this.liveSessions.delete(live.sessionId);
|
|
23803
24043
|
this.pendingLiveSessions.delete(live);
|
|
@@ -23805,6 +24045,8 @@ ${err.stack}` : ""}` : String(err);
|
|
|
23805
24045
|
live.query.close();
|
|
23806
24046
|
} catch {
|
|
23807
24047
|
}
|
|
24048
|
+
if (live.abort.signal.aborted)
|
|
24049
|
+
killProcessTree(live.child, "SIGKILL");
|
|
23808
24050
|
for (const s of live.ipcSockets) {
|
|
23809
24051
|
try {
|
|
23810
24052
|
s.destroy();
|
|
@@ -23832,28 +24074,54 @@ ${err.stack}` : ""}` : String(err);
|
|
|
23832
24074
|
live.pendingSteer = void 0;
|
|
23833
24075
|
live.interrupted = true;
|
|
23834
24076
|
live.query.interrupt().catch((err) => {
|
|
23835
|
-
log("interrupt() failed,
|
|
23836
|
-
|
|
23837
|
-
live.abort.abort();
|
|
23838
|
-
} catch {
|
|
23839
|
-
}
|
|
24077
|
+
log("interrupt() failed, force-killing process tree:", err instanceof Error ? err.message : String(err));
|
|
24078
|
+
this.hardKillSession(live);
|
|
23840
24079
|
});
|
|
24080
|
+
this.armStopWatchdog(live);
|
|
23841
24081
|
return true;
|
|
23842
24082
|
}
|
|
23843
24083
|
for (const pending of this.pendingLiveSessions) {
|
|
23844
24084
|
log("Interrupting pending session (no canonical id yet)");
|
|
23845
24085
|
pending.interrupted = true;
|
|
23846
|
-
pending.query.interrupt().catch(() =>
|
|
23847
|
-
|
|
23848
|
-
pending.abort.abort();
|
|
23849
|
-
} catch {
|
|
23850
|
-
}
|
|
23851
|
-
});
|
|
24086
|
+
pending.query.interrupt().catch(() => this.hardKillSession(pending));
|
|
24087
|
+
this.armStopWatchdog(pending);
|
|
23852
24088
|
return true;
|
|
23853
24089
|
}
|
|
23854
24090
|
log("No active session to interrupt for:", sessionId);
|
|
23855
24091
|
return false;
|
|
23856
24092
|
}
|
|
24093
|
+
/** Arm the Stop watchdog: if the graceful interrupt hasn't ended the turn
|
|
24094
|
+
* within STOP_HARD_KILL_AFTER_MS, force-kill the whole process tree. The
|
|
24095
|
+
* `interrupted` flag is reset to false when the interrupt is acknowledged
|
|
24096
|
+
* (result frame / caught iterator), so a timely graceful stop self-cancels. */
|
|
24097
|
+
armStopWatchdog(live) {
|
|
24098
|
+
if (live.killTimer)
|
|
24099
|
+
clearTimeout(live.killTimer);
|
|
24100
|
+
live.killTimer = setTimeout(() => {
|
|
24101
|
+
live.killTimer = void 0;
|
|
24102
|
+
if (live.interrupted && !live.reaped) {
|
|
24103
|
+
log("Stop watchdog: graceful interrupt stalled \u2014 force-killing process tree for", live.sessionId);
|
|
24104
|
+
this.hardKillSession(live);
|
|
24105
|
+
}
|
|
24106
|
+
}, STOP_HARD_KILL_AFTER_MS);
|
|
24107
|
+
}
|
|
24108
|
+
/** Cancel a pending Stop watchdog (graceful interrupt landed / session torn down). */
|
|
24109
|
+
clearStopWatchdog(live) {
|
|
24110
|
+
if (live.killTimer) {
|
|
24111
|
+
clearTimeout(live.killTimer);
|
|
24112
|
+
live.killTimer = void 0;
|
|
24113
|
+
}
|
|
24114
|
+
}
|
|
24115
|
+
/** Force-stop a session NOW: abort the SDK (so the consumer treats it as an
|
|
24116
|
+
* interrupt) and SIGKILL the CLI's entire process group, reaping any Bash /
|
|
24117
|
+
* tool / MCP grandchildren that a graceful stop left running. */
|
|
24118
|
+
hardKillSession(live) {
|
|
24119
|
+
try {
|
|
24120
|
+
live.abort.abort();
|
|
24121
|
+
} catch {
|
|
24122
|
+
}
|
|
24123
|
+
killProcessTree(live.child, "SIGKILL");
|
|
24124
|
+
}
|
|
23857
24125
|
/** M7 — stop ONE background task (vs interruptSession which aborts the whole
|
|
23858
24126
|
* turn). Driver-only, same origin guard. No-op if the session/task is gone. */
|
|
23859
24127
|
async stopTask(sessionId, taskId, callerOrigin) {
|
|
@@ -24042,6 +24310,7 @@ ${err.stack}` : ""}` : String(err);
|
|
|
24042
24310
|
clearTimeout(live.steerFlushTimer);
|
|
24043
24311
|
live.steerFlushTimer = void 0;
|
|
24044
24312
|
}
|
|
24313
|
+
this.clearStopWatchdog(live);
|
|
24045
24314
|
if (live.sessionId)
|
|
24046
24315
|
this.liveSessions.delete(live.sessionId);
|
|
24047
24316
|
this.pendingLiveSessions.delete(live);
|
|
@@ -24049,6 +24318,7 @@ ${err.stack}` : ""}` : String(err);
|
|
|
24049
24318
|
live.abort.abort();
|
|
24050
24319
|
} catch {
|
|
24051
24320
|
}
|
|
24321
|
+
killProcessTree(live.child, "SIGKILL");
|
|
24052
24322
|
live.pump.end();
|
|
24053
24323
|
}
|
|
24054
24324
|
}
|
|
@@ -24109,6 +24379,7 @@ __export(dist_exports, {
|
|
|
24109
24379
|
getTokenSegments: () => getTokenSegments,
|
|
24110
24380
|
importSession: () => importSession,
|
|
24111
24381
|
isToken: () => isToken,
|
|
24382
|
+
killProcessTree: () => killProcessTree,
|
|
24112
24383
|
log: () => log,
|
|
24113
24384
|
normalizeTokens: () => normalizeTokens,
|
|
24114
24385
|
parseColorToRgb: () => parseColorToRgb,
|
|
@@ -24137,6 +24408,7 @@ var init_dist4 = __esm({
|
|
|
24137
24408
|
init_session_usage();
|
|
24138
24409
|
init_session_transfer();
|
|
24139
24410
|
init_log();
|
|
24411
|
+
init_kill_tree();
|
|
24140
24412
|
init_session_artifacts();
|
|
24141
24413
|
init_imagegen_tools();
|
|
24142
24414
|
init_design_tokens2();
|
|
@@ -26501,7 +26773,7 @@ var require_build2 = __commonJS({
|
|
|
26501
26773
|
throw new Error("BigInt is not defined. Your environment doesn't implement BigInt.");
|
|
26502
26774
|
}
|
|
26503
26775
|
}
|
|
26504
|
-
function
|
|
26776
|
+
function concat2(buffers) {
|
|
26505
26777
|
let outputLength = 0;
|
|
26506
26778
|
let prevLength = 0;
|
|
26507
26779
|
for (let i = 0; i < buffers.length; i++) {
|
|
@@ -26547,7 +26819,7 @@ var require_build2 = __commonJS({
|
|
|
26547
26819
|
this.items.push(buf);
|
|
26548
26820
|
}
|
|
26549
26821
|
final() {
|
|
26550
|
-
return
|
|
26822
|
+
return concat2(this.items);
|
|
26551
26823
|
}
|
|
26552
26824
|
};
|
|
26553
26825
|
var powers2 = [new Uint8Array([1])];
|
|
@@ -28267,7 +28539,7 @@ ${values.join("\n")}` : `${blockName} :`;
|
|
|
28267
28539
|
}
|
|
28268
28540
|
retBuffers.push(valueBuf);
|
|
28269
28541
|
}
|
|
28270
|
-
return
|
|
28542
|
+
return concat2(retBuffers);
|
|
28271
28543
|
}
|
|
28272
28544
|
fromString(string4) {
|
|
28273
28545
|
this.value = [];
|
|
@@ -28496,7 +28768,7 @@ ${values.join("\n")}` : `${blockName} :`;
|
|
|
28496
28768
|
}
|
|
28497
28769
|
retBuffers.push(valueBuf);
|
|
28498
28770
|
}
|
|
28499
|
-
return
|
|
28771
|
+
return concat2(retBuffers);
|
|
28500
28772
|
}
|
|
28501
28773
|
fromString(string4) {
|
|
28502
28774
|
this.value = [];
|
|
@@ -29710,7 +29982,7 @@ var require_concat = __commonJS({
|
|
|
29710
29982
|
"use strict";
|
|
29711
29983
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29712
29984
|
exports.concatToUint8Array = concatToUint8Array;
|
|
29713
|
-
exports.concat =
|
|
29985
|
+
exports.concat = concat2;
|
|
29714
29986
|
var buffer_source_js_1 = require_buffer_source();
|
|
29715
29987
|
function concatToUint8Array(buffers) {
|
|
29716
29988
|
const views = [];
|
|
@@ -29728,7 +30000,7 @@ var require_concat = __commonJS({
|
|
|
29728
30000
|
}
|
|
29729
30001
|
return result;
|
|
29730
30002
|
}
|
|
29731
|
-
function
|
|
30003
|
+
function concat2(first, second, ...rest) {
|
|
29732
30004
|
let buffers;
|
|
29733
30005
|
let type;
|
|
29734
30006
|
if (typeof second === "function") {
|
|
@@ -36978,13 +37250,13 @@ var require_lazy_helpers = __commonJS({
|
|
|
36978
37250
|
}
|
|
36979
37251
|
};
|
|
36980
37252
|
exports.DelayedConstructor = DelayedConstructor;
|
|
36981
|
-
function
|
|
37253
|
+
function delay3(wrappedConstructor) {
|
|
36982
37254
|
if (typeof wrappedConstructor === "undefined") {
|
|
36983
37255
|
throw new Error("Attempt to `delay` undefined. Constructor must be wrapped in a callback");
|
|
36984
37256
|
}
|
|
36985
37257
|
return new DelayedConstructor(wrappedConstructor);
|
|
36986
37258
|
}
|
|
36987
|
-
exports.delay =
|
|
37259
|
+
exports.delay = delay3;
|
|
36988
37260
|
}
|
|
36989
37261
|
});
|
|
36990
37262
|
|
|
@@ -38896,11 +39168,11 @@ var require_x509_cjs = __commonJS({
|
|
|
38896
39168
|
var brainpoolP384t1 = "brainpoolP384t1";
|
|
38897
39169
|
var brainpoolP512r1 = "brainpoolP512r1";
|
|
38898
39170
|
var brainpoolP512t1 = "brainpoolP512t1";
|
|
38899
|
-
var
|
|
39171
|
+
var ECDSA2 = "ECDSA";
|
|
38900
39172
|
exports.EcAlgorithm = EcAlgorithm_1 = class EcAlgorithm {
|
|
38901
39173
|
toAsnAlgorithm(alg) {
|
|
38902
39174
|
switch (alg.name.toLowerCase()) {
|
|
38903
|
-
case
|
|
39175
|
+
case ECDSA2.toLowerCase():
|
|
38904
39176
|
if ("hash" in alg) {
|
|
38905
39177
|
const hash2 = typeof alg.hash === "string" ? alg.hash : alg.hash.name;
|
|
38906
39178
|
switch (hash2.toLowerCase()) {
|
|
@@ -38985,22 +39257,22 @@ var require_x509_cjs = __commonJS({
|
|
|
38985
39257
|
switch (alg.algorithm) {
|
|
38986
39258
|
case asn1Ecc__namespace.id_ecdsaWithSHA1:
|
|
38987
39259
|
return {
|
|
38988
|
-
name:
|
|
39260
|
+
name: ECDSA2,
|
|
38989
39261
|
hash: { name: "SHA-1" }
|
|
38990
39262
|
};
|
|
38991
39263
|
case asn1Ecc__namespace.id_ecdsaWithSHA256:
|
|
38992
39264
|
return {
|
|
38993
|
-
name:
|
|
39265
|
+
name: ECDSA2,
|
|
38994
39266
|
hash: { name: "SHA-256" }
|
|
38995
39267
|
};
|
|
38996
39268
|
case asn1Ecc__namespace.id_ecdsaWithSHA384:
|
|
38997
39269
|
return {
|
|
38998
|
-
name:
|
|
39270
|
+
name: ECDSA2,
|
|
38999
39271
|
hash: { name: "SHA-384" }
|
|
39000
39272
|
};
|
|
39001
39273
|
case asn1Ecc__namespace.id_ecdsaWithSHA512:
|
|
39002
39274
|
return {
|
|
39003
|
-
name:
|
|
39275
|
+
name: ECDSA2,
|
|
39004
39276
|
hash: { name: "SHA-512" }
|
|
39005
39277
|
};
|
|
39006
39278
|
case asn1Ecc__namespace.id_ecPublicKey: {
|
|
@@ -39011,92 +39283,92 @@ var require_x509_cjs = __commonJS({
|
|
|
39011
39283
|
switch (parameters.namedCurve) {
|
|
39012
39284
|
case asn1Ecc__namespace.id_secp256r1:
|
|
39013
39285
|
return {
|
|
39014
|
-
name:
|
|
39286
|
+
name: ECDSA2,
|
|
39015
39287
|
namedCurve: "P-256"
|
|
39016
39288
|
};
|
|
39017
39289
|
case EcAlgorithm_1.SECP256K1:
|
|
39018
39290
|
return {
|
|
39019
|
-
name:
|
|
39291
|
+
name: ECDSA2,
|
|
39020
39292
|
namedCurve: "K-256"
|
|
39021
39293
|
};
|
|
39022
39294
|
case asn1Ecc__namespace.id_secp384r1:
|
|
39023
39295
|
return {
|
|
39024
|
-
name:
|
|
39296
|
+
name: ECDSA2,
|
|
39025
39297
|
namedCurve: "P-384"
|
|
39026
39298
|
};
|
|
39027
39299
|
case asn1Ecc__namespace.id_secp521r1:
|
|
39028
39300
|
return {
|
|
39029
|
-
name:
|
|
39301
|
+
name: ECDSA2,
|
|
39030
39302
|
namedCurve: "P-521"
|
|
39031
39303
|
};
|
|
39032
39304
|
case idBrainpoolP160r1:
|
|
39033
39305
|
return {
|
|
39034
|
-
name:
|
|
39306
|
+
name: ECDSA2,
|
|
39035
39307
|
namedCurve: brainpoolP160r1
|
|
39036
39308
|
};
|
|
39037
39309
|
case idBrainpoolP160t1:
|
|
39038
39310
|
return {
|
|
39039
|
-
name:
|
|
39311
|
+
name: ECDSA2,
|
|
39040
39312
|
namedCurve: brainpoolP160t1
|
|
39041
39313
|
};
|
|
39042
39314
|
case idBrainpoolP192r1:
|
|
39043
39315
|
return {
|
|
39044
|
-
name:
|
|
39316
|
+
name: ECDSA2,
|
|
39045
39317
|
namedCurve: brainpoolP192r1
|
|
39046
39318
|
};
|
|
39047
39319
|
case idBrainpoolP192t1:
|
|
39048
39320
|
return {
|
|
39049
|
-
name:
|
|
39321
|
+
name: ECDSA2,
|
|
39050
39322
|
namedCurve: brainpoolP192t1
|
|
39051
39323
|
};
|
|
39052
39324
|
case idBrainpoolP224r1:
|
|
39053
39325
|
return {
|
|
39054
|
-
name:
|
|
39326
|
+
name: ECDSA2,
|
|
39055
39327
|
namedCurve: brainpoolP224r1
|
|
39056
39328
|
};
|
|
39057
39329
|
case idBrainpoolP224t1:
|
|
39058
39330
|
return {
|
|
39059
|
-
name:
|
|
39331
|
+
name: ECDSA2,
|
|
39060
39332
|
namedCurve: brainpoolP224t1
|
|
39061
39333
|
};
|
|
39062
39334
|
case idBrainpoolP256r1:
|
|
39063
39335
|
return {
|
|
39064
|
-
name:
|
|
39336
|
+
name: ECDSA2,
|
|
39065
39337
|
namedCurve: brainpoolP256r1
|
|
39066
39338
|
};
|
|
39067
39339
|
case idBrainpoolP256t1:
|
|
39068
39340
|
return {
|
|
39069
|
-
name:
|
|
39341
|
+
name: ECDSA2,
|
|
39070
39342
|
namedCurve: brainpoolP256t1
|
|
39071
39343
|
};
|
|
39072
39344
|
case idBrainpoolP320r1:
|
|
39073
39345
|
return {
|
|
39074
|
-
name:
|
|
39346
|
+
name: ECDSA2,
|
|
39075
39347
|
namedCurve: brainpoolP320r1
|
|
39076
39348
|
};
|
|
39077
39349
|
case idBrainpoolP320t1:
|
|
39078
39350
|
return {
|
|
39079
|
-
name:
|
|
39351
|
+
name: ECDSA2,
|
|
39080
39352
|
namedCurve: brainpoolP320t1
|
|
39081
39353
|
};
|
|
39082
39354
|
case idBrainpoolP384r1:
|
|
39083
39355
|
return {
|
|
39084
|
-
name:
|
|
39356
|
+
name: ECDSA2,
|
|
39085
39357
|
namedCurve: brainpoolP384r1
|
|
39086
39358
|
};
|
|
39087
39359
|
case idBrainpoolP384t1:
|
|
39088
39360
|
return {
|
|
39089
|
-
name:
|
|
39361
|
+
name: ECDSA2,
|
|
39090
39362
|
namedCurve: brainpoolP384t1
|
|
39091
39363
|
};
|
|
39092
39364
|
case idBrainpoolP512r1:
|
|
39093
39365
|
return {
|
|
39094
|
-
name:
|
|
39366
|
+
name: ECDSA2,
|
|
39095
39367
|
namedCurve: brainpoolP512r1
|
|
39096
39368
|
};
|
|
39097
39369
|
case idBrainpoolP512t1:
|
|
39098
39370
|
return {
|
|
39099
|
-
name:
|
|
39371
|
+
name: ECDSA2,
|
|
39100
39372
|
namedCurve: brainpoolP512t1
|
|
39101
39373
|
};
|
|
39102
39374
|
}
|
|
@@ -42479,6 +42751,95 @@ var require_selfsigned = __commonJS({
|
|
|
42479
42751
|
}
|
|
42480
42752
|
});
|
|
42481
42753
|
|
|
42754
|
+
// ../../packages/host-core/dist/peer-link/tls-direct.js
|
|
42755
|
+
import tls from "node:tls";
|
|
42756
|
+
import { X509Certificate, timingSafeEqual } from "node:crypto";
|
|
42757
|
+
import { readFileSync as readFileSync9, writeFileSync as writeFileSync9, existsSync as existsSync10, mkdirSync as mkdirSync8 } from "node:fs";
|
|
42758
|
+
import { dirname as dirname10 } from "node:path";
|
|
42759
|
+
function fingerprintOf(certPem) {
|
|
42760
|
+
return new X509Certificate(certPem).fingerprint256;
|
|
42761
|
+
}
|
|
42762
|
+
function fingerprintsEqual(a, b) {
|
|
42763
|
+
if (!a || !b)
|
|
42764
|
+
return false;
|
|
42765
|
+
const ba = Buffer.from(a);
|
|
42766
|
+
const bb = Buffer.from(b);
|
|
42767
|
+
return ba.length === bb.length && timingSafeEqual(ba, bb);
|
|
42768
|
+
}
|
|
42769
|
+
async function loadOrCreateIdentity(certPath, keyPath) {
|
|
42770
|
+
if (existsSync10(certPath) && existsSync10(keyPath)) {
|
|
42771
|
+
const cert = readFileSync9(certPath, "utf8");
|
|
42772
|
+
const key2 = readFileSync9(keyPath, "utf8");
|
|
42773
|
+
return { cert, key: key2, fingerprint256: fingerprintOf(cert) };
|
|
42774
|
+
}
|
|
42775
|
+
const tenYears = new Date(Date.now() + 10 * 365 * 24 * 60 * 60 * 1e3);
|
|
42776
|
+
const pems = await (0, import_selfsigned.generate)([{ name: "commonName", value: "tesseract-peer" }], { keySize: 2048, notAfterDate: tenYears, algorithm: "sha256" });
|
|
42777
|
+
try {
|
|
42778
|
+
mkdirSync8(dirname10(certPath), { recursive: true });
|
|
42779
|
+
} catch {
|
|
42780
|
+
}
|
|
42781
|
+
try {
|
|
42782
|
+
writeFileSync9(certPath, pems.cert, { mode: 384 });
|
|
42783
|
+
writeFileSync9(keyPath, pems.private, { mode: 384 });
|
|
42784
|
+
} catch {
|
|
42785
|
+
}
|
|
42786
|
+
return { cert: pems.cert, key: pems.private, fingerprint256: fingerprintOf(pems.cert) };
|
|
42787
|
+
}
|
|
42788
|
+
function pinnedConnect(opts) {
|
|
42789
|
+
return new Promise((resolve10, reject) => {
|
|
42790
|
+
let settled = false;
|
|
42791
|
+
const done = (err, sock) => {
|
|
42792
|
+
if (settled)
|
|
42793
|
+
return;
|
|
42794
|
+
settled = true;
|
|
42795
|
+
if (err) {
|
|
42796
|
+
try {
|
|
42797
|
+
sock?.destroy();
|
|
42798
|
+
} catch {
|
|
42799
|
+
}
|
|
42800
|
+
reject(err);
|
|
42801
|
+
} else
|
|
42802
|
+
resolve10(sock);
|
|
42803
|
+
};
|
|
42804
|
+
const socket2 = tls.connect({
|
|
42805
|
+
host: opts.host,
|
|
42806
|
+
port: opts.port,
|
|
42807
|
+
// No CA / hostname identity — we authenticate SOLELY by the brokered
|
|
42808
|
+
// fingerprint, so accept the self-signed cert at the TLS layer and verify
|
|
42809
|
+
// the pin ourselves below (fail-closed).
|
|
42810
|
+
rejectUnauthorized: false,
|
|
42811
|
+
checkServerIdentity: () => void 0,
|
|
42812
|
+
timeout: opts.timeoutMs ?? 8e3
|
|
42813
|
+
}, () => {
|
|
42814
|
+
const peer = socket2.getPeerCertificate();
|
|
42815
|
+
const fp = peer && peer.fingerprint256;
|
|
42816
|
+
if (!fingerprintsEqual(fp, opts.peerFingerprint256)) {
|
|
42817
|
+
done(new Error("peer-link: TLS fingerprint mismatch (fail-closed)"));
|
|
42818
|
+
return;
|
|
42819
|
+
}
|
|
42820
|
+
socket2.setNoDelay(true);
|
|
42821
|
+
done(void 0, socket2);
|
|
42822
|
+
});
|
|
42823
|
+
socket2.once("timeout", () => done(new Error("peer-link: TLS connect timeout")));
|
|
42824
|
+
socket2.once("error", (e) => done(e instanceof Error ? e : new Error(String(e))));
|
|
42825
|
+
});
|
|
42826
|
+
}
|
|
42827
|
+
function createListener(identity, port, onSocket) {
|
|
42828
|
+
const server = tls.createServer({ cert: identity.cert, key: identity.key, requestCert: false, rejectUnauthorized: false }, (sock) => {
|
|
42829
|
+
sock.setNoDelay(true);
|
|
42830
|
+
onSocket(sock);
|
|
42831
|
+
});
|
|
42832
|
+
server.listen(port, "0.0.0.0");
|
|
42833
|
+
return server;
|
|
42834
|
+
}
|
|
42835
|
+
var import_selfsigned;
|
|
42836
|
+
var init_tls_direct = __esm({
|
|
42837
|
+
"../../packages/host-core/dist/peer-link/tls-direct.js"() {
|
|
42838
|
+
"use strict";
|
|
42839
|
+
import_selfsigned = __toESM(require_selfsigned(), 1);
|
|
42840
|
+
}
|
|
42841
|
+
});
|
|
42842
|
+
|
|
42482
42843
|
// ../../packages/host-core/dist/peer-link/framing.js
|
|
42483
42844
|
function encodeFrame2(payload) {
|
|
42484
42845
|
const out = new Uint8Array(4 + payload.length);
|
|
@@ -42628,9 +42989,9 @@ var init_transport = __esm({
|
|
|
42628
42989
|
});
|
|
42629
42990
|
|
|
42630
42991
|
// ../../packages/host-core/dist/peer-link/stun.js
|
|
42631
|
-
import { randomBytes } from "node:crypto";
|
|
42992
|
+
import { randomBytes as randomBytes2 } from "node:crypto";
|
|
42632
42993
|
function encodeBindingRequest() {
|
|
42633
|
-
const txId =
|
|
42994
|
+
const txId = randomBytes2(12);
|
|
42634
42995
|
const packet = Buffer.alloc(20);
|
|
42635
42996
|
packet.writeUInt16BE(BINDING_REQUEST, 0);
|
|
42636
42997
|
packet.writeUInt16BE(0, 2);
|
|
@@ -42766,11 +43127,11 @@ function quicByteSocket(stream) {
|
|
|
42766
43127
|
}
|
|
42767
43128
|
function pinningVerify(expectedFingerprint256) {
|
|
42768
43129
|
return async (certs) => {
|
|
42769
|
-
const { X509Certificate: X509Certificate2 } = await import("node:crypto");
|
|
42770
43130
|
if (!certs.length)
|
|
42771
43131
|
throw new Error("quic: peer presented no certificate");
|
|
43132
|
+
const { X509Certificate: X509Certificate2 } = await import("node:crypto");
|
|
42772
43133
|
const fp = new X509Certificate2(Buffer.from(certs[0])).fingerprint256;
|
|
42773
|
-
if (fp
|
|
43134
|
+
if (!fingerprintsEqual(fp, expectedFingerprint256))
|
|
42774
43135
|
throw new Error("quic: TLS fingerprint mismatch (fail-closed)");
|
|
42775
43136
|
return void 0;
|
|
42776
43137
|
};
|
|
@@ -42852,6 +43213,7 @@ var init_quic_transport = __esm({
|
|
|
42852
43213
|
"use strict";
|
|
42853
43214
|
init_transport();
|
|
42854
43215
|
init_stun();
|
|
43216
|
+
init_tls_direct();
|
|
42855
43217
|
ALPN = ["tesseract-peer"];
|
|
42856
43218
|
quicModP = null;
|
|
42857
43219
|
loadQuic = () => quicModP ??= import("@matrixai/quic");
|
|
@@ -43934,17 +44296,17 @@ var require_main = __commonJS({
|
|
|
43934
44296
|
});
|
|
43935
44297
|
|
|
43936
44298
|
// src/cli.ts
|
|
43937
|
-
import { readFileSync as
|
|
44299
|
+
import { readFileSync as readFileSync15 } from "node:fs";
|
|
43938
44300
|
import { createHash as createHash5 } from "node:crypto";
|
|
43939
|
-
import { spawn as
|
|
44301
|
+
import { spawn as spawn8 } from "node:child_process";
|
|
43940
44302
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
43941
|
-
import { homedir as
|
|
43942
|
-
import { dirname as dirname18, join as
|
|
44303
|
+
import { homedir as homedir16 } from "node:os";
|
|
44304
|
+
import { dirname as dirname18, join as join38, resolve as resolve9 } from "node:path";
|
|
43943
44305
|
import { createInterface } from "node:readline/promises";
|
|
43944
44306
|
|
|
43945
44307
|
// ../../packages/host-core/dist/control-link.js
|
|
43946
|
-
import { hostname as hostname3
|
|
43947
|
-
import { join as
|
|
44308
|
+
import { hostname as hostname3 } from "node:os";
|
|
44309
|
+
import { join as join30, basename as basename7 } from "node:path";
|
|
43948
44310
|
import { mkdirSync as mkdirSync11 } from "node:fs";
|
|
43949
44311
|
|
|
43950
44312
|
// ../../node_modules/ws/wrapper.mjs
|
|
@@ -43960,6 +44322,7 @@ var wrapper_default = import_websocket.default;
|
|
|
43960
44322
|
|
|
43961
44323
|
// ../../packages/host-core/dist/path-guard.js
|
|
43962
44324
|
import { realpathSync } from "node:fs";
|
|
44325
|
+
import { homedir } from "node:os";
|
|
43963
44326
|
import { basename, dirname, isAbsolute, join, relative, resolve } from "node:path";
|
|
43964
44327
|
function canonicalForGuard(p) {
|
|
43965
44328
|
const abs = resolve(p);
|
|
@@ -43990,6 +44353,380 @@ function isInsideRoot(p, root) {
|
|
|
43990
44353
|
const rel = relative(realRoot, canonicalForGuard(p));
|
|
43991
44354
|
return rel === "" || !rel.startsWith("..") && !isAbsolute(rel);
|
|
43992
44355
|
}
|
|
44356
|
+
function safeResolve(p, root) {
|
|
44357
|
+
if (!root)
|
|
44358
|
+
return null;
|
|
44359
|
+
try {
|
|
44360
|
+
const real = realpathSync.native(p);
|
|
44361
|
+
return isInsideRoot(real, root) ? real : null;
|
|
44362
|
+
} catch {
|
|
44363
|
+
return isInsideRoot(p, root) ? p : null;
|
|
44364
|
+
}
|
|
44365
|
+
}
|
|
44366
|
+
function resolveContainmentRoot() {
|
|
44367
|
+
const env = process.env.CCB_HOST_ROOT;
|
|
44368
|
+
return env && env.trim() ? env : null;
|
|
44369
|
+
}
|
|
44370
|
+
function resolveHostRoot() {
|
|
44371
|
+
return resolveContainmentRoot() ?? homedir();
|
|
44372
|
+
}
|
|
44373
|
+
|
|
44374
|
+
// ../../packages/e2e/dist/suite.js
|
|
44375
|
+
var subtle = globalThis.crypto.subtle;
|
|
44376
|
+
var bs = (b) => b;
|
|
44377
|
+
var SUITE = "TSE1";
|
|
44378
|
+
var EC = { name: "ECDH", namedCurve: "P-256" };
|
|
44379
|
+
var ECDSA = { name: "ECDSA", namedCurve: "P-256" };
|
|
44380
|
+
function randomBytes(n) {
|
|
44381
|
+
return globalThis.crypto.getRandomValues(new Uint8Array(n));
|
|
44382
|
+
}
|
|
44383
|
+
function b64u(bytes) {
|
|
44384
|
+
let s = "";
|
|
44385
|
+
for (const b of bytes)
|
|
44386
|
+
s += String.fromCharCode(b);
|
|
44387
|
+
return btoa(s).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
44388
|
+
}
|
|
44389
|
+
function unb64u(s) {
|
|
44390
|
+
const pad = s.length % 4 === 0 ? "" : "=".repeat(4 - s.length % 4);
|
|
44391
|
+
const b = atob(s.replace(/-/g, "+").replace(/_/g, "/") + pad);
|
|
44392
|
+
const out = new Uint8Array(b.length);
|
|
44393
|
+
for (let i = 0; i < b.length; i++)
|
|
44394
|
+
out[i] = b.charCodeAt(i);
|
|
44395
|
+
return out;
|
|
44396
|
+
}
|
|
44397
|
+
function utf8(s) {
|
|
44398
|
+
return new TextEncoder().encode(s);
|
|
44399
|
+
}
|
|
44400
|
+
function concat(...parts) {
|
|
44401
|
+
const len = parts.reduce((n, p) => n + p.length, 0);
|
|
44402
|
+
const out = new Uint8Array(len);
|
|
44403
|
+
let o = 0;
|
|
44404
|
+
for (const p of parts) {
|
|
44405
|
+
out.set(p, o);
|
|
44406
|
+
o += p.length;
|
|
44407
|
+
}
|
|
44408
|
+
return out;
|
|
44409
|
+
}
|
|
44410
|
+
async function generateIdentityKeyPair(extractable = false) {
|
|
44411
|
+
const kp = await subtle.generateKey(ECDSA, extractable, ["sign", "verify"]);
|
|
44412
|
+
const publicRaw = new Uint8Array(await subtle.exportKey("raw", kp.publicKey));
|
|
44413
|
+
return { privateKey: kp.privateKey, publicKey: kp.publicKey, publicRaw };
|
|
44414
|
+
}
|
|
44415
|
+
async function importIdentityPublic(raw) {
|
|
44416
|
+
return subtle.importKey("raw", bs(raw), ECDSA, true, ["verify"]);
|
|
44417
|
+
}
|
|
44418
|
+
async function exportIdentity(kp) {
|
|
44419
|
+
const privateJwk = await subtle.exportKey("jwk", kp.privateKey);
|
|
44420
|
+
return { privateJwk, publicRaw: b64u(kp.publicRaw) };
|
|
44421
|
+
}
|
|
44422
|
+
async function importIdentity(privateJwk) {
|
|
44423
|
+
const privateKey = await subtle.importKey("jwk", privateJwk, ECDSA, true, ["sign"]);
|
|
44424
|
+
const pubJwk = { kty: privateJwk.kty, crv: privateJwk.crv, x: privateJwk.x, y: privateJwk.y, ext: true };
|
|
44425
|
+
const publicKey = await subtle.importKey("jwk", pubJwk, ECDSA, true, ["verify"]);
|
|
44426
|
+
const publicRaw = new Uint8Array(await subtle.exportKey("raw", publicKey));
|
|
44427
|
+
return { privateKey, publicKey, publicRaw };
|
|
44428
|
+
}
|
|
44429
|
+
async function sign(privateKey, message) {
|
|
44430
|
+
return new Uint8Array(await subtle.sign({ name: "ECDSA", hash: "SHA-256" }, privateKey, bs(message)));
|
|
44431
|
+
}
|
|
44432
|
+
async function verify(publicRaw, message, sig) {
|
|
44433
|
+
try {
|
|
44434
|
+
const key2 = await importIdentityPublic(publicRaw);
|
|
44435
|
+
return await subtle.verify({ name: "ECDSA", hash: "SHA-256" }, key2, bs(sig), bs(message));
|
|
44436
|
+
} catch {
|
|
44437
|
+
return false;
|
|
44438
|
+
}
|
|
44439
|
+
}
|
|
44440
|
+
async function generateEphemeral() {
|
|
44441
|
+
const kp = await subtle.generateKey(EC, false, ["deriveBits"]);
|
|
44442
|
+
const publicRaw = new Uint8Array(await subtle.exportKey("raw", kp.publicKey));
|
|
44443
|
+
return { privateKey: kp.privateKey, publicRaw };
|
|
44444
|
+
}
|
|
44445
|
+
async function ecdh(privateKey, peerPublicRaw) {
|
|
44446
|
+
const peer = await subtle.importKey("raw", bs(peerPublicRaw), EC, false, []);
|
|
44447
|
+
return new Uint8Array(await subtle.deriveBits({ name: "ECDH", public: peer }, privateKey, 256));
|
|
44448
|
+
}
|
|
44449
|
+
async function hkdf(ikm, salt, info, bytes) {
|
|
44450
|
+
const key2 = await subtle.importKey("raw", bs(ikm), "HKDF", false, ["deriveBits"]);
|
|
44451
|
+
return new Uint8Array(await subtle.deriveBits({ name: "HKDF", hash: "SHA-256", salt: bs(salt), info: bs(info) }, key2, bytes * 8));
|
|
44452
|
+
}
|
|
44453
|
+
async function importAesKey(raw) {
|
|
44454
|
+
return subtle.importKey("raw", bs(raw), { name: "AES-GCM" }, false, ["encrypt", "decrypt"]);
|
|
44455
|
+
}
|
|
44456
|
+
async function aesSeal(key2, nonce2, plaintext, aad) {
|
|
44457
|
+
return new Uint8Array(await subtle.encrypt({ name: "AES-GCM", iv: bs(nonce2), additionalData: bs(aad) }, key2, bs(plaintext)));
|
|
44458
|
+
}
|
|
44459
|
+
async function aesOpen(key2, nonce2, ciphertext, aad) {
|
|
44460
|
+
return new Uint8Array(await subtle.decrypt({ name: "AES-GCM", iv: bs(nonce2), additionalData: bs(aad) }, key2, bs(ciphertext)));
|
|
44461
|
+
}
|
|
44462
|
+
async function sha256(bytes) {
|
|
44463
|
+
return new Uint8Array(await subtle.digest("SHA-256", bs(bytes)));
|
|
44464
|
+
}
|
|
44465
|
+
|
|
44466
|
+
// ../../packages/e2e/dist/record.js
|
|
44467
|
+
function canonicalAad(r, dir, seq) {
|
|
44468
|
+
return utf8(JSON.stringify([1, SUITE, r.ch, r.host, r.client, r.sess, r.bid ?? "", dir, seq]));
|
|
44469
|
+
}
|
|
44470
|
+
function nonce(prefix, seq) {
|
|
44471
|
+
const n = new Uint8Array(12);
|
|
44472
|
+
n.set(prefix.subarray(0, 4), 0);
|
|
44473
|
+
const dv = new DataView(n.buffer);
|
|
44474
|
+
dv.setUint32(4, Math.floor(seq / 4294967296));
|
|
44475
|
+
dv.setUint32(8, seq >>> 0);
|
|
44476
|
+
return n;
|
|
44477
|
+
}
|
|
44478
|
+
var WINDOW = 1024;
|
|
44479
|
+
var ReplayWindow = class {
|
|
44480
|
+
highest = -1;
|
|
44481
|
+
seen = /* @__PURE__ */ new Set();
|
|
44482
|
+
permits(seq) {
|
|
44483
|
+
if (seq < 0)
|
|
44484
|
+
return false;
|
|
44485
|
+
if (seq <= this.highest - WINDOW)
|
|
44486
|
+
return false;
|
|
44487
|
+
return !this.seen.has(seq);
|
|
44488
|
+
}
|
|
44489
|
+
accept(seq) {
|
|
44490
|
+
this.seen.add(seq);
|
|
44491
|
+
if (seq > this.highest)
|
|
44492
|
+
this.highest = seq;
|
|
44493
|
+
for (const v of this.seen)
|
|
44494
|
+
if (v <= this.highest - WINDOW)
|
|
44495
|
+
this.seen.delete(v);
|
|
44496
|
+
}
|
|
44497
|
+
};
|
|
44498
|
+
var Sealer = class {
|
|
44499
|
+
key;
|
|
44500
|
+
prefix;
|
|
44501
|
+
dir;
|
|
44502
|
+
seq = 0;
|
|
44503
|
+
constructor(key2, prefix, dir) {
|
|
44504
|
+
this.key = key2;
|
|
44505
|
+
this.prefix = prefix;
|
|
44506
|
+
this.dir = dir;
|
|
44507
|
+
}
|
|
44508
|
+
async seal(routing, plaintext) {
|
|
44509
|
+
const s = this.seq++;
|
|
44510
|
+
const ct = await aesSeal(this.key, nonce(this.prefix, s), plaintext, canonicalAad(routing, this.dir, s));
|
|
44511
|
+
return { e2e: 1, s, ct: b64u(ct) };
|
|
44512
|
+
}
|
|
44513
|
+
async sealJson(routing, payload) {
|
|
44514
|
+
return this.seal(routing, utf8(JSON.stringify(payload)));
|
|
44515
|
+
}
|
|
44516
|
+
};
|
|
44517
|
+
var Opener = class {
|
|
44518
|
+
key;
|
|
44519
|
+
prefix;
|
|
44520
|
+
dir;
|
|
44521
|
+
window = new ReplayWindow();
|
|
44522
|
+
constructor(key2, prefix, dir) {
|
|
44523
|
+
this.key = key2;
|
|
44524
|
+
this.prefix = prefix;
|
|
44525
|
+
this.dir = dir;
|
|
44526
|
+
}
|
|
44527
|
+
async open(rec, routing) {
|
|
44528
|
+
if (!this.window.permits(rec.s))
|
|
44529
|
+
throw new Error("e2e: replayed or out-of-window sequence");
|
|
44530
|
+
const pt = await aesOpen(this.key, nonce(this.prefix, rec.s), unb64u(rec.ct), canonicalAad(routing, this.dir, rec.s));
|
|
44531
|
+
this.window.accept(rec.s);
|
|
44532
|
+
return pt;
|
|
44533
|
+
}
|
|
44534
|
+
async openJson(rec, routing) {
|
|
44535
|
+
return JSON.parse(new TextDecoder().decode(await this.open(rec, routing)));
|
|
44536
|
+
}
|
|
44537
|
+
};
|
|
44538
|
+
|
|
44539
|
+
// ../../packages/e2e/dist/handshake.js
|
|
44540
|
+
var HELLO_CAPS_DEFAULT = ["fs", "term", "agent"];
|
|
44541
|
+
async function helloTranscript(h) {
|
|
44542
|
+
return sha256(concat(utf8(`${h.suite}|${h.sess}|${h.from}|${h.to}|`), unb64u(h.eD), unb64u(h.nD), utf8(`|${h.caps.join(",")}`)));
|
|
44543
|
+
}
|
|
44544
|
+
function signedBytes(th, eD, eH, nD, nH, signer, tag) {
|
|
44545
|
+
return concat(th, unb64u(eD), unb64u(eH), unb64u(nD), unb64u(nH), utf8(`|${signer}|${tag}`));
|
|
44546
|
+
}
|
|
44547
|
+
async function deriveSession(rk, role, peerDeviceId, caps) {
|
|
44548
|
+
const kD2H = await importAesKey(await hkdf(rk, new Uint8Array(0), utf8("d2h key"), 32));
|
|
44549
|
+
const kH2D = await importAesKey(await hkdf(rk, new Uint8Array(0), utf8("h2d key"), 32));
|
|
44550
|
+
const pD2H = await hkdf(rk, new Uint8Array(0), utf8("d2h nonce"), 4);
|
|
44551
|
+
const pH2D = await hkdf(rk, new Uint8Array(0), utf8("h2d nonce"), 4);
|
|
44552
|
+
return role === "initiator" ? { sealer: new Sealer(kD2H, pD2H, "d2h"), opener: new Opener(kH2D, pH2D, "h2d"), peerDeviceId, caps } : { sealer: new Sealer(kH2D, pH2D, "h2d"), opener: new Opener(kD2H, pD2H, "d2h"), peerDeviceId, caps };
|
|
44553
|
+
}
|
|
44554
|
+
async function confirmKey(rk) {
|
|
44555
|
+
return importAesKey(await hkdf(rk, new Uint8Array(0), utf8("confirm"), 32));
|
|
44556
|
+
}
|
|
44557
|
+
var ZERO_NONCE = new Uint8Array(12);
|
|
44558
|
+
async function startHandshake(opts) {
|
|
44559
|
+
const eph = await generateEphemeral();
|
|
44560
|
+
const hello = {
|
|
44561
|
+
t: "e2e:hello",
|
|
44562
|
+
v: 1,
|
|
44563
|
+
suite: SUITE,
|
|
44564
|
+
sess: opts.sess,
|
|
44565
|
+
from: opts.selfDeviceId,
|
|
44566
|
+
to: opts.peerDeviceId,
|
|
44567
|
+
eD: b64u(eph.publicRaw),
|
|
44568
|
+
nD: b64u(randomBytes(16)),
|
|
44569
|
+
caps: opts.caps ?? HELLO_CAPS_DEFAULT
|
|
44570
|
+
};
|
|
44571
|
+
return { hello, state: { hello, eph, identityPriv: opts.identityPriv, selfDeviceId: opts.selfDeviceId } };
|
|
44572
|
+
}
|
|
44573
|
+
async function finishInitiator(state, resp, peerIdentityPub) {
|
|
44574
|
+
const th = await helloTranscript(state.hello);
|
|
44575
|
+
const okH = await verify(peerIdentityPub, signedBytes(th, state.hello.eD, resp.eH, state.hello.nD, resp.nH, resp.from, "resp"), unb64u(resp.sigH));
|
|
44576
|
+
if (!okH)
|
|
44577
|
+
throw new Error("e2e: host identity signature did not verify");
|
|
44578
|
+
const z2 = await ecdh(state.eph.privateKey, unb64u(resp.eH));
|
|
44579
|
+
const rk = await hkdf(z2, concat(unb64u(state.hello.nD), unb64u(resp.nH)), th, 32);
|
|
44580
|
+
const sigD = await sign(state.identityPriv, signedBytes(th, state.hello.eD, resp.eH, state.hello.nD, resp.nH, state.selfDeviceId, "confirm"));
|
|
44581
|
+
const fin = await aesSeal(await confirmKey(rk), ZERO_NONCE, new Uint8Array(0), th);
|
|
44582
|
+
const confirm2 = { t: "e2e:confirm", v: 1, sess: state.hello.sess, from: state.selfDeviceId, to: resp.from, sigD: b64u(sigD), fin: b64u(fin) };
|
|
44583
|
+
const session = await deriveSession(rk, "initiator", resp.from, state.hello.caps);
|
|
44584
|
+
return { confirm: confirm2, session };
|
|
44585
|
+
}
|
|
44586
|
+
async function respondHandshake(opts) {
|
|
44587
|
+
const { hello } = opts;
|
|
44588
|
+
if (hello.to !== opts.selfDeviceId)
|
|
44589
|
+
throw new Error("e2e: hello addressed to a different device");
|
|
44590
|
+
const th = await helloTranscript(hello);
|
|
44591
|
+
const eph = await generateEphemeral();
|
|
44592
|
+
const nH = b64u(randomBytes(16));
|
|
44593
|
+
const sigH = await sign(opts.identityPriv, signedBytes(th, hello.eD, b64u(eph.publicRaw), hello.nD, nH, opts.selfDeviceId, "resp"));
|
|
44594
|
+
const resp = { t: "e2e:resp", v: 1, sess: hello.sess, from: opts.selfDeviceId, to: hello.from, eH: b64u(eph.publicRaw), nH, sigH: b64u(sigH) };
|
|
44595
|
+
const z2 = await ecdh(eph.privateKey, unb64u(hello.eD));
|
|
44596
|
+
const rk = await hkdf(z2, concat(unb64u(hello.nD), unb64u(nH)), th, 32);
|
|
44597
|
+
return { resp, pending: { hello, th, rk, resp, selfDeviceId: opts.selfDeviceId } };
|
|
44598
|
+
}
|
|
44599
|
+
async function finishResponder(pending, confirm2, peerIdentityPub) {
|
|
44600
|
+
const okD = await verify(peerIdentityPub, signedBytes(pending.th, pending.hello.eD, pending.resp.eH, pending.hello.nD, pending.resp.nH, confirm2.from, "confirm"), unb64u(confirm2.sigD));
|
|
44601
|
+
if (!okD)
|
|
44602
|
+
throw new Error("e2e: driver identity signature did not verify");
|
|
44603
|
+
try {
|
|
44604
|
+
await aesOpen(await confirmKey(pending.rk), ZERO_NONCE, unb64u(confirm2.fin), pending.th);
|
|
44605
|
+
} catch {
|
|
44606
|
+
throw new Error("e2e: key confirmation failed");
|
|
44607
|
+
}
|
|
44608
|
+
return deriveSession(pending.rk, "responder", confirm2.from, pending.hello.caps);
|
|
44609
|
+
}
|
|
44610
|
+
|
|
44611
|
+
// ../../packages/e2e/dist/session.js
|
|
44612
|
+
var E2ESessions = class {
|
|
44613
|
+
cfg;
|
|
44614
|
+
map = /* @__PURE__ */ new Map();
|
|
44615
|
+
constructor(cfg2) {
|
|
44616
|
+
this.cfg = cfg2;
|
|
44617
|
+
}
|
|
44618
|
+
has(sessionId) {
|
|
44619
|
+
return !!this.map.get(sessionId)?.session;
|
|
44620
|
+
}
|
|
44621
|
+
getSession(sessionId) {
|
|
44622
|
+
return this.map.get(sessionId)?.session;
|
|
44623
|
+
}
|
|
44624
|
+
/** Tear a session down (decrypt failure / driver gone). Rejects a pending handshake. */
|
|
44625
|
+
drop(sessionId) {
|
|
44626
|
+
const e = this.map.get(sessionId);
|
|
44627
|
+
if (e?.reject && !e.session)
|
|
44628
|
+
e.reject(new Error("e2e: session dropped"));
|
|
44629
|
+
this.map.delete(sessionId);
|
|
44630
|
+
}
|
|
44631
|
+
/** Initiator: ensure an established session for `sessionId` to `peerDeviceId`. Idempotent —
|
|
44632
|
+
* concurrent callers share the in-flight handshake. */
|
|
44633
|
+
async ensure(sessionId, peerDeviceId) {
|
|
44634
|
+
const existing = this.map.get(sessionId);
|
|
44635
|
+
if (existing?.session)
|
|
44636
|
+
return existing.session;
|
|
44637
|
+
if (existing?.ready)
|
|
44638
|
+
return existing.ready;
|
|
44639
|
+
const entry = {};
|
|
44640
|
+
entry.ready = new Promise((res, rej) => {
|
|
44641
|
+
entry.resolve = res;
|
|
44642
|
+
entry.reject = rej;
|
|
44643
|
+
});
|
|
44644
|
+
this.map.set(sessionId, entry);
|
|
44645
|
+
try {
|
|
44646
|
+
const { hello, state } = await startHandshake({
|
|
44647
|
+
sess: sessionId,
|
|
44648
|
+
selfDeviceId: this.cfg.selfDeviceId,
|
|
44649
|
+
peerDeviceId,
|
|
44650
|
+
identityPriv: this.cfg.identityPriv,
|
|
44651
|
+
caps: this.cfg.caps
|
|
44652
|
+
});
|
|
44653
|
+
entry.initState = state;
|
|
44654
|
+
this.cfg.sendHandshake(hello);
|
|
44655
|
+
} catch (e) {
|
|
44656
|
+
entry.reject?.(e);
|
|
44657
|
+
this.map.delete(sessionId);
|
|
44658
|
+
}
|
|
44659
|
+
return entry.ready;
|
|
44660
|
+
}
|
|
44661
|
+
/** Feed an inbound handshake frame. Throws never (handshake failure rejects the
|
|
44662
|
+
* pending ensure() / drops the responder entry — callers fall back to plaintext or
|
|
44663
|
+
* surface the error). */
|
|
44664
|
+
async onHandshake(msg) {
|
|
44665
|
+
try {
|
|
44666
|
+
if (msg.t === "e2e:hello")
|
|
44667
|
+
return await this.onHello(msg);
|
|
44668
|
+
if (msg.t === "e2e:resp")
|
|
44669
|
+
return await this.onResp(msg);
|
|
44670
|
+
if (msg.t === "e2e:confirm")
|
|
44671
|
+
return await this.onConfirm(msg);
|
|
44672
|
+
} catch {
|
|
44673
|
+
}
|
|
44674
|
+
}
|
|
44675
|
+
async onHello(hello) {
|
|
44676
|
+
const { resp, pending } = await respondHandshake({ hello, selfDeviceId: this.cfg.selfDeviceId, identityPriv: this.cfg.identityPriv });
|
|
44677
|
+
this.map.set(hello.sess, { pending });
|
|
44678
|
+
this.cfg.sendHandshake(resp);
|
|
44679
|
+
}
|
|
44680
|
+
async onResp(resp) {
|
|
44681
|
+
const entry = this.map.get(resp.sess);
|
|
44682
|
+
if (!entry?.initState)
|
|
44683
|
+
return;
|
|
44684
|
+
const peerPub = await this.cfg.getPeerIdentityPub(resp.from);
|
|
44685
|
+
if (!peerPub) {
|
|
44686
|
+
entry.reject?.(new Error("e2e: no identity key for peer " + resp.from));
|
|
44687
|
+
this.map.delete(resp.sess);
|
|
44688
|
+
return;
|
|
44689
|
+
}
|
|
44690
|
+
try {
|
|
44691
|
+
const { confirm: confirm2, session } = await finishInitiator(entry.initState, resp, peerPub);
|
|
44692
|
+
entry.session = session;
|
|
44693
|
+
entry.initState = void 0;
|
|
44694
|
+
this.cfg.sendHandshake(confirm2);
|
|
44695
|
+
entry.resolve?.(session);
|
|
44696
|
+
} catch (e) {
|
|
44697
|
+
entry.reject?.(e);
|
|
44698
|
+
this.map.delete(resp.sess);
|
|
44699
|
+
}
|
|
44700
|
+
}
|
|
44701
|
+
async onConfirm(confirm2) {
|
|
44702
|
+
const entry = this.map.get(confirm2.sess);
|
|
44703
|
+
if (!entry?.pending)
|
|
44704
|
+
return;
|
|
44705
|
+
const peerPub = await this.cfg.getPeerIdentityPub(confirm2.from);
|
|
44706
|
+
if (!peerPub) {
|
|
44707
|
+
this.map.delete(confirm2.sess);
|
|
44708
|
+
return;
|
|
44709
|
+
}
|
|
44710
|
+
try {
|
|
44711
|
+
entry.session = await finishResponder(entry.pending, confirm2, peerPub);
|
|
44712
|
+
entry.pending = void 0;
|
|
44713
|
+
} catch {
|
|
44714
|
+
this.map.delete(confirm2.sess);
|
|
44715
|
+
}
|
|
44716
|
+
}
|
|
44717
|
+
/** Seal a payload for an established session, or null if none is established yet. */
|
|
44718
|
+
async seal(sessionId, routing, payload) {
|
|
44719
|
+
const s = this.map.get(sessionId)?.session;
|
|
44720
|
+
return s ? s.sealer.sealJson(routing, payload) : null;
|
|
44721
|
+
}
|
|
44722
|
+
/** Open a sealed record. Throws if no session or on auth failure (caller drops + rehandshakes). */
|
|
44723
|
+
async open(sessionId, routing, rec) {
|
|
44724
|
+
const s = this.map.get(sessionId)?.session;
|
|
44725
|
+
if (!s)
|
|
44726
|
+
throw new Error("e2e: no session for " + sessionId);
|
|
44727
|
+
return s.opener.openJson(rec, routing);
|
|
44728
|
+
}
|
|
44729
|
+
};
|
|
43993
44730
|
|
|
43994
44731
|
// ../../packages/host-core/dist/daemon-agent.js
|
|
43995
44732
|
import { join as join18 } from "node:path";
|
|
@@ -44056,8 +44793,16 @@ async function createClaudeCodeBackend(send2, onBrowserRequest, loadPluginRuntim
|
|
|
44056
44793
|
}
|
|
44057
44794
|
|
|
44058
44795
|
// ../../packages/host-core/dist/engines/cli-backend-base.js
|
|
44059
|
-
|
|
44796
|
+
init_dist4();
|
|
44797
|
+
import { spawn as spawn3 } from "node:child_process";
|
|
44060
44798
|
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
44799
|
+
var TESSERACT_INTERNAL_SECRET = /^(CCB_JWT_SECRET|JWT_SECRET|TESSERACT_TOKEN|CCB_RELAY_SECRET|TURN_SECRET|LIVEKIT_API_SECRET)$/;
|
|
44800
|
+
function scrubInternalSecrets(env) {
|
|
44801
|
+
for (const k of Object.keys(env))
|
|
44802
|
+
if (TESSERACT_INTERNAL_SECRET.test(k))
|
|
44803
|
+
delete env[k];
|
|
44804
|
+
return env;
|
|
44805
|
+
}
|
|
44061
44806
|
var MAX_LIVE = 8;
|
|
44062
44807
|
var CliAgentBackend = class {
|
|
44063
44808
|
adapter;
|
|
@@ -44098,12 +44843,17 @@ var CliAgentBackend = class {
|
|
|
44098
44843
|
emitMsg({ type: "session:created", sessionId, clientRequestId: params.clientRequestId });
|
|
44099
44844
|
emitMsg({ type: "agent:status", sessionId, clientRequestId: params.clientRequestId, status: "running" });
|
|
44100
44845
|
const args = this.adapter.buildArgs(params, ctx);
|
|
44101
|
-
const child =
|
|
44846
|
+
const child = spawn3(spawnCfg.command, args, {
|
|
44102
44847
|
cwd: params.projectDir,
|
|
44103
44848
|
// engineEnv carries the resolved runner env + enabled-skill secrets (the desktop
|
|
44104
44849
|
// resolves them into the 0600 snapshot); merge so CLI engines get them too.
|
|
44105
|
-
|
|
44106
|
-
|
|
44850
|
+
// scrubInternalSecrets drops Tesseract's own control-plane secrets (provider
|
|
44851
|
+
// keys are kept — the engine needs them).
|
|
44852
|
+
env: scrubInternalSecrets({ ...process.env, ...spawnCfg.env, ...params.engineEnv ?? {} }),
|
|
44853
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
44854
|
+
// Own process group (POSIX) so Stop can SIGKILL the whole tree, not just the
|
|
44855
|
+
// CLI's PID — these engines spawn their own tool subprocesses too.
|
|
44856
|
+
detached: process.platform !== "win32"
|
|
44107
44857
|
});
|
|
44108
44858
|
const turn = { child, sessionId, clientRequestId: params.clientRequestId, messageId: ctx.messageId, acc: "", buf: "", errored: false, interrupted: false };
|
|
44109
44859
|
this.live.set(sessionId, turn);
|
|
@@ -44151,6 +44901,10 @@ var CliAgentBackend = class {
|
|
|
44151
44901
|
emitMsg({ type: "agent:status", sessionId, clientRequestId: turn.clientRequestId, status: "error" });
|
|
44152
44902
|
});
|
|
44153
44903
|
child.on("close", (code) => {
|
|
44904
|
+
if (turn.killTimer) {
|
|
44905
|
+
clearTimeout(turn.killTimer);
|
|
44906
|
+
turn.killTimer = void 0;
|
|
44907
|
+
}
|
|
44154
44908
|
if (this.adapter.streaming) {
|
|
44155
44909
|
if (turn.buf.trim())
|
|
44156
44910
|
handleLine(turn.buf);
|
|
@@ -44180,10 +44934,14 @@ var CliAgentBackend = class {
|
|
|
44180
44934
|
if (!turn)
|
|
44181
44935
|
return false;
|
|
44182
44936
|
turn.interrupted = true;
|
|
44183
|
-
|
|
44184
|
-
|
|
44185
|
-
|
|
44186
|
-
|
|
44937
|
+
killProcessTree(turn.child, "SIGTERM");
|
|
44938
|
+
if (turn.killTimer)
|
|
44939
|
+
clearTimeout(turn.killTimer);
|
|
44940
|
+
turn.killTimer = setTimeout(() => {
|
|
44941
|
+
turn.killTimer = void 0;
|
|
44942
|
+
if (this.live.has(sessionId))
|
|
44943
|
+
killProcessTree(turn.child, "SIGKILL");
|
|
44944
|
+
}, 1200);
|
|
44187
44945
|
return true;
|
|
44188
44946
|
}
|
|
44189
44947
|
// CLI engines don't expose a resumable on-disk session list to us in v1, so the
|
|
@@ -44200,10 +44958,11 @@ var CliAgentBackend = class {
|
|
|
44200
44958
|
if (level !== "critical")
|
|
44201
44959
|
return;
|
|
44202
44960
|
for (const [id, turn] of this.live) {
|
|
44203
|
-
|
|
44204
|
-
turn.
|
|
44205
|
-
|
|
44961
|
+
if (turn.killTimer) {
|
|
44962
|
+
clearTimeout(turn.killTimer);
|
|
44963
|
+
turn.killTimer = void 0;
|
|
44206
44964
|
}
|
|
44965
|
+
killProcessTree(turn.child, "SIGKILL");
|
|
44207
44966
|
this.live.delete(id);
|
|
44208
44967
|
}
|
|
44209
44968
|
}
|
|
@@ -44432,7 +45191,7 @@ init_dist4();
|
|
|
44432
45191
|
// ../../packages/host-core/dist/plugin-tools.js
|
|
44433
45192
|
import { existsSync as existsSync7, readdirSync } from "node:fs";
|
|
44434
45193
|
import { join as join16 } from "node:path";
|
|
44435
|
-
import { homedir as
|
|
45194
|
+
import { homedir as homedir9 } from "node:os";
|
|
44436
45195
|
|
|
44437
45196
|
// ../../packages/plugin-host/dist/discovery.js
|
|
44438
45197
|
import fs2 from "node:fs";
|
|
@@ -44767,9 +45526,9 @@ import { join as join15 } from "node:path";
|
|
|
44767
45526
|
// ../../packages/host-core/dist/credentials.js
|
|
44768
45527
|
import { readFileSync as readFileSync4, writeFileSync as writeFileSync4, mkdirSync as mkdirSync3, existsSync as existsSync3, renameSync as renameSync3, chmodSync } from "node:fs";
|
|
44769
45528
|
import { join as join10 } from "node:path";
|
|
44770
|
-
import { homedir as
|
|
45529
|
+
import { homedir as homedir5 } from "node:os";
|
|
44771
45530
|
function daemonDir() {
|
|
44772
|
-
return process.env.CCB_DAEMON_DIR ?? join10(
|
|
45531
|
+
return process.env.CCB_DAEMON_DIR ?? join10(homedir5(), ".tesseract", "daemon");
|
|
44773
45532
|
}
|
|
44774
45533
|
function credentialsPath() {
|
|
44775
45534
|
return join10(daemonDir(), "credentials.json");
|
|
@@ -44801,24 +45560,24 @@ function writeCredentials(c) {
|
|
|
44801
45560
|
// ../../packages/host-core/dist/runners/resolve.js
|
|
44802
45561
|
init_dist2();
|
|
44803
45562
|
import { readFileSync as readFileSync5, existsSync as existsSync5, statSync, mkdirSync as mkdirSync5, writeFileSync as writeFileSync6 } from "node:fs";
|
|
44804
|
-
import { homedir as
|
|
45563
|
+
import { homedir as homedir7 } from "node:os";
|
|
44805
45564
|
import { join as join12 } from "node:path";
|
|
44806
45565
|
import { createHash as createHash2 } from "node:crypto";
|
|
44807
45566
|
|
|
44808
45567
|
// ../../packages/host-core/dist/runners/proxy-router.js
|
|
44809
45568
|
init_dist2();
|
|
44810
|
-
import { spawn as
|
|
45569
|
+
import { spawn as spawn4, execFile as execFile2, execSync as execSync2 } from "node:child_process";
|
|
44811
45570
|
import { createRequire as createRequire2 } from "node:module";
|
|
44812
45571
|
import { randomUUID as randomUUID4, createHash } from "node:crypto";
|
|
44813
45572
|
import { existsSync as existsSync4, mkdirSync as mkdirSync4, writeFileSync as writeFileSync5 } from "node:fs";
|
|
44814
|
-
import { homedir as
|
|
45573
|
+
import { homedir as homedir6 } from "node:os";
|
|
44815
45574
|
import { join as join11, dirname as dirname6, sep as sep3 } from "node:path";
|
|
44816
45575
|
import { request as httpRequest } from "node:http";
|
|
44817
45576
|
var requireFromHere = createRequire2(import.meta.url);
|
|
44818
45577
|
var ROUTER_PORT = 8787;
|
|
44819
45578
|
var BIND_HOST = "127.0.0.1";
|
|
44820
45579
|
var ROUTER_TOKEN = randomUUID4();
|
|
44821
|
-
var routerHomeDir = join11(
|
|
45580
|
+
var routerHomeDir = join11(homedir6(), ".tesseract", "router-home");
|
|
44822
45581
|
var lastConfigHash = "";
|
|
44823
45582
|
var running = false;
|
|
44824
45583
|
var starting = null;
|
|
@@ -44963,7 +45722,7 @@ async function restart() {
|
|
|
44963
45722
|
await ccr(["stop"]).catch(() => void 0);
|
|
44964
45723
|
try {
|
|
44965
45724
|
const { exe, asNode } = nodeExe();
|
|
44966
|
-
const child =
|
|
45725
|
+
const child = spawn4(exe, [ccrCliPath(), "start"], { env: ccrEnv(asNode), detached: true, stdio: "ignore" });
|
|
44967
45726
|
child.unref();
|
|
44968
45727
|
} catch {
|
|
44969
45728
|
running = false;
|
|
@@ -44989,7 +45748,7 @@ function routerEndpointSync(desired) {
|
|
|
44989
45748
|
}
|
|
44990
45749
|
|
|
44991
45750
|
// ../../packages/host-core/dist/runners/resolve.js
|
|
44992
|
-
var runnerHomeBase = join12(
|
|
45751
|
+
var runnerHomeBase = join12(homedir7(), ".tesseract", "runner-home");
|
|
44993
45752
|
function materializeClaudeAuthHome(contents) {
|
|
44994
45753
|
const id = createHash2("sha256").update(contents).digest("hex").slice(0, 16);
|
|
44995
45754
|
const home = join12(runnerHomeBase, id);
|
|
@@ -45045,7 +45804,7 @@ function resolveRunnerEnv(spec) {
|
|
|
45045
45804
|
return { ANTHROPIC_BASE_URL: endpoint.baseUrl, ANTHROPIC_AUTH_TOKEN: endpoint.authToken, ANTHROPIC_API_KEY: "" };
|
|
45046
45805
|
}
|
|
45047
45806
|
function overridePath() {
|
|
45048
|
-
return join12(process.env.CCB_TESSERACT_DIR ?? join12(
|
|
45807
|
+
return join12(process.env.CCB_TESSERACT_DIR ?? join12(homedir7(), ".tesseract"), "runners.json");
|
|
45049
45808
|
}
|
|
45050
45809
|
var cache2 = null;
|
|
45051
45810
|
var cachedMtime = -1;
|
|
@@ -45274,7 +46033,7 @@ function setActiveConfig(id) {
|
|
|
45274
46033
|
// ../../packages/host-core/dist/daemon-engine-spawn.js
|
|
45275
46034
|
init_dist2();
|
|
45276
46035
|
import { execFileSync } from "node:child_process";
|
|
45277
|
-
import { homedir as
|
|
46036
|
+
import { homedir as homedir8 } from "node:os";
|
|
45278
46037
|
import { join as join14 } from "node:path";
|
|
45279
46038
|
function envKey(id) {
|
|
45280
46039
|
return id.toUpperCase().replace(/[^A-Z0-9]/g, "_");
|
|
@@ -45286,10 +46045,10 @@ function augmentedPath() {
|
|
|
45286
46045
|
"/opt/homebrew/bin",
|
|
45287
46046
|
"/usr/bin",
|
|
45288
46047
|
"/bin",
|
|
45289
|
-
join14(
|
|
45290
|
-
join14(
|
|
45291
|
-
join14(
|
|
45292
|
-
join14(
|
|
46048
|
+
join14(homedir8(), ".local", "bin"),
|
|
46049
|
+
join14(homedir8(), "bin"),
|
|
46050
|
+
join14(homedir8(), ".bun", "bin"),
|
|
46051
|
+
join14(homedir8(), ".npm-global", "bin")
|
|
45293
46052
|
];
|
|
45294
46053
|
const seen = /* @__PURE__ */ new Set();
|
|
45295
46054
|
const parts = [];
|
|
@@ -45432,7 +46191,7 @@ function buildDaemonPluginLoader(pluginsDir) {
|
|
|
45432
46191
|
};
|
|
45433
46192
|
}
|
|
45434
46193
|
function daemonSkillsPluginConfig() {
|
|
45435
|
-
const root = readDaemonAgentSettings().skillsRoot ?? join16(process.env.CCB_TESSERACT_DIR ?? join16(
|
|
46194
|
+
const root = readDaemonAgentSettings().skillsRoot ?? join16(process.env.CCB_TESSERACT_DIR ?? join16(homedir9(), ".tesseract"), "extensions-skills");
|
|
45436
46195
|
if (!existsSync7(join16(root, ".claude-plugin", "plugin.json")))
|
|
45437
46196
|
return null;
|
|
45438
46197
|
const enabled = join16(root, "skills");
|
|
@@ -45728,7 +46487,7 @@ async function closeEnginePage(browserId) {
|
|
|
45728
46487
|
// ../../packages/host-core/dist/image-gen-runner.js
|
|
45729
46488
|
import { randomUUID as randomUUID6 } from "node:crypto";
|
|
45730
46489
|
import { mkdirSync as mkdirSync7, writeFileSync as writeFileSync8, readFileSync as readFileSync8 } from "node:fs";
|
|
45731
|
-
import { homedir as
|
|
46490
|
+
import { homedir as homedir10 } from "node:os";
|
|
45732
46491
|
import { join as join17 } from "node:path";
|
|
45733
46492
|
|
|
45734
46493
|
// ../../packages/image-edit-core/dist/types.js
|
|
@@ -45945,7 +46704,7 @@ function createImageEditProvider(id, cfg2) {
|
|
|
45945
46704
|
|
|
45946
46705
|
// ../../packages/host-core/dist/image-gen-runner.js
|
|
45947
46706
|
function generatedImagesDir() {
|
|
45948
|
-
const home = process.env.CCB_TESSERACT_DIR ?? join17(
|
|
46707
|
+
const home = process.env.CCB_TESSERACT_DIR ?? join17(homedir10(), ".tesseract");
|
|
45949
46708
|
return join17(home, "generated-images");
|
|
45950
46709
|
}
|
|
45951
46710
|
function creds() {
|
|
@@ -46129,12 +46888,143 @@ function refreshDaemonScheduling(b) {
|
|
|
46129
46888
|
}
|
|
46130
46889
|
|
|
46131
46890
|
// ../../packages/host-core/dist/fs-remote.js
|
|
46132
|
-
import { readdir as
|
|
46133
|
-
import { openSync as openSync2, readSync, closeSync as closeSync2, statSync as statSync3, existsSync as existsSync8, watch } from "node:fs";
|
|
46134
|
-
import { basename as basename2, dirname as dirname7, join as
|
|
46891
|
+
import { readdir as readdir3, readFile as readFile8, writeFile as writeFile5, mkdir as mkdir5, cp, rm, rename as renameAsync, stat as stat2 } from "node:fs/promises";
|
|
46892
|
+
import { openSync as openSync2, readSync, closeSync as closeSync2, statSync as statSync3, existsSync as existsSync8, watch, realpathSync as realpathSync2, constants as fsConstants } from "node:fs";
|
|
46893
|
+
import { basename as basename2, dirname as dirname7, join as join20, relative as relative4, resolve as resolve5, sep as sep4, isAbsolute as isAbsolute4 } from "node:path";
|
|
46135
46894
|
import { execFile as execFile3 } from "node:child_process";
|
|
46136
46895
|
import { promisify as promisify2 } from "node:util";
|
|
46896
|
+
|
|
46897
|
+
// ../../packages/host-core/dist/fs-stats.js
|
|
46898
|
+
init_dist2();
|
|
46899
|
+
import { readdir as readdir2, lstat, readFile as readFile7 } from "node:fs/promises";
|
|
46900
|
+
import { join as join19 } from "node:path";
|
|
46901
|
+
var TEXT_CAP = 5 * 1024 * 1024;
|
|
46902
|
+
async function dirStats(root, opts) {
|
|
46903
|
+
const start2 = Date.now();
|
|
46904
|
+
const MAX_ENTRIES = 2e5, TIME_MS = 1e4;
|
|
46905
|
+
let fileCount = 0, dirCount = 0, totalBytes = 0, visited = 0, truncated = false;
|
|
46906
|
+
const stack = [root];
|
|
46907
|
+
while (stack.length) {
|
|
46908
|
+
if (visited >= MAX_ENTRIES || Date.now() - start2 > TIME_MS) {
|
|
46909
|
+
truncated = true;
|
|
46910
|
+
break;
|
|
46911
|
+
}
|
|
46912
|
+
const dir = stack.pop();
|
|
46913
|
+
let ents;
|
|
46914
|
+
try {
|
|
46915
|
+
ents = await readdir2(dir, { withFileTypes: true });
|
|
46916
|
+
} catch {
|
|
46917
|
+
continue;
|
|
46918
|
+
}
|
|
46919
|
+
for (const e of ents) {
|
|
46920
|
+
if (visited >= MAX_ENTRIES || Date.now() - start2 > TIME_MS) {
|
|
46921
|
+
truncated = true;
|
|
46922
|
+
break;
|
|
46923
|
+
}
|
|
46924
|
+
if (STATS_SKIP_FILES.has(e.name))
|
|
46925
|
+
continue;
|
|
46926
|
+
visited++;
|
|
46927
|
+
if (e.isDirectory()) {
|
|
46928
|
+
if (opts?.excludeHeavy && STATS_HEAVY_DIRS.has(e.name))
|
|
46929
|
+
continue;
|
|
46930
|
+
dirCount++;
|
|
46931
|
+
stack.push(join19(dir, e.name));
|
|
46932
|
+
} else if (e.isFile()) {
|
|
46933
|
+
fileCount++;
|
|
46934
|
+
try {
|
|
46935
|
+
totalBytes += (await lstat(join19(dir, e.name))).size;
|
|
46936
|
+
} catch {
|
|
46937
|
+
}
|
|
46938
|
+
}
|
|
46939
|
+
}
|
|
46940
|
+
}
|
|
46941
|
+
return { fileCount, dirCount, totalBytes, truncated };
|
|
46942
|
+
}
|
|
46943
|
+
async function codeStats(root) {
|
|
46944
|
+
const start2 = Date.now();
|
|
46945
|
+
const MAX_FILES = 25e3, MAX_BYTES = 256 * 1024 * 1024, TIME_MS = 12e3;
|
|
46946
|
+
const byLang = /* @__PURE__ */ new Map();
|
|
46947
|
+
let totalFiles = 0, totalLines = 0, bytes = 0, truncated = false;
|
|
46948
|
+
const stack = [root];
|
|
46949
|
+
while (stack.length) {
|
|
46950
|
+
if (totalFiles >= MAX_FILES || bytes >= MAX_BYTES || Date.now() - start2 > TIME_MS) {
|
|
46951
|
+
truncated = true;
|
|
46952
|
+
break;
|
|
46953
|
+
}
|
|
46954
|
+
const dir = stack.pop();
|
|
46955
|
+
let ents;
|
|
46956
|
+
try {
|
|
46957
|
+
ents = await readdir2(dir, { withFileTypes: true });
|
|
46958
|
+
} catch {
|
|
46959
|
+
continue;
|
|
46960
|
+
}
|
|
46961
|
+
for (const e of ents) {
|
|
46962
|
+
if (totalFiles >= MAX_FILES || bytes >= MAX_BYTES || Date.now() - start2 > TIME_MS) {
|
|
46963
|
+
truncated = true;
|
|
46964
|
+
break;
|
|
46965
|
+
}
|
|
46966
|
+
if (STATS_SKIP_FILES.has(e.name))
|
|
46967
|
+
continue;
|
|
46968
|
+
if (e.isDirectory()) {
|
|
46969
|
+
if (!STATS_HEAVY_DIRS.has(e.name))
|
|
46970
|
+
stack.push(join19(dir, e.name));
|
|
46971
|
+
continue;
|
|
46972
|
+
}
|
|
46973
|
+
if (!e.isFile())
|
|
46974
|
+
continue;
|
|
46975
|
+
const lang = langForFilename(e.name);
|
|
46976
|
+
if (!lang)
|
|
46977
|
+
continue;
|
|
46978
|
+
const full = join19(dir, e.name);
|
|
46979
|
+
let size = 0;
|
|
46980
|
+
try {
|
|
46981
|
+
size = (await lstat(full)).size;
|
|
46982
|
+
} catch {
|
|
46983
|
+
continue;
|
|
46984
|
+
}
|
|
46985
|
+
const row = byLang.get(lang) ?? { files: 0, lines: 0 };
|
|
46986
|
+
row.files++;
|
|
46987
|
+
totalFiles++;
|
|
46988
|
+
if (size <= TEXT_CAP) {
|
|
46989
|
+
try {
|
|
46990
|
+
const txt = await readFile7(full, "utf8");
|
|
46991
|
+
bytes += size;
|
|
46992
|
+
let lines = 0;
|
|
46993
|
+
for (let i = 0; i < txt.length; i++)
|
|
46994
|
+
if (txt.charCodeAt(i) === 10)
|
|
46995
|
+
lines++;
|
|
46996
|
+
if (txt.length && txt.charCodeAt(txt.length - 1) !== 10)
|
|
46997
|
+
lines++;
|
|
46998
|
+
row.lines += lines;
|
|
46999
|
+
totalLines += lines;
|
|
47000
|
+
} catch {
|
|
47001
|
+
}
|
|
47002
|
+
}
|
|
47003
|
+
byLang.set(lang, row);
|
|
47004
|
+
}
|
|
47005
|
+
}
|
|
47006
|
+
const byLangSorted = [...byLang.entries()].map(([lang, v]) => ({ lang, files: v.files, lines: v.lines })).sort((a, b) => b.lines - a.lines || b.files - a.files);
|
|
47007
|
+
return { byLang: byLangSorted, totalFiles, totalLines, truncated };
|
|
47008
|
+
}
|
|
47009
|
+
|
|
47010
|
+
// ../../packages/host-core/dist/fs-remote.js
|
|
46137
47011
|
var execFileAsync = promisify2(execFile3);
|
|
47012
|
+
function insideOk(p) {
|
|
47013
|
+
const c = resolveContainmentRoot();
|
|
47014
|
+
return c ? isInsideRoot(p, c) : true;
|
|
47015
|
+
}
|
|
47016
|
+
function safeContentPath(p) {
|
|
47017
|
+
const c = resolveContainmentRoot();
|
|
47018
|
+
if (c)
|
|
47019
|
+
return safeResolve(p, c);
|
|
47020
|
+
try {
|
|
47021
|
+
return realpathSync2.native(p);
|
|
47022
|
+
} catch {
|
|
47023
|
+
return p;
|
|
47024
|
+
}
|
|
47025
|
+
}
|
|
47026
|
+
var O_NOFOLLOW = fsConstants.O_NOFOLLOW ?? 0;
|
|
47027
|
+
var CREATE_EXCL_NOFOLLOW = fsConstants.O_WRONLY | fsConstants.O_CREAT | fsConstants.O_EXCL | O_NOFOLLOW;
|
|
46138
47028
|
var LISTALL_SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
46139
47029
|
".git",
|
|
46140
47030
|
".hg",
|
|
@@ -46169,7 +47059,7 @@ async function listAllFilesUnder(dir) {
|
|
|
46169
47059
|
return;
|
|
46170
47060
|
let entries;
|
|
46171
47061
|
try {
|
|
46172
|
-
entries = await
|
|
47062
|
+
entries = await readdir3(d, { withFileTypes: true });
|
|
46173
47063
|
} catch {
|
|
46174
47064
|
return;
|
|
46175
47065
|
}
|
|
@@ -46178,7 +47068,7 @@ async function listAllFilesUnder(dir) {
|
|
|
46178
47068
|
return;
|
|
46179
47069
|
if (ent.name === ".DS_Store")
|
|
46180
47070
|
continue;
|
|
46181
|
-
const full =
|
|
47071
|
+
const full = join20(d, ent.name);
|
|
46182
47072
|
if (ent.isDirectory()) {
|
|
46183
47073
|
if (!LISTALL_SKIP_DIRS.has(ent.name))
|
|
46184
47074
|
await walk(full);
|
|
@@ -46294,76 +47184,107 @@ function resolveParent(raw, root) {
|
|
|
46294
47184
|
if (raw === "" || raw === "~")
|
|
46295
47185
|
return root;
|
|
46296
47186
|
if (raw.startsWith("~/"))
|
|
46297
|
-
return
|
|
46298
|
-
return isAbsolute4(raw) ? raw :
|
|
47187
|
+
return join20(root, raw.slice(2));
|
|
47188
|
+
return isAbsolute4(raw) ? raw : join20(root, raw);
|
|
46299
47189
|
}
|
|
46300
47190
|
function resolveUniqueName(dir, name) {
|
|
46301
|
-
const direct =
|
|
47191
|
+
const direct = join20(dir, name);
|
|
46302
47192
|
if (!existsSync8(direct))
|
|
46303
47193
|
return direct;
|
|
46304
47194
|
const dot = name.lastIndexOf(".");
|
|
46305
47195
|
const base = dot > 0 ? name.slice(0, dot) : name;
|
|
46306
47196
|
const ext = dot > 0 ? name.slice(dot) : "";
|
|
46307
47197
|
for (let i = 2; i < 1e4; i++) {
|
|
46308
|
-
const candidate =
|
|
47198
|
+
const candidate = join20(dir, `${base} ${i}${ext}`);
|
|
46309
47199
|
if (!existsSync8(candidate))
|
|
46310
47200
|
return candidate;
|
|
46311
47201
|
}
|
|
46312
47202
|
return direct;
|
|
46313
47203
|
}
|
|
47204
|
+
var RL_WINDOW_MS = 1e4;
|
|
47205
|
+
var RL_MAX_OPS = 400;
|
|
47206
|
+
var RL_MAX_WRITE_BYTES = 256 * 1024 * 1024;
|
|
47207
|
+
var rlBuckets = /* @__PURE__ */ new Map();
|
|
47208
|
+
var WRITE_OPS = /* @__PURE__ */ new Set(["writeFile", "writeBinary", "createFile", "createProject"]);
|
|
47209
|
+
var HEAVY_OPS = /* @__PURE__ */ new Set(["dirStats", "codeStats"]);
|
|
47210
|
+
var HEAVY_OP_COST = 25;
|
|
47211
|
+
function withinRate(key2, op, bytes) {
|
|
47212
|
+
const now = Date.now();
|
|
47213
|
+
let b = rlBuckets.get(key2);
|
|
47214
|
+
if (!b || now >= b.resetAt) {
|
|
47215
|
+
b = { resetAt: now + RL_WINDOW_MS, ops: 0, writeBytes: 0 };
|
|
47216
|
+
rlBuckets.set(key2, b);
|
|
47217
|
+
}
|
|
47218
|
+
b.ops += HEAVY_OPS.has(op) ? HEAVY_OP_COST : 1;
|
|
47219
|
+
if (WRITE_OPS.has(op))
|
|
47220
|
+
b.writeBytes += bytes;
|
|
47221
|
+
return b.ops <= RL_MAX_OPS && b.writeBytes <= RL_MAX_WRITE_BYTES;
|
|
47222
|
+
}
|
|
47223
|
+
function releaseFsClient(key2) {
|
|
47224
|
+
rlBuckets.delete(key2);
|
|
47225
|
+
}
|
|
46314
47226
|
var OUTSIDE = (verb) => ({ ok: false, error: `Refusing to ${verb} outside the shared root.` });
|
|
46315
47227
|
var validName = (n) => !!n && !n.includes("/") && !n.includes("\\") && n !== "." && n !== "..";
|
|
46316
47228
|
async function readRemoteBytes(root, path5) {
|
|
46317
|
-
|
|
47229
|
+
const real = safeContentPath(path5);
|
|
47230
|
+
if (!real)
|
|
46318
47231
|
return { error: "Path is outside the shared root." };
|
|
46319
47232
|
try {
|
|
46320
|
-
const s = statSync3(
|
|
47233
|
+
const s = statSync3(real);
|
|
46321
47234
|
if (!s.isFile())
|
|
46322
47235
|
return { error: "Not a file" };
|
|
46323
47236
|
if (s.size > REMOTE_READ_CAP)
|
|
46324
47237
|
return { error: `File is too large to read whole over the network (${(s.size / 1024 / 1024).toFixed(1)} MB) \u2014 open it locally.` };
|
|
46325
|
-
return { bytes: await
|
|
47238
|
+
return { bytes: await readFile8(real), size: s.size };
|
|
46326
47239
|
} catch (err) {
|
|
46327
47240
|
return { error: String(err.message) };
|
|
46328
47241
|
}
|
|
46329
47242
|
}
|
|
46330
|
-
async function runRemoteFsOp(root, op, args) {
|
|
47243
|
+
async function runRemoteFsOp(root, op, args, quotaKey) {
|
|
46331
47244
|
const a = args ?? [];
|
|
47245
|
+
if (quotaKey) {
|
|
47246
|
+
const bytes = op === "writeFile" ? Buffer.byteLength(String(a[1] ?? "")) : op === "writeBinary" ? Math.floor(String(a[1] ?? "").length * 0.75) : 0;
|
|
47247
|
+
if (!withinRate(quotaKey, op, bytes))
|
|
47248
|
+
throw new Error("Too many filesystem requests \u2014 please slow down.");
|
|
47249
|
+
}
|
|
46332
47250
|
switch (op) {
|
|
46333
47251
|
case "readDir": {
|
|
46334
47252
|
const dir = String(a[0] ?? root);
|
|
46335
|
-
|
|
47253
|
+
const real = safeContentPath(dir);
|
|
47254
|
+
if (!real)
|
|
46336
47255
|
return [];
|
|
46337
47256
|
try {
|
|
46338
|
-
const entries = (await
|
|
46339
|
-
const ignored = await gitIgnoredNames(
|
|
46340
|
-
return entries.map((e) => ({ name: e.name, path:
|
|
47257
|
+
const entries = (await readdir3(real, { withFileTypes: true })).filter((e) => e.name !== ".DS_Store");
|
|
47258
|
+
const ignored = await gitIgnoredNames(real, entries.map((e) => e.name));
|
|
47259
|
+
return entries.map((e) => ({ name: e.name, path: join20(dir, e.name), isDir: e.isDirectory(), ignored: ignored.has(e.name) })).sort((x, y) => x.isDir === y.isDir ? x.name.localeCompare(y.name) : x.isDir ? -1 : 1);
|
|
46341
47260
|
} catch {
|
|
46342
47261
|
return [];
|
|
46343
47262
|
}
|
|
46344
47263
|
}
|
|
46345
47264
|
case "listAllFiles": {
|
|
46346
47265
|
const dir = String(a[0] ?? root);
|
|
46347
|
-
|
|
47266
|
+
const real = safeContentPath(dir);
|
|
47267
|
+
if (!real)
|
|
46348
47268
|
return [];
|
|
46349
47269
|
try {
|
|
46350
|
-
return await listAllFilesUnder(
|
|
47270
|
+
return await listAllFilesUnder(real);
|
|
46351
47271
|
} catch {
|
|
46352
47272
|
return [];
|
|
46353
47273
|
}
|
|
46354
47274
|
}
|
|
46355
47275
|
case "readFile": {
|
|
46356
47276
|
const p = String(a[0] ?? "");
|
|
46357
|
-
|
|
47277
|
+
const real = safeContentPath(p);
|
|
47278
|
+
if (!real)
|
|
46358
47279
|
return { error: "Path is outside the shared root." };
|
|
46359
47280
|
const ext = p.split(".").pop()?.toLowerCase() ?? "";
|
|
46360
47281
|
if (BINARY_EXT.has(ext))
|
|
46361
47282
|
return { error: "Binary file \u2014 not shown in the editor." };
|
|
46362
47283
|
try {
|
|
46363
|
-
const s = statSync3(
|
|
47284
|
+
const s = statSync3(real);
|
|
46364
47285
|
if (s.size > TEXT_SIZE_LIMIT)
|
|
46365
47286
|
return { error: "File too large for text view." };
|
|
46366
|
-
return { content: await
|
|
47287
|
+
return { content: await readFile8(real, "utf-8") };
|
|
46367
47288
|
} catch (err) {
|
|
46368
47289
|
return { error: String(err.message) };
|
|
46369
47290
|
}
|
|
@@ -46371,11 +47292,12 @@ async function runRemoteFsOp(root, op, args) {
|
|
|
46371
47292
|
case "writeFile": {
|
|
46372
47293
|
const p = String(a[0] ?? "");
|
|
46373
47294
|
const content = String(a[1] ?? "");
|
|
46374
|
-
|
|
47295
|
+
const real = safeContentPath(p);
|
|
47296
|
+
if (!real)
|
|
46375
47297
|
return OUTSIDE("write");
|
|
46376
47298
|
try {
|
|
46377
|
-
await mkdir5(dirname7(
|
|
46378
|
-
await writeFile5(
|
|
47299
|
+
await mkdir5(dirname7(real), { recursive: true });
|
|
47300
|
+
await writeFile5(real, content, "utf-8");
|
|
46379
47301
|
return { ok: true };
|
|
46380
47302
|
} catch (err) {
|
|
46381
47303
|
return { ok: false, error: String(err.message) };
|
|
@@ -46384,11 +47306,12 @@ async function runRemoteFsOp(root, op, args) {
|
|
|
46384
47306
|
case "writeBinary": {
|
|
46385
47307
|
const p = String(a[0] ?? "");
|
|
46386
47308
|
const base643 = String(a[1] ?? "");
|
|
46387
|
-
|
|
47309
|
+
const real = safeContentPath(p);
|
|
47310
|
+
if (!real)
|
|
46388
47311
|
return OUTSIDE("write");
|
|
46389
47312
|
try {
|
|
46390
|
-
await mkdir5(dirname7(
|
|
46391
|
-
await writeFile5(
|
|
47313
|
+
await mkdir5(dirname7(real), { recursive: true });
|
|
47314
|
+
await writeFile5(real, Buffer.from(base643, "base64"));
|
|
46392
47315
|
return { ok: true };
|
|
46393
47316
|
} catch (err) {
|
|
46394
47317
|
return { ok: false, error: String(err.message) };
|
|
@@ -46396,10 +47319,11 @@ async function runRemoteFsOp(root, op, args) {
|
|
|
46396
47319
|
}
|
|
46397
47320
|
case "statFile": {
|
|
46398
47321
|
const p = String(a[0] ?? "");
|
|
46399
|
-
|
|
47322
|
+
const real = safeContentPath(p);
|
|
47323
|
+
if (!real)
|
|
46400
47324
|
return { error: "Path is outside the shared root." };
|
|
46401
47325
|
try {
|
|
46402
|
-
const s = statSync3(
|
|
47326
|
+
const s = statSync3(real);
|
|
46403
47327
|
if (!s.isFile())
|
|
46404
47328
|
return { error: "Not a file" };
|
|
46405
47329
|
const ext = p.split(".").pop()?.toLowerCase() ?? "";
|
|
@@ -46410,7 +47334,8 @@ async function runRemoteFsOp(root, op, args) {
|
|
|
46410
47334
|
}
|
|
46411
47335
|
case "readChunk": {
|
|
46412
47336
|
const p = String(a[0] ?? "");
|
|
46413
|
-
|
|
47337
|
+
const real = safeContentPath(p);
|
|
47338
|
+
if (!real)
|
|
46414
47339
|
return { error: "Path is outside the shared root." };
|
|
46415
47340
|
const off = Math.max(0, Math.floor(Number(a[1]) || 0));
|
|
46416
47341
|
const len = Math.min(1024 * 1024, Math.max(0, Math.floor(Number(a[2]) || 0)));
|
|
@@ -46418,7 +47343,7 @@ async function runRemoteFsOp(root, op, args) {
|
|
|
46418
47343
|
return { base64: "", bytesRead: 0 };
|
|
46419
47344
|
let fd = null;
|
|
46420
47345
|
try {
|
|
46421
|
-
fd = openSync2(
|
|
47346
|
+
fd = openSync2(real, "r");
|
|
46422
47347
|
const buf = Buffer.alloc(len);
|
|
46423
47348
|
const bytesRead = readSync(fd, buf, 0, len, off);
|
|
46424
47349
|
return { base64: buf.subarray(0, bytesRead).toString("base64"), bytesRead };
|
|
@@ -46434,13 +47359,14 @@ async function runRemoteFsOp(root, op, args) {
|
|
|
46434
47359
|
}
|
|
46435
47360
|
case "readBinary": {
|
|
46436
47361
|
const p = String(a[0] ?? "");
|
|
46437
|
-
|
|
47362
|
+
const real = safeContentPath(p);
|
|
47363
|
+
if (!real)
|
|
46438
47364
|
return { error: "Path is outside the shared root." };
|
|
46439
47365
|
try {
|
|
46440
|
-
const s = statSync3(
|
|
47366
|
+
const s = statSync3(real);
|
|
46441
47367
|
if (s.size > REMOTE_READ_CAP)
|
|
46442
47368
|
return { error: `File is too large to read whole over the network (${(s.size / 1024 / 1024).toFixed(1)} MB) \u2014 open it locally.` };
|
|
46443
|
-
return { base64: (await
|
|
47369
|
+
return { base64: (await readFile8(real)).toString("base64"), size: s.size };
|
|
46444
47370
|
} catch (err) {
|
|
46445
47371
|
return { error: String(err.message) };
|
|
46446
47372
|
}
|
|
@@ -46450,8 +47376,8 @@ async function runRemoteFsOp(root, op, args) {
|
|
|
46450
47376
|
const name = String(a[1] ?? "").trim();
|
|
46451
47377
|
if (!validName(name))
|
|
46452
47378
|
return { ok: false, error: "Invalid project name." };
|
|
46453
|
-
const target =
|
|
46454
|
-
if (!
|
|
47379
|
+
const target = join20(parent, name);
|
|
47380
|
+
if (!insideOk(target))
|
|
46455
47381
|
return { ok: false, error: "Refusing to create a project outside the shared root." };
|
|
46456
47382
|
if (existsSync8(target))
|
|
46457
47383
|
return { ok: false, error: `A folder named "${name}" already exists.` };
|
|
@@ -46468,14 +47394,14 @@ async function runRemoteFsOp(root, op, args) {
|
|
|
46468
47394
|
const name = String(a[1] ?? "").trim();
|
|
46469
47395
|
if (!validName(name))
|
|
46470
47396
|
return { ok: false, error: "Invalid name." };
|
|
46471
|
-
|
|
47397
|
+
const target = join20(parent, name);
|
|
47398
|
+
if (!insideOk(target))
|
|
46472
47399
|
return OUTSIDE("create");
|
|
46473
|
-
const target = join19(parent, name);
|
|
46474
47400
|
if (existsSync8(target))
|
|
46475
47401
|
return { ok: false, error: `"${name}" already exists.` };
|
|
46476
47402
|
try {
|
|
46477
47403
|
if (op === "createFile")
|
|
46478
|
-
await writeFile5(target, "", "utf-8");
|
|
47404
|
+
await writeFile5(target, "", { encoding: "utf-8", flag: CREATE_EXCL_NOFOLLOW });
|
|
46479
47405
|
else
|
|
46480
47406
|
await mkdir5(target);
|
|
46481
47407
|
return { ok: true, path: target };
|
|
@@ -46488,9 +47414,9 @@ async function runRemoteFsOp(root, op, args) {
|
|
|
46488
47414
|
const newName = String(a[1] ?? "").trim();
|
|
46489
47415
|
if (!validName(newName))
|
|
46490
47416
|
return { ok: false, error: "Invalid name." };
|
|
46491
|
-
if (!
|
|
47417
|
+
if (!insideOk(oldPath))
|
|
46492
47418
|
return OUTSIDE("rename");
|
|
46493
|
-
const newPath =
|
|
47419
|
+
const newPath = join20(dirname7(oldPath), newName);
|
|
46494
47420
|
if (existsSync8(newPath))
|
|
46495
47421
|
return { ok: false, error: `A file named "${newName}" already exists.` };
|
|
46496
47422
|
try {
|
|
@@ -46504,7 +47430,7 @@ async function runRemoteFsOp(root, op, args) {
|
|
|
46504
47430
|
const p = String(a[0] ?? "");
|
|
46505
47431
|
if (!p)
|
|
46506
47432
|
return { ok: false, error: "Bad request" };
|
|
46507
|
-
if (!
|
|
47433
|
+
if (!insideOk(p))
|
|
46508
47434
|
return OUTSIDE("delete");
|
|
46509
47435
|
if (resolve5(p) === resolve5(root))
|
|
46510
47436
|
return { ok: false, error: "Refusing to delete the shared root." };
|
|
@@ -46519,14 +47445,14 @@ async function runRemoteFsOp(root, op, args) {
|
|
|
46519
47445
|
case "movePaths": {
|
|
46520
47446
|
const srcPaths = Array.isArray(a[0]) ? a[0] : [];
|
|
46521
47447
|
const destDir = String(a[1] ?? "");
|
|
46522
|
-
if (!
|
|
47448
|
+
if (!insideOk(destDir))
|
|
46523
47449
|
return OUTSIDE(op === "copyPaths" ? "copy" : "move");
|
|
46524
47450
|
try {
|
|
46525
47451
|
const newPaths = [];
|
|
46526
47452
|
for (const src of srcPaths) {
|
|
46527
47453
|
if (typeof src !== "string" || !src)
|
|
46528
47454
|
continue;
|
|
46529
|
-
if (!
|
|
47455
|
+
if (!insideOk(src))
|
|
46530
47456
|
return OUTSIDE(op === "copyPaths" ? "copy from" : "move from");
|
|
46531
47457
|
if (op === "movePaths" && dirname7(src) === destDir) {
|
|
46532
47458
|
newPaths.push(src);
|
|
@@ -46545,18 +47471,31 @@ async function runRemoteFsOp(root, op, args) {
|
|
|
46545
47471
|
}
|
|
46546
47472
|
}
|
|
46547
47473
|
case "openFolder": {
|
|
46548
|
-
const
|
|
46549
|
-
|
|
47474
|
+
const requested = a[0] != null ? resolveParent(String(a[0]), root) : root;
|
|
47475
|
+
const real = safeContentPath(requested);
|
|
47476
|
+
if (!real)
|
|
46550
47477
|
return { error: "outside_root" };
|
|
46551
47478
|
try {
|
|
46552
|
-
const s = await stat2(
|
|
47479
|
+
const s = await stat2(real);
|
|
46553
47480
|
if (!s.isDirectory())
|
|
46554
47481
|
return { error: "not_dir" };
|
|
46555
|
-
return { root: resolve5(
|
|
47482
|
+
return { root: resolve5(real), name: basename2(real) };
|
|
46556
47483
|
} catch {
|
|
46557
47484
|
return { error: "not_found" };
|
|
46558
47485
|
}
|
|
46559
47486
|
}
|
|
47487
|
+
case "dirStats": {
|
|
47488
|
+
const real = safeContentPath(String(a[0] ?? root));
|
|
47489
|
+
if (!real)
|
|
47490
|
+
return { fileCount: 0, dirCount: 0, totalBytes: 0, truncated: false, error: "outside_root" };
|
|
47491
|
+
return dirStats(real, a[1]);
|
|
47492
|
+
}
|
|
47493
|
+
case "codeStats": {
|
|
47494
|
+
const real = safeContentPath(String(a[0] ?? root));
|
|
47495
|
+
if (!real)
|
|
47496
|
+
return { byLang: [], totalFiles: 0, totalLines: 0, truncated: false, error: "outside_root" };
|
|
47497
|
+
return codeStats(real);
|
|
47498
|
+
}
|
|
46560
47499
|
case "currentRoot":
|
|
46561
47500
|
return { root, name: basename2(root) };
|
|
46562
47501
|
case "homeDir":
|
|
@@ -46582,7 +47521,7 @@ function watchRemoteRoot(root, key2, onChange) {
|
|
|
46582
47521
|
const rel = String(filename);
|
|
46583
47522
|
if (rel.split(sep4).some((seg) => IGNORED.has(seg)))
|
|
46584
47523
|
return;
|
|
46585
|
-
pending.add(
|
|
47524
|
+
pending.add(join20(resolved, rel));
|
|
46586
47525
|
if (timer)
|
|
46587
47526
|
clearTimeout(timer);
|
|
46588
47527
|
timer = setTimeout(() => {
|
|
@@ -46766,21 +47705,21 @@ function toNdcIceServers(configs) {
|
|
|
46766
47705
|
if (!m)
|
|
46767
47706
|
continue;
|
|
46768
47707
|
const scheme = m[1].toLowerCase();
|
|
46769
|
-
const
|
|
47708
|
+
const hostname6 = m[2];
|
|
46770
47709
|
const port = m[3] ? parseInt(m[3], 10) : scheme.endsWith("s") ? 5349 : 3478;
|
|
46771
47710
|
if (scheme.startsWith("stun")) {
|
|
46772
|
-
out.push({ hostname:
|
|
47711
|
+
out.push({ hostname: hostname6, port });
|
|
46773
47712
|
continue;
|
|
46774
47713
|
}
|
|
46775
47714
|
const transport = (m[4] ?? "udp").toLowerCase();
|
|
46776
47715
|
const relayType = scheme === "turns" ? "TurnTls" : transport === "tcp" ? "TurnTcp" : "TurnUdp";
|
|
46777
|
-
out.push({ hostname:
|
|
47716
|
+
out.push({ hostname: hostname6, port, username: c.username, password: c.credential, relayType });
|
|
46778
47717
|
}
|
|
46779
47718
|
}
|
|
46780
47719
|
return out;
|
|
46781
47720
|
}
|
|
46782
47721
|
var peers = /* @__PURE__ */ new Map();
|
|
46783
|
-
var
|
|
47722
|
+
var delay2 = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
46784
47723
|
var CHUNK = 64 * 1024;
|
|
46785
47724
|
var MAX_MSG = 256 * 1024;
|
|
46786
47725
|
var HIGH_WATER = 8 * 1024 * 1024;
|
|
@@ -46802,7 +47741,7 @@ async function serveGet(dc, id, path5, root) {
|
|
|
46802
47741
|
} catch {
|
|
46803
47742
|
return;
|
|
46804
47743
|
}
|
|
46805
|
-
const waitDrain = drainers.get(dc) ?? (() =>
|
|
47744
|
+
const waitDrain = drainers.get(dc) ?? (() => delay2(15));
|
|
46806
47745
|
for (let off = 0, seq = 0; off < bytes.length; off += CHUNK, seq++) {
|
|
46807
47746
|
while (dc.isOpen() && dc.bufferedAmount() > HIGH_WATER)
|
|
46808
47747
|
await waitDrain();
|
|
@@ -46946,13 +47885,13 @@ function dynamicMetrics() {
|
|
|
46946
47885
|
}
|
|
46947
47886
|
|
|
46948
47887
|
// ../../packages/host-core/dist/fs-transfer.js
|
|
46949
|
-
import { readdir as
|
|
47888
|
+
import { readdir as readdir4, lstat as lstat2, mkdir as mkdir6, chmod, rm as rm2, readlink, symlink, stat as stat3 } from "node:fs/promises";
|
|
46950
47889
|
import { openSync as openSync3, readSync as readSync2, closeSync as closeSync3, existsSync as existsSync9, createWriteStream } from "node:fs";
|
|
46951
|
-
import { dirname as dirname9, join as
|
|
47890
|
+
import { dirname as dirname9, join as join22, relative as relative6, sep as sep6 } from "node:path";
|
|
46952
47891
|
|
|
46953
47892
|
// ../../packages/host-core/dist/active-link/paths.js
|
|
46954
|
-
import { homedir as
|
|
46955
|
-
import { join as
|
|
47893
|
+
import { homedir as homedir11 } from "node:os";
|
|
47894
|
+
import { join as join21, relative as relative5, sep as sep5 } from "node:path";
|
|
46956
47895
|
function activeLinkEnabled() {
|
|
46957
47896
|
return process.env.CCB_ACTIVE_LINK !== "0";
|
|
46958
47897
|
}
|
|
@@ -46978,7 +47917,7 @@ function toPosixRel(root, abs) {
|
|
|
46978
47917
|
return relative5(root, abs).split(sep5).join("/");
|
|
46979
47918
|
}
|
|
46980
47919
|
function fromPosixRel(root, rel) {
|
|
46981
|
-
return
|
|
47920
|
+
return join21(root, rel.split("/").join(sep5));
|
|
46982
47921
|
}
|
|
46983
47922
|
function isExcludedAbs(abs, root) {
|
|
46984
47923
|
const rel = toPosixRel(root, abs);
|
|
@@ -46987,10 +47926,10 @@ function isExcludedAbs(abs, root) {
|
|
|
46987
47926
|
return rel.split("/").some(isExcludedName);
|
|
46988
47927
|
}
|
|
46989
47928
|
function activeLinkRoot() {
|
|
46990
|
-
return process.env.CCB_ACTIVE_LINK_DIR ??
|
|
47929
|
+
return process.env.CCB_ACTIVE_LINK_DIR ?? join21(homedir11(), ".tesseract", "active-link");
|
|
46991
47930
|
}
|
|
46992
47931
|
function linkStateDir(linkId) {
|
|
46993
|
-
return
|
|
47932
|
+
return join21(activeLinkRoot(), linkId);
|
|
46994
47933
|
}
|
|
46995
47934
|
|
|
46996
47935
|
// ../../packages/host-core/dist/fs-transfer.js
|
|
@@ -47003,9 +47942,9 @@ function clearTimer(t) {
|
|
|
47003
47942
|
return null;
|
|
47004
47943
|
}
|
|
47005
47944
|
function uniqueDir(parent, name) {
|
|
47006
|
-
let candidate =
|
|
47945
|
+
let candidate = join22(parent, name);
|
|
47007
47946
|
for (let i = 2; existsSync9(candidate) && i < 1e4; i++)
|
|
47008
|
-
candidate =
|
|
47947
|
+
candidate = join22(parent, `${name} ${i}`);
|
|
47009
47948
|
return candidate;
|
|
47010
47949
|
}
|
|
47011
47950
|
var producers = /* @__PURE__ */ new Map();
|
|
@@ -47042,15 +47981,15 @@ async function buildManifest(root, followSymlinks, excludeHeavy) {
|
|
|
47042
47981
|
const files = [];
|
|
47043
47982
|
let totalBytes = 0;
|
|
47044
47983
|
async function walk(dir) {
|
|
47045
|
-
const entries = await
|
|
47984
|
+
const entries = await readdir4(dir, { withFileTypes: true });
|
|
47046
47985
|
for (const e of entries) {
|
|
47047
47986
|
if (ALWAYS_SKIP.has(e.name))
|
|
47048
47987
|
continue;
|
|
47049
47988
|
if (e.isDirectory() && excludeHeavy && HEAVY_DIRS.has(e.name))
|
|
47050
47989
|
continue;
|
|
47051
|
-
const abs =
|
|
47990
|
+
const abs = join22(dir, e.name);
|
|
47052
47991
|
const rel = relative6(root, abs).split(sep6).join("/");
|
|
47053
|
-
const st = await
|
|
47992
|
+
const st = await lstat2(abs).catch(() => null);
|
|
47054
47993
|
if (!st)
|
|
47055
47994
|
continue;
|
|
47056
47995
|
if (st.isSymbolicLink() && !followSymlinks) {
|
|
@@ -47115,11 +48054,11 @@ async function startOutboundTransfer(msg, controllerClientId, ownHostId2, hostRo
|
|
|
47115
48054
|
for (let fileIx = 0; fileIx < manifest.files.length && !state.cancelled; fileIx++) {
|
|
47116
48055
|
const f = manifest.files[fileIx];
|
|
47117
48056
|
if (f.kind === "symlink") {
|
|
47118
|
-
const target = await readlink(
|
|
48057
|
+
const target = await readlink(join22(srcPath, f.rel.split("/").join(sep6))).catch(() => "");
|
|
47119
48058
|
send2({ type: "fs:transferChunk", transferId, toHost, fileIx, rel: f.rel, mode: f.mode, kind: "symlink", offset: 0, base64: Buffer.from(target, "utf-8").toString("base64"), eof: true }, { hostId: toHost });
|
|
47120
48059
|
continue;
|
|
47121
48060
|
}
|
|
47122
|
-
const abs =
|
|
48061
|
+
const abs = join22(srcPath, f.rel.split("/").join(sep6));
|
|
47123
48062
|
let fd = null;
|
|
47124
48063
|
try {
|
|
47125
48064
|
fd = openSync3(abs, "r");
|
|
@@ -47231,7 +48170,7 @@ async function beginInboundTransfer(msg, hostRoot, send2) {
|
|
|
47231
48170
|
failNoState("Invalid destination name.");
|
|
47232
48171
|
return;
|
|
47233
48172
|
}
|
|
47234
|
-
let destRoot =
|
|
48173
|
+
let destRoot = join22(parent, msg.destName);
|
|
47235
48174
|
let createdRoot = false;
|
|
47236
48175
|
if (existsSync9(destRoot)) {
|
|
47237
48176
|
if (msg.onCollision === "abort") {
|
|
@@ -47273,7 +48212,7 @@ async function beginInboundTransfer(msg, hostRoot, send2) {
|
|
|
47273
48212
|
try {
|
|
47274
48213
|
await mkdir6(destRoot, { recursive: true });
|
|
47275
48214
|
for (const d of msg.dirs) {
|
|
47276
|
-
const abs =
|
|
48215
|
+
const abs = join22(destRoot, d.rel.split("/").join(sep6));
|
|
47277
48216
|
if (isInsideRoot(abs, destRoot))
|
|
47278
48217
|
await mkdir6(abs, { recursive: true }).catch(() => {
|
|
47279
48218
|
});
|
|
@@ -47287,7 +48226,7 @@ async function writeInboundChunk(msg, send2) {
|
|
|
47287
48226
|
if (!s || s.cancelled)
|
|
47288
48227
|
return;
|
|
47289
48228
|
armConsumerWatchdog(msg.transferId, send2);
|
|
47290
|
-
const abs =
|
|
48229
|
+
const abs = join22(s.destRoot, msg.rel.split("/").join(sep6));
|
|
47291
48230
|
if (msg.rel.includes("..") || !isInsideRoot(abs, s.destRoot)) {
|
|
47292
48231
|
failConsumer(msg.transferId, `Refusing a path outside the destination: ${msg.rel}`, send2);
|
|
47293
48232
|
return;
|
|
@@ -47295,7 +48234,7 @@ async function writeInboundChunk(msg, send2) {
|
|
|
47295
48234
|
try {
|
|
47296
48235
|
if (msg.kind === "symlink") {
|
|
47297
48236
|
const target = Buffer.from(msg.base64, "base64").toString("utf-8");
|
|
47298
|
-
const resolved =
|
|
48237
|
+
const resolved = join22(dirname9(abs), target);
|
|
47299
48238
|
if (isInsideRoot(resolved, s.destRoot)) {
|
|
47300
48239
|
await mkdir6(dirname9(abs), { recursive: true }).catch(() => {
|
|
47301
48240
|
});
|
|
@@ -47370,90 +48309,13 @@ function cancelTransfersForClient(clientId, send2) {
|
|
|
47370
48309
|
}
|
|
47371
48310
|
|
|
47372
48311
|
// ../../packages/host-core/dist/peer-link/index.js
|
|
47373
|
-
import { homedir as
|
|
47374
|
-
import { join as
|
|
47375
|
-
|
|
47376
|
-
// ../../packages/host-core/dist/peer-link/manager.js
|
|
47377
|
-
import { randomBytes as randomBytes2 } from "node:crypto";
|
|
47378
|
-
|
|
47379
|
-
// ../../packages/host-core/dist/peer-link/tls-direct.js
|
|
47380
|
-
var import_selfsigned = __toESM(require_selfsigned(), 1);
|
|
47381
|
-
import tls from "node:tls";
|
|
47382
|
-
import { X509Certificate } from "node:crypto";
|
|
47383
|
-
import { readFileSync as readFileSync9, writeFileSync as writeFileSync9, existsSync as existsSync10, mkdirSync as mkdirSync8 } from "node:fs";
|
|
47384
|
-
import { dirname as dirname10 } from "node:path";
|
|
47385
|
-
function fingerprintOf(certPem) {
|
|
47386
|
-
return new X509Certificate(certPem).fingerprint256;
|
|
47387
|
-
}
|
|
47388
|
-
async function loadOrCreateIdentity(certPath, keyPath) {
|
|
47389
|
-
if (existsSync10(certPath) && existsSync10(keyPath)) {
|
|
47390
|
-
const cert = readFileSync9(certPath, "utf8");
|
|
47391
|
-
const key2 = readFileSync9(keyPath, "utf8");
|
|
47392
|
-
return { cert, key: key2, fingerprint256: fingerprintOf(cert) };
|
|
47393
|
-
}
|
|
47394
|
-
const tenYears = new Date(Date.now() + 10 * 365 * 24 * 60 * 60 * 1e3);
|
|
47395
|
-
const pems = await (0, import_selfsigned.generate)([{ name: "commonName", value: "tesseract-peer" }], { keySize: 2048, notAfterDate: tenYears, algorithm: "sha256" });
|
|
47396
|
-
try {
|
|
47397
|
-
mkdirSync8(dirname10(certPath), { recursive: true });
|
|
47398
|
-
} catch {
|
|
47399
|
-
}
|
|
47400
|
-
try {
|
|
47401
|
-
writeFileSync9(certPath, pems.cert, { mode: 384 });
|
|
47402
|
-
writeFileSync9(keyPath, pems.private, { mode: 384 });
|
|
47403
|
-
} catch {
|
|
47404
|
-
}
|
|
47405
|
-
return { cert: pems.cert, key: pems.private, fingerprint256: fingerprintOf(pems.cert) };
|
|
47406
|
-
}
|
|
47407
|
-
function pinnedConnect(opts) {
|
|
47408
|
-
return new Promise((resolve10, reject) => {
|
|
47409
|
-
let settled = false;
|
|
47410
|
-
const done = (err, sock) => {
|
|
47411
|
-
if (settled)
|
|
47412
|
-
return;
|
|
47413
|
-
settled = true;
|
|
47414
|
-
if (err) {
|
|
47415
|
-
try {
|
|
47416
|
-
sock?.destroy();
|
|
47417
|
-
} catch {
|
|
47418
|
-
}
|
|
47419
|
-
reject(err);
|
|
47420
|
-
} else
|
|
47421
|
-
resolve10(sock);
|
|
47422
|
-
};
|
|
47423
|
-
const socket2 = tls.connect({
|
|
47424
|
-
host: opts.host,
|
|
47425
|
-
port: opts.port,
|
|
47426
|
-
// No CA / hostname identity — we authenticate SOLELY by the brokered
|
|
47427
|
-
// fingerprint, so accept the self-signed cert at the TLS layer and verify
|
|
47428
|
-
// the pin ourselves below (fail-closed).
|
|
47429
|
-
rejectUnauthorized: false,
|
|
47430
|
-
checkServerIdentity: () => void 0,
|
|
47431
|
-
timeout: opts.timeoutMs ?? 8e3
|
|
47432
|
-
}, () => {
|
|
47433
|
-
const peer = socket2.getPeerCertificate();
|
|
47434
|
-
const fp = peer && peer.fingerprint256;
|
|
47435
|
-
if (!fp || fp !== opts.peerFingerprint256) {
|
|
47436
|
-
done(new Error("peer-link: TLS fingerprint mismatch (fail-closed)"));
|
|
47437
|
-
return;
|
|
47438
|
-
}
|
|
47439
|
-
socket2.setNoDelay(true);
|
|
47440
|
-
done(void 0, socket2);
|
|
47441
|
-
});
|
|
47442
|
-
socket2.once("timeout", () => done(new Error("peer-link: TLS connect timeout")));
|
|
47443
|
-
socket2.once("error", (e) => done(e instanceof Error ? e : new Error(String(e))));
|
|
47444
|
-
});
|
|
47445
|
-
}
|
|
47446
|
-
function createListener(identity, port, onSocket) {
|
|
47447
|
-
const server = tls.createServer({ cert: identity.cert, key: identity.key, requestCert: false, rejectUnauthorized: false }, (sock) => {
|
|
47448
|
-
sock.setNoDelay(true);
|
|
47449
|
-
onSocket(sock);
|
|
47450
|
-
});
|
|
47451
|
-
server.listen(port, "0.0.0.0");
|
|
47452
|
-
return server;
|
|
47453
|
-
}
|
|
48312
|
+
import { homedir as homedir12 } from "node:os";
|
|
48313
|
+
import { join as join23 } from "node:path";
|
|
47454
48314
|
|
|
47455
48315
|
// ../../packages/host-core/dist/peer-link/manager.js
|
|
48316
|
+
init_tls_direct();
|
|
47456
48317
|
init_transport();
|
|
48318
|
+
import { randomBytes as randomBytes3, timingSafeEqual as timingSafeEqual2 } from "node:crypto";
|
|
47457
48319
|
|
|
47458
48320
|
// ../../packages/host-core/dist/peer-link/candidates.js
|
|
47459
48321
|
import os2 from "node:os";
|
|
@@ -47484,6 +48346,7 @@ function orderCandidates(cands) {
|
|
|
47484
48346
|
}
|
|
47485
48347
|
|
|
47486
48348
|
// ../../packages/host-core/dist/peer-link/ladder.js
|
|
48349
|
+
init_tls_direct();
|
|
47487
48350
|
init_transport();
|
|
47488
48351
|
function pathFor(c) {
|
|
47489
48352
|
return c.type === "host" ? "lan" : "public";
|
|
@@ -47589,7 +48452,7 @@ var PeerLinkManager = class {
|
|
|
47589
48452
|
const q = await Promise.resolve().then(() => (init_quic_transport(), quic_transport_exports));
|
|
47590
48453
|
const socket2 = await q.makeQuicSocket(this.udpPort);
|
|
47591
48454
|
this.quicSocket = socket2;
|
|
47592
|
-
await q.startQuicServerOnSocket({ socket: socket2, identity, onStream: (
|
|
48455
|
+
await q.startQuicServerOnSocket({ socket: socket2, identity, onStream: (bs2) => this.acceptInbound(bs2, "punch") });
|
|
47593
48456
|
const refresh = async () => {
|
|
47594
48457
|
try {
|
|
47595
48458
|
const s = await q.discoverSrflx(socket2, stunHost, stunPort);
|
|
@@ -47651,10 +48514,13 @@ var PeerLinkManager = class {
|
|
|
47651
48514
|
let matchedHex;
|
|
47652
48515
|
const { link, ready } = socketPeerLink(sock, "", path5, {
|
|
47653
48516
|
expectToken: (f) => {
|
|
47654
|
-
const
|
|
47655
|
-
|
|
47656
|
-
|
|
47657
|
-
|
|
48517
|
+
const hb = Buffer.from(toHex(f));
|
|
48518
|
+
for (const key2 of this.pendingInbound.keys()) {
|
|
48519
|
+
const kb = Buffer.from(key2);
|
|
48520
|
+
if (kb.length === hb.length && timingSafeEqual2(kb, hb)) {
|
|
48521
|
+
matchedHex = key2;
|
|
48522
|
+
return true;
|
|
48523
|
+
}
|
|
47658
48524
|
}
|
|
47659
48525
|
return false;
|
|
47660
48526
|
}
|
|
@@ -47798,7 +48664,7 @@ var PeerLinkManager = class {
|
|
|
47798
48664
|
this.attach(ctx);
|
|
47799
48665
|
await this.ensureListener();
|
|
47800
48666
|
const myFingerprint = (await this.identity()).fingerprint256;
|
|
47801
|
-
const rid =
|
|
48667
|
+
const rid = randomBytes3(8).toString("hex");
|
|
47802
48668
|
const myCandidates = gatherTcpCandidates(this.listenPort, this.observedIp);
|
|
47803
48669
|
return new Promise((resolve10, reject) => {
|
|
47804
48670
|
let settled = false;
|
|
@@ -47882,8 +48748,8 @@ var PeerLinkManager = class {
|
|
|
47882
48748
|
// ../../packages/host-core/dist/peer-link/index.js
|
|
47883
48749
|
var P2P_ENABLED = process.env.CCB_P2P !== "0";
|
|
47884
48750
|
function identityPaths() {
|
|
47885
|
-
const dir = process.env.CCB_P2P_DIR ??
|
|
47886
|
-
return { certPath:
|
|
48751
|
+
const dir = process.env.CCB_P2P_DIR ?? join23(homedir12(), ".tesseract");
|
|
48752
|
+
return { certPath: join23(dir, "peer-cert.pem"), keyPath: join23(dir, "peer-key.pem") };
|
|
47887
48753
|
}
|
|
47888
48754
|
var manager = null;
|
|
47889
48755
|
function peerLinkManager() {
|
|
@@ -47898,9 +48764,9 @@ async function openPeerLink(peerHostId, ctx) {
|
|
|
47898
48764
|
}
|
|
47899
48765
|
|
|
47900
48766
|
// ../../packages/host-core/dist/active-link/engine.js
|
|
47901
|
-
import { lstat as
|
|
48767
|
+
import { lstat as lstat7, readlink as readlink4, rm as rm3, mkdir as mkdir8, symlink as symlink2, rename as rename2, utimes as utimes2, writeFile as writeFile6 } from "node:fs/promises";
|
|
47902
48768
|
import { existsSync as existsSync12 } from "node:fs";
|
|
47903
|
-
import { dirname as dirname14, join as
|
|
48769
|
+
import { dirname as dirname14, join as join28, resolve as resolve8 } from "node:path";
|
|
47904
48770
|
|
|
47905
48771
|
// ../../packages/host-core/dist/active-link/blocks.js
|
|
47906
48772
|
import { createHash as createHash3 } from "node:crypto";
|
|
@@ -48165,7 +49031,7 @@ var BlockAssembler = class {
|
|
|
48165
49031
|
|
|
48166
49032
|
// ../../packages/host-core/dist/active-link/manifest.js
|
|
48167
49033
|
import { readFileSync as readFileSync10, writeFileSync as writeFileSync10, mkdirSync as mkdirSync9, existsSync as existsSync11, renameSync as renameSync5 } from "node:fs";
|
|
48168
|
-
import { join as
|
|
49034
|
+
import { join as join24 } from "node:path";
|
|
48169
49035
|
|
|
48170
49036
|
// ../../packages/host-core/dist/active-link/reconcile.js
|
|
48171
49037
|
var ABSENT = { kind: "absent", t: -Infinity, hash: "" };
|
|
@@ -48201,7 +49067,7 @@ var LinkManifest = class {
|
|
|
48201
49067
|
this.dir = dir;
|
|
48202
49068
|
}
|
|
48203
49069
|
file() {
|
|
48204
|
-
return
|
|
49070
|
+
return join24(this.dir, "manifest.json");
|
|
48205
49071
|
}
|
|
48206
49072
|
load() {
|
|
48207
49073
|
try {
|
|
@@ -48302,14 +49168,14 @@ var LinkManifest = class {
|
|
|
48302
49168
|
};
|
|
48303
49169
|
|
|
48304
49170
|
// ../../packages/host-core/dist/active-link/scanner.js
|
|
48305
|
-
import { readdir as
|
|
48306
|
-
import { join as
|
|
49171
|
+
import { readdir as readdir5, lstat as lstat3, readlink as readlink2 } from "node:fs/promises";
|
|
49172
|
+
import { join as join25, sep as sep7 } from "node:path";
|
|
48307
49173
|
async function walkRels(root, extraIgnore) {
|
|
48308
49174
|
const out = [];
|
|
48309
49175
|
async function rec(dir) {
|
|
48310
49176
|
let entries;
|
|
48311
49177
|
try {
|
|
48312
|
-
entries = await
|
|
49178
|
+
entries = await readdir5(dir, { withFileTypes: true });
|
|
48313
49179
|
} catch {
|
|
48314
49180
|
return;
|
|
48315
49181
|
}
|
|
@@ -48318,7 +49184,7 @@ async function walkRels(root, extraIgnore) {
|
|
|
48318
49184
|
continue;
|
|
48319
49185
|
if (e.isDirectory() && HEAVY_DIRS.has(e.name))
|
|
48320
49186
|
continue;
|
|
48321
|
-
const abs =
|
|
49187
|
+
const abs = join25(dir, e.name);
|
|
48322
49188
|
if (extraIgnore?.(abs))
|
|
48323
49189
|
continue;
|
|
48324
49190
|
if (e.isDirectory())
|
|
@@ -48335,7 +49201,7 @@ async function scanTree(root, prior, allow, extraIgnore) {
|
|
|
48335
49201
|
const rels = allow ? [...allow] : await walkRels(root, extraIgnore);
|
|
48336
49202
|
for (const rel of rels) {
|
|
48337
49203
|
const abs = fromPosixRel(root, rel);
|
|
48338
|
-
const st = await
|
|
49204
|
+
const st = await lstat3(abs).catch(() => null);
|
|
48339
49205
|
if (!st)
|
|
48340
49206
|
continue;
|
|
48341
49207
|
if (st.isSymbolicLink()) {
|
|
@@ -48343,9 +49209,9 @@ async function scanTree(root, prior, allow, extraIgnore) {
|
|
|
48343
49209
|
records.set(rel, { kind: "symlink", mtimeMs: Math.floor(st.mtimeMs), size: 0, mode: st.mode, hash: hashString(target), blocks: [] });
|
|
48344
49210
|
} else if (st.isFile()) {
|
|
48345
49211
|
const mtimeMs = Math.floor(st.mtimeMs);
|
|
48346
|
-
const
|
|
48347
|
-
if (
|
|
48348
|
-
records.set(rel, { ...
|
|
49212
|
+
const cached3 = prior?.getRecord(rel);
|
|
49213
|
+
if (cached3 && cached3.kind === "file" && cached3.size === st.size && cached3.mtimeMs === mtimeMs) {
|
|
49214
|
+
records.set(rel, { ...cached3, mode: st.mode });
|
|
48349
49215
|
} else {
|
|
48350
49216
|
const h = await hashFileBlocks(abs).catch(() => null);
|
|
48351
49217
|
if (!h)
|
|
@@ -48357,7 +49223,7 @@ async function scanTree(root, prior, allow, extraIgnore) {
|
|
|
48357
49223
|
return { records };
|
|
48358
49224
|
}
|
|
48359
49225
|
function absOf(root, rel) {
|
|
48360
|
-
return
|
|
49226
|
+
return join25(root, rel.split("/").join(sep7));
|
|
48361
49227
|
}
|
|
48362
49228
|
|
|
48363
49229
|
// ../../packages/host-core/dist/active-link/gitignore.js
|
|
@@ -48375,7 +49241,7 @@ async function gitSyncedFiles(root) {
|
|
|
48375
49241
|
|
|
48376
49242
|
// ../../packages/host-core/dist/active-link/session.js
|
|
48377
49243
|
import { createHash as createHash4 } from "node:crypto";
|
|
48378
|
-
import { lstat as
|
|
49244
|
+
import { lstat as lstat4, readlink as readlink3, open as open2 } from "node:fs/promises";
|
|
48379
49245
|
var OutboundSender = class {
|
|
48380
49246
|
deps;
|
|
48381
49247
|
queue = [];
|
|
@@ -48433,8 +49299,12 @@ var OutboundSender = class {
|
|
|
48433
49299
|
}
|
|
48434
49300
|
}
|
|
48435
49301
|
async sendWholeFile(rel) {
|
|
49302
|
+
if (rel.includes("..") || !isInsideRoot(absOf(this.deps.localRoot, rel), this.deps.localRoot)) {
|
|
49303
|
+
this.deps.onError?.(`Refused a path outside the link root: ${rel}`);
|
|
49304
|
+
return;
|
|
49305
|
+
}
|
|
48436
49306
|
const abs = absOf(this.deps.localRoot, rel);
|
|
48437
|
-
const st = await
|
|
49307
|
+
const st = await lstat4(abs).catch(() => null);
|
|
48438
49308
|
if (!st)
|
|
48439
49309
|
return;
|
|
48440
49310
|
const { linkId, ownHostId: ownHostId2, send: send2 } = this.deps;
|
|
@@ -48522,8 +49392,12 @@ var OutboundSender = class {
|
|
|
48522
49392
|
this.deps.onSent?.(rel);
|
|
48523
49393
|
}
|
|
48524
49394
|
async sendBlocks(rel, indexes) {
|
|
49395
|
+
if (rel.includes("..") || !isInsideRoot(absOf(this.deps.localRoot, rel), this.deps.localRoot)) {
|
|
49396
|
+
this.deps.onError?.(`Refused a path outside the link root: ${rel}`);
|
|
49397
|
+
return;
|
|
49398
|
+
}
|
|
48525
49399
|
const abs = absOf(this.deps.localRoot, rel);
|
|
48526
|
-
const st = await
|
|
49400
|
+
const st = await lstat4(abs).catch(() => null);
|
|
48527
49401
|
if (!st || !st.isFile())
|
|
48528
49402
|
return;
|
|
48529
49403
|
const { linkId, ownHostId: ownHostId2, send: send2 } = this.deps;
|
|
@@ -48568,12 +49442,12 @@ var OutboundSender = class {
|
|
|
48568
49442
|
|
|
48569
49443
|
// ../../node_modules/chokidar/esm/index.js
|
|
48570
49444
|
import { stat as statcb } from "fs";
|
|
48571
|
-
import { stat as stat6, readdir as
|
|
49445
|
+
import { stat as stat6, readdir as readdir7 } from "fs/promises";
|
|
48572
49446
|
import { EventEmitter as EventEmitter2 } from "events";
|
|
48573
49447
|
import * as sysPath2 from "path";
|
|
48574
49448
|
|
|
48575
49449
|
// ../../node_modules/readdirp/esm/index.js
|
|
48576
|
-
import { stat as stat4, lstat as
|
|
49450
|
+
import { stat as stat4, lstat as lstat5, readdir as readdir6, realpath } from "node:fs/promises";
|
|
48577
49451
|
import { Readable } from "node:stream";
|
|
48578
49452
|
import { resolve as presolve, relative as prelative, join as pjoin, sep as psep } from "node:path";
|
|
48579
49453
|
var EntryTypes = {
|
|
@@ -48640,7 +49514,7 @@ var ReaddirpStream = class extends Readable {
|
|
|
48640
49514
|
const { root, type } = opts;
|
|
48641
49515
|
this._fileFilter = normalizeFilter(opts.fileFilter);
|
|
48642
49516
|
this._directoryFilter = normalizeFilter(opts.directoryFilter);
|
|
48643
|
-
const statMethod = opts.lstat ?
|
|
49517
|
+
const statMethod = opts.lstat ? lstat5 : stat4;
|
|
48644
49518
|
if (wantBigintFsStats) {
|
|
48645
49519
|
this._stat = (path5) => statMethod(path5, { bigint: true });
|
|
48646
49520
|
} else {
|
|
@@ -48711,7 +49585,7 @@ var ReaddirpStream = class extends Readable {
|
|
|
48711
49585
|
async _exploreDir(path5, depth) {
|
|
48712
49586
|
let files;
|
|
48713
49587
|
try {
|
|
48714
|
-
files = await
|
|
49588
|
+
files = await readdir6(path5, this._rdOptions);
|
|
48715
49589
|
} catch (error51) {
|
|
48716
49590
|
this._onError(error51);
|
|
48717
49591
|
}
|
|
@@ -48750,7 +49624,7 @@ var ReaddirpStream = class extends Readable {
|
|
|
48750
49624
|
const full = entry.fullPath;
|
|
48751
49625
|
try {
|
|
48752
49626
|
const entryRealPath = await realpath(full);
|
|
48753
|
-
const entryRealPathStats = await
|
|
49627
|
+
const entryRealPathStats = await lstat5(entryRealPath);
|
|
48754
49628
|
if (entryRealPathStats.isFile()) {
|
|
48755
49629
|
return "file";
|
|
48756
49630
|
}
|
|
@@ -48793,7 +49667,7 @@ function readdirp(root, options = {}) {
|
|
|
48793
49667
|
|
|
48794
49668
|
// ../../node_modules/chokidar/esm/handler.js
|
|
48795
49669
|
import { watchFile, unwatchFile, watch as fs_watch } from "fs";
|
|
48796
|
-
import { open as open3, stat as stat5, lstat as
|
|
49670
|
+
import { open as open3, stat as stat5, lstat as lstat6, realpath as fsrealpath } from "fs/promises";
|
|
48797
49671
|
import * as sysPath from "path";
|
|
48798
49672
|
import { type as osType } from "os";
|
|
48799
49673
|
var STR_DATA = "data";
|
|
@@ -48820,7 +49694,7 @@ var EVENTS = {
|
|
|
48820
49694
|
};
|
|
48821
49695
|
var EV = EVENTS;
|
|
48822
49696
|
var THROTTLE_MODE_WATCH = "watch";
|
|
48823
|
-
var statMethods = { lstat:
|
|
49697
|
+
var statMethods = { lstat: lstat6, stat: stat5 };
|
|
48824
49698
|
var KEY_LISTENERS = "listeners";
|
|
48825
49699
|
var KEY_ERR = "errHandlers";
|
|
48826
49700
|
var KEY_RAW = "rawEmitters";
|
|
@@ -49680,7 +50554,7 @@ var DirEntry = class {
|
|
|
49680
50554
|
return;
|
|
49681
50555
|
const dir = this.path;
|
|
49682
50556
|
try {
|
|
49683
|
-
await
|
|
50557
|
+
await readdir7(dir);
|
|
49684
50558
|
} catch (err) {
|
|
49685
50559
|
if (this._removeWatcher) {
|
|
49686
50560
|
this._removeWatcher(sysPath2.dirname(dir), sysPath2.basename(dir));
|
|
@@ -50374,7 +51248,7 @@ var ActiveLinkEngine = class {
|
|
|
50374
51248
|
}
|
|
50375
51249
|
async start() {
|
|
50376
51250
|
this.emit({ type: "status", linkId: this.opts.linkId, state: "scanning" });
|
|
50377
|
-
await rm3(
|
|
51251
|
+
await rm3(join28(this.stateDir, ".staging"), { recursive: true, force: true }).catch(() => {
|
|
50378
51252
|
});
|
|
50379
51253
|
this.manifest.load();
|
|
50380
51254
|
await this.reconcileScan();
|
|
@@ -50596,16 +51470,18 @@ var ActiveLinkEngine = class {
|
|
|
50596
51470
|
// ── Block-delta: sender side (reply to a block-map request) ───────────────────
|
|
50597
51471
|
async onBlockMapReq(rel) {
|
|
50598
51472
|
const abs = absOf(this.opts.localRoot, rel);
|
|
50599
|
-
|
|
51473
|
+
if (rel.includes("..") || !isInsideRoot(abs, this.opts.localRoot))
|
|
51474
|
+
return;
|
|
51475
|
+
const st = await lstat7(abs).catch(() => null);
|
|
50600
51476
|
if (!st || !st.isFile())
|
|
50601
51477
|
return;
|
|
50602
51478
|
const mtimeMs = Math.floor(st.mtimeMs);
|
|
50603
|
-
const
|
|
51479
|
+
const cached3 = this.manifest.getRecord(rel);
|
|
50604
51480
|
let wholeHash;
|
|
50605
51481
|
let blocks;
|
|
50606
|
-
if (
|
|
50607
|
-
wholeHash =
|
|
50608
|
-
blocks =
|
|
51482
|
+
if (cached3 && cached3.kind === "file" && cached3.size === st.size && cached3.mtimeMs === mtimeMs) {
|
|
51483
|
+
wholeHash = cached3.hash;
|
|
51484
|
+
blocks = cached3.blocks;
|
|
50609
51485
|
} else {
|
|
50610
51486
|
const h = await hashFileBlocks(abs).catch(() => null);
|
|
50611
51487
|
if (!h)
|
|
@@ -50819,6 +51695,8 @@ var ActiveLinkEngine = class {
|
|
|
50819
51695
|
const fromRec = this.manifest.getRecord(fromRel);
|
|
50820
51696
|
const fromAbs = absOf(this.opts.localRoot, fromRel);
|
|
50821
51697
|
const toAbs2 = absOf(this.opts.localRoot, toRel2);
|
|
51698
|
+
if (fromRel.includes("..") || !isInsideRoot(fromAbs, this.opts.localRoot))
|
|
51699
|
+
return;
|
|
50822
51700
|
if (toRel2.includes("..") || !isInsideRoot(toAbs2, this.opts.localRoot))
|
|
50823
51701
|
return;
|
|
50824
51702
|
if (fromRec && fromRec.kind === "file") {
|
|
@@ -50916,7 +51794,7 @@ var ActiveLinkEngine = class {
|
|
|
50916
51794
|
/** Current on-disk record for a rel, or null if missing/a directory. */
|
|
50917
51795
|
async computeRecord(rel) {
|
|
50918
51796
|
const abs = absOf(this.opts.localRoot, rel);
|
|
50919
|
-
const st = await
|
|
51797
|
+
const st = await lstat7(abs).catch(() => null);
|
|
50920
51798
|
if (!st || st.isDirectory())
|
|
50921
51799
|
return null;
|
|
50922
51800
|
if (st.isSymbolicLink()) {
|
|
@@ -50926,9 +51804,9 @@ var ActiveLinkEngine = class {
|
|
|
50926
51804
|
if (!st.isFile())
|
|
50927
51805
|
return null;
|
|
50928
51806
|
const mtimeMs = Math.floor(st.mtimeMs);
|
|
50929
|
-
const
|
|
50930
|
-
if (
|
|
50931
|
-
return { ...
|
|
51807
|
+
const cached3 = this.manifest.getRecord(rel);
|
|
51808
|
+
if (cached3 && cached3.kind === "file" && cached3.size === st.size && cached3.mtimeMs === mtimeMs) {
|
|
51809
|
+
return { ...cached3, mode: st.mode };
|
|
50932
51810
|
}
|
|
50933
51811
|
const h = await hashFileBlocks(abs).catch(() => null);
|
|
50934
51812
|
if (!h)
|
|
@@ -50998,7 +51876,7 @@ var ActiveLinkEngine = class {
|
|
|
50998
51876
|
this.inboundWatchdog.clear();
|
|
50999
51877
|
}
|
|
51000
51878
|
stagingPath(rel, tag) {
|
|
51001
|
-
return
|
|
51879
|
+
return join28(this.stateDir, ".staging", `${hashString(rel)}.${tag}`);
|
|
51002
51880
|
}
|
|
51003
51881
|
pruneSuppress() {
|
|
51004
51882
|
const now = this.now();
|
|
@@ -51024,9 +51902,9 @@ var ActiveLinkEngine = class {
|
|
|
51024
51902
|
|
|
51025
51903
|
// ../../packages/host-core/dist/active-link/config.js
|
|
51026
51904
|
import { readFileSync as readFileSync11, writeFileSync as writeFileSync11, mkdirSync as mkdirSync10, existsSync as existsSync13, renameSync as renameSync6, watch as watch4 } from "node:fs";
|
|
51027
|
-
import { join as
|
|
51905
|
+
import { join as join29 } from "node:path";
|
|
51028
51906
|
function configPath2() {
|
|
51029
|
-
return
|
|
51907
|
+
return join29(activeLinkRoot(), "links.json");
|
|
51030
51908
|
}
|
|
51031
51909
|
function loadLinkConfigs() {
|
|
51032
51910
|
try {
|
|
@@ -51063,7 +51941,7 @@ function saveLinkConfig(cfg2) {
|
|
|
51063
51941
|
writeAll(links);
|
|
51064
51942
|
}
|
|
51065
51943
|
function pendingPath() {
|
|
51066
|
-
return
|
|
51944
|
+
return join29(activeLinkRoot(), "pending-deprovisions.json");
|
|
51067
51945
|
}
|
|
51068
51946
|
function loadPendingDeprovisions() {
|
|
51069
51947
|
try {
|
|
@@ -51239,7 +52117,7 @@ init_dist4();
|
|
|
51239
52117
|
|
|
51240
52118
|
// ../../packages/host-core/dist/pty-manager.js
|
|
51241
52119
|
import { existsSync as existsSync14 } from "node:fs";
|
|
51242
|
-
import { homedir as
|
|
52120
|
+
import { homedir as homedir13 } from "node:os";
|
|
51243
52121
|
var ptyPromise = null;
|
|
51244
52122
|
function loadPty() {
|
|
51245
52123
|
if (!ptyPromise) {
|
|
@@ -51276,15 +52154,13 @@ async function openPty(opts, sink) {
|
|
|
51276
52154
|
const pty = await loadPty();
|
|
51277
52155
|
if (!pty)
|
|
51278
52156
|
throw new Error("Terminal support is unavailable on this machine.");
|
|
52157
|
+
if (!opts.local && opts.shell && !new Set(listInterpreters().map((i) => i.path)).has(opts.shell)) {
|
|
52158
|
+
throw new Error("Requested shell is not an allowed interpreter on this machine.");
|
|
52159
|
+
}
|
|
51279
52160
|
const shell = opts.shell || defaultShell();
|
|
51280
52161
|
const args = opts.args ?? defaultArgs(shell);
|
|
51281
52162
|
const cwd = resolveCwd(opts.cwd, opts.hostRoot);
|
|
51282
|
-
const env =
|
|
51283
|
-
...process.env,
|
|
51284
|
-
...opts.env,
|
|
51285
|
-
TERM: "xterm-256color",
|
|
51286
|
-
COLORTERM: "truecolor"
|
|
51287
|
-
};
|
|
52163
|
+
const env = buildChildEnv(opts);
|
|
51288
52164
|
let proc;
|
|
51289
52165
|
try {
|
|
51290
52166
|
proc = pty.spawn(shell, args, {
|
|
@@ -51464,8 +52340,34 @@ function defaultArgs(shell) {
|
|
|
51464
52340
|
return [];
|
|
51465
52341
|
return ["bash", "zsh", "fish"].includes(idForPath(shell)) ? ["-l"] : [];
|
|
51466
52342
|
}
|
|
52343
|
+
var DANGEROUS_ENV_KEY = /^(LD_|DYLD_)|PRELOAD|^NODE_OPTIONS$|^PATH$|^BASH_ENV$|^ENV$|^ZDOTDIR$|^IFS$|^PROMPT_COMMAND$|^PERL5LIB$|^PYTHONPATH$|^PYTHONSTARTUP$|^GIT_SSH|^GIT_EXTERNAL_DIFF$/i;
|
|
52344
|
+
var REMOTE_ENV_ALLOW = ["HOME", "USER", "LOGNAME", "LANG", "LC_ALL", "LC_CTYPE", "TZ", "PATH", "SHELL", "TMPDIR", "SystemRoot", "ComSpec", "PATHEXT", "USERPROFILE", "APPDATA", "LOCALAPPDATA"];
|
|
52345
|
+
function stripDangerousEnv(env) {
|
|
52346
|
+
const out = {};
|
|
52347
|
+
if (env) {
|
|
52348
|
+
for (const [k, v] of Object.entries(env))
|
|
52349
|
+
if (!DANGEROUS_ENV_KEY.test(k))
|
|
52350
|
+
out[k] = v;
|
|
52351
|
+
}
|
|
52352
|
+
return out;
|
|
52353
|
+
}
|
|
52354
|
+
function buildChildEnv(opts) {
|
|
52355
|
+
const base = {};
|
|
52356
|
+
if (opts.local) {
|
|
52357
|
+
Object.assign(base, process.env);
|
|
52358
|
+
Object.assign(base, opts.env ?? {});
|
|
52359
|
+
} else {
|
|
52360
|
+
for (const k of REMOTE_ENV_ALLOW)
|
|
52361
|
+
if (process.env[k] != null)
|
|
52362
|
+
base[k] = process.env[k];
|
|
52363
|
+
Object.assign(base, stripDangerousEnv(opts.env));
|
|
52364
|
+
}
|
|
52365
|
+
base.TERM = "xterm-256color";
|
|
52366
|
+
base.COLORTERM = "truecolor";
|
|
52367
|
+
return base;
|
|
52368
|
+
}
|
|
51467
52369
|
function resolveCwd(cwd, hostRoot) {
|
|
51468
|
-
const fallback = hostRoot ||
|
|
52370
|
+
const fallback = hostRoot || homedir13();
|
|
51469
52371
|
if (!cwd)
|
|
51470
52372
|
return fallback;
|
|
51471
52373
|
if (!existsSync14(cwd))
|
|
@@ -51666,7 +52568,17 @@ async function createElevenLabsAgent(args) {
|
|
|
51666
52568
|
first_message: "",
|
|
51667
52569
|
language
|
|
51668
52570
|
},
|
|
51669
|
-
|
|
52571
|
+
// ElevenLabs retired "Original ASR" (provider:'elevenlabs') — after 2026-07-08
|
|
52572
|
+
// create/patch with it returns an error. scribe_realtime (Scribe v2.2 Realtime)
|
|
52573
|
+
// is the successor and supports pcm_16000 (verified against a live agent).
|
|
52574
|
+
// keyterm prompting boosts recognition of brand/feature terms the guide
|
|
52575
|
+
// discusses (Scribe realtime caps at 50 terms × 20 chars).
|
|
52576
|
+
asr: {
|
|
52577
|
+
quality: "high",
|
|
52578
|
+
provider: "scribe_realtime",
|
|
52579
|
+
user_input_audio_format: "pcm_16000",
|
|
52580
|
+
keywords: ["Tesseract", "Claude", "Anthropic", "MCP", "worktree", "subagent", "plugin"]
|
|
52581
|
+
},
|
|
51670
52582
|
tts: {
|
|
51671
52583
|
model_id: "eleven_flash_v2",
|
|
51672
52584
|
voice_id: voiceId,
|
|
@@ -51675,7 +52587,12 @@ async function createElevenLabsAgent(args) {
|
|
|
51675
52587
|
speed: 1,
|
|
51676
52588
|
similarity_boost: 0.8
|
|
51677
52589
|
},
|
|
51678
|
-
turn
|
|
52590
|
+
// turn_v3 = latest turn-taking model (better accuracy + lower turn latency).
|
|
52591
|
+
// Eagerness stays 'normal' (NOT 'patient' like the phone info-collection
|
|
52592
|
+
// bots): the guide is a real-time desktop assistant and must stay responsive.
|
|
52593
|
+
turn: { turn_timeout: 7, turn_eagerness: "normal", turn_model: "turn_v3" },
|
|
52594
|
+
// Ignore background voices (room noise / other people) to cut false barge-ins.
|
|
52595
|
+
vad: { background_voice_detection: true },
|
|
51679
52596
|
conversation: {
|
|
51680
52597
|
text_only: false,
|
|
51681
52598
|
// 2h — long working sessions. NOT per-conversation overridable (EL pins
|
|
@@ -52879,7 +53796,8 @@ function turnEnded(key2) {
|
|
|
52879
53796
|
function turnRekeyed(from, to) {
|
|
52880
53797
|
cfg?.power.turnRekeyed(from, to);
|
|
52881
53798
|
}
|
|
52882
|
-
var HOST_ROOT =
|
|
53799
|
+
var HOST_ROOT = resolveHostRoot();
|
|
53800
|
+
var HOST_CONTAIN = resolveContainmentRoot() ?? void 0;
|
|
52883
53801
|
var hooks = null;
|
|
52884
53802
|
var socket = null;
|
|
52885
53803
|
var heartbeat = null;
|
|
@@ -52920,13 +53838,43 @@ function wsUrl(token) {
|
|
|
52920
53838
|
return `${backendOrigin.replace(/^http/, "ws")}/ws?token=${encodeURIComponent(token)}`;
|
|
52921
53839
|
}
|
|
52922
53840
|
var outbox = [];
|
|
52923
|
-
function
|
|
53841
|
+
function rawSend(channel, payload, extra) {
|
|
52924
53842
|
const data = JSON.stringify({ channel, payload, ...extra });
|
|
52925
53843
|
if (socket && socket.readyState === 1)
|
|
52926
53844
|
socket.send(data);
|
|
52927
53845
|
else if (socket && socket.readyState === 0 && outbox.length < 256)
|
|
52928
53846
|
outbox.push(data);
|
|
52929
53847
|
}
|
|
53848
|
+
var hostSessions = null;
|
|
53849
|
+
var sessToClient = /* @__PURE__ */ new Map();
|
|
53850
|
+
var clientToSess = /* @__PURE__ */ new Map();
|
|
53851
|
+
var sessHost = /* @__PURE__ */ new Map();
|
|
53852
|
+
var sealChains = /* @__PURE__ */ new Map();
|
|
53853
|
+
var SEALABLE_CHANNELS = /* @__PURE__ */ new Set(["agent", "fs", "term", "browser", "editor"]);
|
|
53854
|
+
function e2eRouting(channel, sess, browserId) {
|
|
53855
|
+
return { ch: channel, host: sessHost.get(sess) ?? "", client: "", sess, bid: typeof browserId === "string" ? browserId : "" };
|
|
53856
|
+
}
|
|
53857
|
+
function e2eRequired() {
|
|
53858
|
+
return !!hostSessions && process.env.CCB_E2E_REQUIRED === "1";
|
|
53859
|
+
}
|
|
53860
|
+
function send(channel, payload, extra) {
|
|
53861
|
+
const clientId = extra && typeof extra.clientId === "string" ? extra.clientId : void 0;
|
|
53862
|
+
const sess = clientId ? clientToSess.get(clientId) : void 0;
|
|
53863
|
+
const isHandshake = !!payload && typeof payload.t === "string" && payload.t.startsWith("e2e:");
|
|
53864
|
+
if (hostSessions && clientId && sess && hostSessions.has(sess) && SEALABLE_CHANNELS.has(channel) && !isHandshake) {
|
|
53865
|
+
const routing = e2eRouting(channel, sess, extra?.browserId);
|
|
53866
|
+
const prev = sealChains.get(clientId) ?? Promise.resolve();
|
|
53867
|
+
const next = prev.then(async () => {
|
|
53868
|
+
const rec = await hostSessions.seal(sess, routing, payload);
|
|
53869
|
+
rawSend(channel, rec ?? payload, extra);
|
|
53870
|
+
}).catch(() => {
|
|
53871
|
+
rawSend(channel, payload, extra);
|
|
53872
|
+
});
|
|
53873
|
+
sealChains.set(clientId, next);
|
|
53874
|
+
return;
|
|
53875
|
+
}
|
|
53876
|
+
rawSend(channel, payload, extra);
|
|
53877
|
+
}
|
|
52930
53878
|
var p2pSubscribers = /* @__PURE__ */ new Set();
|
|
52931
53879
|
var p2pCtx = null;
|
|
52932
53880
|
var p2pReady = false;
|
|
@@ -53160,9 +54108,9 @@ function provisionActiveLinkMirror(payload) {
|
|
|
53160
54108
|
const remoteSubfolder = typeof payload.remoteSubfolder === "string" ? payload.remoteSubfolder : void 0;
|
|
53161
54109
|
let localRoot;
|
|
53162
54110
|
if (remoteParent && remoteSubfolder) {
|
|
53163
|
-
localRoot =
|
|
54111
|
+
localRoot = join30(remoteParent, safeFolderName(remoteSubfolder));
|
|
53164
54112
|
} else {
|
|
53165
|
-
localRoot =
|
|
54113
|
+
localRoot = join30(HOST_ROOT, "Tesseract Mirrors", safeFolderName(projectName));
|
|
53166
54114
|
}
|
|
53167
54115
|
if (loadLinkConfigs().some((c) => c.localRoot === localRoot && c.linkId !== linkId)) {
|
|
53168
54116
|
localRoot = `${localRoot}-${linkId.slice(0, 6)}`;
|
|
@@ -53387,6 +54335,8 @@ async function handleInboundAgent(msg, driverClientId) {
|
|
|
53387
54335
|
const newId = await b.forkSession?.(msg.sessionId, { projectDir: msg.projectDir, upToMessageId: msg.upToMessageId, title: msg.title }).catch(() => null);
|
|
53388
54336
|
if (newId)
|
|
53389
54337
|
send("agent", { type: "session:forked", clientRequestId: msg.clientRequestId, sessionId: newId, sourceSessionId: msg.sessionId, title: msg.title }, route2);
|
|
54338
|
+
else
|
|
54339
|
+
send("agent", { type: "session:forkFailed", clientRequestId: msg.clientRequestId, sourceSessionId: msg.sessionId }, route2);
|
|
53390
54340
|
break;
|
|
53391
54341
|
}
|
|
53392
54342
|
case "agent:context_usage":
|
|
@@ -53463,7 +54413,7 @@ async function serveInboundFs(requestId, op, args, driverClientId) {
|
|
|
53463
54413
|
return;
|
|
53464
54414
|
const route2 = driverClientId ? { clientId: driverClientId } : {};
|
|
53465
54415
|
try {
|
|
53466
|
-
const result = await runRemoteFsOp(HOST_ROOT, op, args ?? []);
|
|
54416
|
+
const result = await runRemoteFsOp(HOST_ROOT, op, args ?? [], driverClientId);
|
|
53467
54417
|
send("fs", { type: "fs:res", requestId, result }, route2);
|
|
53468
54418
|
if (op === "openFolder" && driverClientId && result && typeof result.root === "string") {
|
|
53469
54419
|
const root = result.root;
|
|
@@ -53482,7 +54432,7 @@ async function serveInboundTerm(p, driverClientId) {
|
|
|
53482
54432
|
exit: (ptyId, exitCode, signal) => send("term", { type: "term:exit", ptyId, exitCode, signal }, route2)
|
|
53483
54433
|
};
|
|
53484
54434
|
try {
|
|
53485
|
-
const { pid } = await openPty({ ptyId: p.ptyId, clientId: driverClientId, shell: p.shell, args: p.args, cwd: p.cwd, cols: p.cols, rows: p.rows, env: p.env, hostRoot:
|
|
54435
|
+
const { pid } = await openPty({ ptyId: p.ptyId, clientId: driverClientId, shell: p.shell, args: p.args, cwd: p.cwd, cols: p.cols, rows: p.rows, env: p.env, hostRoot: HOST_CONTAIN }, sink);
|
|
53486
54436
|
send("term", { type: "term:opened", ptyId: p.ptyId, requestId: p.requestId, pid }, route2);
|
|
53487
54437
|
} catch (err) {
|
|
53488
54438
|
send("term", { type: "term:openError", ptyId: p.ptyId, requestId: p.requestId, error: err instanceof Error ? err.message : String(err) }, route2);
|
|
@@ -53505,6 +54455,15 @@ async function serveInboundTerm(p, driverClientId) {
|
|
|
53505
54455
|
}
|
|
53506
54456
|
function releaseDriver(clientId) {
|
|
53507
54457
|
unwatchRemoteClient(clientId);
|
|
54458
|
+
releaseFsClient(clientId);
|
|
54459
|
+
const sess = clientToSess.get(clientId);
|
|
54460
|
+
if (sess) {
|
|
54461
|
+
hostSessions?.drop(sess);
|
|
54462
|
+
sessToClient.delete(sess);
|
|
54463
|
+
sessHost.delete(sess);
|
|
54464
|
+
}
|
|
54465
|
+
clientToSess.delete(clientId);
|
|
54466
|
+
sealChains.delete(clientId);
|
|
53508
54467
|
voiceControls.get(clientId)?.stop("disconnected");
|
|
53509
54468
|
voiceControls.delete(clientId);
|
|
53510
54469
|
unwatchSessionClient(clientId);
|
|
@@ -53617,7 +54576,7 @@ function registerAsHost() {
|
|
|
53617
54576
|
void ensureP2pReady();
|
|
53618
54577
|
const kind = process.env.CCB_HOST_KIND === "cloud" ? "cloud" : void 0;
|
|
53619
54578
|
const name = kind === "cloud" ? "Cloud agent" : hostname3();
|
|
53620
|
-
send("host", { type: "host:register", name, platform: process.platform, deviceId, caps: ["agent", "fs", "term"], ...kind ? { kind } : {}, ...staticMetrics(hostVersion), ...p2pAdvert ?? {} });
|
|
54579
|
+
send("host", { type: "host:register", name, platform: process.platform, deviceId, caps: hostSessions ? ["agent", "fs", "term", "e2e"] : ["agent", "fs", "term"], ...kind ? { kind } : {}, ...staticMetrics(hostVersion), ...p2pAdvert ?? {} });
|
|
53621
54580
|
if (!heartbeat)
|
|
53622
54581
|
heartbeat = setInterval(() => send("host", { type: "host:heartbeat", ...dynamicMetrics() }), 2e4);
|
|
53623
54582
|
}
|
|
@@ -53654,10 +54613,8 @@ function connect() {
|
|
|
53654
54613
|
}
|
|
53655
54614
|
}
|
|
53656
54615
|
};
|
|
53657
|
-
|
|
53658
|
-
|
|
53659
|
-
if (typeof data !== "string")
|
|
53660
|
-
return;
|
|
54616
|
+
let inboundChain = Promise.resolve();
|
|
54617
|
+
const handleInbound = async (data) => {
|
|
53661
54618
|
let env;
|
|
53662
54619
|
try {
|
|
53663
54620
|
env = JSON.parse(data);
|
|
@@ -53681,18 +54638,50 @@ function connect() {
|
|
|
53681
54638
|
}
|
|
53682
54639
|
const inbound = typeof env.hostId === "string" && env.hostId.endsWith(`:${deviceId}`);
|
|
53683
54640
|
const clientId = typeof env.clientId === "string" ? env.clientId : void 0;
|
|
54641
|
+
const driving = inbound && sharingEnabled() && !entitlementDenied;
|
|
54642
|
+
if (hostSessions && inbound && clientId && channel && SEALABLE_CHANNELS.has(channel)) {
|
|
54643
|
+
const pp = env.payload;
|
|
54644
|
+
if (pp && typeof pp.t === "string" && pp.t.startsWith("e2e:")) {
|
|
54645
|
+
const hs = env.payload;
|
|
54646
|
+
if (hs.t === "e2e:hello") {
|
|
54647
|
+
sessToClient.set(hs.sess, clientId);
|
|
54648
|
+
clientToSess.set(clientId, hs.sess);
|
|
54649
|
+
if (typeof env.hostId === "string")
|
|
54650
|
+
sessHost.set(hs.sess, env.hostId);
|
|
54651
|
+
}
|
|
54652
|
+
await hostSessions.onHandshake(hs);
|
|
54653
|
+
return;
|
|
54654
|
+
}
|
|
54655
|
+
if (pp && pp.e2e === 1) {
|
|
54656
|
+
if (!driving)
|
|
54657
|
+
return;
|
|
54658
|
+
const sess = clientToSess.get(clientId);
|
|
54659
|
+
if (!sess || !hostSessions.has(sess))
|
|
54660
|
+
return;
|
|
54661
|
+
try {
|
|
54662
|
+
const plain = await hostSessions.open(sess, e2eRouting(channel, sess, env.browserId), env.payload);
|
|
54663
|
+
await handleInbound(JSON.stringify({ ...env, payload: plain, e2eOpened: true }));
|
|
54664
|
+
} catch {
|
|
54665
|
+
}
|
|
54666
|
+
return;
|
|
54667
|
+
}
|
|
54668
|
+
}
|
|
53684
54669
|
if (channel === "activelink") {
|
|
53685
54670
|
handleActiveLinkChannel(env.payload, inbound);
|
|
53686
54671
|
return;
|
|
53687
54672
|
}
|
|
53688
54673
|
if (channel === "voice") {
|
|
53689
|
-
if (inbound)
|
|
53690
|
-
|
|
54674
|
+
if (inbound) {
|
|
54675
|
+
if (driving)
|
|
54676
|
+
void handleInboundVoice(env.payload, clientId);
|
|
54677
|
+
return;
|
|
54678
|
+
}
|
|
53691
54679
|
return;
|
|
53692
54680
|
}
|
|
53693
54681
|
if (channel === "agent") {
|
|
53694
54682
|
if (inbound) {
|
|
53695
|
-
|
|
54683
|
+
if (driving)
|
|
54684
|
+
void handleInboundAgent(env.payload, clientId);
|
|
53696
54685
|
return;
|
|
53697
54686
|
}
|
|
53698
54687
|
const p = env.payload;
|
|
@@ -53709,7 +54698,13 @@ function connect() {
|
|
|
53709
54698
|
if (channel === "fs") {
|
|
53710
54699
|
const p = env.payload;
|
|
53711
54700
|
if (inbound && p.type === "fs:req") {
|
|
53712
|
-
|
|
54701
|
+
if (driving) {
|
|
54702
|
+
if (e2eRequired() && !env.e2eOpened) {
|
|
54703
|
+
send("fs", { type: "fs:res", requestId: p.requestId, error: "This machine requires an encrypted connection \u2014 please update your app." }, clientId ? { clientId } : {});
|
|
54704
|
+
return;
|
|
54705
|
+
}
|
|
54706
|
+
void serveInboundFs(p.requestId, p.op, p.args, clientId);
|
|
54707
|
+
}
|
|
53713
54708
|
return;
|
|
53714
54709
|
}
|
|
53715
54710
|
if (p.type === "fs:res") {
|
|
@@ -53725,27 +54720,27 @@ function connect() {
|
|
|
53725
54720
|
return;
|
|
53726
54721
|
}
|
|
53727
54722
|
if (p.type === "fs:transferStart") {
|
|
53728
|
-
if (
|
|
54723
|
+
if (driving)
|
|
53729
54724
|
void beginOutboundTransfer(p, clientId ?? "");
|
|
53730
54725
|
return;
|
|
53731
54726
|
}
|
|
53732
54727
|
if (p.type === "fs:transferManifest") {
|
|
53733
|
-
if (
|
|
54728
|
+
if (driving)
|
|
53734
54729
|
void beginInboundTransfer(p, HOST_ROOT, transferSend);
|
|
53735
54730
|
return;
|
|
53736
54731
|
}
|
|
53737
54732
|
if (p.type === "fs:transferChunk") {
|
|
53738
|
-
if (
|
|
54733
|
+
if (driving)
|
|
53739
54734
|
void writeInboundChunk(p, transferSend);
|
|
53740
54735
|
return;
|
|
53741
54736
|
}
|
|
53742
54737
|
if (p.type === "fs:transferAck") {
|
|
53743
|
-
if (
|
|
54738
|
+
if (driving)
|
|
53744
54739
|
onTransferAck(p);
|
|
53745
54740
|
return;
|
|
53746
54741
|
}
|
|
53747
54742
|
if (p.type === "fs:transferCancel") {
|
|
53748
|
-
if (
|
|
54743
|
+
if (driving)
|
|
53749
54744
|
cancelTransfer(p.transferId, transferSend);
|
|
53750
54745
|
return;
|
|
53751
54746
|
}
|
|
@@ -53758,7 +54753,14 @@ function connect() {
|
|
|
53758
54753
|
if (channel === "term") {
|
|
53759
54754
|
const p = env.payload;
|
|
53760
54755
|
if (inbound) {
|
|
53761
|
-
|
|
54756
|
+
if (driving) {
|
|
54757
|
+
if (e2eRequired() && !env.e2eOpened) {
|
|
54758
|
+
if (p.type === "term:open")
|
|
54759
|
+
send("term", { type: "term:openError", ptyId: p.ptyId, requestId: p.requestId, error: "This machine requires an encrypted connection \u2014 please update your app." }, clientId ? { clientId } : {});
|
|
54760
|
+
return;
|
|
54761
|
+
}
|
|
54762
|
+
void serveInboundTerm(p, clientId);
|
|
54763
|
+
}
|
|
53762
54764
|
return;
|
|
53763
54765
|
}
|
|
53764
54766
|
hooks?.pushTerm(p);
|
|
@@ -53773,7 +54775,7 @@ function connect() {
|
|
|
53773
54775
|
r(p.iceServers);
|
|
53774
54776
|
return;
|
|
53775
54777
|
}
|
|
53776
|
-
if (
|
|
54778
|
+
if (driving && clientId && typeof p?.type === "string" && p.type.startsWith("signal:dc-")) {
|
|
53777
54779
|
void handleDcSignal(clientId, p, HOST_ROOT, ownHostId(), (payload) => send("signal", payload, { clientId, browserId: "fsdc" }));
|
|
53778
54780
|
}
|
|
53779
54781
|
return;
|
|
@@ -53790,7 +54792,7 @@ function connect() {
|
|
|
53790
54792
|
}
|
|
53791
54793
|
if (channel === "browser") {
|
|
53792
54794
|
console.log(`[browser/host] '${String(env.payload?.type)}' inbound=${inbound} client=${clientId ? String(clientId).slice(0, 8) : "none"} bid=${env.browserId ?? "default"}`);
|
|
53793
|
-
if (!
|
|
54795
|
+
if (!driving)
|
|
53794
54796
|
return;
|
|
53795
54797
|
const p = env.payload;
|
|
53796
54798
|
if (!p || typeof p.type !== "string" || !clientId)
|
|
@@ -53828,7 +54830,7 @@ function connect() {
|
|
|
53828
54830
|
if (channel === "editor") {
|
|
53829
54831
|
const ep = env.payload;
|
|
53830
54832
|
console.log(`[editor/host] '${String(ep?.channel)}' inbound=${inbound} client=${clientId ? String(clientId).slice(0, 8) : "none"} bid=${env.browserId ?? "default"}`);
|
|
53831
|
-
if (!
|
|
54833
|
+
if (!driving)
|
|
53832
54834
|
return;
|
|
53833
54835
|
if (!ep || typeof ep.channel !== "string" || !clientId)
|
|
53834
54836
|
return;
|
|
@@ -53847,6 +54849,16 @@ function connect() {
|
|
|
53847
54849
|
return;
|
|
53848
54850
|
}
|
|
53849
54851
|
};
|
|
54852
|
+
socket.onmessage = (ev) => {
|
|
54853
|
+
const data = ev.data;
|
|
54854
|
+
if (typeof data !== "string")
|
|
54855
|
+
return;
|
|
54856
|
+
if (hostSessions)
|
|
54857
|
+
inboundChain = inboundChain.then(() => handleInbound(data)).catch(() => {
|
|
54858
|
+
});
|
|
54859
|
+
else
|
|
54860
|
+
void handleInbound(data);
|
|
54861
|
+
};
|
|
53850
54862
|
socket.onclose = () => {
|
|
53851
54863
|
cleanup();
|
|
53852
54864
|
if (!stopped)
|
|
@@ -53862,6 +54874,19 @@ function startControlLink(config2, h) {
|
|
|
53862
54874
|
hostVersion = config2.version;
|
|
53863
54875
|
hooks = h;
|
|
53864
54876
|
stopped = false;
|
|
54877
|
+
if (config2.e2e && !hostSessions) {
|
|
54878
|
+
const e2e = config2.e2e;
|
|
54879
|
+
hostSessions = new E2ESessions({
|
|
54880
|
+
selfDeviceId: e2e.selfDeviceId,
|
|
54881
|
+
identityPriv: e2e.identityPriv,
|
|
54882
|
+
getPeerIdentityPub: e2e.getPeerIdentityPub,
|
|
54883
|
+
sendHandshake: (msg) => {
|
|
54884
|
+
const cid = sessToClient.get(msg.sess);
|
|
54885
|
+
if (cid)
|
|
54886
|
+
rawSend("agent", msg, { clientId: cid });
|
|
54887
|
+
}
|
|
54888
|
+
});
|
|
54889
|
+
}
|
|
53865
54890
|
hooks.pushHosts(presentedHosts());
|
|
53866
54891
|
hooks.ownHostResolved(ownHostId());
|
|
53867
54892
|
ensureLinkConfigWatch();
|
|
@@ -53915,8 +54940,26 @@ function stopControlLink() {
|
|
|
53915
54940
|
}
|
|
53916
54941
|
|
|
53917
54942
|
// ../../packages/host-core/dist/local-server.js
|
|
54943
|
+
import { timingSafeEqual as timingSafeEqual3 } from "node:crypto";
|
|
53918
54944
|
init_dist2();
|
|
53919
54945
|
var connSeq = 0;
|
|
54946
|
+
function tokenMatches(got, want) {
|
|
54947
|
+
if (!got)
|
|
54948
|
+
return false;
|
|
54949
|
+
const a = Buffer.from(got);
|
|
54950
|
+
const b = Buffer.from(want);
|
|
54951
|
+
return a.length === b.length && timingSafeEqual3(a, b);
|
|
54952
|
+
}
|
|
54953
|
+
function originAllowed(origin) {
|
|
54954
|
+
if (!origin)
|
|
54955
|
+
return true;
|
|
54956
|
+
try {
|
|
54957
|
+
const h = new URL(origin).hostname;
|
|
54958
|
+
return h === "127.0.0.1" || h === "localhost" || h === "::1" || h === "[::1]";
|
|
54959
|
+
} catch {
|
|
54960
|
+
return false;
|
|
54961
|
+
}
|
|
54962
|
+
}
|
|
53920
54963
|
async function startLocalServer(opts) {
|
|
53921
54964
|
const log3 = opts.log ?? (() => {
|
|
53922
54965
|
});
|
|
@@ -53930,7 +54973,8 @@ async function startLocalServer(opts) {
|
|
|
53930
54973
|
log3(`local listener on 127.0.0.1:${port}`);
|
|
53931
54974
|
wss.on("connection", (ws, req) => {
|
|
53932
54975
|
const url2 = new URL(req.url ?? "/", "http://127.0.0.1");
|
|
53933
|
-
|
|
54976
|
+
const origin = req.headers.origin ?? void 0;
|
|
54977
|
+
if (!originAllowed(origin) || !tokenMatches(url2.searchParams.get("token"), opts.token)) {
|
|
53934
54978
|
try {
|
|
53935
54979
|
ws.close(1008, "unauthorized");
|
|
53936
54980
|
} catch {
|
|
@@ -53949,11 +54993,11 @@ async function startLocalServer(opts) {
|
|
|
53949
54993
|
};
|
|
53950
54994
|
const heldSessions = /* @__PURE__ */ new Set();
|
|
53951
54995
|
let voiceControl = null;
|
|
53952
|
-
const unregisterSink = registerAgentSink((msg,
|
|
53953
|
-
const isCron =
|
|
53954
|
-
if (
|
|
54996
|
+
const unregisterSink = registerAgentSink((msg, origin2) => {
|
|
54997
|
+
const isCron = origin2 === "cron" && (MIRRORED_AGENT_EVENTS.has(msg.type) || msg.type === "session:created");
|
|
54998
|
+
if (origin2 !== loopbackOrigin && !isCron)
|
|
53955
54999
|
return;
|
|
53956
|
-
if (
|
|
55000
|
+
if (origin2 === loopbackOrigin)
|
|
53957
55001
|
voiceControl?.feedAgentEvent(msg);
|
|
53958
55002
|
if (msg.type === "chat:echoed")
|
|
53959
55003
|
return;
|
|
@@ -54060,6 +55104,8 @@ async function startLocalServer(opts) {
|
|
|
54060
55104
|
const newId = await b.forkSession?.(msg.sessionId, { projectDir: msg.projectDir, upToMessageId: msg.upToMessageId, title: msg.title }).catch(() => null);
|
|
54061
55105
|
if (newId)
|
|
54062
55106
|
send2("agent", { type: "session:forked", clientRequestId: msg.clientRequestId, sessionId: newId, sourceSessionId: msg.sessionId, title: msg.title });
|
|
55107
|
+
else
|
|
55108
|
+
send2("agent", { type: "session:forkFailed", clientRequestId: msg.clientRequestId, sourceSessionId: msg.sessionId });
|
|
54063
55109
|
break;
|
|
54064
55110
|
}
|
|
54065
55111
|
case "agent:context_usage":
|
|
@@ -54162,7 +55208,9 @@ async function startLocalServer(opts) {
|
|
|
54162
55208
|
const { pid } = await openPty(
|
|
54163
55209
|
// `local`: a loopback terminal is the user's OWN machine — never
|
|
54164
55210
|
// reaped by the host-role teardown that runs in this same process.
|
|
54165
|
-
|
|
55211
|
+
// hostRoot = the containment boundary (undefined when uncontained), so a
|
|
55212
|
+
// loopback terminal on a personal machine isn't clamped to a start dir.
|
|
55213
|
+
{ ptyId: p.ptyId, clientId: termClientId, shell: p.shell, args: p.args, cwd: p.cwd, cols: p.cols, rows: p.rows, env: p.env, hostRoot: resolveContainmentRoot() ?? void 0, local: true },
|
|
54166
55214
|
termSink
|
|
54167
55215
|
);
|
|
54168
55216
|
send2("term", { type: "term:opened", ptyId: p.ptyId, requestId: p.requestId, pid });
|
|
@@ -54194,13 +55242,94 @@ async function startLocalServer(opts) {
|
|
|
54194
55242
|
}
|
|
54195
55243
|
|
|
54196
55244
|
// ../../packages/host-core/dist/daemon-runtime.js
|
|
54197
|
-
import { readFileSync as
|
|
54198
|
-
import { join as
|
|
54199
|
-
import {
|
|
54200
|
-
import {
|
|
55245
|
+
import { readFileSync as readFileSync13, writeFileSync as writeFileSync13, existsSync as existsSync16, mkdirSync as mkdirSync13, unlinkSync as unlinkSync2, chmodSync as chmodSync4 } from "node:fs";
|
|
55246
|
+
import { join as join32 } from "node:path";
|
|
55247
|
+
import { randomBytes as randomBytes4 } from "node:crypto";
|
|
55248
|
+
import { hostname as hostname4 } from "node:os";
|
|
55249
|
+
|
|
55250
|
+
// ../../packages/host-core/dist/device-identity.js
|
|
55251
|
+
import { join as join31 } from "node:path";
|
|
55252
|
+
import { readFileSync as readFileSync12, writeFileSync as writeFileSync12, existsSync as existsSync15, mkdirSync as mkdirSync12, renameSync as renameSync7, chmodSync as chmodSync3 } from "node:fs";
|
|
55253
|
+
var cached2 = null;
|
|
55254
|
+
function identityPath() {
|
|
55255
|
+
return join31(daemonDir(), "identity.json");
|
|
55256
|
+
}
|
|
55257
|
+
async function loadOrCreateDeviceIdentity() {
|
|
55258
|
+
if (cached2)
|
|
55259
|
+
return cached2;
|
|
55260
|
+
try {
|
|
55261
|
+
const stored = JSON.parse(readFileSync12(identityPath(), "utf-8"));
|
|
55262
|
+
if (stored?.privateJwk) {
|
|
55263
|
+
cached2 = await importIdentity(stored.privateJwk);
|
|
55264
|
+
return cached2;
|
|
55265
|
+
}
|
|
55266
|
+
} catch {
|
|
55267
|
+
}
|
|
55268
|
+
const kp = await generateIdentityKeyPair(true);
|
|
55269
|
+
const { privateJwk, publicRaw } = await exportIdentity(kp);
|
|
55270
|
+
const dir = daemonDir();
|
|
55271
|
+
if (!existsSync15(dir))
|
|
55272
|
+
mkdirSync12(dir, { recursive: true, mode: 448 });
|
|
55273
|
+
const tmp = `${identityPath()}.tmp`;
|
|
55274
|
+
writeFileSync12(tmp, JSON.stringify({ privateJwk, publicRaw, alg: "P256-ECDSA" }, null, 2), { mode: 384 });
|
|
55275
|
+
renameSync7(tmp, identityPath());
|
|
55276
|
+
try {
|
|
55277
|
+
chmodSync3(identityPath(), 384);
|
|
55278
|
+
} catch {
|
|
55279
|
+
}
|
|
55280
|
+
cached2 = kp;
|
|
55281
|
+
return kp;
|
|
55282
|
+
}
|
|
55283
|
+
var DEVICES_BASE = (apiOrigin) => `${apiOrigin.replace(/\/$/, "")}/api/core/tesseract/devices`;
|
|
55284
|
+
async function registerDeviceKey(opts) {
|
|
55285
|
+
try {
|
|
55286
|
+
const r = await fetch(`${DEVICES_BASE(opts.apiOrigin)}/key`, {
|
|
55287
|
+
method: "POST",
|
|
55288
|
+
headers: { "content-type": "application/json", authorization: `Bearer ${opts.accessToken}` },
|
|
55289
|
+
body: JSON.stringify({
|
|
55290
|
+
deviceId: opts.deviceId,
|
|
55291
|
+
identityPub: b64u(opts.publicRaw),
|
|
55292
|
+
alg: "P256-ECDSA",
|
|
55293
|
+
tlsFingerprint256: opts.tlsFingerprint256,
|
|
55294
|
+
label: opts.label
|
|
55295
|
+
})
|
|
55296
|
+
});
|
|
55297
|
+
return r.ok;
|
|
55298
|
+
} catch {
|
|
55299
|
+
return false;
|
|
55300
|
+
}
|
|
55301
|
+
}
|
|
55302
|
+
var PEER_TTL_MS = 6e4;
|
|
55303
|
+
var peerCache = null;
|
|
55304
|
+
async function refreshPeerKeys(apiOrigin, accessToken) {
|
|
55305
|
+
try {
|
|
55306
|
+
const r = await fetch(`${DEVICES_BASE(apiOrigin)}/keys`, { headers: { authorization: `Bearer ${accessToken}` } });
|
|
55307
|
+
if (!r.ok)
|
|
55308
|
+
return;
|
|
55309
|
+
const list = await r.json();
|
|
55310
|
+
const byDevice = /* @__PURE__ */ new Map();
|
|
55311
|
+
for (const k of list)
|
|
55312
|
+
byDevice.set(k.deviceId, k);
|
|
55313
|
+
peerCache = { at: Date.now(), byDevice };
|
|
55314
|
+
} catch {
|
|
55315
|
+
}
|
|
55316
|
+
}
|
|
55317
|
+
async function getPeerIdentityPub(opts) {
|
|
55318
|
+
const stale = !peerCache || Date.now() - peerCache.at > PEER_TTL_MS;
|
|
55319
|
+
if (stale || !peerCache?.byDevice.has(opts.deviceId))
|
|
55320
|
+
await refreshPeerKeys(opts.apiOrigin, opts.accessToken);
|
|
55321
|
+
const row = peerCache?.byDevice.get(opts.deviceId);
|
|
55322
|
+
if (!row || row.revokedAt)
|
|
55323
|
+
return null;
|
|
55324
|
+
try {
|
|
55325
|
+
return unb64u(row.identityPub);
|
|
55326
|
+
} catch {
|
|
55327
|
+
return null;
|
|
55328
|
+
}
|
|
55329
|
+
}
|
|
54201
55330
|
|
|
54202
55331
|
// ../../packages/host-core/dist/power-node.js
|
|
54203
|
-
import { spawn as
|
|
55332
|
+
import { spawn as spawn5 } from "node:child_process";
|
|
54204
55333
|
import { platform } from "node:os";
|
|
54205
55334
|
function createNodePowerController() {
|
|
54206
55335
|
const active = /* @__PURE__ */ new Set();
|
|
@@ -54208,9 +55337,9 @@ function createNodePowerController() {
|
|
|
54208
55337
|
function spawnInhibitor() {
|
|
54209
55338
|
try {
|
|
54210
55339
|
if (platform() === "darwin")
|
|
54211
|
-
return
|
|
55340
|
+
return spawn5("caffeinate", ["-i"], { stdio: "ignore" });
|
|
54212
55341
|
if (platform() === "linux") {
|
|
54213
|
-
return
|
|
55342
|
+
return spawn5("systemd-inhibit", ["--what=sleep", "--why=tesseract agent running", "--mode=block", "sleep", "infinity"], { stdio: "ignore" });
|
|
54214
55343
|
}
|
|
54215
55344
|
} catch {
|
|
54216
55345
|
}
|
|
@@ -54262,8 +55391,8 @@ async function runDaemon(opts = {}) {
|
|
|
54262
55391
|
const VERSION = opts.version ?? process.env.CCB_DAEMON_VERSION ?? "dev";
|
|
54263
55392
|
const TAG = opts.tag ?? "host-daemon";
|
|
54264
55393
|
const log3 = (...args) => console.log(`[${TAG} ${(/* @__PURE__ */ new Date()).toISOString()}]`, ...args);
|
|
54265
|
-
const pidfile = () =>
|
|
54266
|
-
const statusFile = () =>
|
|
55394
|
+
const pidfile = () => join32(daemonDir(), "daemon.lock");
|
|
55395
|
+
const statusFile = () => join32(daemonDir(), "status.json");
|
|
54267
55396
|
const isAlive2 = (pid) => {
|
|
54268
55397
|
try {
|
|
54269
55398
|
process.kill(pid, 0);
|
|
@@ -54275,17 +55404,17 @@ async function runDaemon(opts = {}) {
|
|
|
54275
55404
|
function acquireLock() {
|
|
54276
55405
|
const f = pidfile();
|
|
54277
55406
|
try {
|
|
54278
|
-
if (
|
|
54279
|
-
const prev = Number(
|
|
55407
|
+
if (existsSync16(f)) {
|
|
55408
|
+
const prev = Number(readFileSync13(f, "utf-8").trim());
|
|
54280
55409
|
if (prev && prev !== process.pid && isAlive2(prev))
|
|
54281
55410
|
return false;
|
|
54282
55411
|
}
|
|
54283
55412
|
} catch {
|
|
54284
55413
|
}
|
|
54285
55414
|
try {
|
|
54286
|
-
if (!
|
|
54287
|
-
|
|
54288
|
-
|
|
55415
|
+
if (!existsSync16(daemonDir()))
|
|
55416
|
+
mkdirSync13(daemonDir(), { recursive: true, mode: 448 });
|
|
55417
|
+
writeFileSync13(f, String(process.pid));
|
|
54289
55418
|
} catch {
|
|
54290
55419
|
}
|
|
54291
55420
|
return true;
|
|
@@ -54293,15 +55422,15 @@ async function runDaemon(opts = {}) {
|
|
|
54293
55422
|
function releaseLock() {
|
|
54294
55423
|
try {
|
|
54295
55424
|
const f = pidfile();
|
|
54296
|
-
if (
|
|
55425
|
+
if (existsSync16(f) && Number(readFileSync13(f, "utf-8").trim()) === process.pid)
|
|
54297
55426
|
unlinkSync2(f);
|
|
54298
55427
|
} catch {
|
|
54299
55428
|
}
|
|
54300
55429
|
}
|
|
54301
55430
|
function writeStatus(s) {
|
|
54302
55431
|
try {
|
|
54303
|
-
|
|
54304
|
-
|
|
55432
|
+
writeFileSync13(statusFile(), JSON.stringify({ pid: process.pid, version: VERSION, execPath: process.execPath, updatedAt: (/* @__PURE__ */ new Date()).toISOString(), ...localInfo ?? {}, ...s }));
|
|
55433
|
+
chmodSync4(statusFile(), 384);
|
|
54305
55434
|
} catch {
|
|
54306
55435
|
}
|
|
54307
55436
|
}
|
|
@@ -54334,9 +55463,9 @@ async function runDaemon(opts = {}) {
|
|
|
54334
55463
|
process.on("SIGTERM", () => shutdown(0));
|
|
54335
55464
|
process.on("SIGINT", () => shutdown(0));
|
|
54336
55465
|
process.env.CCB_HOST_DAEMON = "1";
|
|
54337
|
-
const localToken =
|
|
55466
|
+
const localToken = randomBytes4(24).toString("hex");
|
|
54338
55467
|
try {
|
|
54339
|
-
localServer = await startLocalServer({ hostRoot:
|
|
55468
|
+
localServer = await startLocalServer({ hostRoot: resolveHostRoot(), token: localToken, log: log3 });
|
|
54340
55469
|
localInfo = { localPort: localServer.port, localToken };
|
|
54341
55470
|
} catch (e) {
|
|
54342
55471
|
log3("local listener failed to start", e.message);
|
|
@@ -54351,6 +55480,15 @@ async function runDaemon(opts = {}) {
|
|
|
54351
55480
|
const c = creds2;
|
|
54352
55481
|
let accessToken = c.accessToken;
|
|
54353
55482
|
const tokenSource = { getToken: () => accessToken, getUserId: () => c.userId, backendOrigin: c.backendOrigin };
|
|
55483
|
+
void (async () => {
|
|
55484
|
+
try {
|
|
55485
|
+
const id = await loadOrCreateDeviceIdentity();
|
|
55486
|
+
const ok = await registerDeviceKey({ apiOrigin: c.apiOrigin, accessToken, deviceId: c.deviceId, publicRaw: id.publicRaw, label: hostname4() });
|
|
55487
|
+
log3(ok ? "e2e identity registered" : "e2e identity registration not accepted (will retry on restart)");
|
|
55488
|
+
} catch (e) {
|
|
55489
|
+
log3("e2e identity registration failed (non-fatal)", e.message);
|
|
55490
|
+
}
|
|
55491
|
+
})();
|
|
54354
55492
|
async function refresh() {
|
|
54355
55493
|
try {
|
|
54356
55494
|
const r = await fetch(`${c.apiOrigin}/auth/device-token/refresh`, {
|
|
@@ -54363,7 +55501,7 @@ async function runDaemon(opts = {}) {
|
|
|
54363
55501
|
if (r.status === 401) {
|
|
54364
55502
|
log3("device token no longer valid \u2014 dropping to local-only on restart");
|
|
54365
55503
|
try {
|
|
54366
|
-
unlinkSync2(
|
|
55504
|
+
unlinkSync2(join32(daemonDir(), "credentials.json"));
|
|
54367
55505
|
} catch {
|
|
54368
55506
|
}
|
|
54369
55507
|
shutdown(0);
|
|
@@ -54396,7 +55534,20 @@ async function runDaemon(opts = {}) {
|
|
|
54396
55534
|
pushTerm: () => {
|
|
54397
55535
|
}
|
|
54398
55536
|
};
|
|
54399
|
-
|
|
55537
|
+
let e2e;
|
|
55538
|
+
if (process.env.CCB_E2E === "1") {
|
|
55539
|
+
try {
|
|
55540
|
+
const id = await loadOrCreateDeviceIdentity();
|
|
55541
|
+
e2e = {
|
|
55542
|
+
selfDeviceId: c.deviceId,
|
|
55543
|
+
identityPriv: id.privateKey,
|
|
55544
|
+
getPeerIdentityPub: (d) => getPeerIdentityPub({ apiOrigin: c.apiOrigin, accessToken, deviceId: d })
|
|
55545
|
+
};
|
|
55546
|
+
} catch (err) {
|
|
55547
|
+
log3("e2e responder init failed (staying plaintext)", err.message);
|
|
55548
|
+
}
|
|
55549
|
+
}
|
|
55550
|
+
startControlLink({ deviceId: c.deviceId, power: createNodePowerController(), tokenSource, version: VERSION, e2e }, hooks2);
|
|
54400
55551
|
log3(`started \u2014 device ${c.deviceId}, user ${c.userId}, relay ${c.backendOrigin}, local :${localInfo?.localPort ?? "off"}, version ${VERSION}`);
|
|
54401
55552
|
await refresh();
|
|
54402
55553
|
timer = setInterval(() => {
|
|
@@ -54408,17 +55559,17 @@ async function runDaemon(opts = {}) {
|
|
|
54408
55559
|
import { platform as platform2 } from "node:os";
|
|
54409
55560
|
|
|
54410
55561
|
// ../../packages/host-core/dist/service/launchd.js
|
|
54411
|
-
import { writeFileSync as
|
|
54412
|
-
import { dirname as dirname15, join as
|
|
54413
|
-
import { homedir as
|
|
55562
|
+
import { writeFileSync as writeFileSync14, existsSync as existsSync17, mkdirSync as mkdirSync14, unlinkSync as unlinkSync3 } from "node:fs";
|
|
55563
|
+
import { dirname as dirname15, join as join33 } from "node:path";
|
|
55564
|
+
import { homedir as homedir14, userInfo } from "node:os";
|
|
54414
55565
|
|
|
54415
55566
|
// ../../packages/host-core/dist/service/exec.js
|
|
54416
|
-
import { execFile as execFile5, spawn as
|
|
55567
|
+
import { execFile as execFile5, spawn as spawn6 } from "node:child_process";
|
|
54417
55568
|
import { promisify as promisify4 } from "node:util";
|
|
54418
55569
|
var run = promisify4(execFile5);
|
|
54419
55570
|
function runInherit(cmd, args) {
|
|
54420
55571
|
return new Promise((resolve10) => {
|
|
54421
|
-
const p =
|
|
55572
|
+
const p = spawn6(cmd, args, { stdio: "inherit" });
|
|
54422
55573
|
p.on("error", () => resolve10());
|
|
54423
55574
|
p.on("close", () => resolve10());
|
|
54424
55575
|
});
|
|
@@ -54433,10 +55584,10 @@ function xml(s) {
|
|
|
54433
55584
|
}
|
|
54434
55585
|
function makeLaunchd(instanceId) {
|
|
54435
55586
|
const label = SERVICE_LABEL_BASE + (instanceId ? `.${instanceId}` : "");
|
|
54436
|
-
const plistPath = () =>
|
|
55587
|
+
const plistPath = () => join33(homedir14(), "Library", "LaunchAgents", `${label}.plist`);
|
|
54437
55588
|
const domainTarget = () => `gui/${userInfo().uid}`;
|
|
54438
55589
|
const serviceTarget = () => `${domainTarget()}/${label}`;
|
|
54439
|
-
const logFile = () =>
|
|
55590
|
+
const logFile = () => join33(daemonDir(), "daemon.log");
|
|
54440
55591
|
function buildPlist(spec) {
|
|
54441
55592
|
const args = [spec.exec, ...spec.args].map((a) => ` <string>${xml(a)}</string>`).join("\n");
|
|
54442
55593
|
const env = Object.entries(spec.env).map(([k, v]) => ` <key>${xml(k)}</key>
|
|
@@ -54476,9 +55627,9 @@ ${env}
|
|
|
54476
55627
|
}
|
|
54477
55628
|
return {
|
|
54478
55629
|
async install(spec) {
|
|
54479
|
-
|
|
54480
|
-
|
|
54481
|
-
|
|
55630
|
+
mkdirSync14(dirname15(plistPath()), { recursive: true });
|
|
55631
|
+
mkdirSync14(dirname15(spec.logPath), { recursive: true });
|
|
55632
|
+
writeFileSync14(plistPath(), buildPlist(spec));
|
|
54482
55633
|
await run("launchctl", ["bootout", serviceTarget()]).catch(() => {
|
|
54483
55634
|
});
|
|
54484
55635
|
await run("launchctl", ["bootstrap", domainTarget(), plistPath()]);
|
|
@@ -54490,7 +55641,7 @@ ${env}
|
|
|
54490
55641
|
async uninstall() {
|
|
54491
55642
|
await run("launchctl", ["bootout", serviceTarget()]).catch(() => {
|
|
54492
55643
|
});
|
|
54493
|
-
if (
|
|
55644
|
+
if (existsSync17(plistPath())) {
|
|
54494
55645
|
try {
|
|
54495
55646
|
unlinkSync3(plistPath());
|
|
54496
55647
|
} catch {
|
|
@@ -54501,7 +55652,7 @@ ${env}
|
|
|
54501
55652
|
await this.install(spec);
|
|
54502
55653
|
},
|
|
54503
55654
|
async isInstalled() {
|
|
54504
|
-
return
|
|
55655
|
+
return existsSync17(plistPath());
|
|
54505
55656
|
},
|
|
54506
55657
|
async start() {
|
|
54507
55658
|
await run("launchctl", ["bootstrap", domainTarget(), plistPath()]).catch(() => {
|
|
@@ -54518,7 +55669,7 @@ ${env}
|
|
|
54518
55669
|
},
|
|
54519
55670
|
async logs(follow) {
|
|
54520
55671
|
const f = logFile();
|
|
54521
|
-
if (!
|
|
55672
|
+
if (!existsSync17(f)) {
|
|
54522
55673
|
console.log(`No log yet at ${f} \u2014 has the service started?`);
|
|
54523
55674
|
return;
|
|
54524
55675
|
}
|
|
@@ -54528,17 +55679,17 @@ ${env}
|
|
|
54528
55679
|
}
|
|
54529
55680
|
|
|
54530
55681
|
// ../../packages/host-core/dist/service/systemd.js
|
|
54531
|
-
import { writeFileSync as
|
|
54532
|
-
import { dirname as dirname16, join as
|
|
54533
|
-
import { homedir as
|
|
55682
|
+
import { writeFileSync as writeFileSync15, existsSync as existsSync18, mkdirSync as mkdirSync15, unlinkSync as unlinkSync4 } from "node:fs";
|
|
55683
|
+
import { dirname as dirname16, join as join34 } from "node:path";
|
|
55684
|
+
import { homedir as homedir15, userInfo as userInfo2 } from "node:os";
|
|
54534
55685
|
function shq(s) {
|
|
54535
55686
|
return `"${s.replace(/(["\\$`])/g, "\\$1")}"`;
|
|
54536
55687
|
}
|
|
54537
55688
|
function makeSystemd(instanceId) {
|
|
54538
55689
|
const unit = `tesseract-host${instanceId ? `-${instanceId}` : ""}.service`;
|
|
54539
55690
|
const unitPath = () => {
|
|
54540
|
-
const base = process.env.XDG_CONFIG_HOME ||
|
|
54541
|
-
return
|
|
55691
|
+
const base = process.env.XDG_CONFIG_HOME || join34(homedir15(), ".config");
|
|
55692
|
+
return join34(base, "systemd", "user", unit);
|
|
54542
55693
|
};
|
|
54543
55694
|
function buildUnit(spec) {
|
|
54544
55695
|
const execStart = [spec.exec, ...spec.args].map(shq).join(" ");
|
|
@@ -54563,9 +55714,9 @@ WantedBy=default.target
|
|
|
54563
55714
|
}
|
|
54564
55715
|
return {
|
|
54565
55716
|
async install(spec) {
|
|
54566
|
-
|
|
54567
|
-
|
|
54568
|
-
|
|
55717
|
+
mkdirSync15(dirname16(unitPath()), { recursive: true });
|
|
55718
|
+
mkdirSync15(dirname16(spec.logPath), { recursive: true });
|
|
55719
|
+
writeFileSync15(unitPath(), buildUnit(spec));
|
|
54569
55720
|
await run("loginctl", ["enable-linger", userInfo2().username]).catch(() => {
|
|
54570
55721
|
});
|
|
54571
55722
|
await run("systemctl", ["--user", "daemon-reload"]).catch(() => {
|
|
@@ -54577,7 +55728,7 @@ WantedBy=default.target
|
|
|
54577
55728
|
async uninstall() {
|
|
54578
55729
|
await run("systemctl", ["--user", "disable", "--now", unit]).catch(() => {
|
|
54579
55730
|
});
|
|
54580
|
-
if (
|
|
55731
|
+
if (existsSync18(unitPath())) {
|
|
54581
55732
|
try {
|
|
54582
55733
|
unlinkSync4(unitPath());
|
|
54583
55734
|
} catch {
|
|
@@ -54590,7 +55741,7 @@ WantedBy=default.target
|
|
|
54590
55741
|
await this.install(spec);
|
|
54591
55742
|
},
|
|
54592
55743
|
async isInstalled() {
|
|
54593
|
-
return
|
|
55744
|
+
return existsSync18(unitPath());
|
|
54594
55745
|
},
|
|
54595
55746
|
async start() {
|
|
54596
55747
|
await run("systemctl", ["--user", "start", unit]);
|
|
@@ -54608,25 +55759,28 @@ WantedBy=default.target
|
|
|
54608
55759
|
}
|
|
54609
55760
|
|
|
54610
55761
|
// ../../packages/host-core/dist/service/windows.js
|
|
54611
|
-
import { writeFileSync as
|
|
54612
|
-
import { dirname as dirname17, join as
|
|
55762
|
+
import { writeFileSync as writeFileSync16, existsSync as existsSync19, mkdirSync as mkdirSync16, unlinkSync as unlinkSync5 } from "node:fs";
|
|
55763
|
+
import { dirname as dirname17, join as join35 } from "node:path";
|
|
54613
55764
|
function makeWindowsTask(instanceId) {
|
|
54614
55765
|
const task = `Fineguide\\TesseractHost${instanceId ? `-${instanceId}` : ""}`;
|
|
54615
|
-
const wrapperPath = () =>
|
|
54616
|
-
const logFile = () =>
|
|
55766
|
+
const wrapperPath = () => join35(daemonDir(), `run-host${instanceId ? `-${instanceId}` : ""}.cmd`);
|
|
55767
|
+
const logFile = () => join35(daemonDir(), "daemon.log");
|
|
55768
|
+
const oneLine2 = (s) => s.replace(/[\r\n]+/g, " ");
|
|
55769
|
+
const envVal = (v) => oneLine2(v).replace(/%/g, "%%").replace(/"/g, "");
|
|
55770
|
+
const cmdArg = (a) => `"${oneLine2(a).replace(/"/g, "")}"`;
|
|
54617
55771
|
function buildWrapper(spec) {
|
|
54618
|
-
const env = Object.entries(spec.env).map(([k, v]) => `set "${k}=${v}"`).join("\r\n");
|
|
54619
|
-
const cmd = [spec.exec, ...spec.args].map(
|
|
55772
|
+
const env = Object.entries(spec.env).map(([k, v]) => `set "${envVal(k)}=${envVal(v)}"`).join("\r\n");
|
|
55773
|
+
const cmd = [spec.exec, ...spec.args].map(cmdArg).join(" ");
|
|
54620
55774
|
return `@echo off\r
|
|
54621
55775
|
${env}\r
|
|
54622
|
-
start "" /b ${cmd} >> "${spec.logPath}" 2>&1\r
|
|
55776
|
+
start "" /b ${cmd} >> "${oneLine2(spec.logPath).replace(/"/g, "")}" 2>&1\r
|
|
54623
55777
|
`;
|
|
54624
55778
|
}
|
|
54625
55779
|
return {
|
|
54626
55780
|
async install(spec) {
|
|
54627
|
-
|
|
54628
|
-
|
|
54629
|
-
|
|
55781
|
+
mkdirSync16(dirname17(spec.logPath), { recursive: true });
|
|
55782
|
+
mkdirSync16(dirname17(wrapperPath()), { recursive: true });
|
|
55783
|
+
writeFileSync16(wrapperPath(), buildWrapper(spec));
|
|
54630
55784
|
await run("schtasks", ["/Create", "/TN", task, "/SC", "ONLOGON", "/RL", "LIMITED", "/F", "/TR", `"${wrapperPath()}"`]);
|
|
54631
55785
|
await run("schtasks", ["/Run", "/TN", task]).catch(() => {
|
|
54632
55786
|
});
|
|
@@ -54634,7 +55788,7 @@ start "" /b ${cmd} >> "${spec.logPath}" 2>&1\r
|
|
|
54634
55788
|
async uninstall() {
|
|
54635
55789
|
await run("schtasks", ["/Delete", "/TN", task, "/F"]).catch(() => {
|
|
54636
55790
|
});
|
|
54637
|
-
if (
|
|
55791
|
+
if (existsSync19(wrapperPath())) {
|
|
54638
55792
|
try {
|
|
54639
55793
|
unlinkSync5(wrapperPath());
|
|
54640
55794
|
} catch {
|
|
@@ -54664,7 +55818,7 @@ start "" /b ${cmd} >> "${spec.logPath}" 2>&1\r
|
|
|
54664
55818
|
},
|
|
54665
55819
|
async logs(follow) {
|
|
54666
55820
|
const f = logFile();
|
|
54667
|
-
if (!
|
|
55821
|
+
if (!existsSync19(f)) {
|
|
54668
55822
|
console.log(`No log yet at ${f} \u2014 has the task run?`);
|
|
54669
55823
|
return;
|
|
54670
55824
|
}
|
|
@@ -54691,15 +55845,30 @@ function getServiceController(instanceId) {
|
|
|
54691
55845
|
// src/auth.ts
|
|
54692
55846
|
var import_qrcode_terminal = __toESM(require_main(), 1);
|
|
54693
55847
|
import { randomUUID as randomUUID8 } from "node:crypto";
|
|
54694
|
-
import { existsSync as
|
|
54695
|
-
import { join as
|
|
55848
|
+
import { existsSync as existsSync20, rmSync } from "node:fs";
|
|
55849
|
+
import { join as join36 } from "node:path";
|
|
54696
55850
|
|
|
54697
55851
|
// src/config.ts
|
|
54698
|
-
import { hostname as
|
|
55852
|
+
import { hostname as hostname5 } from "node:os";
|
|
54699
55853
|
var DEFAULT_API = (process.env.CCB_TESSERACT_API ?? "https://app.tesseract.dev").replace(/\/$/, "");
|
|
54700
55854
|
function defaultRelay(api) {
|
|
54701
55855
|
return (process.env.CCB_BACKEND_ORIGIN ?? api).replace(/\/$/, "");
|
|
54702
55856
|
}
|
|
55857
|
+
function isAllowedOrigin(origin) {
|
|
55858
|
+
let h, proto;
|
|
55859
|
+
try {
|
|
55860
|
+
const u = new URL(origin);
|
|
55861
|
+
h = u.hostname;
|
|
55862
|
+
proto = u.protocol;
|
|
55863
|
+
} catch {
|
|
55864
|
+
return false;
|
|
55865
|
+
}
|
|
55866
|
+
if (h === "localhost" || h === "127.0.0.1" || h === "::1") return true;
|
|
55867
|
+
if (proto !== "https:" && proto !== "wss:") return false;
|
|
55868
|
+
if (h === "tesseract.dev" || h.endsWith(".tesseract.dev")) return true;
|
|
55869
|
+
const extra = (process.env.CCB_ALLOWED_API_HOSTS ?? "").split(",").map((s) => s.trim()).filter(Boolean);
|
|
55870
|
+
return extra.some((a) => h === a || h.endsWith(`.${a}`));
|
|
55871
|
+
}
|
|
54703
55872
|
function decodeJwtSub(token) {
|
|
54704
55873
|
try {
|
|
54705
55874
|
const part = token.split(".")[1];
|
|
@@ -54711,7 +55880,7 @@ function decodeJwtSub(token) {
|
|
|
54711
55880
|
}
|
|
54712
55881
|
}
|
|
54713
55882
|
function defaultLabel() {
|
|
54714
|
-
return
|
|
55883
|
+
return hostname5() || "server";
|
|
54715
55884
|
}
|
|
54716
55885
|
|
|
54717
55886
|
// src/auth.ts
|
|
@@ -54808,6 +55977,15 @@ function persist2(api, relay, deviceId2, dt) {
|
|
|
54808
55977
|
async function login(opts) {
|
|
54809
55978
|
const api = (opts.api ?? DEFAULT_API).replace(/\/$/, "");
|
|
54810
55979
|
const relay = (opts.relay ?? defaultRelay(api)).replace(/\/$/, "");
|
|
55980
|
+
if (!opts.allowCustomApi) {
|
|
55981
|
+
const bad = [api, relay].find((o) => !isAllowedOrigin(o));
|
|
55982
|
+
if (bad) {
|
|
55983
|
+
console.error(`Refusing to use a non-default server origin: ${bad}`);
|
|
55984
|
+
console.error("If this is intentional (self-hosted), re-run with --allow-custom-api,");
|
|
55985
|
+
console.error("or set CCB_ALLOWED_API_HOSTS=your.host to allowlist it permanently.");
|
|
55986
|
+
process.exit(1);
|
|
55987
|
+
}
|
|
55988
|
+
}
|
|
54811
55989
|
const deviceId2 = readCredentials()?.deviceId ?? process.env.CCB_DEVICE_ID ?? randomUUID8().slice(0, 8);
|
|
54812
55990
|
const label = opts.name ?? defaultLabel();
|
|
54813
55991
|
if (opts.token) {
|
|
@@ -54869,8 +56047,8 @@ async function logout() {
|
|
|
54869
56047
|
}
|
|
54870
56048
|
for (const f of ["credentials.json", "status.json", "daemon.lock"]) {
|
|
54871
56049
|
try {
|
|
54872
|
-
const p =
|
|
54873
|
-
if (
|
|
56050
|
+
const p = join36(daemonDir(), f);
|
|
56051
|
+
if (existsSync20(p)) rmSync(p);
|
|
54874
56052
|
} catch {
|
|
54875
56053
|
}
|
|
54876
56054
|
}
|
|
@@ -54890,9 +56068,12 @@ function whoami() {
|
|
|
54890
56068
|
}
|
|
54891
56069
|
|
|
54892
56070
|
// src/self-update.ts
|
|
54893
|
-
import { spawn as
|
|
56071
|
+
import { spawn as spawn7 } from "node:child_process";
|
|
56072
|
+
import { createPublicKey, verify as cryptoVerify } from "node:crypto";
|
|
54894
56073
|
var RESTART_EXIT_CODE = 75;
|
|
54895
|
-
var
|
|
56074
|
+
var REGISTRY_BASE = "https://registry.npmjs.org/tesseract-agent";
|
|
56075
|
+
var REGISTRY_URL = `${REGISTRY_BASE}/latest`;
|
|
56076
|
+
var NPM_SIGNING_KEY_SPKI_B64 = "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEY6Ya7W++7aUPzvMTrezH6Ycx3c+HOKYCcNGybJZSCJq/fd7Qa8uuAKtdIkUQtQiEKERhAmE5lMMJhP8OkDOa2g==";
|
|
54896
56077
|
var FIRST_CHECK_MS = Number(process.env.CCB_UPDATE_FIRST_MS) || 3 * 6e4;
|
|
54897
56078
|
var INTERVAL_MS = Number(process.env.CCB_UPDATE_INTERVAL_MS) || 6 * 60 * 6e4;
|
|
54898
56079
|
function disabled() {
|
|
@@ -54930,13 +56111,39 @@ async function fetchLatest() {
|
|
|
54930
56111
|
clearTimeout(t);
|
|
54931
56112
|
}
|
|
54932
56113
|
}
|
|
56114
|
+
async function fetchVersionDist(version2) {
|
|
56115
|
+
const ac = new AbortController();
|
|
56116
|
+
const t = setTimeout(() => ac.abort(), 15e3);
|
|
56117
|
+
try {
|
|
56118
|
+
const r = await fetch(`${REGISTRY_BASE}/${encodeURIComponent(version2)}`, { headers: { accept: "application/json" }, signal: ac.signal });
|
|
56119
|
+
if (!r.ok) return void 0;
|
|
56120
|
+
const j = await r.json();
|
|
56121
|
+
const dist = j.dist;
|
|
56122
|
+
const s = dist?.signatures?.[0];
|
|
56123
|
+
return { integrity: dist?.integrity, sig: s?.sig, keyid: s?.keyid };
|
|
56124
|
+
} catch {
|
|
56125
|
+
return void 0;
|
|
56126
|
+
} finally {
|
|
56127
|
+
clearTimeout(t);
|
|
56128
|
+
}
|
|
56129
|
+
}
|
|
56130
|
+
function verifyNpmSignature(version2, integrity, sig) {
|
|
56131
|
+
if (!integrity || !sig) return false;
|
|
56132
|
+
try {
|
|
56133
|
+
const key2 = createPublicKey({ key: Buffer.from(NPM_SIGNING_KEY_SPKI_B64, "base64"), format: "der", type: "spki" });
|
|
56134
|
+
const message = Buffer.from(`tesseract-agent@${version2}:${integrity}`);
|
|
56135
|
+
return cryptoVerify("sha256", message, { key: key2, dsaEncoding: "der" }, Buffer.from(sig, "base64"));
|
|
56136
|
+
} catch {
|
|
56137
|
+
return false;
|
|
56138
|
+
}
|
|
56139
|
+
}
|
|
54933
56140
|
function installVersion(target) {
|
|
54934
56141
|
return new Promise((resolve10) => {
|
|
54935
56142
|
const npm = process.platform === "win32" ? "npm.cmd" : "npm";
|
|
54936
56143
|
log2(`installing tesseract-agent@${target} \u2026`);
|
|
54937
56144
|
let child;
|
|
54938
56145
|
try {
|
|
54939
|
-
child =
|
|
56146
|
+
child = spawn7(npm, ["install", "-g", `tesseract-agent@${target}`], { stdio: ["ignore", "inherit", "inherit"] });
|
|
54940
56147
|
} catch (e) {
|
|
54941
56148
|
log2("npm spawn threw:", e instanceof Error ? e.message : String(e));
|
|
54942
56149
|
resolve10(false);
|
|
@@ -54965,6 +56172,12 @@ async function checkOnce(current) {
|
|
|
54965
56172
|
const latest = await fetchLatest();
|
|
54966
56173
|
if (!latest || !isNewer(latest, current)) return;
|
|
54967
56174
|
log2(`update available: ${current} \u2192 ${latest}`);
|
|
56175
|
+
const dist = await fetchVersionDist(latest);
|
|
56176
|
+
if (!verifyNpmSignature(latest, dist?.integrity, dist?.sig)) {
|
|
56177
|
+
log2(`REFUSING update to ${latest}: registry signature did not verify \u2014 staying on ${current}`);
|
|
56178
|
+
return;
|
|
56179
|
+
}
|
|
56180
|
+
log2(`registry signature verified for ${latest} (keyid ${dist?.keyid ?? "?"})`);
|
|
54968
56181
|
if (await installVersion(latest)) {
|
|
54969
56182
|
log2("restarting on the new version (supervisor re-spawns) \u2026");
|
|
54970
56183
|
process.exit(RESTART_EXIT_CODE);
|
|
@@ -54993,9 +56206,9 @@ function startSelfUpdater(current) {
|
|
|
54993
56206
|
}
|
|
54994
56207
|
|
|
54995
56208
|
// src/service.ts
|
|
54996
|
-
import { join as
|
|
56209
|
+
import { join as join37 } from "node:path";
|
|
54997
56210
|
import { platform as platform3 } from "node:os";
|
|
54998
|
-
import { existsSync as
|
|
56211
|
+
import { existsSync as existsSync21, readFileSync as readFileSync14 } from "node:fs";
|
|
54999
56212
|
function controller(instanceId) {
|
|
55000
56213
|
const c = getServiceController(instanceId);
|
|
55001
56214
|
if (!c) {
|
|
@@ -55008,7 +56221,7 @@ function buildSpec(cliEntry) {
|
|
|
55008
56221
|
exec: process.execPath,
|
|
55009
56222
|
args: [cliEntry, "run"],
|
|
55010
56223
|
env: { CCB_DAEMON_DIR: daemonDir() },
|
|
55011
|
-
logPath:
|
|
56224
|
+
logPath: join37(daemonDir(), "daemon.log")
|
|
55012
56225
|
};
|
|
55013
56226
|
}
|
|
55014
56227
|
var isAlive = (pid) => {
|
|
@@ -55020,10 +56233,10 @@ var isAlive = (pid) => {
|
|
|
55020
56233
|
}
|
|
55021
56234
|
};
|
|
55022
56235
|
function runningDaemonInfo() {
|
|
55023
|
-
const lock =
|
|
56236
|
+
const lock = join37(daemonDir(), "daemon.lock");
|
|
55024
56237
|
try {
|
|
55025
|
-
if (!
|
|
55026
|
-
const pid = Number(
|
|
56238
|
+
if (!existsSync21(lock)) return null;
|
|
56239
|
+
const pid = Number(readFileSync14(lock, "utf-8").trim());
|
|
55027
56240
|
if (!pid || !isAlive(pid)) return null;
|
|
55028
56241
|
return { pid, device: readCredentials()?.deviceId };
|
|
55029
56242
|
} catch {
|
|
@@ -55059,8 +56272,8 @@ async function logs(instanceId, follow) {
|
|
|
55059
56272
|
// src/cli.ts
|
|
55060
56273
|
function pkgVersion() {
|
|
55061
56274
|
try {
|
|
55062
|
-
const p =
|
|
55063
|
-
return JSON.parse(
|
|
56275
|
+
const p = join38(dirname18(fileURLToPath2(import.meta.url)), "..", "package.json");
|
|
56276
|
+
return JSON.parse(readFileSync15(p, "utf8")).version ?? "0.0.0";
|
|
55064
56277
|
} catch {
|
|
55065
56278
|
return "0.0.0";
|
|
55066
56279
|
}
|
|
@@ -55116,7 +56329,7 @@ async function superviseDaemon(cliEntry) {
|
|
|
55116
56329
|
let stopping = false;
|
|
55117
56330
|
let child = null;
|
|
55118
56331
|
const spawnWorker = () => {
|
|
55119
|
-
const c =
|
|
56332
|
+
const c = spawn8(process.execPath, [cliEntry, "__worker"], { stdio: "inherit", env: process.env });
|
|
55120
56333
|
child = c;
|
|
55121
56334
|
let settled = false;
|
|
55122
56335
|
const settle = (fn) => {
|
|
@@ -55322,7 +56535,7 @@ async function main() {
|
|
|
55322
56535
|
process.exit(1);
|
|
55323
56536
|
}
|
|
55324
56537
|
instance = flags.instance;
|
|
55325
|
-
process.env.CCB_DAEMON_DIR =
|
|
56538
|
+
process.env.CCB_DAEMON_DIR = join38(homedir16(), ".tesseract", `daemon-${instance}`);
|
|
55326
56539
|
}
|
|
55327
56540
|
const force = flags.yes === true || flags.force === true;
|
|
55328
56541
|
const cliEntry = fileURLToPath2(import.meta.url);
|
|
@@ -55332,7 +56545,8 @@ async function main() {
|
|
|
55332
56545
|
api: typeof flags.api === "string" ? flags.api : void 0,
|
|
55333
56546
|
relay: typeof flags.relay === "string" ? flags.relay : void 0,
|
|
55334
56547
|
token: typeof flags.token === "string" ? flags.token : void 0,
|
|
55335
|
-
name: typeof flags.name === "string" ? flags.name : void 0
|
|
56548
|
+
name: typeof flags.name === "string" ? flags.name : void 0,
|
|
56549
|
+
allowCustomApi: flags["allow-custom-api"] === true
|
|
55336
56550
|
});
|
|
55337
56551
|
break;
|
|
55338
56552
|
case "logout":
|