tesseract-agent 0.4.17 → 0.4.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist-bundle/cli.js +1500 -381
  2. package/package.json +3 -2
@@ -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 concat(list, totalLength) {
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 = concat(fragments, messageLength);
1274
+ data = concat2(fragments, messageLength);
1275
1275
  } else if (this._binaryType === "arraybuffer") {
1276
- data = toArrayBuffer(concat(fragments, messageLength));
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 = concat(fragments, messageLength);
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: randomBytes4, createHash: createHash6 } = __require("crypto");
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 = randomBytes4(16).toString("base64");
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 nonce = `${process.pid}-${now()}-${++nonceSeq}`;
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 === 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(homedir(), ".claude", "projects");
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 homedir2 } from "node:os";
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(homedir2(), ".claude"));
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 homedir3 } from "node:os";
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 bs = [...b].sort();
22526
+ const bs2 = [...b].sort();
22333
22527
  for (let i = 0; i < as.length; i++)
22334
- if (as[i] !== bs[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(homedir3(), ".tesseract"), "canvas");
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 concat(buffers) {
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 concat(this.items);
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 concat(retBuffers);
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 concat(retBuffers);
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 = 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 concat(first, second, ...rest) {
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 ECDSA = "ECDSA";
39171
+ var ECDSA2 = "ECDSA";
38978
39172
  exports.EcAlgorithm = EcAlgorithm_1 = class EcAlgorithm {
38979
39173
  toAsnAlgorithm(alg) {
38980
39174
  switch (alg.name.toLowerCase()) {
38981
- case ECDSA.toLowerCase():
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: ECDSA,
39260
+ name: ECDSA2,
39067
39261
  hash: { name: "SHA-1" }
39068
39262
  };
39069
39263
  case asn1Ecc__namespace.id_ecdsaWithSHA256:
39070
39264
  return {
39071
- name: ECDSA,
39265
+ name: ECDSA2,
39072
39266
  hash: { name: "SHA-256" }
39073
39267
  };
39074
39268
  case asn1Ecc__namespace.id_ecdsaWithSHA384:
39075
39269
  return {
39076
- name: ECDSA,
39270
+ name: ECDSA2,
39077
39271
  hash: { name: "SHA-384" }
39078
39272
  };
39079
39273
  case asn1Ecc__namespace.id_ecdsaWithSHA512:
39080
39274
  return {
39081
- name: ECDSA,
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: ECDSA,
39286
+ name: ECDSA2,
39093
39287
  namedCurve: "P-256"
39094
39288
  };
39095
39289
  case EcAlgorithm_1.SECP256K1:
39096
39290
  return {
39097
- name: ECDSA,
39291
+ name: ECDSA2,
39098
39292
  namedCurve: "K-256"
39099
39293
  };
39100
39294
  case asn1Ecc__namespace.id_secp384r1:
39101
39295
  return {
39102
- name: ECDSA,
39296
+ name: ECDSA2,
39103
39297
  namedCurve: "P-384"
39104
39298
  };
39105
39299
  case asn1Ecc__namespace.id_secp521r1:
39106
39300
  return {
39107
- name: ECDSA,
39301
+ name: ECDSA2,
39108
39302
  namedCurve: "P-521"
39109
39303
  };
39110
39304
  case idBrainpoolP160r1:
39111
39305
  return {
39112
- name: ECDSA,
39306
+ name: ECDSA2,
39113
39307
  namedCurve: brainpoolP160r1
39114
39308
  };
39115
39309
  case idBrainpoolP160t1:
39116
39310
  return {
39117
- name: ECDSA,
39311
+ name: ECDSA2,
39118
39312
  namedCurve: brainpoolP160t1
39119
39313
  };
39120
39314
  case idBrainpoolP192r1:
39121
39315
  return {
39122
- name: ECDSA,
39316
+ name: ECDSA2,
39123
39317
  namedCurve: brainpoolP192r1
39124
39318
  };
39125
39319
  case idBrainpoolP192t1:
39126
39320
  return {
39127
- name: ECDSA,
39321
+ name: ECDSA2,
39128
39322
  namedCurve: brainpoolP192t1
39129
39323
  };
39130
39324
  case idBrainpoolP224r1:
39131
39325
  return {
39132
- name: ECDSA,
39326
+ name: ECDSA2,
39133
39327
  namedCurve: brainpoolP224r1
39134
39328
  };
39135
39329
  case idBrainpoolP224t1:
39136
39330
  return {
39137
- name: ECDSA,
39331
+ name: ECDSA2,
39138
39332
  namedCurve: brainpoolP224t1
39139
39333
  };
39140
39334
  case idBrainpoolP256r1:
39141
39335
  return {
39142
- name: ECDSA,
39336
+ name: ECDSA2,
39143
39337
  namedCurve: brainpoolP256r1
39144
39338
  };
39145
39339
  case idBrainpoolP256t1:
39146
39340
  return {
39147
- name: ECDSA,
39341
+ name: ECDSA2,
39148
39342
  namedCurve: brainpoolP256t1
39149
39343
  };
39150
39344
  case idBrainpoolP320r1:
39151
39345
  return {
39152
- name: ECDSA,
39346
+ name: ECDSA2,
39153
39347
  namedCurve: brainpoolP320r1
39154
39348
  };
39155
39349
  case idBrainpoolP320t1:
39156
39350
  return {
39157
- name: ECDSA,
39351
+ name: ECDSA2,
39158
39352
  namedCurve: brainpoolP320t1
39159
39353
  };
39160
39354
  case idBrainpoolP384r1:
39161
39355
  return {
39162
- name: ECDSA,
39356
+ name: ECDSA2,
39163
39357
  namedCurve: brainpoolP384r1
39164
39358
  };
39165
39359
  case idBrainpoolP384t1:
39166
39360
  return {
39167
- name: ECDSA,
39361
+ name: ECDSA2,
39168
39362
  namedCurve: brainpoolP384t1
39169
39363
  };
39170
39364
  case idBrainpoolP512r1:
39171
39365
  return {
39172
- name: ECDSA,
39366
+ name: ECDSA2,
39173
39367
  namedCurve: brainpoolP512r1
39174
39368
  };
39175
39369
  case idBrainpoolP512t1:
39176
39370
  return {
39177
- name: ECDSA,
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 = randomBytes(12);
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 !== expectedFingerprint256)
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 readFileSync14 } from "node:fs";
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 homedir17 } from "node:os";
44020
- import { dirname as dirname18, join as join36, resolve as resolve9 } from "node:path";
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, homedir as homedir13 } from "node:os";
44025
- import { join as join29, basename as basename7 } from "node:path";
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
- env: { ...process.env, ...spawnCfg.env, ...params.engineEnv ?? {} },
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 homedir8 } from "node:os";
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 homedir4 } from "node:os";
45529
+ import { homedir as homedir5 } from "node:os";
44862
45530
  function daemonDir() {
44863
- return process.env.CCB_DAEMON_DIR ?? join10(homedir4(), ".tesseract", "daemon");
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 homedir6 } from "node:os";
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 homedir5 } from "node:os";
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(homedir5(), ".tesseract", "router-home");
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(homedir6(), ".tesseract", "runner-home");
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(homedir6(), ".tesseract"), "runners.json");
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 homedir7 } from "node:os";
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(homedir7(), ".local", "bin"),
45381
- join14(homedir7(), "bin"),
45382
- join14(homedir7(), ".bun", "bin"),
45383
- join14(homedir7(), ".npm-global", "bin")
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(homedir8(), ".tesseract"), "extensions-skills");
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 homedir9 } from "node:os";
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(homedir9(), ".tesseract");
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 readdir2, readFile as readFile7, writeFile as writeFile5, mkdir as mkdir5, cp, rm, rename as renameAsync, stat as stat2 } from "node:fs/promises";
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 join19, relative as relative4, resolve as resolve5, sep as sep4, isAbsolute as isAbsolute4 } from "node:path";
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 readdir2(d, { withFileTypes: true });
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 = join19(d, ent.name);
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 join19(root, raw.slice(2));
46389
- return isAbsolute4(raw) ? raw : join19(root, 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 = join19(dir, name);
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 = join19(dir, `${base} ${i}${ext}`);
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
- if (!isInsideRoot(path5, root))
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(path5);
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 readFile7(path5), size: s.size };
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
- if (!isInsideRoot(dir, root))
47253
+ const real = safeContentPath(dir);
47254
+ if (!real)
46427
47255
  return [];
