tesseract-agent 0.4.17 → 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 +1500 -381
- 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));
|
|
@@ -21972,7 +22166,7 @@ var init_log = __esm({
|
|
|
21972
22166
|
import { readFile as readFile5 } from "node:fs/promises";
|
|
21973
22167
|
import { execFile } from "node:child_process";
|
|
21974
22168
|
import { promisify } from "node:util";
|
|
21975
|
-
import { homedir as
|
|
22169
|
+
import { homedir as homedir3 } from "node:os";
|
|
21976
22170
|
import { join as join8 } from "node:path";
|
|
21977
22171
|
function tokenFromCredsBlob(raw) {
|
|
21978
22172
|
try {
|
|
@@ -22002,7 +22196,7 @@ async function resolveOauthToken(env) {
|
|
|
22002
22196
|
const ambientTok = process.env.CLAUDE_CODE_OAUTH_TOKEN?.trim();
|
|
22003
22197
|
if (ambientTok)
|
|
22004
22198
|
return ambientTok;
|
|
22005
|
-
const fromAmbientDir = await tokenFromConfigDir(process.env.CLAUDE_CONFIG_DIR ?? join8(
|
|
22199
|
+
const fromAmbientDir = await tokenFromConfigDir(process.env.CLAUDE_CONFIG_DIR ?? join8(homedir3(), ".claude"));
|
|
22006
22200
|
if (fromAmbientDir)
|
|
22007
22201
|
return fromAmbientDir;
|
|
22008
22202
|
if (process.platform === "darwin") {
|
|
@@ -22150,7 +22344,7 @@ var init_kill_tree = __esm({
|
|
|
22150
22344
|
import { query, createSdkMcpServer } from "@anthropic-ai/claude-agent-sdk";
|
|
22151
22345
|
import { writeFile as writeFile4, mkdir as mkdir4, readFile as readFile6 } from "node:fs/promises";
|
|
22152
22346
|
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
22153
|
-
import { tmpdir, homedir as
|
|
22347
|
+
import { tmpdir, homedir as homedir4 } from "node:os";
|
|
22154
22348
|
import { join as join9, dirname as dirname5 } from "node:path";
|
|
22155
22349
|
import { existsSync as existsSync2, readFileSync as readFileSync3 } from "node:fs";
|
|
22156
22350
|
import { fileURLToPath } from "node:url";
|
|
@@ -22329,9 +22523,9 @@ function sourcesEqual(a, b) {
|
|
|
22329
22523
|
if (a.length !== b.length)
|
|
22330
22524
|
return false;
|
|
22331
22525
|
const as = [...a].sort();
|
|
22332
|
-
const
|
|
22526
|
+
const bs2 = [...b].sort();
|
|
22333
22527
|
for (let i = 0; i < as.length; i++)
|
|
22334
|
-
if (as[i] !==
|
|
22528
|
+
if (as[i] !== bs2[i])
|
|
22335
22529
|
return false;
|
|
22336
22530
|
return true;
|
|
22337
22531
|
}
|
|
@@ -23182,7 +23376,7 @@ CRITICAL RULES:
|
|
|
23182
23376
|
// fall back to ~/.tesseract/canvas when the session isn't on disk yet.
|
|
23183
23377
|
canvasDir: async () => {
|
|
23184
23378
|
const dir = await sessionSidecarDir(live.sessionId, projectDir);
|
|
23185
|
-
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");
|
|
23186
23380
|
},
|
|
23187
23381
|
emitCanvasReady: (p) => {
|
|
23188
23382
|
const ts = Date.now();
|
|
@@ -26579,7 +26773,7 @@ var require_build2 = __commonJS({
|
|
|
26579
26773
|
throw new Error("BigInt is not defined. Your environment doesn't implement BigInt.");
|
|
26580
26774
|
}
|
|
26581
26775
|
}
|
|
26582
|
-
function
|
|
26776
|
+
function concat2(buffers) {
|
|
26583
26777
|
let outputLength = 0;
|
|
26584
26778
|
let prevLength = 0;
|
|
26585
26779
|
for (let i = 0; i < buffers.length; i++) {
|
|
@@ -26625,7 +26819,7 @@ var require_build2 = __commonJS({
|
|
|
26625
26819
|
this.items.push(buf);
|
|
26626
26820
|
}
|
|
26627
26821
|
final() {
|
|
26628
|
-
return
|
|
26822
|
+
return concat2(this.items);
|
|
26629
26823
|
}
|
|
26630
26824
|
};
|
|
26631
26825
|
var powers2 = [new Uint8Array([1])];
|
|
@@ -28345,7 +28539,7 @@ ${values.join("\n")}` : `${blockName} :`;
|
|
|
28345
28539
|
}
|
|
28346
28540
|
retBuffers.push(valueBuf);
|
|
28347
28541
|
}
|
|
28348
|
-
return
|
|
28542
|
+
return concat2(retBuffers);
|
|
28349
28543
|
}
|
|
28350
28544
|
fromString(string4) {
|
|
28351
28545
|
this.value = [];
|
|
@@ -28574,7 +28768,7 @@ ${values.join("\n")}` : `${blockName} :`;
|
|
|
28574
28768
|
}
|
|
28575
28769
|
retBuffers.push(valueBuf);
|
|
28576
28770
|
}
|
|
28577
|
-
return
|
|
28771
|
+
return concat2(retBuffers);
|
|
28578
28772
|
}
|
|
28579
28773
|
fromString(string4) {
|
|
28580
28774
|
this.value = [];
|
|
@@ -29788,7 +29982,7 @@ var require_concat = __commonJS({
|
|
|
29788
29982
|
"use strict";
|
|
29789
29983
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29790
29984
|
exports.concatToUint8Array = concatToUint8Array;
|
|
29791
|
-
exports.concat =
|
|
29985
|
+
exports.concat = concat2;
|
|
29792
29986
|
var buffer_source_js_1 = require_buffer_source();
|
|
29793
29987
|
function concatToUint8Array(buffers) {
|
|
29794
29988
|
const views = [];
|
|
@@ -29806,7 +30000,7 @@ var require_concat = __commonJS({
|
|
|
29806
30000
|
}
|
|
29807
30001
|
return result;
|
|
29808
30002
|
}
|
|
29809
|
-
function
|
|
30003
|
+
function concat2(first, second, ...rest) {
|
|
29810
30004
|
let buffers;
|
|
29811
30005
|
let type;
|
|
29812
30006
|
if (typeof second === "function") {
|
|
@@ -38974,11 +39168,11 @@ var require_x509_cjs = __commonJS({
|
|
|
38974
39168
|
var brainpoolP384t1 = "brainpoolP384t1";
|
|
38975
39169
|
var brainpoolP512r1 = "brainpoolP512r1";
|
|
38976
39170
|
var brainpoolP512t1 = "brainpoolP512t1";
|
|
38977
|
-
var
|
|
39171
|
+
var ECDSA2 = "ECDSA";
|
|
38978
39172
|
exports.EcAlgorithm = EcAlgorithm_1 = class EcAlgorithm {
|
|
38979
39173
|
toAsnAlgorithm(alg) {
|
|
38980
39174
|
switch (alg.name.toLowerCase()) {
|
|
38981
|
-
case
|
|
39175
|
+
case ECDSA2.toLowerCase():
|
|
38982
39176
|
if ("hash" in alg) {
|
|
38983
39177
|
const hash2 = typeof alg.hash === "string" ? alg.hash : alg.hash.name;
|
|
38984
39178
|
switch (hash2.toLowerCase()) {
|
|
@@ -39063,22 +39257,22 @@ var require_x509_cjs = __commonJS({
|
|
|
39063
39257
|
switch (alg.algorithm) {
|
|
39064
39258
|
case asn1Ecc__namespace.id_ecdsaWithSHA1:
|
|
39065
39259
|
return {
|
|
39066
|
-
name:
|
|
39260
|
+
name: ECDSA2,
|
|
39067
39261
|
hash: { name: "SHA-1" }
|
|
39068
39262
|
};
|
|
39069
39263
|
case asn1Ecc__namespace.id_ecdsaWithSHA256:
|
|
39070
39264
|
return {
|
|
39071
|
-
name:
|
|
39265
|
+
name: ECDSA2,
|
|
39072
39266
|
hash: { name: "SHA-256" }
|
|
39073
39267
|
};
|
|
39074
39268
|
case asn1Ecc__namespace.id_ecdsaWithSHA384:
|
|
39075
39269
|
return {
|
|
39076
|
-
name:
|
|
39270
|
+
name: ECDSA2,
|
|
39077
39271
|
hash: { name: "SHA-384" }
|
|
39078
39272
|
};
|
|
39079
39273
|
case asn1Ecc__namespace.id_ecdsaWithSHA512:
|
|
39080
39274
|
return {
|
|
39081
|
-
name:
|
|
39275
|
+
name: ECDSA2,
|
|
39082
39276
|
hash: { name: "SHA-512" }
|
|
39083
39277
|
};
|
|
39084
39278
|
case asn1Ecc__namespace.id_ecPublicKey: {
|
|
@@ -39089,92 +39283,92 @@ var require_x509_cjs = __commonJS({
|
|
|
39089
39283
|
switch (parameters.namedCurve) {
|
|
39090
39284
|
case asn1Ecc__namespace.id_secp256r1:
|
|
39091
39285
|
return {
|
|
39092
|
-
name:
|
|
39286
|
+
name: ECDSA2,
|
|
39093
39287
|
namedCurve: "P-256"
|
|
39094
39288
|
};
|
|
39095
39289
|
case EcAlgorithm_1.SECP256K1:
|
|
39096
39290
|
return {
|
|
39097
|
-
name:
|
|
39291
|
+
name: ECDSA2,
|
|
39098
39292
|
namedCurve: "K-256"
|
|
39099
39293
|
};
|
|
39100
39294
|
case asn1Ecc__namespace.id_secp384r1:
|
|
39101
39295
|
return {
|
|
39102
|
-
name:
|
|
39296
|
+
name: ECDSA2,
|
|
39103
39297
|
namedCurve: "P-384"
|
|
39104
39298
|
};
|
|
39105
39299
|
case asn1Ecc__namespace.id_secp521r1:
|
|
39106
39300
|
return {
|
|
39107
|
-
name:
|
|
39301
|
+
name: ECDSA2,
|
|
39108
39302
|
namedCurve: "P-521"
|
|
39109
39303
|
};
|
|
39110
39304
|
case idBrainpoolP160r1:
|
|
39111
39305
|
return {
|
|
39112
|
-
name:
|
|
39306
|
+
name: ECDSA2,
|
|
39113
39307
|
namedCurve: brainpoolP160r1
|
|
39114
39308
|
};
|
|
39115
39309
|
case idBrainpoolP160t1:
|
|
39116
39310
|
return {
|
|
39117
|
-
name:
|
|
39311
|
+
name: ECDSA2,
|
|
39118
39312
|
namedCurve: brainpoolP160t1
|
|
39119
39313
|
};
|
|
39120
39314
|
case idBrainpoolP192r1:
|
|
39121
39315
|
return {
|
|
39122
|
-
name:
|
|
39316
|
+
name: ECDSA2,
|
|
39123
39317
|
namedCurve: brainpoolP192r1
|
|
39124
39318
|
};
|
|
39125
39319
|
case idBrainpoolP192t1:
|
|
39126
39320
|
return {
|
|
39127
|
-
name:
|
|
39321
|
+
name: ECDSA2,
|
|
39128
39322
|
namedCurve: brainpoolP192t1
|
|
39129
39323
|
};
|
|
39130
39324
|
case idBrainpoolP224r1:
|
|
39131
39325
|
return {
|
|
39132
|
-
name:
|
|
39326
|
+
name: ECDSA2,
|
|
39133
39327
|
namedCurve: brainpoolP224r1
|
|
39134
39328
|
};
|
|
39135
39329
|
case idBrainpoolP224t1:
|
|
39136
39330
|
return {
|
|
39137
|
-
name:
|
|
39331
|
+
name: ECDSA2,
|
|
39138
39332
|
namedCurve: brainpoolP224t1
|
|
39139
39333
|
};
|
|
39140
39334
|
case idBrainpoolP256r1:
|
|
39141
39335
|
return {
|
|
39142
|
-
name:
|
|
39336
|
+
name: ECDSA2,
|
|
39143
39337
|
namedCurve: brainpoolP256r1
|
|
39144
39338
|
};
|
|
39145
39339
|
case idBrainpoolP256t1:
|
|
39146
39340
|
return {
|
|
39147
|
-
name:
|
|
39341
|
+
name: ECDSA2,
|
|
39148
39342
|
namedCurve: brainpoolP256t1
|
|
39149
39343
|
};
|
|
39150
39344
|
case idBrainpoolP320r1:
|
|
39151
39345
|
return {
|
|
39152
|
-
name:
|
|
39346
|
+
name: ECDSA2,
|
|
39153
39347
|
namedCurve: brainpoolP320r1
|
|
39154
39348
|
};
|
|
39155
39349
|
case idBrainpoolP320t1:
|
|
39156
39350
|
return {
|
|
39157
|
-
name:
|
|
39351
|
+
name: ECDSA2,
|
|
39158
39352
|
namedCurve: brainpoolP320t1
|
|
39159
39353
|
};
|
|
39160
39354
|
case idBrainpoolP384r1:
|
|
39161
39355
|
return {
|
|
39162
|
-
name:
|
|
39356
|
+
name: ECDSA2,
|
|
39163
39357
|
namedCurve: brainpoolP384r1
|
|
39164
39358
|
};
|
|
39165
39359
|
case idBrainpoolP384t1:
|
|
39166
39360
|
return {
|
|
39167
|
-
name:
|
|
39361
|
+
name: ECDSA2,
|
|
39168
39362
|
namedCurve: brainpoolP384t1
|
|
39169
39363
|
};
|
|
39170
39364
|
case idBrainpoolP512r1:
|
|
39171
39365
|
return {
|
|
39172
|
-
name:
|
|
39366
|
+
name: ECDSA2,
|
|
39173
39367
|
namedCurve: brainpoolP512r1
|
|
39174
39368
|
};
|
|
39175
39369
|
case idBrainpoolP512t1:
|
|
39176
39370
|
return {
|
|
39177
|
-
name:
|
|
39371
|
+
name: ECDSA2,
|
|
39178
39372
|
namedCurve: brainpoolP512t1
|
|
39179
39373
|
};
|
|
39180
39374
|
}
|
|
@@ -42557,6 +42751,95 @@ var require_selfsigned = __commonJS({
|
|
|
42557
42751
|
}
|
|
42558
42752
|
});
|
|
42559
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
|
+
|
|
42560
42843
|
// ../../packages/host-core/dist/peer-link/framing.js
|
|
42561
42844
|
function encodeFrame2(payload) {
|
|
42562
42845
|
const out = new Uint8Array(4 + payload.length);
|
|
@@ -42706,9 +42989,9 @@ var init_transport = __esm({
|
|
|
42706
42989
|
});
|
|
42707
42990
|
|
|
42708
42991
|
// ../../packages/host-core/dist/peer-link/stun.js
|
|
42709
|
-
import { randomBytes } from "node:crypto";
|
|
42992
|
+
import { randomBytes as randomBytes2 } from "node:crypto";
|
|
42710
42993
|
function encodeBindingRequest() {
|
|
42711
|
-
const txId =
|
|
42994
|
+
const txId = randomBytes2(12);
|
|
42712
42995
|
const packet = Buffer.alloc(20);
|
|
42713
42996
|
packet.writeUInt16BE(BINDING_REQUEST, 0);
|
|
42714
42997
|
packet.writeUInt16BE(0, 2);
|
|
@@ -42844,11 +43127,11 @@ function quicByteSocket(stream) {
|
|
|
42844
43127
|
}
|
|
42845
43128
|
function pinningVerify(expectedFingerprint256) {
|
|
42846
43129
|
return async (certs) => {
|
|
42847
|
-
const { X509Certificate: X509Certificate2 } = await import("node:crypto");
|
|
42848
43130
|
if (!certs.length)
|
|
42849
43131
|
throw new Error("quic: peer presented no certificate");
|
|
43132
|
+
const { X509Certificate: X509Certificate2 } = await import("node:crypto");
|
|
42850
43133
|
const fp = new X509Certificate2(Buffer.from(certs[0])).fingerprint256;
|
|
42851
|
-
if (fp
|
|
43134
|
+
if (!fingerprintsEqual(fp, expectedFingerprint256))
|
|
42852
43135
|
throw new Error("quic: TLS fingerprint mismatch (fail-closed)");
|
|
42853
43136
|
return void 0;
|
|
42854
43137
|
};
|
|
@@ -42930,6 +43213,7 @@ var init_quic_transport = __esm({
|
|
|
42930
43213
|
"use strict";
|
|
42931
43214
|
init_transport();
|
|
42932
43215
|
init_stun();
|
|
43216
|
+
init_tls_direct();
|
|
42933
43217
|
ALPN = ["tesseract-peer"];
|
|
42934
43218
|
quicModP = null;
|
|
42935
43219
|
loadQuic = () => quicModP ??= import("@matrixai/quic");
|
|
@@ -44012,17 +44296,17 @@ var require_main = __commonJS({
|
|
|
44012
44296
|
});
|
|
44013
44297
|
|
|
44014
44298
|
// src/cli.ts
|
|
44015
|
-
import { readFileSync as
|
|
44299
|
+
import { readFileSync as readFileSync15 } from "node:fs";
|
|
44016
44300
|
import { createHash as createHash5 } from "node:crypto";
|
|
44017
44301
|
import { spawn as spawn8 } from "node:child_process";
|
|
44018
44302
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
44019
|
-
import { homedir as
|
|
44020
|
-
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";
|
|
44021
44305
|
import { createInterface } from "node:readline/promises";
|
|
44022
44306
|
|
|
44023
44307
|
// ../../packages/host-core/dist/control-link.js
|
|
44024
|
-
import { hostname as hostname3
|
|
44025
|
-
import { join as
|
|
44308
|
+
import { hostname as hostname3 } from "node:os";
|
|
44309
|
+
import { join as join30, basename as basename7 } from "node:path";
|
|
44026
44310
|
import { mkdirSync as mkdirSync11 } from "node:fs";
|
|
44027
44311
|
|
|
44028
44312
|
// ../../node_modules/ws/wrapper.mjs
|
|
@@ -44038,6 +44322,7 @@ var wrapper_default = import_websocket.default;
|
|
|
44038
44322
|
|
|
44039
44323
|
// ../../packages/host-core/dist/path-guard.js
|
|
44040
44324
|
import { realpathSync } from "node:fs";
|
|
44325
|
+
import { homedir } from "node:os";
|
|
44041
44326
|
import { basename, dirname, isAbsolute, join, relative, resolve } from "node:path";
|
|
44042
44327
|
function canonicalForGuard(p) {
|
|
44043
44328
|
const abs = resolve(p);
|
|
@@ -44068,6 +44353,380 @@ function isInsideRoot(p, root) {
|
|
|
44068
44353
|
const rel = relative(realRoot, canonicalForGuard(p));
|
|
44069
44354
|
return rel === "" || !rel.startsWith("..") && !isAbsolute(rel);
|
|
44070
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
|
+
};
|
|
44071
44730
|
|
|
44072
44731
|
// ../../packages/host-core/dist/daemon-agent.js
|
|
44073
44732
|
import { join as join18 } from "node:path";
|
|
@@ -44137,6 +44796,13 @@ async function createClaudeCodeBackend(send2, onBrowserRequest, loadPluginRuntim
|
|
|
44137
44796
|
init_dist4();
|
|
44138
44797
|
import { spawn as spawn3 } from "node:child_process";
|
|
44139
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
|
+
}
|
|
44140
44806
|
var MAX_LIVE = 8;
|
|
44141
44807
|
var CliAgentBackend = class {
|
|
44142
44808
|
adapter;
|
|
@@ -44181,7 +44847,9 @@ var CliAgentBackend = class {
|
|
|
44181
44847
|
cwd: params.projectDir,
|
|
44182
44848
|
// engineEnv carries the resolved runner env + enabled-skill secrets (the desktop
|
|
44183
44849
|
// resolves them into the 0600 snapshot); merge so CLI engines get them too.
|
|
44184
|
-
|
|
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 ?? {} }),
|
|
44185
44853
|
stdio: ["ignore", "pipe", "pipe"],
|
|
44186
44854
|
// Own process group (POSIX) so Stop can SIGKILL the whole tree, not just the
|
|
44187
44855
|
// CLI's PID — these engines spawn their own tool subprocesses too.
|
|
@@ -44523,7 +45191,7 @@ init_dist4();
|
|
|
44523
45191
|
// ../../packages/host-core/dist/plugin-tools.js
|
|
44524
45192
|
import { existsSync as existsSync7, readdirSync } from "node:fs";
|
|
44525
45193
|
import { join as join16 } from "node:path";
|
|
44526
|
-
import { homedir as
|
|
45194
|
+
import { homedir as homedir9 } from "node:os";
|
|
44527
45195
|
|
|
44528
45196
|
// ../../packages/plugin-host/dist/discovery.js
|
|
44529
45197
|
import fs2 from "node:fs";
|
|
@@ -44858,9 +45526,9 @@ import { join as join15 } from "node:path";
|
|
|
44858
45526
|
// ../../packages/host-core/dist/credentials.js
|
|
44859
45527
|
import { readFileSync as readFileSync4, writeFileSync as writeFileSync4, mkdirSync as mkdirSync3, existsSync as existsSync3, renameSync as renameSync3, chmodSync } from "node:fs";
|
|
44860
45528
|
import { join as join10 } from "node:path";
|
|
44861
|
-
import { homedir as
|
|
45529
|
+
import { homedir as homedir5 } from "node:os";
|
|
44862
45530
|
function daemonDir() {
|
|
44863
|
-
return process.env.CCB_DAEMON_DIR ?? join10(
|
|
45531
|
+
return process.env.CCB_DAEMON_DIR ?? join10(homedir5(), ".tesseract", "daemon");
|
|
44864
45532
|
}
|
|
44865
45533
|
function credentialsPath() {
|
|
44866
45534
|
return join10(daemonDir(), "credentials.json");
|
|
@@ -44892,7 +45560,7 @@ function writeCredentials(c) {
|
|
|
44892
45560
|
// ../../packages/host-core/dist/runners/resolve.js
|
|
44893
45561
|
init_dist2();
|
|
44894
45562
|
import { readFileSync as readFileSync5, existsSync as existsSync5, statSync, mkdirSync as mkdirSync5, writeFileSync as writeFileSync6 } from "node:fs";
|
|
44895
|
-
import { homedir as
|
|
45563
|
+
import { homedir as homedir7 } from "node:os";
|
|
44896
45564
|
import { join as join12 } from "node:path";
|
|
44897
45565
|
import { createHash as createHash2 } from "node:crypto";
|
|
44898
45566
|
|
|
@@ -44902,14 +45570,14 @@ import { spawn as spawn4, execFile as execFile2, execSync as execSync2 } from "n
|
|
|
44902
45570
|
import { createRequire as createRequire2 } from "node:module";
|
|
44903
45571
|
import { randomUUID as randomUUID4, createHash } from "node:crypto";
|
|
44904
45572
|
import { existsSync as existsSync4, mkdirSync as mkdirSync4, writeFileSync as writeFileSync5 } from "node:fs";
|
|
44905
|
-
import { homedir as
|
|
45573
|
+
import { homedir as homedir6 } from "node:os";
|
|
44906
45574
|
import { join as join11, dirname as dirname6, sep as sep3 } from "node:path";
|
|
44907
45575
|
import { request as httpRequest } from "node:http";
|
|
44908
45576
|
var requireFromHere = createRequire2(import.meta.url);
|
|
44909
45577
|
var ROUTER_PORT = 8787;
|
|
44910
45578
|
var BIND_HOST = "127.0.0.1";
|
|
44911
45579
|
var ROUTER_TOKEN = randomUUID4();
|
|
44912
|
-
var routerHomeDir = join11(
|
|
45580
|
+
var routerHomeDir = join11(homedir6(), ".tesseract", "router-home");
|
|
44913
45581
|
var lastConfigHash = "";
|
|
44914
45582
|
var running = false;
|
|
44915
45583
|
var starting = null;
|
|
@@ -45080,7 +45748,7 @@ function routerEndpointSync(desired) {
|
|
|
45080
45748
|
}
|
|
45081
45749
|
|
|
45082
45750
|
// ../../packages/host-core/dist/runners/resolve.js
|
|
45083
|
-
var runnerHomeBase = join12(
|
|
45751
|
+
var runnerHomeBase = join12(homedir7(), ".tesseract", "runner-home");
|
|
45084
45752
|
function materializeClaudeAuthHome(contents) {
|
|
45085
45753
|
const id = createHash2("sha256").update(contents).digest("hex").slice(0, 16);
|
|
45086
45754
|
const home = join12(runnerHomeBase, id);
|
|
@@ -45136,7 +45804,7 @@ function resolveRunnerEnv(spec) {
|
|
|
45136
45804
|
return { ANTHROPIC_BASE_URL: endpoint.baseUrl, ANTHROPIC_AUTH_TOKEN: endpoint.authToken, ANTHROPIC_API_KEY: "" };
|
|
45137
45805
|
}
|
|
45138
45806
|
function overridePath() {
|
|
45139
|
-
return join12(process.env.CCB_TESSERACT_DIR ?? join12(
|
|
45807
|
+
return join12(process.env.CCB_TESSERACT_DIR ?? join12(homedir7(), ".tesseract"), "runners.json");
|
|
45140
45808
|
}
|
|
45141
45809
|
var cache2 = null;
|
|
45142
45810
|
var cachedMtime = -1;
|
|
@@ -45365,7 +46033,7 @@ function setActiveConfig(id) {
|
|
|
45365
46033
|
// ../../packages/host-core/dist/daemon-engine-spawn.js
|
|
45366
46034
|
init_dist2();
|
|
45367
46035
|
import { execFileSync } from "node:child_process";
|
|
45368
|
-
import { homedir as
|
|
46036
|
+
import { homedir as homedir8 } from "node:os";
|
|
45369
46037
|
import { join as join14 } from "node:path";
|
|
45370
46038
|
function envKey(id) {
|
|
45371
46039
|
return id.toUpperCase().replace(/[^A-Z0-9]/g, "_");
|
|
@@ -45377,10 +46045,10 @@ function augmentedPath() {
|
|
|
45377
46045
|
"/opt/homebrew/bin",
|
|
45378
46046
|
"/usr/bin",
|
|
45379
46047
|
"/bin",
|
|
45380
|
-
join14(
|
|
45381
|
-
join14(
|
|
45382
|
-
join14(
|
|
45383
|
-
join14(
|
|
46048
|
+
join14(homedir8(), ".local", "bin"),
|
|
46049
|
+
join14(homedir8(), "bin"),
|
|
46050
|
+
join14(homedir8(), ".bun", "bin"),
|
|
46051
|
+
join14(homedir8(), ".npm-global", "bin")
|
|
45384
46052
|
];
|
|
45385
46053
|
const seen = /* @__PURE__ */ new Set();
|
|
45386
46054
|
const parts = [];
|
|
@@ -45523,7 +46191,7 @@ function buildDaemonPluginLoader(pluginsDir) {
|
|
|
45523
46191
|
};
|
|
45524
46192
|
}
|
|
45525
46193
|
function daemonSkillsPluginConfig() {
|
|
45526
|
-
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");
|
|
45527
46195
|
if (!existsSync7(join16(root, ".claude-plugin", "plugin.json")))
|
|
45528
46196
|
return null;
|
|
45529
46197
|
const enabled = join16(root, "skills");
|
|
@@ -45819,7 +46487,7 @@ async function closeEnginePage(browserId) {
|
|
|
45819
46487
|
// ../../packages/host-core/dist/image-gen-runner.js
|
|
45820
46488
|
import { randomUUID as randomUUID6 } from "node:crypto";
|
|
45821
46489
|
import { mkdirSync as mkdirSync7, writeFileSync as writeFileSync8, readFileSync as readFileSync8 } from "node:fs";
|
|
45822
|
-
import { homedir as
|
|
46490
|
+
import { homedir as homedir10 } from "node:os";
|
|
45823
46491
|
import { join as join17 } from "node:path";
|
|
45824
46492
|
|
|
45825
46493
|
// ../../packages/image-edit-core/dist/types.js
|
|
@@ -46036,7 +46704,7 @@ function createImageEditProvider(id, cfg2) {
|
|
|
46036
46704
|
|
|
46037
46705
|
// ../../packages/host-core/dist/image-gen-runner.js
|
|
46038
46706
|
function generatedImagesDir() {
|
|
46039
|
-
const home = process.env.CCB_TESSERACT_DIR ?? join17(
|
|
46707
|
+
const home = process.env.CCB_TESSERACT_DIR ?? join17(homedir10(), ".tesseract");
|
|
46040
46708
|
return join17(home, "generated-images");
|
|
46041
46709
|
}
|
|
46042
46710
|
function creds() {
|
|
@@ -46220,12 +46888,143 @@ function refreshDaemonScheduling(b) {
|
|
|
46220
46888
|
}
|
|
46221
46889
|
|
|
46222
46890
|
// ../../packages/host-core/dist/fs-remote.js
|
|
46223
|
-
import { readdir as
|
|
46224
|
-
import { openSync as openSync2, readSync, closeSync as closeSync2, statSync as statSync3, existsSync as existsSync8, watch } from "node:fs";
|
|
46225
|
-
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";
|
|
46226
46894
|
import { execFile as execFile3 } from "node:child_process";
|
|
46227
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
|
|
46228
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;
|
|
46229
47028
|
var LISTALL_SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
46230
47029
|
".git",
|
|
46231
47030
|
".hg",
|
|
@@ -46260,7 +47059,7 @@ async function listAllFilesUnder(dir) {
|
|
|
46260
47059
|
return;
|
|
46261
47060
|
let entries;
|
|
46262
47061
|
try {
|
|
46263
|
-
entries = await
|
|
47062
|
+
entries = await readdir3(d, { withFileTypes: true });
|
|
46264
47063
|
} catch {
|
|
46265
47064
|
return;
|
|
46266
47065
|
}
|
|
@@ -46269,7 +47068,7 @@ async function listAllFilesUnder(dir) {
|
|
|
46269
47068
|
return;
|
|
46270
47069
|
if (ent.name === ".DS_Store")
|
|
46271
47070
|
continue;
|
|
46272
|
-
const full =
|
|
47071
|
+
const full = join20(d, ent.name);
|
|
46273
47072
|
if (ent.isDirectory()) {
|
|
46274
47073
|
if (!LISTALL_SKIP_DIRS.has(ent.name))
|
|
46275
47074
|
await walk(full);
|
|
@@ -46385,76 +47184,107 @@ function resolveParent(raw, root) {
|
|
|
46385
47184
|
if (raw === "" || raw === "~")
|
|
46386
47185
|
return root;
|
|
46387
47186
|
if (raw.startsWith("~/"))
|
|
46388
|
-
return
|
|
46389
|
-
return isAbsolute4(raw) ? raw :
|
|
47187
|
+
return join20(root, raw.slice(2));
|
|
47188
|
+
return isAbsolute4(raw) ? raw : join20(root, raw);
|
|
46390
47189
|
}
|
|
46391
47190
|
function resolveUniqueName(dir, name) {
|
|
46392
|
-
const direct =
|
|
47191
|
+
const direct = join20(dir, name);
|
|
46393
47192
|
if (!existsSync8(direct))
|
|
46394
47193
|
return direct;
|
|
46395
47194
|
const dot = name.lastIndexOf(".");
|
|
46396
47195
|
const base = dot > 0 ? name.slice(0, dot) : name;
|
|
46397
47196
|
const ext = dot > 0 ? name.slice(dot) : "";
|
|
46398
47197
|
for (let i = 2; i < 1e4; i++) {
|
|
46399
|
-
const candidate =
|
|
47198
|
+
const candidate = join20(dir, `${base} ${i}${ext}`);
|
|
46400
47199
|
if (!existsSync8(candidate))
|
|
46401
47200
|
return candidate;
|
|
46402
47201
|
}
|
|
46403
47202
|
return direct;
|
|
46404
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
|
+
}
|
|
46405
47226
|
var OUTSIDE = (verb) => ({ ok: false, error: `Refusing to ${verb} outside the shared root.` });
|
|
46406
47227
|
var validName = (n) => !!n && !n.includes("/") && !n.includes("\\") && n !== "." && n !== "..";
|
|
46407
47228
|
async function readRemoteBytes(root, path5) {
|
|
46408
|
-
|
|
47229
|
+
const real = safeContentPath(path5);
|
|
47230
|
+
if (!real)
|
|
46409
47231
|
return { error: "Path is outside the shared root." };
|
|
46410
47232
|
try {
|
|
46411
|
-
const s = statSync3(
|
|
47233
|
+
const s = statSync3(real);
|
|
46412
47234
|
if (!s.isFile())
|
|
46413
47235
|
return { error: "Not a file" };
|
|
46414
47236
|
if (s.size > REMOTE_READ_CAP)
|
|
46415
47237
|
return { error: `File is too large to read whole over the network (${(s.size / 1024 / 1024).toFixed(1)} MB) \u2014 open it locally.` };
|
|
46416
|
-
return { bytes: await
|
|
47238
|
+
return { bytes: await readFile8(real), size: s.size };
|
|
46417
47239
|
} catch (err) {
|
|
46418
47240
|
return { error: String(err.message) };
|
|
46419
47241
|
}
|
|
46420
47242
|
}
|
|
46421
|
-
async function runRemoteFsOp(root, op, args) {
|
|
47243
|
+
async function runRemoteFsOp(root, op, args, quotaKey) {
|
|
46422
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
|
+
}
|
|
46423
47250
|
switch (op) {
|
|
46424
47251
|
case "readDir": {
|
|
46425
47252
|
const dir = String(a[0] ?? root);
|
|
46426
|
-
|
|
47253
|
+
const real = safeContentPath(dir);
|
|
47254
|
+
if (!real)
|
|
46427
47255
|
return [];
|
|
46428
47256
|
try {
|
|
46429
|
-
const entries = (await
|
|
46430
|
-
const ignored = await gitIgnoredNames(
|
|
46431
|
-
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);
|
|
46432
47260
|
} catch {
|
|
46433
47261
|
return [];
|
|
46434
47262
|
}
|
|
46435
47263
|
}
|
|
46436
47264
|
case "listAllFiles": {
|
|
46437
47265
|
const dir = String(a[0] ?? root);
|
|
46438
|
-
|
|
47266
|
+
const real = safeContentPath(dir);
|
|
47267
|
+
if (!real)
|
|
46439
47268
|
return [];
|
|
46440
47269
|
try {
|
|
46441
|
-
return await listAllFilesUnder(
|
|
47270
|
+
return await listAllFilesUnder(real);
|
|
46442
47271
|
} catch {
|
|
46443
47272
|
return [];
|
|
46444
47273
|
}
|
|
46445
47274
|
}
|
|
46446
47275
|
case "readFile": {
|
|
46447
47276
|
const p = String(a[0] ?? "");
|
|
46448
|
-
|
|
47277
|
+
const real = safeContentPath(p);
|
|
47278
|
+
if (!real)
|
|
46449
47279
|
return { error: "Path is outside the shared root." };
|
|
46450
47280
|
const ext = p.split(".").pop()?.toLowerCase() ?? "";
|
|
46451
47281
|
if (BINARY_EXT.has(ext))
|
|
46452
47282
|
return { error: "Binary file \u2014 not shown in the editor." };
|
|
46453
47283
|
try {
|
|
46454
|
-
const s = statSync3(
|
|
47284
|
+
const s = statSync3(real);
|
|
46455
47285
|
if (s.size > TEXT_SIZE_LIMIT)
|
|
46456
47286
|
return { error: "File too large for text view." };
|
|
46457
|
-
return { content: await
|
|
47287
|
+
return { content: await readFile8(real, "utf-8") };
|
|
46458
47288
|
} catch (err) {
|
|
46459
47289
|
return { error: String(err.message) };
|
|
46460
47290
|
}
|
|
@@ -46462,11 +47292,12 @@ async function runRemoteFsOp(root, op, args) {
|
|
|
46462
47292
|
case "writeFile": {
|
|
46463
47293
|
const p = String(a[0] ?? "");
|
|
46464
47294
|
const content = String(a[1] ?? "");
|
|
46465
|
-
|
|
47295
|
+
const real = safeContentPath(p);
|
|
47296
|
+
if (!real)
|
|
46466
47297
|
return OUTSIDE("write");
|
|
46467
47298
|
try {
|
|
46468
|
-
await mkdir5(dirname7(
|
|
46469
|
-
await writeFile5(
|
|
47299
|
+
await mkdir5(dirname7(real), { recursive: true });
|
|
47300
|
+
await writeFile5(real, content, "utf-8");
|
|
46470
47301
|
return { ok: true };
|
|
46471
47302
|
} catch (err) {
|
|
46472
47303
|
return { ok: false, error: String(err.message) };
|
|
@@ -46475,11 +47306,12 @@ async function runRemoteFsOp(root, op, args) {
|
|
|
46475
47306
|
case "writeBinary": {
|
|
46476
47307
|
const p = String(a[0] ?? "");
|
|
46477
47308
|
const base643 = String(a[1] ?? "");
|
|
46478
|
-
|
|
47309
|
+
const real = safeContentPath(p);
|
|
47310
|
+
if (!real)
|
|
46479
47311
|
return OUTSIDE("write");
|
|
46480
47312
|
try {
|
|
46481
|
-
await mkdir5(dirname7(
|
|
46482
|
-
await writeFile5(
|
|
47313
|
+
await mkdir5(dirname7(real), { recursive: true });
|
|
47314
|
+
await writeFile5(real, Buffer.from(base643, "base64"));
|
|
46483
47315
|
return { ok: true };
|
|
46484
47316
|
} catch (err) {
|
|
46485
47317
|
return { ok: false, error: String(err.message) };
|
|
@@ -46487,10 +47319,11 @@ async function runRemoteFsOp(root, op, args) {
|
|
|
46487
47319
|
}
|
|
46488
47320
|
case "statFile": {
|
|
46489
47321
|
const p = String(a[0] ?? "");
|
|
46490
|
-
|
|
47322
|
+
const real = safeContentPath(p);
|
|
47323
|
+
if (!real)
|
|
46491
47324
|
return { error: "Path is outside the shared root." };
|
|
46492
47325
|
try {
|
|
46493
|
-
const s = statSync3(
|
|
47326
|
+
const s = statSync3(real);
|
|
46494
47327
|
if (!s.isFile())
|
|
46495
47328
|
return { error: "Not a file" };
|
|
46496
47329
|
const ext = p.split(".").pop()?.toLowerCase() ?? "";
|
|
@@ -46501,7 +47334,8 @@ async function runRemoteFsOp(root, op, args) {
|
|
|
46501
47334
|
}
|
|
46502
47335
|
case "readChunk": {
|
|
46503
47336
|
const p = String(a[0] ?? "");
|
|
46504
|
-
|
|
47337
|
+
const real = safeContentPath(p);
|
|
47338
|
+
if (!real)
|
|
46505
47339
|
return { error: "Path is outside the shared root." };
|
|
46506
47340
|
const off = Math.max(0, Math.floor(Number(a[1]) || 0));
|
|
46507
47341
|
const len = Math.min(1024 * 1024, Math.max(0, Math.floor(Number(a[2]) || 0)));
|
|
@@ -46509,7 +47343,7 @@ async function runRemoteFsOp(root, op, args) {
|
|
|
46509
47343
|
return { base64: "", bytesRead: 0 };
|
|
46510
47344
|
let fd = null;
|
|
46511
47345
|
try {
|
|
46512
|
-
fd = openSync2(
|
|
47346
|
+
fd = openSync2(real, "r");
|
|
46513
47347
|
const buf = Buffer.alloc(len);
|
|
46514
47348
|
const bytesRead = readSync(fd, buf, 0, len, off);
|
|
46515
47349
|
return { base64: buf.subarray(0, bytesRead).toString("base64"), bytesRead };
|
|
@@ -46525,13 +47359,14 @@ async function runRemoteFsOp(root, op, args) {
|
|
|
46525
47359
|
}
|
|
46526
47360
|
case "readBinary": {
|
|
46527
47361
|
const p = String(a[0] ?? "");
|
|
46528
|
-
|
|
47362
|
+
const real = safeContentPath(p);
|
|
47363
|
+
if (!real)
|
|
46529
47364
|
return { error: "Path is outside the shared root." };
|
|
46530
47365
|
try {
|
|
46531
|
-
const s = statSync3(
|
|
47366
|
+
const s = statSync3(real);
|
|
46532
47367
|
if (s.size > REMOTE_READ_CAP)
|
|
46533
47368
|
return { error: `File is too large to read whole over the network (${(s.size / 1024 / 1024).toFixed(1)} MB) \u2014 open it locally.` };
|
|
46534
|
-
return { base64: (await
|
|
47369
|
+
return { base64: (await readFile8(real)).toString("base64"), size: s.size };
|
|
46535
47370
|
} catch (err) {
|
|
46536
47371
|
return { error: String(err.message) };
|
|
46537
47372
|
}
|
|
@@ -46541,8 +47376,8 @@ async function runRemoteFsOp(root, op, args) {
|
|
|
46541
47376
|
const name = String(a[1] ?? "").trim();
|
|
46542
47377
|
if (!validName(name))
|
|
46543
47378
|
return { ok: false, error: "Invalid project name." };
|
|
46544
|
-
const target =
|
|
46545
|
-
if (!
|
|
47379
|
+
const target = join20(parent, name);
|
|
47380
|
+
if (!insideOk(target))
|
|
46546
47381
|
return { ok: false, error: "Refusing to create a project outside the shared root." };
|
|
46547
47382
|
if (existsSync8(target))
|
|
46548
47383
|
return { ok: false, error: `A folder named "${name}" already exists.` };
|
|
@@ -46559,14 +47394,14 @@ async function runRemoteFsOp(root, op, args) {
|
|
|
46559
47394
|
const name = String(a[1] ?? "").trim();
|
|
46560
47395
|
if (!validName(name))
|
|
46561
47396
|
return { ok: false, error: "Invalid name." };
|
|
46562
|
-
|
|
47397
|
+
const target = join20(parent, name);
|
|
47398
|
+
if (!insideOk(target))
|
|
46563
47399
|
return OUTSIDE("create");
|
|
46564
|
-
const target = join19(parent, name);
|
|
46565
47400
|
if (existsSync8(target))
|
|
46566
47401
|
return { ok: false, error: `"${name}" already exists.` };
|
|
46567
47402
|
try {
|
|
46568
47403
|
if (op === "createFile")
|
|
46569
|
-
await writeFile5(target, "", "utf-8");
|
|
47404
|
+
await writeFile5(target, "", { encoding: "utf-8", flag: CREATE_EXCL_NOFOLLOW });
|
|
46570
47405
|
else
|
|
46571
47406
|
await mkdir5(target);
|
|
46572
47407
|
return { ok: true, path: target };
|
|
@@ -46579,9 +47414,9 @@ async function runRemoteFsOp(root, op, args) {
|
|
|
46579
47414
|
const newName = String(a[1] ?? "").trim();
|
|
46580
47415
|
if (!validName(newName))
|
|
46581
47416
|
return { ok: false, error: "Invalid name." };
|
|
46582
|
-
if (!
|
|
47417
|
+
if (!insideOk(oldPath))
|
|
46583
47418
|
return OUTSIDE("rename");
|
|
46584
|
-
const newPath =
|
|
47419
|
+
const newPath = join20(dirname7(oldPath), newName);
|
|
46585
47420
|
if (existsSync8(newPath))
|
|
46586
47421
|
return { ok: false, error: `A file named "${newName}" already exists.` };
|
|
46587
47422
|
try {
|
|
@@ -46595,7 +47430,7 @@ async function runRemoteFsOp(root, op, args) {
|
|
|
46595
47430
|
const p = String(a[0] ?? "");
|
|
46596
47431
|
if (!p)
|
|
46597
47432
|
return { ok: false, error: "Bad request" };
|
|
46598
|
-
if (!
|
|
47433
|
+
if (!insideOk(p))
|
|
46599
47434
|
return OUTSIDE("delete");
|
|
46600
47435
|
if (resolve5(p) === resolve5(root))
|
|
46601
47436
|
return { ok: false, error: "Refusing to delete the shared root." };
|
|
@@ -46610,14 +47445,14 @@ async function runRemoteFsOp(root, op, args) {
|
|
|
46610
47445
|
case "movePaths": {
|
|
46611
47446
|
const srcPaths = Array.isArray(a[0]) ? a[0] : [];
|
|
46612
47447
|
const destDir = String(a[1] ?? "");
|
|
46613
|
-
if (!
|
|
47448
|
+
if (!insideOk(destDir))
|
|
46614
47449
|
return OUTSIDE(op === "copyPaths" ? "copy" : "move");
|
|
46615
47450
|
try {
|
|
46616
47451
|
const newPaths = [];
|
|
46617
47452
|
for (const src of srcPaths) {
|
|
46618
47453
|
if (typeof src !== "string" || !src)
|
|
46619
47454
|
continue;
|
|
46620
|
-
if (!
|
|
47455
|
+
if (!insideOk(src))
|
|
46621
47456
|
return OUTSIDE(op === "copyPaths" ? "copy from" : "move from");
|
|
46622
47457
|
if (op === "movePaths" && dirname7(src) === destDir) {
|
|
46623
47458
|
newPaths.push(src);
|
|
@@ -46636,18 +47471,31 @@ async function runRemoteFsOp(root, op, args) {
|
|
|
46636
47471
|
}
|
|
46637
47472
|
}
|
|
46638
47473
|
case "openFolder": {
|
|
46639
|
-
const
|
|
46640
|
-
|
|
47474
|
+
const requested = a[0] != null ? resolveParent(String(a[0]), root) : root;
|
|
47475
|
+
const real = safeContentPath(requested);
|
|
47476
|
+
if (!real)
|
|
46641
47477
|
return { error: "outside_root" };
|
|
46642
47478
|
try {
|
|
46643
|
-
const s = await stat2(
|
|
47479
|
+
const s = await stat2(real);
|
|
46644
47480
|
if (!s.isDirectory())
|
|
46645
47481
|
return { error: "not_dir" };
|
|
46646
|
-
return { root: resolve5(
|
|
47482
|
+
return { root: resolve5(real), name: basename2(real) };
|
|
46647
47483
|
} catch {
|
|
46648
47484
|
return { error: "not_found" };
|
|
46649
47485
|
}
|
|
46650
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
|
+
}
|
|
46651
47499
|
case "currentRoot":
|
|
46652
47500
|
return { root, name: basename2(root) };
|
|
46653
47501
|
case "homeDir":
|
|
@@ -46673,7 +47521,7 @@ function watchRemoteRoot(root, key2, onChange) {
|
|
|
46673
47521
|
const rel = String(filename);
|
|
46674
47522
|
if (rel.split(sep4).some((seg) => IGNORED.has(seg)))
|
|
46675
47523
|
return;
|
|
46676
|
-
pending.add(
|
|
47524
|
+
pending.add(join20(resolved, rel));
|
|
46677
47525
|
if (timer)
|
|
46678
47526
|
clearTimeout(timer);
|
|
46679
47527
|
timer = setTimeout(() => {
|
|
@@ -46857,15 +47705,15 @@ function toNdcIceServers(configs) {
|
|
|
46857
47705
|
if (!m)
|
|
46858
47706
|
continue;
|
|
46859
47707
|
const scheme = m[1].toLowerCase();
|
|
46860
|
-
const
|
|
47708
|
+
const hostname6 = m[2];
|
|
46861
47709
|
const port = m[3] ? parseInt(m[3], 10) : scheme.endsWith("s") ? 5349 : 3478;
|
|
46862
47710
|
if (scheme.startsWith("stun")) {
|
|
46863
|
-
out.push({ hostname:
|
|
47711
|
+
out.push({ hostname: hostname6, port });
|
|
46864
47712
|
continue;
|
|
46865
47713
|
}
|
|
46866
47714
|
const transport = (m[4] ?? "udp").toLowerCase();
|
|
46867
47715
|
const relayType = scheme === "turns" ? "TurnTls" : transport === "tcp" ? "TurnTcp" : "TurnUdp";
|
|
46868
|
-
out.push({ hostname:
|
|
47716
|
+
out.push({ hostname: hostname6, port, username: c.username, password: c.credential, relayType });
|
|
46869
47717
|
}
|
|
46870
47718
|
}
|
|
46871
47719
|
return out;
|
|
@@ -47037,13 +47885,13 @@ function dynamicMetrics() {
|
|
|
47037
47885
|
}
|
|
47038
47886
|
|
|
47039
47887
|
// ../../packages/host-core/dist/fs-transfer.js
|
|
47040
|
-
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";
|
|
47041
47889
|
import { openSync as openSync3, readSync as readSync2, closeSync as closeSync3, existsSync as existsSync9, createWriteStream } from "node:fs";
|
|
47042
|
-
import { dirname as dirname9, join as
|
|
47890
|
+
import { dirname as dirname9, join as join22, relative as relative6, sep as sep6 } from "node:path";
|
|
47043
47891
|
|
|
47044
47892
|
// ../../packages/host-core/dist/active-link/paths.js
|
|
47045
|
-
import { homedir as
|
|
47046
|
-
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";
|
|
47047
47895
|
function activeLinkEnabled() {
|
|
47048
47896
|
return process.env.CCB_ACTIVE_LINK !== "0";
|
|
47049
47897
|
}
|
|
@@ -47069,7 +47917,7 @@ function toPosixRel(root, abs) {
|
|
|
47069
47917
|
return relative5(root, abs).split(sep5).join("/");
|
|
47070
47918
|
}
|
|
47071
47919
|
function fromPosixRel(root, rel) {
|
|
47072
|
-
return
|
|
47920
|
+
return join21(root, rel.split("/").join(sep5));
|
|
47073
47921
|
}
|
|
47074
47922
|
function isExcludedAbs(abs, root) {
|
|
47075
47923
|
const rel = toPosixRel(root, abs);
|
|
@@ -47078,10 +47926,10 @@ function isExcludedAbs(abs, root) {
|
|
|
47078
47926
|
return rel.split("/").some(isExcludedName);
|
|
47079
47927
|
}
|
|
47080
47928
|
function activeLinkRoot() {
|
|
47081
|
-
return process.env.CCB_ACTIVE_LINK_DIR ??
|
|
47929
|
+
return process.env.CCB_ACTIVE_LINK_DIR ?? join21(homedir11(), ".tesseract", "active-link");
|
|
47082
47930
|
}
|
|
47083
47931
|
function linkStateDir(linkId) {
|
|
47084
|
-
return
|
|
47932
|
+
return join21(activeLinkRoot(), linkId);
|
|
47085
47933
|
}
|
|
47086
47934
|
|
|
47087
47935
|
// ../../packages/host-core/dist/fs-transfer.js
|
|
@@ -47094,9 +47942,9 @@ function clearTimer(t) {
|
|
|
47094
47942
|
return null;
|
|
47095
47943
|
}
|
|
47096
47944
|
function uniqueDir(parent, name) {
|
|
47097
|
-
let candidate =
|
|
47945
|
+
let candidate = join22(parent, name);
|
|
47098
47946
|
for (let i = 2; existsSync9(candidate) && i < 1e4; i++)
|
|
47099
|
-
candidate =
|
|
47947
|
+
candidate = join22(parent, `${name} ${i}`);
|
|
47100
47948
|
return candidate;
|
|
47101
47949
|
}
|
|
47102
47950
|
var producers = /* @__PURE__ */ new Map();
|
|
@@ -47133,15 +47981,15 @@ async function buildManifest(root, followSymlinks, excludeHeavy) {
|
|
|
47133
47981
|
const files = [];
|
|
47134
47982
|
let totalBytes = 0;
|
|
47135
47983
|
async function walk(dir) {
|
|
47136
|
-
const entries = await
|
|
47984
|
+
const entries = await readdir4(dir, { withFileTypes: true });
|
|
47137
47985
|
for (const e of entries) {
|
|
47138
47986
|
if (ALWAYS_SKIP.has(e.name))
|
|
47139
47987
|
continue;
|
|
47140
47988
|
if (e.isDirectory() && excludeHeavy && HEAVY_DIRS.has(e.name))
|
|
47141
47989
|
continue;
|
|
47142
|
-
const abs =
|
|
47990
|
+
const abs = join22(dir, e.name);
|
|
47143
47991
|
const rel = relative6(root, abs).split(sep6).join("/");
|
|
47144
|
-
const st = await
|
|
47992
|
+
const st = await lstat2(abs).catch(() => null);
|
|
47145
47993
|
if (!st)
|
|
47146
47994
|
continue;
|
|
47147
47995
|
if (st.isSymbolicLink() && !followSymlinks) {
|
|
@@ -47206,11 +48054,11 @@ async function startOutboundTransfer(msg, controllerClientId, ownHostId2, hostRo
|
|
|
47206
48054
|
for (let fileIx = 0; fileIx < manifest.files.length && !state.cancelled; fileIx++) {
|
|
47207
48055
|
const f = manifest.files[fileIx];
|
|
47208
48056
|
if (f.kind === "symlink") {
|
|
47209
|
-
const target = await readlink(
|
|
48057
|
+
const target = await readlink(join22(srcPath, f.rel.split("/").join(sep6))).catch(() => "");
|
|
47210
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 });
|
|
47211
48059
|
continue;
|
|
47212
48060
|
}
|
|
47213
|
-
const abs =
|
|
48061
|
+
const abs = join22(srcPath, f.rel.split("/").join(sep6));
|
|
47214
48062
|
let fd = null;
|
|
47215
48063
|
try {
|
|
47216
48064
|
fd = openSync3(abs, "r");
|
|
@@ -47322,7 +48170,7 @@ async function beginInboundTransfer(msg, hostRoot, send2) {
|
|
|
47322
48170
|
failNoState("Invalid destination name.");
|
|
47323
48171
|
return;
|
|
47324
48172
|
}
|
|
47325
|
-
let destRoot =
|
|
48173
|
+
let destRoot = join22(parent, msg.destName);
|
|
47326
48174
|
let createdRoot = false;
|
|
47327
48175
|
if (existsSync9(destRoot)) {
|
|
47328
48176
|
if (msg.onCollision === "abort") {
|
|
@@ -47364,7 +48212,7 @@ async function beginInboundTransfer(msg, hostRoot, send2) {
|
|
|
47364
48212
|
try {
|
|
47365
48213
|
await mkdir6(destRoot, { recursive: true });
|
|
47366
48214
|
for (const d of msg.dirs) {
|
|
47367
|
-
const abs =
|
|
48215
|
+
const abs = join22(destRoot, d.rel.split("/").join(sep6));
|
|
47368
48216
|
if (isInsideRoot(abs, destRoot))
|
|
47369
48217
|
await mkdir6(abs, { recursive: true }).catch(() => {
|
|
47370
48218
|
});
|
|
@@ -47378,7 +48226,7 @@ async function writeInboundChunk(msg, send2) {
|
|
|
47378
48226
|
if (!s || s.cancelled)
|
|
47379
48227
|
return;
|
|
47380
48228
|
armConsumerWatchdog(msg.transferId, send2);
|
|
47381
|
-
const abs =
|
|
48229
|
+
const abs = join22(s.destRoot, msg.rel.split("/").join(sep6));
|
|
47382
48230
|
if (msg.rel.includes("..") || !isInsideRoot(abs, s.destRoot)) {
|
|
47383
48231
|
failConsumer(msg.transferId, `Refusing a path outside the destination: ${msg.rel}`, send2);
|
|
47384
48232
|
return;
|
|
@@ -47386,7 +48234,7 @@ async function writeInboundChunk(msg, send2) {
|
|
|
47386
48234
|
try {
|
|
47387
48235
|
if (msg.kind === "symlink") {
|
|
47388
48236
|
const target = Buffer.from(msg.base64, "base64").toString("utf-8");
|
|
47389
|
-
const resolved =
|
|
48237
|
+
const resolved = join22(dirname9(abs), target);
|
|
47390
48238
|
if (isInsideRoot(resolved, s.destRoot)) {
|
|
47391
48239
|
await mkdir6(dirname9(abs), { recursive: true }).catch(() => {
|
|
47392
48240
|
});
|
|
@@ -47461,90 +48309,13 @@ function cancelTransfersForClient(clientId, send2) {
|
|
|
47461
48309
|
}
|
|
47462
48310
|
|
|
47463
48311
|
// ../../packages/host-core/dist/peer-link/index.js
|
|
47464
|
-
import { homedir as
|
|
47465
|
-
import { join as
|
|
47466
|
-
|
|
47467
|
-
// ../../packages/host-core/dist/peer-link/manager.js
|
|
47468
|
-
import { randomBytes as randomBytes2 } from "node:crypto";
|
|
47469
|
-
|
|
47470
|
-
// ../../packages/host-core/dist/peer-link/tls-direct.js
|
|
47471
|
-
var import_selfsigned = __toESM(require_selfsigned(), 1);
|
|
47472
|
-
import tls from "node:tls";
|
|
47473
|
-
import { X509Certificate } from "node:crypto";
|
|
47474
|
-
import { readFileSync as readFileSync9, writeFileSync as writeFileSync9, existsSync as existsSync10, mkdirSync as mkdirSync8 } from "node:fs";
|
|
47475
|
-
import { dirname as dirname10 } from "node:path";
|
|
47476
|
-
function fingerprintOf(certPem) {
|
|
47477
|
-
return new X509Certificate(certPem).fingerprint256;
|
|
47478
|
-
}
|
|
47479
|
-
async function loadOrCreateIdentity(certPath, keyPath) {
|
|
47480
|
-
if (existsSync10(certPath) && existsSync10(keyPath)) {
|
|
47481
|
-
const cert = readFileSync9(certPath, "utf8");
|
|
47482
|
-
const key2 = readFileSync9(keyPath, "utf8");
|
|
47483
|
-
return { cert, key: key2, fingerprint256: fingerprintOf(cert) };
|
|
47484
|
-
}
|
|
47485
|
-
const tenYears = new Date(Date.now() + 10 * 365 * 24 * 60 * 60 * 1e3);
|
|
47486
|
-
const pems = await (0, import_selfsigned.generate)([{ name: "commonName", value: "tesseract-peer" }], { keySize: 2048, notAfterDate: tenYears, algorithm: "sha256" });
|
|
47487
|
-
try {
|
|
47488
|
-
mkdirSync8(dirname10(certPath), { recursive: true });
|
|
47489
|
-
} catch {
|
|
47490
|
-
}
|
|
47491
|
-
try {
|
|
47492
|
-
writeFileSync9(certPath, pems.cert, { mode: 384 });
|
|
47493
|
-
writeFileSync9(keyPath, pems.private, { mode: 384 });
|
|
47494
|
-
} catch {
|
|
47495
|
-
}
|
|
47496
|
-
return { cert: pems.cert, key: pems.private, fingerprint256: fingerprintOf(pems.cert) };
|
|
47497
|
-
}
|
|
47498
|
-
function pinnedConnect(opts) {
|
|
47499
|
-
return new Promise((resolve10, reject) => {
|
|
47500
|
-
let settled = false;
|
|
47501
|
-
const done = (err, sock) => {
|
|
47502
|
-
if (settled)
|
|
47503
|
-
return;
|
|
47504
|
-
settled = true;
|
|
47505
|
-
if (err) {
|
|
47506
|
-
try {
|
|
47507
|
-
sock?.destroy();
|
|
47508
|
-
} catch {
|
|
47509
|
-
}
|
|
47510
|
-
reject(err);
|
|
47511
|
-
} else
|
|
47512
|
-
resolve10(sock);
|
|
47513
|
-
};
|
|
47514
|
-
const socket2 = tls.connect({
|
|
47515
|
-
host: opts.host,
|
|
47516
|
-
port: opts.port,
|
|
47517
|
-
// No CA / hostname identity — we authenticate SOLELY by the brokered
|
|
47518
|
-
// fingerprint, so accept the self-signed cert at the TLS layer and verify
|
|
47519
|
-
// the pin ourselves below (fail-closed).
|
|
47520
|
-
rejectUnauthorized: false,
|
|
47521
|
-
checkServerIdentity: () => void 0,
|
|
47522
|
-
timeout: opts.timeoutMs ?? 8e3
|
|
47523
|
-
}, () => {
|
|
47524
|
-
const peer = socket2.getPeerCertificate();
|
|
47525
|
-
const fp = peer && peer.fingerprint256;
|
|
47526
|
-
if (!fp || fp !== opts.peerFingerprint256) {
|
|
47527
|
-
done(new Error("peer-link: TLS fingerprint mismatch (fail-closed)"));
|
|
47528
|
-
return;
|
|
47529
|
-
}
|
|
47530
|
-
socket2.setNoDelay(true);
|
|
47531
|
-
done(void 0, socket2);
|
|
47532
|
-
});
|
|
47533
|
-
socket2.once("timeout", () => done(new Error("peer-link: TLS connect timeout")));
|
|
47534
|
-
socket2.once("error", (e) => done(e instanceof Error ? e : new Error(String(e))));
|
|
47535
|
-
});
|
|
47536
|
-
}
|
|
47537
|
-
function createListener(identity, port, onSocket) {
|
|
47538
|
-
const server = tls.createServer({ cert: identity.cert, key: identity.key, requestCert: false, rejectUnauthorized: false }, (sock) => {
|
|
47539
|
-
sock.setNoDelay(true);
|
|
47540
|
-
onSocket(sock);
|
|
47541
|
-
});
|
|
47542
|
-
server.listen(port, "0.0.0.0");
|
|
47543
|
-
return server;
|
|
47544
|
-
}
|
|
48312
|
+
import { homedir as homedir12 } from "node:os";
|
|
48313
|
+
import { join as join23 } from "node:path";
|
|
47545
48314
|
|
|
47546
48315
|
// ../../packages/host-core/dist/peer-link/manager.js
|
|
48316
|
+
init_tls_direct();
|
|
47547
48317
|
init_transport();
|
|
48318
|
+
import { randomBytes as randomBytes3, timingSafeEqual as timingSafeEqual2 } from "node:crypto";
|
|
47548
48319
|
|
|
47549
48320
|
// ../../packages/host-core/dist/peer-link/candidates.js
|
|
47550
48321
|
import os2 from "node:os";
|
|
@@ -47575,6 +48346,7 @@ function orderCandidates(cands) {
|
|
|
47575
48346
|
}
|
|
47576
48347
|
|
|
47577
48348
|
// ../../packages/host-core/dist/peer-link/ladder.js
|
|
48349
|
+
init_tls_direct();
|
|
47578
48350
|
init_transport();
|
|
47579
48351
|
function pathFor(c) {
|
|
47580
48352
|
return c.type === "host" ? "lan" : "public";
|
|
@@ -47680,7 +48452,7 @@ var PeerLinkManager = class {
|
|
|
47680
48452
|
const q = await Promise.resolve().then(() => (init_quic_transport(), quic_transport_exports));
|
|
47681
48453
|
const socket2 = await q.makeQuicSocket(this.udpPort);
|
|
47682
48454
|
this.quicSocket = socket2;
|
|
47683
|
-
await q.startQuicServerOnSocket({ socket: socket2, identity, onStream: (
|
|
48455
|
+
await q.startQuicServerOnSocket({ socket: socket2, identity, onStream: (bs2) => this.acceptInbound(bs2, "punch") });
|
|
47684
48456
|
const refresh = async () => {
|
|
47685
48457
|
try {
|
|
47686
48458
|
const s = await q.discoverSrflx(socket2, stunHost, stunPort);
|
|
@@ -47742,10 +48514,13 @@ var PeerLinkManager = class {
|
|
|
47742
48514
|
let matchedHex;
|
|
47743
48515
|
const { link, ready } = socketPeerLink(sock, "", path5, {
|
|
47744
48516
|
expectToken: (f) => {
|
|
47745
|
-
const
|
|
47746
|
-
|
|
47747
|
-
|
|
47748
|
-
|
|
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
|
+
}
|
|
47749
48524
|
}
|
|
47750
48525
|
return false;
|
|
47751
48526
|
}
|
|
@@ -47889,7 +48664,7 @@ var PeerLinkManager = class {
|
|
|
47889
48664
|
this.attach(ctx);
|
|
47890
48665
|
await this.ensureListener();
|
|
47891
48666
|
const myFingerprint = (await this.identity()).fingerprint256;
|
|
47892
|
-
const rid =
|
|
48667
|
+
const rid = randomBytes3(8).toString("hex");
|
|
47893
48668
|
const myCandidates = gatherTcpCandidates(this.listenPort, this.observedIp);
|
|
47894
48669
|
return new Promise((resolve10, reject) => {
|
|
47895
48670
|
let settled = false;
|
|
@@ -47973,8 +48748,8 @@ var PeerLinkManager = class {
|
|
|
47973
48748
|
// ../../packages/host-core/dist/peer-link/index.js
|
|
47974
48749
|
var P2P_ENABLED = process.env.CCB_P2P !== "0";
|
|
47975
48750
|
function identityPaths() {
|
|
47976
|
-
const dir = process.env.CCB_P2P_DIR ??
|
|
47977
|
-
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") };
|
|
47978
48753
|
}
|
|
47979
48754
|
var manager = null;
|
|
47980
48755
|
function peerLinkManager() {
|
|
@@ -47989,9 +48764,9 @@ async function openPeerLink(peerHostId, ctx) {
|
|
|
47989
48764
|
}
|
|
47990
48765
|
|
|
47991
48766
|
// ../../packages/host-core/dist/active-link/engine.js
|
|
47992
|
-
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";
|
|
47993
48768
|
import { existsSync as existsSync12 } from "node:fs";
|
|
47994
|
-
import { dirname as dirname14, join as
|
|
48769
|
+
import { dirname as dirname14, join as join28, resolve as resolve8 } from "node:path";
|
|
47995
48770
|
|
|
47996
48771
|
// ../../packages/host-core/dist/active-link/blocks.js
|
|
47997
48772
|
import { createHash as createHash3 } from "node:crypto";
|
|
@@ -48256,7 +49031,7 @@ var BlockAssembler = class {
|
|
|
48256
49031
|
|
|
48257
49032
|
// ../../packages/host-core/dist/active-link/manifest.js
|
|
48258
49033
|
import { readFileSync as readFileSync10, writeFileSync as writeFileSync10, mkdirSync as mkdirSync9, existsSync as existsSync11, renameSync as renameSync5 } from "node:fs";
|
|
48259
|
-
import { join as
|
|
49034
|
+
import { join as join24 } from "node:path";
|
|
48260
49035
|
|
|
48261
49036
|
// ../../packages/host-core/dist/active-link/reconcile.js
|
|
48262
49037
|
var ABSENT = { kind: "absent", t: -Infinity, hash: "" };
|
|
@@ -48292,7 +49067,7 @@ var LinkManifest = class {
|
|
|
48292
49067
|
this.dir = dir;
|
|
48293
49068
|
}
|
|
48294
49069
|
file() {
|
|
48295
|
-
return
|
|
49070
|
+
return join24(this.dir, "manifest.json");
|
|
48296
49071
|
}
|
|
48297
49072
|
load() {
|
|
48298
49073
|
try {
|
|
@@ -48393,14 +49168,14 @@ var LinkManifest = class {
|
|
|
48393
49168
|
};
|
|
48394
49169
|
|
|
48395
49170
|
// ../../packages/host-core/dist/active-link/scanner.js
|
|
48396
|
-
import { readdir as
|
|
48397
|
-
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";
|
|
48398
49173
|
async function walkRels(root, extraIgnore) {
|
|
48399
49174
|
const out = [];
|
|
48400
49175
|
async function rec(dir) {
|
|
48401
49176
|
let entries;
|
|
48402
49177
|
try {
|
|
48403
|
-
entries = await
|
|
49178
|
+
entries = await readdir5(dir, { withFileTypes: true });
|
|
48404
49179
|
} catch {
|
|
48405
49180
|
return;
|
|
48406
49181
|
}
|
|
@@ -48409,7 +49184,7 @@ async function walkRels(root, extraIgnore) {
|
|
|
48409
49184
|
continue;
|
|
48410
49185
|
if (e.isDirectory() && HEAVY_DIRS.has(e.name))
|
|
48411
49186
|
continue;
|
|
48412
|
-
const abs =
|
|
49187
|
+
const abs = join25(dir, e.name);
|
|
48413
49188
|
if (extraIgnore?.(abs))
|
|
48414
49189
|
continue;
|
|
48415
49190
|
if (e.isDirectory())
|
|
@@ -48426,7 +49201,7 @@ async function scanTree(root, prior, allow, extraIgnore) {
|
|
|
48426
49201
|
const rels = allow ? [...allow] : await walkRels(root, extraIgnore);
|
|
48427
49202
|
for (const rel of rels) {
|
|
48428
49203
|
const abs = fromPosixRel(root, rel);
|
|
48429
|
-
const st = await
|
|
49204
|
+
const st = await lstat3(abs).catch(() => null);
|
|
48430
49205
|
if (!st)
|
|
48431
49206
|
continue;
|
|
48432
49207
|
if (st.isSymbolicLink()) {
|
|
@@ -48434,9 +49209,9 @@ async function scanTree(root, prior, allow, extraIgnore) {
|
|
|
48434
49209
|
records.set(rel, { kind: "symlink", mtimeMs: Math.floor(st.mtimeMs), size: 0, mode: st.mode, hash: hashString(target), blocks: [] });
|
|
48435
49210
|
} else if (st.isFile()) {
|
|
48436
49211
|
const mtimeMs = Math.floor(st.mtimeMs);
|
|
48437
|
-
const
|
|
48438
|
-
if (
|
|
48439
|
-
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 });
|
|
48440
49215
|
} else {
|
|
48441
49216
|
const h = await hashFileBlocks(abs).catch(() => null);
|
|
48442
49217
|
if (!h)
|
|
@@ -48448,7 +49223,7 @@ async function scanTree(root, prior, allow, extraIgnore) {
|
|
|
48448
49223
|
return { records };
|
|
48449
49224
|
}
|
|
48450
49225
|
function absOf(root, rel) {
|
|
48451
|
-
return
|
|
49226
|
+
return join25(root, rel.split("/").join(sep7));
|
|
48452
49227
|
}
|
|
48453
49228
|
|
|
48454
49229
|
// ../../packages/host-core/dist/active-link/gitignore.js
|
|
@@ -48466,7 +49241,7 @@ async function gitSyncedFiles(root) {
|
|
|
48466
49241
|
|
|
48467
49242
|
// ../../packages/host-core/dist/active-link/session.js
|
|
48468
49243
|
import { createHash as createHash4 } from "node:crypto";
|
|
48469
|
-
import { lstat as
|
|
49244
|
+
import { lstat as lstat4, readlink as readlink3, open as open2 } from "node:fs/promises";
|
|
48470
49245
|
var OutboundSender = class {
|
|
48471
49246
|
deps;
|
|
48472
49247
|
queue = [];
|
|
@@ -48524,8 +49299,12 @@ var OutboundSender = class {
|
|
|
48524
49299
|
}
|
|
48525
49300
|
}
|
|
48526
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
|
+
}
|
|
48527
49306
|
const abs = absOf(this.deps.localRoot, rel);
|
|
48528
|
-
const st = await
|
|
49307
|
+
const st = await lstat4(abs).catch(() => null);
|
|
48529
49308
|
if (!st)
|
|
48530
49309
|
return;
|
|
48531
49310
|
const { linkId, ownHostId: ownHostId2, send: send2 } = this.deps;
|
|
@@ -48613,8 +49392,12 @@ var OutboundSender = class {
|
|
|
48613
49392
|
this.deps.onSent?.(rel);
|
|
48614
49393
|
}
|
|
48615
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
|
+
}
|
|
48616
49399
|
const abs = absOf(this.deps.localRoot, rel);
|
|
48617
|
-
const st = await
|
|
49400
|
+
const st = await lstat4(abs).catch(() => null);
|
|
48618
49401
|
if (!st || !st.isFile())
|
|
48619
49402
|
return;
|
|
48620
49403
|
const { linkId, ownHostId: ownHostId2, send: send2 } = this.deps;
|
|
@@ -48659,12 +49442,12 @@ var OutboundSender = class {
|
|
|
48659
49442
|
|
|
48660
49443
|
// ../../node_modules/chokidar/esm/index.js
|
|
48661
49444
|
import { stat as statcb } from "fs";
|
|
48662
|
-
import { stat as stat6, readdir as
|
|
49445
|
+
import { stat as stat6, readdir as readdir7 } from "fs/promises";
|
|
48663
49446
|
import { EventEmitter as EventEmitter2 } from "events";
|
|
48664
49447
|
import * as sysPath2 from "path";
|
|
48665
49448
|
|
|
48666
49449
|
// ../../node_modules/readdirp/esm/index.js
|
|
48667
|
-
import { stat as stat4, lstat as
|
|
49450
|
+
import { stat as stat4, lstat as lstat5, readdir as readdir6, realpath } from "node:fs/promises";
|
|
48668
49451
|
import { Readable } from "node:stream";
|
|
48669
49452
|
import { resolve as presolve, relative as prelative, join as pjoin, sep as psep } from "node:path";
|
|
48670
49453
|
var EntryTypes = {
|
|
@@ -48731,7 +49514,7 @@ var ReaddirpStream = class extends Readable {
|
|
|
48731
49514
|
const { root, type } = opts;
|
|
48732
49515
|
this._fileFilter = normalizeFilter(opts.fileFilter);
|
|
48733
49516
|
this._directoryFilter = normalizeFilter(opts.directoryFilter);
|
|
48734
|
-
const statMethod = opts.lstat ?
|
|
49517
|
+
const statMethod = opts.lstat ? lstat5 : stat4;
|
|
48735
49518
|
if (wantBigintFsStats) {
|
|
48736
49519
|
this._stat = (path5) => statMethod(path5, { bigint: true });
|
|
48737
49520
|
} else {
|
|
@@ -48802,7 +49585,7 @@ var ReaddirpStream = class extends Readable {
|
|
|
48802
49585
|
async _exploreDir(path5, depth) {
|
|
48803
49586
|
let files;
|
|
48804
49587
|
try {
|
|
48805
|
-
files = await
|
|
49588
|
+
files = await readdir6(path5, this._rdOptions);
|
|
48806
49589
|
} catch (error51) {
|
|
48807
49590
|
this._onError(error51);
|
|
48808
49591
|
}
|
|
@@ -48841,7 +49624,7 @@ var ReaddirpStream = class extends Readable {
|
|
|
48841
49624
|
const full = entry.fullPath;
|
|
48842
49625
|
try {
|
|
48843
49626
|
const entryRealPath = await realpath(full);
|
|
48844
|
-
const entryRealPathStats = await
|
|
49627
|
+
const entryRealPathStats = await lstat5(entryRealPath);
|
|
48845
49628
|
if (entryRealPathStats.isFile()) {
|
|
48846
49629
|
return "file";
|
|
48847
49630
|
}
|
|
@@ -48884,7 +49667,7 @@ function readdirp(root, options = {}) {
|
|
|
48884
49667
|
|
|
48885
49668
|
// ../../node_modules/chokidar/esm/handler.js
|
|
48886
49669
|
import { watchFile, unwatchFile, watch as fs_watch } from "fs";
|
|
48887
|
-
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";
|
|
48888
49671
|
import * as sysPath from "path";
|
|
48889
49672
|
import { type as osType } from "os";
|
|
48890
49673
|
var STR_DATA = "data";
|
|
@@ -48911,7 +49694,7 @@ var EVENTS = {
|
|
|
48911
49694
|
};
|
|
48912
49695
|
var EV = EVENTS;
|
|
48913
49696
|
var THROTTLE_MODE_WATCH = "watch";
|
|
48914
|
-
var statMethods = { lstat:
|
|
49697
|
+
var statMethods = { lstat: lstat6, stat: stat5 };
|
|
48915
49698
|
var KEY_LISTENERS = "listeners";
|
|
48916
49699
|
var KEY_ERR = "errHandlers";
|
|
48917
49700
|
var KEY_RAW = "rawEmitters";
|
|
@@ -49771,7 +50554,7 @@ var DirEntry = class {
|
|
|
49771
50554
|
return;
|
|
49772
50555
|
const dir = this.path;
|
|
49773
50556
|
try {
|
|
49774
|
-
await
|
|
50557
|
+
await readdir7(dir);
|
|
49775
50558
|
} catch (err) {
|
|
49776
50559
|
if (this._removeWatcher) {
|
|
49777
50560
|
this._removeWatcher(sysPath2.dirname(dir), sysPath2.basename(dir));
|
|
@@ -50465,7 +51248,7 @@ var ActiveLinkEngine = class {
|
|
|
50465
51248
|
}
|
|
50466
51249
|
async start() {
|
|
50467
51250
|
this.emit({ type: "status", linkId: this.opts.linkId, state: "scanning" });
|
|
50468
|
-
await rm3(
|
|
51251
|
+
await rm3(join28(this.stateDir, ".staging"), { recursive: true, force: true }).catch(() => {
|
|
50469
51252
|
});
|
|
50470
51253
|
this.manifest.load();
|
|
50471
51254
|
await this.reconcileScan();
|
|
@@ -50687,16 +51470,18 @@ var ActiveLinkEngine = class {
|
|
|
50687
51470
|
// ── Block-delta: sender side (reply to a block-map request) ───────────────────
|
|
50688
51471
|
async onBlockMapReq(rel) {
|
|
50689
51472
|
const abs = absOf(this.opts.localRoot, rel);
|
|
50690
|
-
|
|
51473
|
+
if (rel.includes("..") || !isInsideRoot(abs, this.opts.localRoot))
|
|
51474
|
+
return;
|
|
51475
|
+
const st = await lstat7(abs).catch(() => null);
|
|
50691
51476
|
if (!st || !st.isFile())
|
|
50692
51477
|
return;
|
|
50693
51478
|
const mtimeMs = Math.floor(st.mtimeMs);
|
|
50694
|
-
const
|
|
51479
|
+
const cached3 = this.manifest.getRecord(rel);
|
|
50695
51480
|
let wholeHash;
|
|
50696
51481
|
let blocks;
|
|
50697
|
-
if (
|
|
50698
|
-
wholeHash =
|
|
50699
|
-
blocks =
|
|
51482
|
+
if (cached3 && cached3.kind === "file" && cached3.size === st.size && cached3.mtimeMs === mtimeMs) {
|
|
51483
|
+
wholeHash = cached3.hash;
|
|
51484
|
+
blocks = cached3.blocks;
|
|
50700
51485
|
} else {
|
|
50701
51486
|
const h = await hashFileBlocks(abs).catch(() => null);
|
|
50702
51487
|
if (!h)
|
|
@@ -50910,6 +51695,8 @@ var ActiveLinkEngine = class {
|
|
|
50910
51695
|
const fromRec = this.manifest.getRecord(fromRel);
|
|
50911
51696
|
const fromAbs = absOf(this.opts.localRoot, fromRel);
|
|
50912
51697
|
const toAbs2 = absOf(this.opts.localRoot, toRel2);
|
|
51698
|
+
if (fromRel.includes("..") || !isInsideRoot(fromAbs, this.opts.localRoot))
|
|
51699
|
+
return;
|
|
50913
51700
|
if (toRel2.includes("..") || !isInsideRoot(toAbs2, this.opts.localRoot))
|
|
50914
51701
|
return;
|
|
50915
51702
|
if (fromRec && fromRec.kind === "file") {
|
|
@@ -51007,7 +51794,7 @@ var ActiveLinkEngine = class {
|
|
|
51007
51794
|
/** Current on-disk record for a rel, or null if missing/a directory. */
|
|
51008
51795
|
async computeRecord(rel) {
|
|
51009
51796
|
const abs = absOf(this.opts.localRoot, rel);
|
|
51010
|
-
const st = await
|
|
51797
|
+
const st = await lstat7(abs).catch(() => null);
|
|
51011
51798
|
if (!st || st.isDirectory())
|
|
51012
51799
|
return null;
|
|
51013
51800
|
if (st.isSymbolicLink()) {
|
|
@@ -51017,9 +51804,9 @@ var ActiveLinkEngine = class {
|
|
|
51017
51804
|
if (!st.isFile())
|
|
51018
51805
|
return null;
|
|
51019
51806
|
const mtimeMs = Math.floor(st.mtimeMs);
|
|
51020
|
-
const
|
|
51021
|
-
if (
|
|
51022
|
-
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 };
|
|
51023
51810
|
}
|
|
51024
51811
|
const h = await hashFileBlocks(abs).catch(() => null);
|
|
51025
51812
|
if (!h)
|
|
@@ -51089,7 +51876,7 @@ var ActiveLinkEngine = class {
|
|
|
51089
51876
|
this.inboundWatchdog.clear();
|
|
51090
51877
|
}
|
|
51091
51878
|
stagingPath(rel, tag) {
|
|
51092
|
-
return
|
|
51879
|
+
return join28(this.stateDir, ".staging", `${hashString(rel)}.${tag}`);
|
|
51093
51880
|
}
|
|
51094
51881
|
pruneSuppress() {
|
|
51095
51882
|
const now = this.now();
|
|
@@ -51115,9 +51902,9 @@ var ActiveLinkEngine = class {
|
|
|
51115
51902
|
|
|
51116
51903
|
// ../../packages/host-core/dist/active-link/config.js
|
|
51117
51904
|
import { readFileSync as readFileSync11, writeFileSync as writeFileSync11, mkdirSync as mkdirSync10, existsSync as existsSync13, renameSync as renameSync6, watch as watch4 } from "node:fs";
|
|
51118
|
-
import { join as
|
|
51905
|
+
import { join as join29 } from "node:path";
|
|
51119
51906
|
function configPath2() {
|
|
51120
|
-
return
|
|
51907
|
+
return join29(activeLinkRoot(), "links.json");
|
|
51121
51908
|
}
|
|
51122
51909
|
function loadLinkConfigs() {
|
|
51123
51910
|
try {
|
|
@@ -51154,7 +51941,7 @@ function saveLinkConfig(cfg2) {
|
|
|
51154
51941
|
writeAll(links);
|
|
51155
51942
|
}
|
|
51156
51943
|
function pendingPath() {
|
|
51157
|
-
return
|
|
51944
|
+
return join29(activeLinkRoot(), "pending-deprovisions.json");
|
|
51158
51945
|
}
|
|
51159
51946
|
function loadPendingDeprovisions() {
|
|
51160
51947
|
try {
|
|
@@ -51330,7 +52117,7 @@ init_dist4();
|
|
|
51330
52117
|
|
|
51331
52118
|
// ../../packages/host-core/dist/pty-manager.js
|
|
51332
52119
|
import { existsSync as existsSync14 } from "node:fs";
|
|
51333
|
-
import { homedir as
|
|
52120
|
+
import { homedir as homedir13 } from "node:os";
|
|
51334
52121
|
var ptyPromise = null;
|
|
51335
52122
|
function loadPty() {
|
|
51336
52123
|
if (!ptyPromise) {
|
|
@@ -51367,15 +52154,13 @@ async function openPty(opts, sink) {
|
|
|
51367
52154
|
const pty = await loadPty();
|
|
51368
52155
|
if (!pty)
|
|
51369
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
|
+
}
|
|
51370
52160
|
const shell = opts.shell || defaultShell();
|
|
51371
52161
|
const args = opts.args ?? defaultArgs(shell);
|
|
51372
52162
|
const cwd = resolveCwd(opts.cwd, opts.hostRoot);
|
|
51373
|
-
const env =
|
|
51374
|
-
...process.env,
|
|
51375
|
-
...opts.env,
|
|
51376
|
-
TERM: "xterm-256color",
|
|
51377
|
-
COLORTERM: "truecolor"
|
|
51378
|
-
};
|
|
52163
|
+
const env = buildChildEnv(opts);
|
|
51379
52164
|
let proc;
|
|
51380
52165
|
try {
|
|
51381
52166
|
proc = pty.spawn(shell, args, {
|
|
@@ -51555,8 +52340,34 @@ function defaultArgs(shell) {
|
|
|
51555
52340
|
return [];
|
|
51556
52341
|
return ["bash", "zsh", "fish"].includes(idForPath(shell)) ? ["-l"] : [];
|
|
51557
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
|
+
}
|
|
51558
52369
|
function resolveCwd(cwd, hostRoot) {
|
|
51559
|
-
const fallback = hostRoot ||
|
|
52370
|
+
const fallback = hostRoot || homedir13();
|
|
51560
52371
|
if (!cwd)
|
|
51561
52372
|
return fallback;
|
|
51562
52373
|
if (!existsSync14(cwd))
|
|
@@ -51757,7 +52568,17 @@ async function createElevenLabsAgent(args) {
|
|
|
51757
52568
|
first_message: "",
|
|
51758
52569
|
language
|
|
51759
52570
|
},
|
|
51760
|
-
|
|
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
|
+
},
|
|
51761
52582
|
tts: {
|
|
51762
52583
|
model_id: "eleven_flash_v2",
|
|
51763
52584
|
voice_id: voiceId,
|
|
@@ -51766,7 +52587,12 @@ async function createElevenLabsAgent(args) {
|
|
|
51766
52587
|
speed: 1,
|
|
51767
52588
|
similarity_boost: 0.8
|
|
51768
52589
|
},
|
|
51769
|
-
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 },
|
|
51770
52596
|
conversation: {
|
|
51771
52597
|
text_only: false,
|
|
51772
52598
|
// 2h — long working sessions. NOT per-conversation overridable (EL pins
|
|
@@ -52970,7 +53796,8 @@ function turnEnded(key2) {
|
|
|
52970
53796
|
function turnRekeyed(from, to) {
|
|
52971
53797
|
cfg?.power.turnRekeyed(from, to);
|
|
52972
53798
|
}
|
|
52973
|
-
var HOST_ROOT =
|
|
53799
|
+
var HOST_ROOT = resolveHostRoot();
|
|
53800
|
+
var HOST_CONTAIN = resolveContainmentRoot() ?? void 0;
|
|
52974
53801
|
var hooks = null;
|
|
52975
53802
|
var socket = null;
|
|
52976
53803
|
var heartbeat = null;
|
|
@@ -53011,13 +53838,43 @@ function wsUrl(token) {
|
|
|
53011
53838
|
return `${backendOrigin.replace(/^http/, "ws")}/ws?token=${encodeURIComponent(token)}`;
|
|
53012
53839
|
}
|
|
53013
53840
|
var outbox = [];
|
|
53014
|
-
function
|
|
53841
|
+
function rawSend(channel, payload, extra) {
|
|
53015
53842
|
const data = JSON.stringify({ channel, payload, ...extra });
|
|
53016
53843
|
if (socket && socket.readyState === 1)
|
|
53017
53844
|
socket.send(data);
|
|
53018
53845
|
else if (socket && socket.readyState === 0 && outbox.length < 256)
|
|
53019
53846
|
outbox.push(data);
|
|
53020
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
|
+
}
|
|
53021
53878
|
var p2pSubscribers = /* @__PURE__ */ new Set();
|
|
53022
53879
|
var p2pCtx = null;
|
|
53023
53880
|
var p2pReady = false;
|
|
@@ -53251,9 +54108,9 @@ function provisionActiveLinkMirror(payload) {
|
|
|
53251
54108
|
const remoteSubfolder = typeof payload.remoteSubfolder === "string" ? payload.remoteSubfolder : void 0;
|
|
53252
54109
|
let localRoot;
|
|
53253
54110
|
if (remoteParent && remoteSubfolder) {
|
|
53254
|
-
localRoot =
|
|
54111
|
+
localRoot = join30(remoteParent, safeFolderName(remoteSubfolder));
|
|
53255
54112
|
} else {
|
|
53256
|
-
localRoot =
|
|
54113
|
+
localRoot = join30(HOST_ROOT, "Tesseract Mirrors", safeFolderName(projectName));
|
|
53257
54114
|
}
|
|
53258
54115
|
if (loadLinkConfigs().some((c) => c.localRoot === localRoot && c.linkId !== linkId)) {
|
|
53259
54116
|
localRoot = `${localRoot}-${linkId.slice(0, 6)}`;
|
|
@@ -53556,7 +54413,7 @@ async function serveInboundFs(requestId, op, args, driverClientId) {
|
|
|
53556
54413
|
return;
|
|
53557
54414
|
const route2 = driverClientId ? { clientId: driverClientId } : {};
|
|
53558
54415
|
try {
|
|
53559
|
-
const result = await runRemoteFsOp(HOST_ROOT, op, args ?? []);
|
|
54416
|
+
const result = await runRemoteFsOp(HOST_ROOT, op, args ?? [], driverClientId);
|
|
53560
54417
|
send("fs", { type: "fs:res", requestId, result }, route2);
|
|
53561
54418
|
if (op === "openFolder" && driverClientId && result && typeof result.root === "string") {
|
|
53562
54419
|
const root = result.root;
|
|
@@ -53575,7 +54432,7 @@ async function serveInboundTerm(p, driverClientId) {
|
|
|
53575
54432
|
exit: (ptyId, exitCode, signal) => send("term", { type: "term:exit", ptyId, exitCode, signal }, route2)
|
|
53576
54433
|
};
|
|
53577
54434
|
try {
|
|
53578
|
-
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);
|
|
53579
54436
|
send("term", { type: "term:opened", ptyId: p.ptyId, requestId: p.requestId, pid }, route2);
|
|
53580
54437
|
} catch (err) {
|
|
53581
54438
|
send("term", { type: "term:openError", ptyId: p.ptyId, requestId: p.requestId, error: err instanceof Error ? err.message : String(err) }, route2);
|
|
@@ -53598,6 +54455,15 @@ async function serveInboundTerm(p, driverClientId) {
|
|
|
53598
54455
|
}
|
|
53599
54456
|
function releaseDriver(clientId) {
|
|
53600
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);
|
|
53601
54467
|
voiceControls.get(clientId)?.stop("disconnected");
|
|
53602
54468
|
voiceControls.delete(clientId);
|
|
53603
54469
|
unwatchSessionClient(clientId);
|
|
@@ -53710,7 +54576,7 @@ function registerAsHost() {
|
|
|
53710
54576
|
void ensureP2pReady();
|
|
53711
54577
|
const kind = process.env.CCB_HOST_KIND === "cloud" ? "cloud" : void 0;
|
|
53712
54578
|
const name = kind === "cloud" ? "Cloud agent" : hostname3();
|
|
53713
|
-
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 ?? {} });
|
|
53714
54580
|
if (!heartbeat)
|
|
53715
54581
|
heartbeat = setInterval(() => send("host", { type: "host:heartbeat", ...dynamicMetrics() }), 2e4);
|
|
53716
54582
|
}
|
|
@@ -53747,10 +54613,8 @@ function connect() {
|
|
|
53747
54613
|
}
|
|
53748
54614
|
}
|
|
53749
54615
|
};
|
|
53750
|
-
|
|
53751
|
-
|
|
53752
|
-
if (typeof data !== "string")
|
|
53753
|
-
return;
|
|
54616
|
+
let inboundChain = Promise.resolve();
|
|
54617
|
+
const handleInbound = async (data) => {
|
|
53754
54618
|
let env;
|
|
53755
54619
|
try {
|
|
53756
54620
|
env = JSON.parse(data);
|
|
@@ -53774,18 +54638,50 @@ function connect() {
|
|
|
53774
54638
|
}
|
|
53775
54639
|
const inbound = typeof env.hostId === "string" && env.hostId.endsWith(`:${deviceId}`);
|
|
53776
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
|
+
}
|
|
53777
54669
|
if (channel === "activelink") {
|
|
53778
54670
|
handleActiveLinkChannel(env.payload, inbound);
|
|
53779
54671
|
return;
|
|
53780
54672
|
}
|
|
53781
54673
|
if (channel === "voice") {
|
|
53782
|
-
if (inbound)
|
|
53783
|
-
|
|
54674
|
+
if (inbound) {
|
|
54675
|
+
if (driving)
|
|
54676
|
+
void handleInboundVoice(env.payload, clientId);
|
|
54677
|
+
return;
|
|
54678
|
+
}
|
|
53784
54679
|
return;
|
|
53785
54680
|
}
|
|
53786
54681
|
if (channel === "agent") {
|
|
53787
54682
|
if (inbound) {
|
|
53788
|
-
|
|
54683
|
+
if (driving)
|
|
54684
|
+
void handleInboundAgent(env.payload, clientId);
|
|
53789
54685
|
return;
|
|
53790
54686
|
}
|
|
53791
54687
|
const p = env.payload;
|
|
@@ -53802,7 +54698,13 @@ function connect() {
|
|
|
53802
54698
|
if (channel === "fs") {
|
|
53803
54699
|
const p = env.payload;
|
|
53804
54700
|
if (inbound && p.type === "fs:req") {
|
|
53805
|
-
|
|
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
|
+
}
|
|
53806
54708
|
return;
|
|
53807
54709
|
}
|
|
53808
54710
|
if (p.type === "fs:res") {
|
|
@@ -53818,27 +54720,27 @@ function connect() {
|
|
|
53818
54720
|
return;
|
|
53819
54721
|
}
|
|
53820
54722
|
if (p.type === "fs:transferStart") {
|
|
53821
|
-
if (
|
|
54723
|
+
if (driving)
|
|
53822
54724
|
void beginOutboundTransfer(p, clientId ?? "");
|
|
53823
54725
|
return;
|
|
53824
54726
|
}
|
|
53825
54727
|
if (p.type === "fs:transferManifest") {
|
|
53826
|
-
if (
|
|
54728
|
+
if (driving)
|
|
53827
54729
|
void beginInboundTransfer(p, HOST_ROOT, transferSend);
|
|
53828
54730
|
return;
|
|
53829
54731
|
}
|
|
53830
54732
|
if (p.type === "fs:transferChunk") {
|
|
53831
|
-
if (
|
|
54733
|
+
if (driving)
|
|
53832
54734
|
void writeInboundChunk(p, transferSend);
|
|
53833
54735
|
return;
|
|
53834
54736
|
}
|
|
53835
54737
|
if (p.type === "fs:transferAck") {
|
|
53836
|
-
if (
|
|
54738
|
+
if (driving)
|
|
53837
54739
|
onTransferAck(p);
|
|
53838
54740
|
return;
|
|
53839
54741
|
}
|
|
53840
54742
|
if (p.type === "fs:transferCancel") {
|
|
53841
|
-
if (
|
|
54743
|
+
if (driving)
|
|
53842
54744
|
cancelTransfer(p.transferId, transferSend);
|
|
53843
54745
|
return;
|
|
53844
54746
|
}
|
|
@@ -53851,7 +54753,14 @@ function connect() {
|
|
|
53851
54753
|
if (channel === "term") {
|
|
53852
54754
|
const p = env.payload;
|
|
53853
54755
|
if (inbound) {
|
|
53854
|
-
|
|
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
|
+
}
|
|
53855
54764
|
return;
|
|
53856
54765
|
}
|
|
53857
54766
|
hooks?.pushTerm(p);
|
|
@@ -53866,7 +54775,7 @@ function connect() {
|
|
|
53866
54775
|
r(p.iceServers);
|
|
53867
54776
|
return;
|
|
53868
54777
|
}
|
|
53869
|
-
if (
|
|
54778
|
+
if (driving && clientId && typeof p?.type === "string" && p.type.startsWith("signal:dc-")) {
|
|
53870
54779
|
void handleDcSignal(clientId, p, HOST_ROOT, ownHostId(), (payload) => send("signal", payload, { clientId, browserId: "fsdc" }));
|
|
53871
54780
|
}
|
|
53872
54781
|
return;
|
|
@@ -53883,7 +54792,7 @@ function connect() {
|
|
|
53883
54792
|
}
|
|
53884
54793
|
if (channel === "browser") {
|
|
53885
54794
|
console.log(`[browser/host] '${String(env.payload?.type)}' inbound=${inbound} client=${clientId ? String(clientId).slice(0, 8) : "none"} bid=${env.browserId ?? "default"}`);
|
|
53886
|
-
if (!
|
|
54795
|
+
if (!driving)
|
|
53887
54796
|
return;
|
|
53888
54797
|
const p = env.payload;
|
|
53889
54798
|
if (!p || typeof p.type !== "string" || !clientId)
|
|
@@ -53921,7 +54830,7 @@ function connect() {
|
|
|
53921
54830
|
if (channel === "editor") {
|
|
53922
54831
|
const ep = env.payload;
|
|
53923
54832
|
console.log(`[editor/host] '${String(ep?.channel)}' inbound=${inbound} client=${clientId ? String(clientId).slice(0, 8) : "none"} bid=${env.browserId ?? "default"}`);
|
|
53924
|
-
if (!
|
|
54833
|
+
if (!driving)
|
|
53925
54834
|
return;
|
|
53926
54835
|
if (!ep || typeof ep.channel !== "string" || !clientId)
|
|
53927
54836
|
return;
|
|
@@ -53940,6 +54849,16 @@ function connect() {
|
|
|
53940
54849
|
return;
|
|
53941
54850
|
}
|
|
53942
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
|
+
};
|
|
53943
54862
|
socket.onclose = () => {
|
|
53944
54863
|
cleanup();
|
|
53945
54864
|
if (!stopped)
|
|
@@ -53955,6 +54874,19 @@ function startControlLink(config2, h) {
|
|
|
53955
54874
|
hostVersion = config2.version;
|
|
53956
54875
|
hooks = h;
|
|
53957
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
|
+
}
|
|
53958
54890
|
hooks.pushHosts(presentedHosts());
|
|
53959
54891
|
hooks.ownHostResolved(ownHostId());
|
|
53960
54892
|
ensureLinkConfigWatch();
|
|
@@ -54008,8 +54940,26 @@ function stopControlLink() {
|
|
|
54008
54940
|
}
|
|
54009
54941
|
|
|
54010
54942
|
// ../../packages/host-core/dist/local-server.js
|
|
54943
|
+
import { timingSafeEqual as timingSafeEqual3 } from "node:crypto";
|
|
54011
54944
|
init_dist2();
|
|
54012
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
|
+
}
|
|
54013
54963
|
async function startLocalServer(opts) {
|
|
54014
54964
|
const log3 = opts.log ?? (() => {
|
|
54015
54965
|
});
|
|
@@ -54023,7 +54973,8 @@ async function startLocalServer(opts) {
|
|
|
54023
54973
|
log3(`local listener on 127.0.0.1:${port}`);
|
|
54024
54974
|
wss.on("connection", (ws, req) => {
|
|
54025
54975
|
const url2 = new URL(req.url ?? "/", "http://127.0.0.1");
|
|
54026
|
-
|
|
54976
|
+
const origin = req.headers.origin ?? void 0;
|
|
54977
|
+
if (!originAllowed(origin) || !tokenMatches(url2.searchParams.get("token"), opts.token)) {
|
|
54027
54978
|
try {
|
|
54028
54979
|
ws.close(1008, "unauthorized");
|
|
54029
54980
|
} catch {
|
|
@@ -54042,11 +54993,11 @@ async function startLocalServer(opts) {
|
|
|
54042
54993
|
};
|
|
54043
54994
|
const heldSessions = /* @__PURE__ */ new Set();
|
|
54044
54995
|
let voiceControl = null;
|
|
54045
|
-
const unregisterSink = registerAgentSink((msg,
|
|
54046
|
-
const isCron =
|
|
54047
|
-
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)
|
|
54048
54999
|
return;
|
|
54049
|
-
if (
|
|
55000
|
+
if (origin2 === loopbackOrigin)
|
|
54050
55001
|
voiceControl?.feedAgentEvent(msg);
|
|
54051
55002
|
if (msg.type === "chat:echoed")
|
|
54052
55003
|
return;
|
|
@@ -54257,7 +55208,9 @@ async function startLocalServer(opts) {
|
|
|
54257
55208
|
const { pid } = await openPty(
|
|
54258
55209
|
// `local`: a loopback terminal is the user's OWN machine — never
|
|
54259
55210
|
// reaped by the host-role teardown that runs in this same process.
|
|
54260
|
-
|
|
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 },
|
|
54261
55214
|
termSink
|
|
54262
55215
|
);
|
|
54263
55216
|
send2("term", { type: "term:opened", ptyId: p.ptyId, requestId: p.requestId, pid });
|
|
@@ -54289,10 +55242,91 @@ async function startLocalServer(opts) {
|
|
|
54289
55242
|
}
|
|
54290
55243
|
|
|
54291
55244
|
// ../../packages/host-core/dist/daemon-runtime.js
|
|
54292
|
-
import { readFileSync as
|
|
54293
|
-
import { join as
|
|
54294
|
-
import {
|
|
54295
|
-
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
|
+
}
|
|
54296
55330
|
|
|
54297
55331
|
// ../../packages/host-core/dist/power-node.js
|
|
54298
55332
|
import { spawn as spawn5 } from "node:child_process";
|
|
@@ -54357,8 +55391,8 @@ async function runDaemon(opts = {}) {
|
|
|
54357
55391
|
const VERSION = opts.version ?? process.env.CCB_DAEMON_VERSION ?? "dev";
|
|
54358
55392
|
const TAG = opts.tag ?? "host-daemon";
|
|
54359
55393
|
const log3 = (...args) => console.log(`[${TAG} ${(/* @__PURE__ */ new Date()).toISOString()}]`, ...args);
|
|
54360
|
-
const pidfile = () =>
|
|
54361
|
-
const statusFile = () =>
|
|
55394
|
+
const pidfile = () => join32(daemonDir(), "daemon.lock");
|
|
55395
|
+
const statusFile = () => join32(daemonDir(), "status.json");
|
|
54362
55396
|
const isAlive2 = (pid) => {
|
|
54363
55397
|
try {
|
|
54364
55398
|
process.kill(pid, 0);
|
|
@@ -54370,17 +55404,17 @@ async function runDaemon(opts = {}) {
|
|
|
54370
55404
|
function acquireLock() {
|
|
54371
55405
|
const f = pidfile();
|
|
54372
55406
|
try {
|
|
54373
|
-
if (
|
|
54374
|
-
const prev = Number(
|
|
55407
|
+
if (existsSync16(f)) {
|
|
55408
|
+
const prev = Number(readFileSync13(f, "utf-8").trim());
|
|
54375
55409
|
if (prev && prev !== process.pid && isAlive2(prev))
|
|
54376
55410
|
return false;
|
|
54377
55411
|
}
|
|
54378
55412
|
} catch {
|
|
54379
55413
|
}
|
|
54380
55414
|
try {
|
|
54381
|
-
if (!
|
|
54382
|
-
|
|
54383
|
-
|
|
55415
|
+
if (!existsSync16(daemonDir()))
|
|
55416
|
+
mkdirSync13(daemonDir(), { recursive: true, mode: 448 });
|
|
55417
|
+
writeFileSync13(f, String(process.pid));
|
|
54384
55418
|
} catch {
|
|
54385
55419
|
}
|
|
54386
55420
|
return true;
|
|
@@ -54388,15 +55422,15 @@ async function runDaemon(opts = {}) {
|
|
|
54388
55422
|
function releaseLock() {
|
|
54389
55423
|
try {
|
|
54390
55424
|
const f = pidfile();
|
|
54391
|
-
if (
|
|
55425
|
+
if (existsSync16(f) && Number(readFileSync13(f, "utf-8").trim()) === process.pid)
|
|
54392
55426
|
unlinkSync2(f);
|
|
54393
55427
|
} catch {
|
|
54394
55428
|
}
|
|
54395
55429
|
}
|
|
54396
55430
|
function writeStatus(s) {
|
|
54397
55431
|
try {
|
|
54398
|
-
|
|
54399
|
-
|
|
55432
|
+
writeFileSync13(statusFile(), JSON.stringify({ pid: process.pid, version: VERSION, execPath: process.execPath, updatedAt: (/* @__PURE__ */ new Date()).toISOString(), ...localInfo ?? {}, ...s }));
|
|
55433
|
+
chmodSync4(statusFile(), 384);
|
|
54400
55434
|
} catch {
|
|
54401
55435
|
}
|
|
54402
55436
|
}
|
|
@@ -54429,9 +55463,9 @@ async function runDaemon(opts = {}) {
|
|
|
54429
55463
|
process.on("SIGTERM", () => shutdown(0));
|
|
54430
55464
|
process.on("SIGINT", () => shutdown(0));
|
|
54431
55465
|
process.env.CCB_HOST_DAEMON = "1";
|
|
54432
|
-
const localToken =
|
|
55466
|
+
const localToken = randomBytes4(24).toString("hex");
|
|
54433
55467
|
try {
|
|
54434
|
-
localServer = await startLocalServer({ hostRoot:
|
|
55468
|
+
localServer = await startLocalServer({ hostRoot: resolveHostRoot(), token: localToken, log: log3 });
|
|
54435
55469
|
localInfo = { localPort: localServer.port, localToken };
|
|
54436
55470
|
} catch (e) {
|
|
54437
55471
|
log3("local listener failed to start", e.message);
|
|
@@ -54446,6 +55480,15 @@ async function runDaemon(opts = {}) {
|
|
|
54446
55480
|
const c = creds2;
|
|
54447
55481
|
let accessToken = c.accessToken;
|
|
54448
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
|
+
})();
|
|
54449
55492
|
async function refresh() {
|
|
54450
55493
|
try {
|
|
54451
55494
|
const r = await fetch(`${c.apiOrigin}/auth/device-token/refresh`, {
|
|
@@ -54458,7 +55501,7 @@ async function runDaemon(opts = {}) {
|
|
|
54458
55501
|
if (r.status === 401) {
|
|
54459
55502
|
log3("device token no longer valid \u2014 dropping to local-only on restart");
|
|
54460
55503
|
try {
|
|
54461
|
-
unlinkSync2(
|
|
55504
|
+
unlinkSync2(join32(daemonDir(), "credentials.json"));
|
|
54462
55505
|
} catch {
|
|
54463
55506
|
}
|
|
54464
55507
|
shutdown(0);
|
|
@@ -54491,7 +55534,20 @@ async function runDaemon(opts = {}) {
|
|
|
54491
55534
|
pushTerm: () => {
|
|
54492
55535
|
}
|
|
54493
55536
|
};
|
|
54494
|
-
|
|
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);
|
|
54495
55551
|
log3(`started \u2014 device ${c.deviceId}, user ${c.userId}, relay ${c.backendOrigin}, local :${localInfo?.localPort ?? "off"}, version ${VERSION}`);
|
|
54496
55552
|
await refresh();
|
|
54497
55553
|
timer = setInterval(() => {
|
|
@@ -54503,9 +55559,9 @@ async function runDaemon(opts = {}) {
|
|
|
54503
55559
|
import { platform as platform2 } from "node:os";
|
|
54504
55560
|
|
|
54505
55561
|
// ../../packages/host-core/dist/service/launchd.js
|
|
54506
|
-
import { writeFileSync as
|
|
54507
|
-
import { dirname as dirname15, join as
|
|
54508
|
-
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";
|
|
54509
55565
|
|
|
54510
55566
|
// ../../packages/host-core/dist/service/exec.js
|
|
54511
55567
|
import { execFile as execFile5, spawn as spawn6 } from "node:child_process";
|
|
@@ -54528,10 +55584,10 @@ function xml(s) {
|
|
|
54528
55584
|
}
|
|
54529
55585
|
function makeLaunchd(instanceId) {
|
|
54530
55586
|
const label = SERVICE_LABEL_BASE + (instanceId ? `.${instanceId}` : "");
|
|
54531
|
-
const plistPath = () =>
|
|
55587
|
+
const plistPath = () => join33(homedir14(), "Library", "LaunchAgents", `${label}.plist`);
|
|
54532
55588
|
const domainTarget = () => `gui/${userInfo().uid}`;
|
|
54533
55589
|
const serviceTarget = () => `${domainTarget()}/${label}`;
|
|
54534
|
-
const logFile = () =>
|
|
55590
|
+
const logFile = () => join33(daemonDir(), "daemon.log");
|
|
54535
55591
|
function buildPlist(spec) {
|
|
54536
55592
|
const args = [spec.exec, ...spec.args].map((a) => ` <string>${xml(a)}</string>`).join("\n");
|
|
54537
55593
|
const env = Object.entries(spec.env).map(([k, v]) => ` <key>${xml(k)}</key>
|
|
@@ -54571,9 +55627,9 @@ ${env}
|
|
|
54571
55627
|
}
|
|
54572
55628
|
return {
|
|
54573
55629
|
async install(spec) {
|
|
54574
|
-
|
|
54575
|
-
|
|
54576
|
-
|
|
55630
|
+
mkdirSync14(dirname15(plistPath()), { recursive: true });
|
|
55631
|
+
mkdirSync14(dirname15(spec.logPath), { recursive: true });
|
|
55632
|
+
writeFileSync14(plistPath(), buildPlist(spec));
|
|
54577
55633
|
await run("launchctl", ["bootout", serviceTarget()]).catch(() => {
|
|
54578
55634
|
});
|
|
54579
55635
|
await run("launchctl", ["bootstrap", domainTarget(), plistPath()]);
|
|
@@ -54585,7 +55641,7 @@ ${env}
|
|
|
54585
55641
|
async uninstall() {
|
|
54586
55642
|
await run("launchctl", ["bootout", serviceTarget()]).catch(() => {
|
|
54587
55643
|
});
|
|
54588
|
-
if (
|
|
55644
|
+
if (existsSync17(plistPath())) {
|
|
54589
55645
|
try {
|
|
54590
55646
|
unlinkSync3(plistPath());
|
|
54591
55647
|
} catch {
|
|
@@ -54596,7 +55652,7 @@ ${env}
|
|
|
54596
55652
|
await this.install(spec);
|
|
54597
55653
|
},
|
|
54598
55654
|
async isInstalled() {
|
|
54599
|
-
return
|
|
55655
|
+
return existsSync17(plistPath());
|
|
54600
55656
|
},
|
|
54601
55657
|
async start() {
|
|
54602
55658
|
await run("launchctl", ["bootstrap", domainTarget(), plistPath()]).catch(() => {
|
|
@@ -54613,7 +55669,7 @@ ${env}
|
|
|
54613
55669
|
},
|
|
54614
55670
|
async logs(follow) {
|
|
54615
55671
|
const f = logFile();
|
|
54616
|
-
if (!
|
|
55672
|
+
if (!existsSync17(f)) {
|
|
54617
55673
|
console.log(`No log yet at ${f} \u2014 has the service started?`);
|
|
54618
55674
|
return;
|
|
54619
55675
|
}
|
|
@@ -54623,17 +55679,17 @@ ${env}
|
|
|
54623
55679
|
}
|
|
54624
55680
|
|
|
54625
55681
|
// ../../packages/host-core/dist/service/systemd.js
|
|
54626
|
-
import { writeFileSync as
|
|
54627
|
-
import { dirname as dirname16, join as
|
|
54628
|
-
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";
|
|
54629
55685
|
function shq(s) {
|
|
54630
55686
|
return `"${s.replace(/(["\\$`])/g, "\\$1")}"`;
|
|
54631
55687
|
}
|
|
54632
55688
|
function makeSystemd(instanceId) {
|
|
54633
55689
|
const unit = `tesseract-host${instanceId ? `-${instanceId}` : ""}.service`;
|
|
54634
55690
|
const unitPath = () => {
|
|
54635
|
-
const base = process.env.XDG_CONFIG_HOME ||
|
|
54636
|
-
return
|
|
55691
|
+
const base = process.env.XDG_CONFIG_HOME || join34(homedir15(), ".config");
|
|
55692
|
+
return join34(base, "systemd", "user", unit);
|
|
54637
55693
|
};
|
|
54638
55694
|
function buildUnit(spec) {
|
|
54639
55695
|
const execStart = [spec.exec, ...spec.args].map(shq).join(" ");
|
|
@@ -54658,9 +55714,9 @@ WantedBy=default.target
|
|
|
54658
55714
|
}
|
|
54659
55715
|
return {
|
|
54660
55716
|
async install(spec) {
|
|
54661
|
-
|
|
54662
|
-
|
|
54663
|
-
|
|
55717
|
+
mkdirSync15(dirname16(unitPath()), { recursive: true });
|
|
55718
|
+
mkdirSync15(dirname16(spec.logPath), { recursive: true });
|
|
55719
|
+
writeFileSync15(unitPath(), buildUnit(spec));
|
|
54664
55720
|
await run("loginctl", ["enable-linger", userInfo2().username]).catch(() => {
|
|
54665
55721
|
});
|
|
54666
55722
|
await run("systemctl", ["--user", "daemon-reload"]).catch(() => {
|
|
@@ -54672,7 +55728,7 @@ WantedBy=default.target
|
|
|
54672
55728
|
async uninstall() {
|
|
54673
55729
|
await run("systemctl", ["--user", "disable", "--now", unit]).catch(() => {
|
|
54674
55730
|
});
|
|
54675
|
-
if (
|
|
55731
|
+
if (existsSync18(unitPath())) {
|
|
54676
55732
|
try {
|
|
54677
55733
|
unlinkSync4(unitPath());
|
|
54678
55734
|
} catch {
|
|
@@ -54685,7 +55741,7 @@ WantedBy=default.target
|
|
|
54685
55741
|
await this.install(spec);
|
|
54686
55742
|
},
|
|
54687
55743
|
async isInstalled() {
|
|
54688
|
-
return
|
|
55744
|
+
return existsSync18(unitPath());
|
|
54689
55745
|
},
|
|
54690
55746
|
async start() {
|
|
54691
55747
|
await run("systemctl", ["--user", "start", unit]);
|
|
@@ -54703,25 +55759,28 @@ WantedBy=default.target
|
|
|
54703
55759
|
}
|
|
54704
55760
|
|
|
54705
55761
|
// ../../packages/host-core/dist/service/windows.js
|
|
54706
|
-
import { writeFileSync as
|
|
54707
|
-
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";
|
|
54708
55764
|
function makeWindowsTask(instanceId) {
|
|
54709
55765
|
const task = `Fineguide\\TesseractHost${instanceId ? `-${instanceId}` : ""}`;
|
|
54710
|
-
const wrapperPath = () =>
|
|
54711
|
-
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, "")}"`;
|
|
54712
55771
|
function buildWrapper(spec) {
|
|
54713
|
-
const env = Object.entries(spec.env).map(([k, v]) => `set "${k}=${v}"`).join("\r\n");
|
|
54714
|
-
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(" ");
|
|
54715
55774
|
return `@echo off\r
|
|
54716
55775
|
${env}\r
|
|
54717
|
-
start "" /b ${cmd} >> "${spec.logPath}" 2>&1\r
|
|
55776
|
+
start "" /b ${cmd} >> "${oneLine2(spec.logPath).replace(/"/g, "")}" 2>&1\r
|
|
54718
55777
|
`;
|
|
54719
55778
|
}
|
|
54720
55779
|
return {
|
|
54721
55780
|
async install(spec) {
|
|
54722
|
-
|
|
54723
|
-
|
|
54724
|
-
|
|
55781
|
+
mkdirSync16(dirname17(spec.logPath), { recursive: true });
|
|
55782
|
+
mkdirSync16(dirname17(wrapperPath()), { recursive: true });
|
|
55783
|
+
writeFileSync16(wrapperPath(), buildWrapper(spec));
|
|
54725
55784
|
await run("schtasks", ["/Create", "/TN", task, "/SC", "ONLOGON", "/RL", "LIMITED", "/F", "/TR", `"${wrapperPath()}"`]);
|
|
54726
55785
|
await run("schtasks", ["/Run", "/TN", task]).catch(() => {
|
|
54727
55786
|
});
|
|
@@ -54729,7 +55788,7 @@ start "" /b ${cmd} >> "${spec.logPath}" 2>&1\r
|
|
|
54729
55788
|
async uninstall() {
|
|
54730
55789
|
await run("schtasks", ["/Delete", "/TN", task, "/F"]).catch(() => {
|
|
54731
55790
|
});
|
|
54732
|
-
if (
|
|
55791
|
+
if (existsSync19(wrapperPath())) {
|
|
54733
55792
|
try {
|
|
54734
55793
|
unlinkSync5(wrapperPath());
|
|
54735
55794
|
} catch {
|
|
@@ -54759,7 +55818,7 @@ start "" /b ${cmd} >> "${spec.logPath}" 2>&1\r
|
|
|
54759
55818
|
},
|
|
54760
55819
|
async logs(follow) {
|
|
54761
55820
|
const f = logFile();
|
|
54762
|
-
if (!
|
|
55821
|
+
if (!existsSync19(f)) {
|
|
54763
55822
|
console.log(`No log yet at ${f} \u2014 has the task run?`);
|
|
54764
55823
|
return;
|
|
54765
55824
|
}
|
|
@@ -54786,15 +55845,30 @@ function getServiceController(instanceId) {
|
|
|
54786
55845
|
// src/auth.ts
|
|
54787
55846
|
var import_qrcode_terminal = __toESM(require_main(), 1);
|
|
54788
55847
|
import { randomUUID as randomUUID8 } from "node:crypto";
|
|
54789
|
-
import { existsSync as
|
|
54790
|
-
import { join as
|
|
55848
|
+
import { existsSync as existsSync20, rmSync } from "node:fs";
|
|
55849
|
+
import { join as join36 } from "node:path";
|
|
54791
55850
|
|
|
54792
55851
|
// src/config.ts
|
|
54793
|
-
import { hostname as
|
|
55852
|
+
import { hostname as hostname5 } from "node:os";
|
|
54794
55853
|
var DEFAULT_API = (process.env.CCB_TESSERACT_API ?? "https://app.tesseract.dev").replace(/\/$/, "");
|
|
54795
55854
|
function defaultRelay(api) {
|
|
54796
55855
|
return (process.env.CCB_BACKEND_ORIGIN ?? api).replace(/\/$/, "");
|
|
54797
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
|
+
}
|
|
54798
55872
|
function decodeJwtSub(token) {
|
|
54799
55873
|
try {
|
|
54800
55874
|
const part = token.split(".")[1];
|
|
@@ -54806,7 +55880,7 @@ function decodeJwtSub(token) {
|
|
|
54806
55880
|
}
|
|
54807
55881
|
}
|
|
54808
55882
|
function defaultLabel() {
|
|
54809
|
-
return
|
|
55883
|
+
return hostname5() || "server";
|
|
54810
55884
|
}
|
|
54811
55885
|
|
|
54812
55886
|
// src/auth.ts
|
|
@@ -54903,6 +55977,15 @@ function persist2(api, relay, deviceId2, dt) {
|
|
|
54903
55977
|
async function login(opts) {
|
|
54904
55978
|
const api = (opts.api ?? DEFAULT_API).replace(/\/$/, "");
|
|
54905
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
|
+
}
|
|
54906
55989
|
const deviceId2 = readCredentials()?.deviceId ?? process.env.CCB_DEVICE_ID ?? randomUUID8().slice(0, 8);
|
|
54907
55990
|
const label = opts.name ?? defaultLabel();
|
|
54908
55991
|
if (opts.token) {
|
|
@@ -54964,8 +56047,8 @@ async function logout() {
|
|
|
54964
56047
|
}
|
|
54965
56048
|
for (const f of ["credentials.json", "status.json", "daemon.lock"]) {
|
|
54966
56049
|
try {
|
|
54967
|
-
const p =
|
|
54968
|
-
if (
|
|
56050
|
+
const p = join36(daemonDir(), f);
|
|
56051
|
+
if (existsSync20(p)) rmSync(p);
|
|
54969
56052
|
} catch {
|
|
54970
56053
|
}
|
|
54971
56054
|
}
|
|
@@ -54986,8 +56069,11 @@ function whoami() {
|
|
|
54986
56069
|
|
|
54987
56070
|
// src/self-update.ts
|
|
54988
56071
|
import { spawn as spawn7 } from "node:child_process";
|
|
56072
|
+
import { createPublicKey, verify as cryptoVerify } from "node:crypto";
|
|
54989
56073
|
var RESTART_EXIT_CODE = 75;
|
|
54990
|
-
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==";
|
|
54991
56077
|
var FIRST_CHECK_MS = Number(process.env.CCB_UPDATE_FIRST_MS) || 3 * 6e4;
|
|
54992
56078
|
var INTERVAL_MS = Number(process.env.CCB_UPDATE_INTERVAL_MS) || 6 * 60 * 6e4;
|
|
54993
56079
|
function disabled() {
|
|
@@ -55025,6 +56111,32 @@ async function fetchLatest() {
|
|
|
55025
56111
|
clearTimeout(t);
|
|
55026
56112
|
}
|
|
55027
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
|
+
}
|
|
55028
56140
|
function installVersion(target) {
|
|
55029
56141
|
return new Promise((resolve10) => {
|
|
55030
56142
|
const npm = process.platform === "win32" ? "npm.cmd" : "npm";
|
|
@@ -55060,6 +56172,12 @@ async function checkOnce(current) {
|
|
|
55060
56172
|
const latest = await fetchLatest();
|
|
55061
56173
|
if (!latest || !isNewer(latest, current)) return;
|
|
55062
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 ?? "?"})`);
|
|
55063
56181
|
if (await installVersion(latest)) {
|
|
55064
56182
|
log2("restarting on the new version (supervisor re-spawns) \u2026");
|
|
55065
56183
|
process.exit(RESTART_EXIT_CODE);
|
|
@@ -55088,9 +56206,9 @@ function startSelfUpdater(current) {
|
|
|
55088
56206
|
}
|
|
55089
56207
|
|
|
55090
56208
|
// src/service.ts
|
|
55091
|
-
import { join as
|
|
56209
|
+
import { join as join37 } from "node:path";
|
|
55092
56210
|
import { platform as platform3 } from "node:os";
|
|
55093
|
-
import { existsSync as
|
|
56211
|
+
import { existsSync as existsSync21, readFileSync as readFileSync14 } from "node:fs";
|
|
55094
56212
|
function controller(instanceId) {
|
|
55095
56213
|
const c = getServiceController(instanceId);
|
|
55096
56214
|
if (!c) {
|
|
@@ -55103,7 +56221,7 @@ function buildSpec(cliEntry) {
|
|
|
55103
56221
|
exec: process.execPath,
|
|
55104
56222
|
args: [cliEntry, "run"],
|
|
55105
56223
|
env: { CCB_DAEMON_DIR: daemonDir() },
|
|
55106
|
-
logPath:
|
|
56224
|
+
logPath: join37(daemonDir(), "daemon.log")
|
|
55107
56225
|
};
|
|
55108
56226
|
}
|
|
55109
56227
|
var isAlive = (pid) => {
|
|
@@ -55115,10 +56233,10 @@ var isAlive = (pid) => {
|
|
|
55115
56233
|
}
|
|
55116
56234
|
};
|
|
55117
56235
|
function runningDaemonInfo() {
|
|
55118
|
-
const lock =
|
|
56236
|
+
const lock = join37(daemonDir(), "daemon.lock");
|
|
55119
56237
|
try {
|
|
55120
|
-
if (!
|
|
55121
|
-
const pid = Number(
|
|
56238
|
+
if (!existsSync21(lock)) return null;
|
|
56239
|
+
const pid = Number(readFileSync14(lock, "utf-8").trim());
|
|
55122
56240
|
if (!pid || !isAlive(pid)) return null;
|
|
55123
56241
|
return { pid, device: readCredentials()?.deviceId };
|
|
55124
56242
|
} catch {
|
|
@@ -55154,8 +56272,8 @@ async function logs(instanceId, follow) {
|
|
|
55154
56272
|
// src/cli.ts
|
|
55155
56273
|
function pkgVersion() {
|
|
55156
56274
|
try {
|
|
55157
|
-
const p =
|
|
55158
|
-
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";
|
|
55159
56277
|
} catch {
|
|
55160
56278
|
return "0.0.0";
|
|
55161
56279
|
}
|
|
@@ -55417,7 +56535,7 @@ async function main() {
|
|
|
55417
56535
|
process.exit(1);
|
|
55418
56536
|
}
|
|
55419
56537
|
instance = flags.instance;
|
|
55420
|
-
process.env.CCB_DAEMON_DIR =
|
|
56538
|
+
process.env.CCB_DAEMON_DIR = join38(homedir16(), ".tesseract", `daemon-${instance}`);
|
|
55421
56539
|
}
|
|
55422
56540
|
const force = flags.yes === true || flags.force === true;
|
|
55423
56541
|
const cliEntry = fileURLToPath2(import.meta.url);
|
|
@@ -55427,7 +56545,8 @@ async function main() {
|
|
|
55427
56545
|
api: typeof flags.api === "string" ? flags.api : void 0,
|
|
55428
56546
|
relay: typeof flags.relay === "string" ? flags.relay : void 0,
|
|
55429
56547
|
token: typeof flags.token === "string" ? flags.token : void 0,
|
|
55430
|
-
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
|
|
55431
56550
|
});
|
|
55432
56551
|
break;
|
|
55433
56552
|
case "logout":
|