46428
47256
  try {
46429
- const entries = (await readdir2(dir, { withFileTypes: true })).filter((e) => e.name !== ".DS_Store");
46430
- const ignored = await gitIgnoredNames(dir, entries.map((e) => e.name));
46431
- return entries.map((e) => ({ name: e.name, path: join19(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);
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
- if (!isInsideRoot(dir, root))
47266
+ const real = safeContentPath(dir);
47267
+ if (!real)
46439
47268
  return [];
46440
47269
  try {
46441
- return await listAllFilesUnder(dir);
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
- if (!isInsideRoot(p, root))
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(p);
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 readFile7(p, "utf-8") };
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
- if (!isInsideRoot(p, root))
47295
+ const real = safeContentPath(p);
47296
+ if (!real)
46466
47297
  return OUTSIDE("write");
46467
47298
  try {
46468
- await mkdir5(dirname7(p), { recursive: true });
46469
- await writeFile5(p, content, "utf-8");
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
- if (!isInsideRoot(p, root))
47309
+ const real = safeContentPath(p);
47310
+ if (!real)
46479
47311
  return OUTSIDE("write");
46480
47312
  try {
46481
- await mkdir5(dirname7(p), { recursive: true });
46482
- await writeFile5(p, Buffer.from(base643, "base64"));
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
- if (!isInsideRoot(p, root))
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(p);
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
- if (!isInsideRoot(p, root))
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(p, "r");
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
- if (!isInsideRoot(p, root))
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(p);
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 readFile7(p)).toString("base64"), size: s.size };
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 = join19(parent, name);
46545
- if (!isInsideRoot(target, root))
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
- if (!isInsideRoot(parent, root))
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 (!isInsideRoot(oldPath, root))
47417
+ if (!insideOk(oldPath))
46583
47418
  return OUTSIDE("rename");
46584
- const newPath = join19(dirname7(oldPath), newName);
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 (!isInsideRoot(p, root))
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 (!isInsideRoot(destDir, root))
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 (!isInsideRoot(src, root))
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 p = a[0] != null ? resolveParent(String(a[0]), root) : root;
46640
- if (!isInsideRoot(p, root))
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(p);
47479
+ const s = await stat2(real);
46644
47480
  if (!s.isDirectory())
46645
47481
  return { error: "not_dir" };
46646
- return { root: resolve5(p), name: basename2(p) };
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(join19(resolved, rel));
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 hostname5 = m[2];
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: hostname5, port });
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: hostname5, port, username: c.username, password: c.credential, relayType });
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 readdir3, lstat, mkdir as mkdir6, chmod, rm as rm2, readlink, symlink, stat as stat3 } from "node:fs/promises";
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 join21, relative as relative6, sep as sep6 } from "node:path";
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 homedir10 } from "node:os";
47046
- import { join as join20, relative as relative5, sep as sep5 } from "node:path";
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 join20(root, rel.split("/").join(sep5));
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 ?? join20(homedir10(), ".tesseract", "active-link");
47929
+ return process.env.CCB_ACTIVE_LINK_DIR ?? join21(homedir11(), ".tesseract", "active-link");
47082
47930
  }
47083
47931
  function linkStateDir(linkId) {
47084
- return join20(activeLinkRoot(), linkId);
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 = join21(parent, name);
47945
+ let candidate = join22(parent, name);
47098
47946
  for (let i = 2; existsSync9(candidate) && i < 1e4; i++)
47099
- candidate = join21(parent, `${name} ${i}`);
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 readdir3(dir, { withFileTypes: true });
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 = join21(dir, e.name);
47990
+ const abs = join22(dir, e.name);
47143
47991
  const rel = relative6(root, abs).split(sep6).join("/");
47144
- const st = await lstat(abs).catch(() => null);
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(join21(srcPath, f.rel.split("/").join(sep6))).catch(() => "");
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 = join21(srcPath, f.rel.split("/").join(sep6));
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 = join21(parent, msg.destName);
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 = join21(destRoot, d.rel.split("/").join(sep6));
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 = join21(s.destRoot, msg.rel.split("/").join(sep6));
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 = join21(dirname9(abs), target);
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 homedir11 } from "node:os";
47465
- import { join as join22 } from "node:path";
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: (bs) => this.acceptInbound(bs, "punch") });
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 h = toHex(f);
47746
- if (this.pendingInbound.has(h)) {
47747
- matchedHex = h;
47748
- return true;
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 = randomBytes2(8).toString("hex");
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 ?? join22(homedir11(), ".tesseract");
47977
- return { certPath: join22(dir, "peer-cert.pem"), keyPath: join22(dir, "peer-key.pem") };
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 lstat6, 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";
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 join27, resolve as resolve8 } from "node:path";
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 join23 } from "node:path";
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 join23(this.dir, "manifest.json");
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 readdir4, lstat as lstat2, readlink as readlink2 } from "node:fs/promises";
48397
- import { join as join24, sep as sep7 } from "node:path";
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 readdir4(dir, { withFileTypes: true });
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 = join24(dir, e.name);
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 lstat2(abs).catch(() => null);
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 cached2 = prior?.getRecord(rel);
48438
- if (cached2 && cached2.kind === "file" && cached2.size === st.size && cached2.mtimeMs === mtimeMs) {
48439
- records.set(rel, { ...cached2, mode: st.mode });
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 join24(root, rel.split("/").join(sep7));
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 lstat3, readlink as readlink3, open as open2 } from "node:fs/promises";
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 lstat3(abs).catch(() => null);
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 lstat3(abs).catch(() => null);
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 readdir6 } from "fs/promises";
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 lstat4, readdir as readdir5, realpath } from "node:fs/promises";
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 ? lstat4 : stat4;
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 readdir5(path5, this._rdOptions);
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 lstat4(entryRealPath);
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 lstat5, realpath as fsrealpath } from "fs/promises";
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: lstat5, stat: stat5 };
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 readdir6(dir);
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(join27(this.stateDir, ".staging"), { recursive: true, force: true }).catch(() => {
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
- const st = await lstat6(abs).catch(() => null);
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 cached2 = this.manifest.getRecord(rel);
51479
+ const cached3 = this.manifest.getRecord(rel);
50695
51480
  let wholeHash;
50696
51481
  let blocks;
50697
- if (cached2 && cached2.kind === "file" && cached2.size === st.size && cached2.mtimeMs === mtimeMs) {
50698
- wholeHash = cached2.hash;
50699
- blocks = cached2.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 lstat6(abs).catch(() => null);
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 cached2 = this.manifest.getRecord(rel);
51021
- if (cached2 && cached2.kind === "file" && cached2.size === st.size && cached2.mtimeMs === mtimeMs) {
51022
- return { ...cached2, mode: st.mode };
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 join27(this.stateDir, ".staging", `${hashString(rel)}.${tag}`);
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 join28 } from "node:path";
51905
+ import { join as join29 } from "node:path";
51119
51906
  function configPath2() {
51120
- return join28(activeLinkRoot(), "links.json");
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 join28(activeLinkRoot(), "pending-deprovisions.json");
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 homedir12 } from "node:os";
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 || homedir12();
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
- asr: { quality: "high", provider: "elevenlabs", user_input_audio_format: "pcm_16000" },
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: { turn_timeout: 7, turn_eagerness: "normal" },
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 = process.env.CCB_HOST_ROOT ?? homedir13();
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 send(channel, payload, extra) {
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 = join29(remoteParent, safeFolderName(remoteSubfolder));
54111
+ localRoot = join30(remoteParent, safeFolderName(remoteSubfolder));
53255
54112
  } else {
53256
- localRoot = join29(HOST_ROOT, "Tesseract Mirrors", safeFolderName(projectName));
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: HOST_ROOT }, sink);
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
- socket.onmessage = (ev) => {
53751
- const data = ev.data;
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
- void handleInboundVoice(env.payload, clientId);
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
- void handleInboundAgent(env.payload, clientId);
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
- void serveInboundFs(p.requestId, p.op, p.args, clientId);
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 (inbound)
54723
+ if (driving)
53822
54724
  void beginOutboundTransfer(p, clientId ?? "");
53823
54725
  return;
53824
54726
  }
53825
54727
  if (p.type === "fs:transferManifest") {
53826
- if (inbound)
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 (inbound)
54733
+ if (driving)
53832
54734
  void writeInboundChunk(p, transferSend);
53833
54735
  return;
53834
54736
  }
53835
54737
  if (p.type === "fs:transferAck") {
53836
- if (inbound)
54738
+ if (driving)
53837
54739
  onTransferAck(p);
53838
54740
  return;
53839
54741
  }
53840
54742
  if (p.type === "fs:transferCancel") {
53841
- if (inbound)
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
- void serveInboundTerm(p, clientId);
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 (inbound && clientId && typeof p?.type === "string" && p.type.startsWith("signal:dc-")) {
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 (!inbound)
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 (!inbound)
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
- if (url2.searchParams.get("token") !== opts.token) {
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, origin) => {
54046
- const isCron = origin === "cron" && (MIRRORED_AGENT_EVENTS.has(msg.type) || msg.type === "session:created");
54047
- if (origin !== loopbackOrigin && !isCron)
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 (origin === loopbackOrigin)
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
- { ptyId: p.ptyId, clientId: termClientId, shell: p.shell, args: p.args, cwd: p.cwd, cols: p.cols, rows: p.rows, env: p.env, hostRoot: opts.hostRoot, local: true },
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 readFileSync12, writeFileSync as writeFileSync12, existsSync as existsSync15, mkdirSync as mkdirSync12, unlinkSync as unlinkSync2, chmodSync as chmodSync3 } from "node:fs";
54293
- import { join as join30 } from "node:path";
54294
- import { homedir as homedir14 } from "node:os";
54295
- import { randomBytes as randomBytes3 } from "node:crypto";
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 = () => join30(daemonDir(), "daemon.lock");
54361
- const statusFile = () => join30(daemonDir(), "status.json");
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 (existsSync15(f)) {
54374
- const prev = Number(readFileSync12(f, "utf-8").trim());
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 (!existsSync15(daemonDir()))
54382
- mkdirSync12(daemonDir(), { recursive: true, mode: 448 });
54383
- writeFileSync12(f, String(process.pid));
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 (existsSync15(f) && Number(readFileSync12(f, "utf-8").trim()) === process.pid)
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
- writeFileSync12(statusFile(), JSON.stringify({ pid: process.pid, version: VERSION, execPath: process.execPath, updatedAt: (/* @__PURE__ */ new Date()).toISOString(), ...localInfo ?? {}, ...s }));
54399
- chmodSync3(statusFile(), 384);
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 = randomBytes3(24).toString("hex");
55466
+ const localToken = randomBytes4(24).toString("hex");
54433
55467
  try {
54434
- localServer = await startLocalServer({ hostRoot: process.env.CCB_HOST_ROOT ?? homedir14(), token: localToken, log: log3 });
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(join30(daemonDir(), "credentials.json"));
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
- startControlLink({ deviceId: c.deviceId, power: createNodePowerController(), tokenSource, version: VERSION }, hooks2);
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 writeFileSync13, existsSync as existsSync16, mkdirSync as mkdirSync13, unlinkSync as unlinkSync3 } from "node:fs";
54507
- import { dirname as dirname15, join as join31 } from "node:path";
54508
- import { homedir as homedir15, userInfo } from "node:os";
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 = () => join31(homedir15(), "Library", "LaunchAgents", `${label}.plist`);
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 = () => join31(daemonDir(), "daemon.log");
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
- mkdirSync13(dirname15(plistPath()), { recursive: true });
54575
- mkdirSync13(dirname15(spec.logPath), { recursive: true });
54576
- writeFileSync13(plistPath(), buildPlist(spec));
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 (existsSync16(plistPath())) {
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 existsSync16(plistPath());
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 (!existsSync16(f)) {
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 writeFileSync14, existsSync as existsSync17, mkdirSync as mkdirSync14, unlinkSync as unlinkSync4 } from "node:fs";
54627
- import { dirname as dirname16, join as join32 } from "node:path";
54628
- import { homedir as homedir16, userInfo as userInfo2 } from "node:os";
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 || join32(homedir16(), ".config");
54636
- return join32(base, "systemd", "user", unit);
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
- mkdirSync14(dirname16(unitPath()), { recursive: true });
54662
- mkdirSync14(dirname16(spec.logPath), { recursive: true });
54663
- writeFileSync14(unitPath(), buildUnit(spec));
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 (existsSync17(unitPath())) {
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 existsSync17(unitPath());
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 writeFileSync15, existsSync as existsSync18, mkdirSync as mkdirSync15, unlinkSync as unlinkSync5 } from "node:fs";
54707
- import { dirname as dirname17, join as join33 } from "node:path";
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 = () => join33(daemonDir(), `run-host${instanceId ? `-${instanceId}` : ""}.cmd`);
54711
- const logFile = () => join33(daemonDir(), "daemon.log");
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((a) => `"${a}"`).join(" ");
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
- mkdirSync15(dirname17(spec.logPath), { recursive: true });
54723
- mkdirSync15(dirname17(wrapperPath()), { recursive: true });
54724
- writeFileSync15(wrapperPath(), buildWrapper(spec));
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 (existsSync18(wrapperPath())) {
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 (!existsSync18(f)) {
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 existsSync19, rmSync } from "node:fs";
54790
- import { join as join34 } from "node:path";
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 hostname4 } from "node:os";
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 hostname4() || "server";
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 = join34(daemonDir(), f);
54968
- if (existsSync19(p)) rmSync(p);
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 REGISTRY_URL = "https://registry.npmjs.org/tesseract-agent/latest";
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 join35 } from "node:path";
56209
+ import { join as join37 } from "node:path";
55092
56210
  import { platform as platform3 } from "node:os";
55093
- import { existsSync as existsSync20, readFileSync as readFileSync13 } from "node:fs";
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: join35(daemonDir(), "daemon.log")
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 = join35(daemonDir(), "daemon.lock");
56236
+ const lock = join37(daemonDir(), "daemon.lock");
55119
56237
  try {
55120
- if (!existsSync20(lock)) return null;
55121
- const pid = Number(readFileSync13(lock, "utf-8").trim());
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 = join36(dirname18(fileURLToPath2(import.meta.url)), "..", "package.json");
55158
- return JSON.parse(readFileSync14(p, "utf8")).version ?? "0.0.0";
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 = join36(homedir17(), ".tesseract", `daemon-${instance}`);
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":