meshy-node 0.0.7 → 0.0.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/main.cjs CHANGED
@@ -14251,11 +14251,11 @@ var require_mime_types = __commonJS({
14251
14251
  }
14252
14252
  return exts[0];
14253
14253
  }
14254
- function lookup(path16) {
14255
- if (!path16 || typeof path16 !== "string") {
14254
+ function lookup(path17) {
14255
+ if (!path17 || typeof path17 !== "string") {
14256
14256
  return false;
14257
14257
  }
14258
- var extension2 = extname3("x." + path16).toLowerCase().substr(1);
14258
+ var extension2 = extname3("x." + path17).toLowerCase().substr(1);
14259
14259
  if (!extension2) {
14260
14260
  return false;
14261
14261
  }
@@ -17773,7 +17773,7 @@ var require_path_to_regexp = __commonJS({
17773
17773
  "use strict";
17774
17774
  module2.exports = pathToRegexp;
17775
17775
  var MATCHING_GROUP_REGEXP = /\\.|\((?:\?<(.*?)>)?(?!\?)/g;
17776
- function pathToRegexp(path16, keys, options) {
17776
+ function pathToRegexp(path17, keys, options) {
17777
17777
  options = options || {};
17778
17778
  keys = keys || [];
17779
17779
  var strict = options.strict;
@@ -17787,8 +17787,8 @@ var require_path_to_regexp = __commonJS({
17787
17787
  var pos = 0;
17788
17788
  var backtrack = "";
17789
17789
  var m;
17790
- if (path16 instanceof RegExp) {
17791
- while (m = MATCHING_GROUP_REGEXP.exec(path16.source)) {
17790
+ if (path17 instanceof RegExp) {
17791
+ while (m = MATCHING_GROUP_REGEXP.exec(path17.source)) {
17792
17792
  if (m[0][0] === "\\") continue;
17793
17793
  keys.push({
17794
17794
  name: m[1] || name++,
@@ -17796,18 +17796,18 @@ var require_path_to_regexp = __commonJS({
17796
17796
  offset: m.index
17797
17797
  });
17798
17798
  }
17799
- return path16;
17799
+ return path17;
17800
17800
  }
17801
- if (Array.isArray(path16)) {
17802
- path16 = path16.map(function(value) {
17801
+ if (Array.isArray(path17)) {
17802
+ path17 = path17.map(function(value) {
17803
17803
  return pathToRegexp(value, keys, options).source;
17804
17804
  });
17805
- return new RegExp(path16.join("|"), flags);
17805
+ return new RegExp(path17.join("|"), flags);
17806
17806
  }
17807
- if (typeof path16 !== "string") {
17807
+ if (typeof path17 !== "string") {
17808
17808
  throw new TypeError("path must be a string, array of strings, or regular expression");
17809
17809
  }
17810
- path16 = path16.replace(
17810
+ path17 = path17.replace(
17811
17811
  /\\.|(\/)?(\.)?:(\w+)(\(.*?\))?(\*)?(\?)?|[.*]|\/\(/g,
17812
17812
  function(match, slash, format, key, capture, star, optional, offset) {
17813
17813
  if (match[0] === "\\") {
@@ -17824,7 +17824,7 @@ var require_path_to_regexp = __commonJS({
17824
17824
  if (slash || format) {
17825
17825
  backtrack = "";
17826
17826
  } else {
17827
- backtrack += path16.slice(pos, offset);
17827
+ backtrack += path17.slice(pos, offset);
17828
17828
  }
17829
17829
  pos = offset + match.length;
17830
17830
  if (match === "*") {
@@ -17854,7 +17854,7 @@ var require_path_to_regexp = __commonJS({
17854
17854
  return result;
17855
17855
  }
17856
17856
  );
17857
- while (m = MATCHING_GROUP_REGEXP.exec(path16)) {
17857
+ while (m = MATCHING_GROUP_REGEXP.exec(path17)) {
17858
17858
  if (m[0][0] === "\\") continue;
17859
17859
  if (keysOffset + i === keys.length || keys[keysOffset + i].offset > m.index) {
17860
17860
  keys.splice(keysOffset + i, 0, {
@@ -17866,13 +17866,13 @@ var require_path_to_regexp = __commonJS({
17866
17866
  }
17867
17867
  i++;
17868
17868
  }
17869
- path16 += strict ? "" : path16[path16.length - 1] === "/" ? "?" : "/?";
17869
+ path17 += strict ? "" : path17[path17.length - 1] === "/" ? "?" : "/?";
17870
17870
  if (end) {
17871
- path16 += "$";
17872
- } else if (path16[path16.length - 1] !== "/") {
17873
- path16 += lookahead ? "(?=/|$)" : "(?:/|$)";
17871
+ path17 += "$";
17872
+ } else if (path17[path17.length - 1] !== "/") {
17873
+ path17 += lookahead ? "(?=/|$)" : "(?:/|$)";
17874
17874
  }
17875
- return new RegExp("^" + path16, flags);
17875
+ return new RegExp("^" + path17, flags);
17876
17876
  }
17877
17877
  }
17878
17878
  });
@@ -17885,19 +17885,19 @@ var require_layer = __commonJS({
17885
17885
  var debug = require_src()("express:router:layer");
17886
17886
  var hasOwnProperty = Object.prototype.hasOwnProperty;
17887
17887
  module2.exports = Layer;
17888
- function Layer(path16, options, fn) {
17888
+ function Layer(path17, options, fn) {
17889
17889
  if (!(this instanceof Layer)) {
17890
- return new Layer(path16, options, fn);
17890
+ return new Layer(path17, options, fn);
17891
17891
  }
17892
- debug("new %o", path16);
17892
+ debug("new %o", path17);
17893
17893
  var opts = options || {};
17894
17894
  this.handle = fn;
17895
17895
  this.name = fn.name || "<anonymous>";
17896
17896
  this.params = void 0;
17897
17897
  this.path = void 0;
17898
- this.regexp = pathRegexp(path16, this.keys = [], opts);
17899
- this.regexp.fast_star = path16 === "*";
17900
- this.regexp.fast_slash = path16 === "/" && opts.end === false;
17898
+ this.regexp = pathRegexp(path17, this.keys = [], opts);
17899
+ this.regexp.fast_star = path17 === "*";
17900
+ this.regexp.fast_slash = path17 === "/" && opts.end === false;
17901
17901
  }
17902
17902
  Layer.prototype.handle_error = function handle_error(error, req, res, next) {
17903
17903
  var fn = this.handle;
@@ -17921,20 +17921,20 @@ var require_layer = __commonJS({
17921
17921
  next(err);
17922
17922
  }
17923
17923
  };
17924
- Layer.prototype.match = function match(path16) {
17924
+ Layer.prototype.match = function match(path17) {
17925
17925
  var match2;
17926
- if (path16 != null) {
17926
+ if (path17 != null) {
17927
17927
  if (this.regexp.fast_slash) {
17928
17928
  this.params = {};
17929
17929
  this.path = "";
17930
17930
  return true;
17931
17931
  }
17932
17932
  if (this.regexp.fast_star) {
17933
- this.params = { "0": decode_param(path16) };
17934
- this.path = path16;
17933
+ this.params = { "0": decode_param(path17) };
17934
+ this.path = path17;
17935
17935
  return true;
17936
17936
  }
17937
- match2 = this.regexp.exec(path16);
17937
+ match2 = this.regexp.exec(path17);
17938
17938
  }
17939
17939
  if (!match2) {
17940
17940
  this.params = void 0;
@@ -18027,10 +18027,10 @@ var require_route = __commonJS({
18027
18027
  var slice = Array.prototype.slice;
18028
18028
  var toString = Object.prototype.toString;
18029
18029
  module2.exports = Route;
18030
- function Route(path16) {
18031
- this.path = path16;
18030
+ function Route(path17) {
18031
+ this.path = path17;
18032
18032
  this.stack = [];
18033
- debug("new %o", path16);
18033
+ debug("new %o", path17);
18034
18034
  this.methods = {};
18035
18035
  }
18036
18036
  Route.prototype._handles_method = function _handles_method(method) {
@@ -18243,8 +18243,8 @@ var require_router = __commonJS({
18243
18243
  if (++sync > 100) {
18244
18244
  return setImmediate(next, err);
18245
18245
  }
18246
- var path16 = getPathname(req);
18247
- if (path16 == null) {
18246
+ var path17 = getPathname(req);
18247
+ if (path17 == null) {
18248
18248
  return done(layerError);
18249
18249
  }
18250
18250
  var layer;
@@ -18252,7 +18252,7 @@ var require_router = __commonJS({
18252
18252
  var route;
18253
18253
  while (match !== true && idx < stack.length) {
18254
18254
  layer = stack[idx++];
18255
- match = matchLayer(layer, path16);
18255
+ match = matchLayer(layer, path17);
18256
18256
  route = layer.route;
18257
18257
  if (typeof match !== "boolean") {
18258
18258
  layerError = layerError || match;
@@ -18290,18 +18290,18 @@ var require_router = __commonJS({
18290
18290
  } else if (route) {
18291
18291
  layer.handle_request(req, res, next);
18292
18292
  } else {
18293
- trim_prefix(layer, layerError, layerPath, path16);
18293
+ trim_prefix(layer, layerError, layerPath, path17);
18294
18294
  }
18295
18295
  sync = 0;
18296
18296
  });
18297
18297
  }
18298
- function trim_prefix(layer, layerError, layerPath, path16) {
18298
+ function trim_prefix(layer, layerError, layerPath, path17) {
18299
18299
  if (layerPath.length !== 0) {
18300
- if (layerPath !== path16.slice(0, layerPath.length)) {
18300
+ if (layerPath !== path17.slice(0, layerPath.length)) {
18301
18301
  next(layerError);
18302
18302
  return;
18303
18303
  }
18304
- var c = path16[layerPath.length];
18304
+ var c = path17[layerPath.length];
18305
18305
  if (c && c !== "/" && c !== ".") return next(layerError);
18306
18306
  debug("trim prefix (%s) from url %s", layerPath, req.url);
18307
18307
  removed = layerPath;
@@ -18379,7 +18379,7 @@ var require_router = __commonJS({
18379
18379
  };
18380
18380
  proto.use = function use(fn) {
18381
18381
  var offset = 0;
18382
- var path16 = "/";
18382
+ var path17 = "/";
18383
18383
  if (typeof fn !== "function") {
18384
18384
  var arg = fn;
18385
18385
  while (Array.isArray(arg) && arg.length !== 0) {
@@ -18387,7 +18387,7 @@ var require_router = __commonJS({
18387
18387
  }
18388
18388
  if (typeof arg !== "function") {
18389
18389
  offset = 1;
18390
- path16 = fn;
18390
+ path17 = fn;
18391
18391
  }
18392
18392
  }
18393
18393
  var callbacks = flatten(slice.call(arguments, offset));
@@ -18399,8 +18399,8 @@ var require_router = __commonJS({
18399
18399
  if (typeof fn !== "function") {
18400
18400
  throw new TypeError("Router.use() requires a middleware function but got a " + gettype(fn));
18401
18401
  }
18402
- debug("use %o %s", path16, fn.name || "<anonymous>");
18403
- var layer = new Layer(path16, {
18402
+ debug("use %o %s", path17, fn.name || "<anonymous>");
18403
+ var layer = new Layer(path17, {
18404
18404
  sensitive: this.caseSensitive,
18405
18405
  strict: false,
18406
18406
  end: false
@@ -18410,9 +18410,9 @@ var require_router = __commonJS({
18410
18410
  }
18411
18411
  return this;
18412
18412
  };
18413
- proto.route = function route(path16) {
18414
- var route2 = new Route(path16);
18415
- var layer = new Layer(path16, {
18413
+ proto.route = function route(path17) {
18414
+ var route2 = new Route(path17);
18415
+ var layer = new Layer(path17, {
18416
18416
  sensitive: this.caseSensitive,
18417
18417
  strict: this.strict,
18418
18418
  end: true
@@ -18422,8 +18422,8 @@ var require_router = __commonJS({
18422
18422
  return route2;
18423
18423
  };
18424
18424
  methods.concat("all").forEach(function(method) {
18425
- proto[method] = function(path16) {
18426
- var route = this.route(path16);
18425
+ proto[method] = function(path17) {
18426
+ var route = this.route(path17);
18427
18427
  route[method].apply(route, slice.call(arguments, 1));
18428
18428
  return this;
18429
18429
  };
@@ -18459,9 +18459,9 @@ var require_router = __commonJS({
18459
18459
  }
18460
18460
  return toString.call(obj).replace(objectRegExp, "$1");
18461
18461
  }
18462
- function matchLayer(layer, path16) {
18462
+ function matchLayer(layer, path17) {
18463
18463
  try {
18464
- return layer.match(path16);
18464
+ return layer.match(path17);
18465
18465
  } catch (err) {
18466
18466
  return err;
18467
18467
  }
@@ -18579,13 +18579,13 @@ var require_view = __commonJS({
18579
18579
  "../../node_modules/.pnpm/express@4.22.1/node_modules/express/lib/view.js"(exports2, module2) {
18580
18580
  "use strict";
18581
18581
  var debug = require_src()("express:view");
18582
- var path16 = require("path");
18582
+ var path17 = require("path");
18583
18583
  var fs17 = require("fs");
18584
- var dirname3 = path16.dirname;
18585
- var basename4 = path16.basename;
18586
- var extname3 = path16.extname;
18587
- var join12 = path16.join;
18588
- var resolve10 = path16.resolve;
18584
+ var dirname3 = path17.dirname;
18585
+ var basename4 = path17.basename;
18586
+ var extname3 = path17.extname;
18587
+ var join13 = path17.join;
18588
+ var resolve10 = path17.resolve;
18589
18589
  module2.exports = View;
18590
18590
  function View(name, options) {
18591
18591
  var opts = options || {};
@@ -18614,17 +18614,17 @@ var require_view = __commonJS({
18614
18614
  this.path = this.lookup(fileName);
18615
18615
  }
18616
18616
  View.prototype.lookup = function lookup(name) {
18617
- var path17;
18617
+ var path18;
18618
18618
  var roots = [].concat(this.root);
18619
18619
  debug('lookup "%s"', name);
18620
- for (var i = 0; i < roots.length && !path17; i++) {
18620
+ for (var i = 0; i < roots.length && !path18; i++) {
18621
18621
  var root = roots[i];
18622
18622
  var loc = resolve10(root, name);
18623
18623
  var dir = dirname3(loc);
18624
18624
  var file = basename4(loc);
18625
- path17 = this.resolve(dir, file);
18625
+ path18 = this.resolve(dir, file);
18626
18626
  }
18627
- return path17;
18627
+ return path18;
18628
18628
  };
18629
18629
  View.prototype.render = function render(options, callback) {
18630
18630
  debug('render "%s"', this.path);
@@ -18632,21 +18632,21 @@ var require_view = __commonJS({
18632
18632
  };
18633
18633
  View.prototype.resolve = function resolve11(dir, file) {
18634
18634
  var ext = this.ext;
18635
- var path17 = join12(dir, file);
18636
- var stat = tryStat(path17);
18635
+ var path18 = join13(dir, file);
18636
+ var stat = tryStat(path18);
18637
18637
  if (stat && stat.isFile()) {
18638
- return path17;
18638
+ return path18;
18639
18639
  }
18640
- path17 = join12(dir, basename4(file, ext), "index" + ext);
18641
- stat = tryStat(path17);
18640
+ path18 = join13(dir, basename4(file, ext), "index" + ext);
18641
+ stat = tryStat(path18);
18642
18642
  if (stat && stat.isFile()) {
18643
- return path17;
18643
+ return path18;
18644
18644
  }
18645
18645
  };
18646
- function tryStat(path17) {
18647
- debug('stat "%s"', path17);
18646
+ function tryStat(path18) {
18647
+ debug('stat "%s"', path18);
18648
18648
  try {
18649
- return fs17.statSync(path17);
18649
+ return fs17.statSync(path18);
18650
18650
  } catch (e) {
18651
18651
  return void 0;
18652
18652
  }
@@ -19002,7 +19002,7 @@ var require_types = __commonJS({
19002
19002
  var require_mime = __commonJS({
19003
19003
  "../../node_modules/.pnpm/mime@1.6.0/node_modules/mime/mime.js"(exports2, module2) {
19004
19004
  "use strict";
19005
- var path16 = require("path");
19005
+ var path17 = require("path");
19006
19006
  var fs17 = require("fs");
19007
19007
  function Mime() {
19008
19008
  this.types = /* @__PURE__ */ Object.create(null);
@@ -19032,8 +19032,8 @@ var require_mime = __commonJS({
19032
19032
  this.define(map);
19033
19033
  this._loading = null;
19034
19034
  };
19035
- Mime.prototype.lookup = function(path17, fallback) {
19036
- var ext = path17.replace(/^.*[\.\/\\]/, "").toLowerCase();
19035
+ Mime.prototype.lookup = function(path18, fallback) {
19036
+ var ext = path18.replace(/^.*[\.\/\\]/, "").toLowerCase();
19037
19037
  return this.types[ext] || fallback || this.default_type;
19038
19038
  };
19039
19039
  Mime.prototype.extension = function(mimeType) {
@@ -19268,28 +19268,28 @@ var require_send = __commonJS({
19268
19268
  var ms = require_ms2();
19269
19269
  var onFinished = require_on_finished();
19270
19270
  var parseRange = require_range_parser();
19271
- var path16 = require("path");
19271
+ var path17 = require("path");
19272
19272
  var statuses = require_statuses();
19273
19273
  var Stream = require("stream");
19274
19274
  var util2 = require("util");
19275
- var extname3 = path16.extname;
19276
- var join12 = path16.join;
19277
- var normalize = path16.normalize;
19278
- var resolve10 = path16.resolve;
19279
- var sep3 = path16.sep;
19275
+ var extname3 = path17.extname;
19276
+ var join13 = path17.join;
19277
+ var normalize = path17.normalize;
19278
+ var resolve10 = path17.resolve;
19279
+ var sep3 = path17.sep;
19280
19280
  var BYTES_RANGE_REGEXP = /^ *bytes=/;
19281
19281
  var MAX_MAXAGE = 60 * 60 * 24 * 365 * 1e3;
19282
19282
  var UP_PATH_REGEXP = /(?:^|[\\/])\.\.(?:[\\/]|$)/;
19283
19283
  module2.exports = send;
19284
19284
  module2.exports.mime = mime;
19285
- function send(req, path17, options) {
19286
- return new SendStream(req, path17, options);
19285
+ function send(req, path18, options) {
19286
+ return new SendStream(req, path18, options);
19287
19287
  }
19288
- function SendStream(req, path17, options) {
19288
+ function SendStream(req, path18, options) {
19289
19289
  Stream.call(this);
19290
19290
  var opts = options || {};
19291
19291
  this.options = opts;
19292
- this.path = path17;
19292
+ this.path = path18;
19293
19293
  this.req = req;
19294
19294
  this._acceptRanges = opts.acceptRanges !== void 0 ? Boolean(opts.acceptRanges) : true;
19295
19295
  this._cacheControl = opts.cacheControl !== void 0 ? Boolean(opts.cacheControl) : true;
@@ -19335,8 +19335,8 @@ var require_send = __commonJS({
19335
19335
  this._index = index2;
19336
19336
  return this;
19337
19337
  }, "send.index: pass index as option");
19338
- SendStream.prototype.root = function root(path17) {
19339
- this._root = resolve10(String(path17));
19338
+ SendStream.prototype.root = function root(path18) {
19339
+ this._root = resolve10(String(path18));
19340
19340
  debug("root %s", this._root);
19341
19341
  return this;
19342
19342
  };
@@ -19449,10 +19449,10 @@ var require_send = __commonJS({
19449
19449
  var lastModified = this.res.getHeader("Last-Modified");
19450
19450
  return parseHttpDate(lastModified) <= parseHttpDate(ifRange);
19451
19451
  };
19452
- SendStream.prototype.redirect = function redirect(path17) {
19452
+ SendStream.prototype.redirect = function redirect(path18) {
19453
19453
  var res = this.res;
19454
19454
  if (hasListeners(this, "directory")) {
19455
- this.emit("directory", res, path17);
19455
+ this.emit("directory", res, path18);
19456
19456
  return;
19457
19457
  }
19458
19458
  if (this.hasTrailingSlash()) {
@@ -19472,42 +19472,42 @@ var require_send = __commonJS({
19472
19472
  SendStream.prototype.pipe = function pipe(res) {
19473
19473
  var root = this._root;
19474
19474
  this.res = res;
19475
- var path17 = decode(this.path);
19476
- if (path17 === -1) {
19475
+ var path18 = decode(this.path);
19476
+ if (path18 === -1) {
19477
19477
  this.error(400);
19478
19478
  return res;
19479
19479
  }
19480
- if (~path17.indexOf("\0")) {
19480
+ if (~path18.indexOf("\0")) {
19481
19481
  this.error(400);
19482
19482
  return res;
19483
19483
  }
19484
19484
  var parts;
19485
19485
  if (root !== null) {
19486
- if (path17) {
19487
- path17 = normalize("." + sep3 + path17);
19486
+ if (path18) {
19487
+ path18 = normalize("." + sep3 + path18);
19488
19488
  }
19489
- if (UP_PATH_REGEXP.test(path17)) {
19490
- debug('malicious path "%s"', path17);
19489
+ if (UP_PATH_REGEXP.test(path18)) {
19490
+ debug('malicious path "%s"', path18);
19491
19491
  this.error(403);
19492
19492
  return res;
19493
19493
  }
19494
- parts = path17.split(sep3);
19495
- path17 = normalize(join12(root, path17));
19494
+ parts = path18.split(sep3);
19495
+ path18 = normalize(join13(root, path18));
19496
19496
  } else {
19497
- if (UP_PATH_REGEXP.test(path17)) {
19498
- debug('malicious path "%s"', path17);
19497
+ if (UP_PATH_REGEXP.test(path18)) {
19498
+ debug('malicious path "%s"', path18);
19499
19499
  this.error(403);
19500
19500
  return res;
19501
19501
  }
19502
- parts = normalize(path17).split(sep3);
19503
- path17 = resolve10(path17);
19502
+ parts = normalize(path18).split(sep3);
19503
+ path18 = resolve10(path18);
19504
19504
  }
19505
19505
  if (containsDotFile(parts)) {
19506
19506
  var access = this._dotfiles;
19507
19507
  if (access === void 0) {
19508
19508
  access = parts[parts.length - 1][0] === "." ? this._hidden ? "allow" : "ignore" : "allow";
19509
19509
  }
19510
- debug('%s dotfile "%s"', access, path17);
19510
+ debug('%s dotfile "%s"', access, path18);
19511
19511
  switch (access) {
19512
19512
  case "allow":
19513
19513
  break;
@@ -19521,13 +19521,13 @@ var require_send = __commonJS({
19521
19521
  }
19522
19522
  }
19523
19523
  if (this._index.length && this.hasTrailingSlash()) {
19524
- this.sendIndex(path17);
19524
+ this.sendIndex(path18);
19525
19525
  return res;
19526
19526
  }
19527
- this.sendFile(path17);
19527
+ this.sendFile(path18);
19528
19528
  return res;
19529
19529
  };
19530
- SendStream.prototype.send = function send2(path17, stat) {
19530
+ SendStream.prototype.send = function send2(path18, stat) {
19531
19531
  var len = stat.size;
19532
19532
  var options = this.options;
19533
19533
  var opts = {};
@@ -19539,9 +19539,9 @@ var require_send = __commonJS({
19539
19539
  this.headersAlreadySent();
19540
19540
  return;
19541
19541
  }
19542
- debug('pipe "%s"', path17);
19543
- this.setHeader(path17, stat);
19544
- this.type(path17);
19542
+ debug('pipe "%s"', path18);
19543
+ this.setHeader(path18, stat);
19544
+ this.type(path18);
19545
19545
  if (this.isConditionalGET()) {
19546
19546
  if (this.isPreconditionFailure()) {
19547
19547
  this.error(412);
@@ -19590,26 +19590,26 @@ var require_send = __commonJS({
19590
19590
  res.end();
19591
19591
  return;
19592
19592
  }
19593
- this.stream(path17, opts);
19593
+ this.stream(path18, opts);
19594
19594
  };
19595
- SendStream.prototype.sendFile = function sendFile(path17) {
19595
+ SendStream.prototype.sendFile = function sendFile(path18) {
19596
19596
  var i = 0;
19597
19597
  var self = this;
19598
- debug('stat "%s"', path17);
19599
- fs17.stat(path17, function onstat(err, stat) {
19600
- if (err && err.code === "ENOENT" && !extname3(path17) && path17[path17.length - 1] !== sep3) {
19598
+ debug('stat "%s"', path18);
19599
+ fs17.stat(path18, function onstat(err, stat) {
19600
+ if (err && err.code === "ENOENT" && !extname3(path18) && path18[path18.length - 1] !== sep3) {
19601
19601
  return next(err);
19602
19602
  }
19603
19603
  if (err) return self.onStatError(err);
19604
- if (stat.isDirectory()) return self.redirect(path17);
19605
- self.emit("file", path17, stat);
19606
- self.send(path17, stat);
19604
+ if (stat.isDirectory()) return self.redirect(path18);
19605
+ self.emit("file", path18, stat);
19606
+ self.send(path18, stat);
19607
19607
  });
19608
19608
  function next(err) {
19609
19609
  if (self._extensions.length <= i) {
19610
19610
  return err ? self.onStatError(err) : self.error(404);
19611
19611
  }
19612
- var p = path17 + "." + self._extensions[i++];
19612
+ var p = path18 + "." + self._extensions[i++];
19613
19613
  debug('stat "%s"', p);
19614
19614
  fs17.stat(p, function(err2, stat) {
19615
19615
  if (err2) return next(err2);
@@ -19619,7 +19619,7 @@ var require_send = __commonJS({
19619
19619
  });
19620
19620
  }
19621
19621
  };
19622
- SendStream.prototype.sendIndex = function sendIndex(path17) {
19622
+ SendStream.prototype.sendIndex = function sendIndex(path18) {
19623
19623
  var i = -1;
19624
19624
  var self = this;
19625
19625
  function next(err) {
@@ -19627,7 +19627,7 @@ var require_send = __commonJS({
19627
19627
  if (err) return self.onStatError(err);
19628
19628
  return self.error(404);
19629
19629
  }
19630
- var p = join12(path17, self._index[i]);
19630
+ var p = join13(path18, self._index[i]);
19631
19631
  debug('stat "%s"', p);
19632
19632
  fs17.stat(p, function(err2, stat) {
19633
19633
  if (err2) return next(err2);
@@ -19638,10 +19638,10 @@ var require_send = __commonJS({
19638
19638
  }
19639
19639
  next();
19640
19640
  };
19641
- SendStream.prototype.stream = function stream(path17, options) {
19641
+ SendStream.prototype.stream = function stream(path18, options) {
19642
19642
  var self = this;
19643
19643
  var res = this.res;
19644
- var stream2 = fs17.createReadStream(path17, options);
19644
+ var stream2 = fs17.createReadStream(path18, options);
19645
19645
  this.emit("stream", stream2);
19646
19646
  stream2.pipe(res);
19647
19647
  function cleanup() {
@@ -19656,10 +19656,10 @@ var require_send = __commonJS({
19656
19656
  self.emit("end");
19657
19657
  });
19658
19658
  };
19659
- SendStream.prototype.type = function type(path17) {
19659
+ SendStream.prototype.type = function type(path18) {
19660
19660
  var res = this.res;
19661
19661
  if (res.getHeader("Content-Type")) return;
19662
- var type2 = mime.lookup(path17);
19662
+ var type2 = mime.lookup(path18);
19663
19663
  if (!type2) {
19664
19664
  debug("no content-type");
19665
19665
  return;
@@ -19668,9 +19668,9 @@ var require_send = __commonJS({
19668
19668
  debug("content-type %s", type2);
19669
19669
  res.setHeader("Content-Type", type2 + (charset ? "; charset=" + charset : ""));
19670
19670
  };
19671
- SendStream.prototype.setHeader = function setHeader(path17, stat) {
19671
+ SendStream.prototype.setHeader = function setHeader(path18, stat) {
19672
19672
  var res = this.res;
19673
- this.emit("headers", res, path17, stat);
19673
+ this.emit("headers", res, path18, stat);
19674
19674
  if (this._acceptRanges && !res.getHeader("Accept-Ranges")) {
19675
19675
  debug("accept ranges");
19676
19676
  res.setHeader("Accept-Ranges", "bytes");
@@ -19729,9 +19729,9 @@ var require_send = __commonJS({
19729
19729
  }
19730
19730
  return err instanceof Error ? createError(status, err, { expose: false }) : createError(status, err);
19731
19731
  }
19732
- function decode(path17) {
19732
+ function decode(path18) {
19733
19733
  try {
19734
- return decodeURIComponent(path17);
19734
+ return decodeURIComponent(path18);
19735
19735
  } catch (err) {
19736
19736
  return -1;
19737
19737
  }
@@ -20641,10 +20641,10 @@ var require_utils2 = __commonJS({
20641
20641
  var querystring = require("querystring");
20642
20642
  exports2.etag = createETagGenerator({ weak: false });
20643
20643
  exports2.wetag = createETagGenerator({ weak: true });
20644
- exports2.isAbsolute = function(path16) {
20645
- if ("/" === path16[0]) return true;
20646
- if (":" === path16[1] && ("\\" === path16[2] || "/" === path16[2])) return true;
20647
- if ("\\\\" === path16.substring(0, 2)) return true;
20644
+ exports2.isAbsolute = function(path17) {
20645
+ if ("/" === path17[0]) return true;
20646
+ if (":" === path17[1] && ("\\" === path17[2] || "/" === path17[2])) return true;
20647
+ if ("\\\\" === path17.substring(0, 2)) return true;
20648
20648
  };
20649
20649
  exports2.flatten = deprecate.function(
20650
20650
  flatten,
@@ -20855,7 +20855,7 @@ var require_application = __commonJS({
20855
20855
  };
20856
20856
  app.use = function use(fn) {
20857
20857
  var offset = 0;
20858
- var path16 = "/";
20858
+ var path17 = "/";
20859
20859
  if (typeof fn !== "function") {
20860
20860
  var arg = fn;
20861
20861
  while (Array.isArray(arg) && arg.length !== 0) {
@@ -20863,7 +20863,7 @@ var require_application = __commonJS({
20863
20863
  }
20864
20864
  if (typeof arg !== "function") {
20865
20865
  offset = 1;
20866
- path16 = fn;
20866
+ path17 = fn;
20867
20867
  }
20868
20868
  }
20869
20869
  var fns = flatten(slice.call(arguments, offset));
@@ -20874,12 +20874,12 @@ var require_application = __commonJS({
20874
20874
  var router = this._router;
20875
20875
  fns.forEach(function(fn2) {
20876
20876
  if (!fn2 || !fn2.handle || !fn2.set) {
20877
- return router.use(path16, fn2);
20877
+ return router.use(path17, fn2);
20878
20878
  }
20879
- debug(".use app under %s", path16);
20880
- fn2.mountpath = path16;
20879
+ debug(".use app under %s", path17);
20880
+ fn2.mountpath = path17;
20881
20881
  fn2.parent = this;
20882
- router.use(path16, function mounted_app(req, res, next) {
20882
+ router.use(path17, function mounted_app(req, res, next) {
20883
20883
  var orig = req.app;
20884
20884
  fn2.handle(req, res, function(err) {
20885
20885
  setPrototypeOf(req, orig.request);
@@ -20891,9 +20891,9 @@ var require_application = __commonJS({
20891
20891
  }, this);
20892
20892
  return this;
20893
20893
  };
20894
- app.route = function route(path16) {
20894
+ app.route = function route(path17) {
20895
20895
  this.lazyrouter();
20896
- return this._router.route(path16);
20896
+ return this._router.route(path17);
20897
20897
  };
20898
20898
  app.engine = function engine(ext, fn) {
20899
20899
  if (typeof fn !== "function") {
@@ -20944,7 +20944,7 @@ var require_application = __commonJS({
20944
20944
  }
20945
20945
  return this;
20946
20946
  };
20947
- app.path = function path16() {
20947
+ app.path = function path17() {
20948
20948
  return this.parent ? this.parent.path() + this.mountpath : "";
20949
20949
  };
20950
20950
  app.enabled = function enabled(setting) {
@@ -20960,19 +20960,19 @@ var require_application = __commonJS({
20960
20960
  return this.set(setting, false);
20961
20961
  };
20962
20962
  methods.forEach(function(method) {
20963
- app[method] = function(path16) {
20963
+ app[method] = function(path17) {
20964
20964
  if (method === "get" && arguments.length === 1) {
20965
- return this.set(path16);
20965
+ return this.set(path17);
20966
20966
  }
20967
20967
  this.lazyrouter();
20968
- var route = this._router.route(path16);
20968
+ var route = this._router.route(path17);
20969
20969
  route[method].apply(route, slice.call(arguments, 1));
20970
20970
  return this;
20971
20971
  };
20972
20972
  });
20973
- app.all = function all(path16) {
20973
+ app.all = function all(path17) {
20974
20974
  this.lazyrouter();
20975
- var route = this._router.route(path16);
20975
+ var route = this._router.route(path17);
20976
20976
  var args = slice.call(arguments, 1);
20977
20977
  for (var i = 0; i < methods.length; i++) {
20978
20978
  route[methods[i]].apply(route, args);
@@ -21731,7 +21731,7 @@ var require_request = __commonJS({
21731
21731
  var subdomains2 = !isIP(hostname2) ? hostname2.split(".").reverse() : [hostname2];
21732
21732
  return subdomains2.slice(offset);
21733
21733
  });
21734
- defineGetter(req, "path", function path16() {
21734
+ defineGetter(req, "path", function path17() {
21735
21735
  return parse(this).pathname;
21736
21736
  });
21737
21737
  defineGetter(req, "hostname", function hostname2() {
@@ -22054,7 +22054,7 @@ var require_response = __commonJS({
22054
22054
  var http2 = require("http");
22055
22055
  var isAbsolute2 = require_utils2().isAbsolute;
22056
22056
  var onFinished = require_on_finished();
22057
- var path16 = require("path");
22057
+ var path17 = require("path");
22058
22058
  var statuses = require_statuses();
22059
22059
  var merge = require_utils_merge();
22060
22060
  var sign = require_cookie_signature().sign;
@@ -22063,9 +22063,9 @@ var require_response = __commonJS({
22063
22063
  var setCharset = require_utils2().setCharset;
22064
22064
  var cookie = require_cookie();
22065
22065
  var send = require_send();
22066
- var extname3 = path16.extname;
22066
+ var extname3 = path17.extname;
22067
22067
  var mime = send.mime;
22068
- var resolve10 = path16.resolve;
22068
+ var resolve10 = path17.resolve;
22069
22069
  var vary = require_vary();
22070
22070
  var res = Object.create(http2.ServerResponse.prototype);
22071
22071
  module2.exports = res;
@@ -22242,26 +22242,26 @@ var require_response = __commonJS({
22242
22242
  this.type("txt");
22243
22243
  return this.send(body);
22244
22244
  };
22245
- res.sendFile = function sendFile(path17, options, callback) {
22245
+ res.sendFile = function sendFile(path18, options, callback) {
22246
22246
  var done = callback;
22247
22247
  var req = this.req;
22248
22248
  var res2 = this;
22249
22249
  var next = req.next;
22250
22250
  var opts = options || {};
22251
- if (!path17) {
22251
+ if (!path18) {
22252
22252
  throw new TypeError("path argument is required to res.sendFile");
22253
22253
  }
22254
- if (typeof path17 !== "string") {
22254
+ if (typeof path18 !== "string") {
22255
22255
  throw new TypeError("path must be a string to res.sendFile");
22256
22256
  }
22257
22257
  if (typeof options === "function") {
22258
22258
  done = options;
22259
22259
  opts = {};
22260
22260
  }
22261
- if (!opts.root && !isAbsolute2(path17)) {
22261
+ if (!opts.root && !isAbsolute2(path18)) {
22262
22262
  throw new TypeError("path must be absolute or specify root to res.sendFile");
22263
22263
  }
22264
- var pathname = encodeURI(path17);
22264
+ var pathname = encodeURI(path18);
22265
22265
  var file = send(req, pathname, opts);
22266
22266
  sendfile(res2, file, opts, function(err) {
22267
22267
  if (done) return done(err);
@@ -22271,7 +22271,7 @@ var require_response = __commonJS({
22271
22271
  }
22272
22272
  });
22273
22273
  };
22274
- res.sendfile = function(path17, options, callback) {
22274
+ res.sendfile = function(path18, options, callback) {
22275
22275
  var done = callback;
22276
22276
  var req = this.req;
22277
22277
  var res2 = this;
@@ -22281,7 +22281,7 @@ var require_response = __commonJS({
22281
22281
  done = options;
22282
22282
  opts = {};
22283
22283
  }
22284
- var file = send(req, path17, opts);
22284
+ var file = send(req, path18, opts);
22285
22285
  sendfile(res2, file, opts, function(err) {
22286
22286
  if (done) return done(err);
22287
22287
  if (err && err.code === "EISDIR") return next();
@@ -22294,7 +22294,7 @@ var require_response = __commonJS({
22294
22294
  res.sendfile,
22295
22295
  "res.sendfile: Use res.sendFile instead"
22296
22296
  );
22297
- res.download = function download(path17, filename, options, callback) {
22297
+ res.download = function download(path18, filename, options, callback) {
22298
22298
  var done = callback;
22299
22299
  var name = filename;
22300
22300
  var opts = options || null;
@@ -22311,7 +22311,7 @@ var require_response = __commonJS({
22311
22311
  opts = filename;
22312
22312
  }
22313
22313
  var headers = {
22314
- "Content-Disposition": contentDisposition(name || path17)
22314
+ "Content-Disposition": contentDisposition(name || path18)
22315
22315
  };
22316
22316
  if (opts && opts.headers) {
22317
22317
  var keys = Object.keys(opts.headers);
@@ -22324,7 +22324,7 @@ var require_response = __commonJS({
22324
22324
  }
22325
22325
  opts = Object.create(opts);
22326
22326
  opts.headers = headers;
22327
- var fullPath = !opts.root ? resolve10(path17) : path17;
22327
+ var fullPath = !opts.root ? resolve10(path18) : path18;
22328
22328
  return this.sendFile(fullPath, opts, done);
22329
22329
  };
22330
22330
  res.contentType = res.type = function contentType(type) {
@@ -22625,11 +22625,11 @@ var require_serve_static = __commonJS({
22625
22625
  }
22626
22626
  var forwardError = !fallthrough;
22627
22627
  var originalUrl = parseUrl.original(req);
22628
- var path16 = parseUrl(req).pathname;
22629
- if (path16 === "/" && originalUrl.pathname.substr(-1) !== "/") {
22630
- path16 = "";
22628
+ var path17 = parseUrl(req).pathname;
22629
+ if (path17 === "/" && originalUrl.pathname.substr(-1) !== "/") {
22630
+ path17 = "";
22631
22631
  }
22632
- var stream = send(req, path16, opts);
22632
+ var stream = send(req, path17, opts);
22633
22633
  stream.on("directory", onDirectory);
22634
22634
  if (setHeaders) {
22635
22635
  stream.on("headers", setHeaders);
@@ -22774,6 +22774,7 @@ __export(main_exports, {
22774
22774
  applyStartMetadata: () => applyStartMetadata,
22775
22775
  createDefaultConfig: () => createDefaultConfig,
22776
22776
  createRuntimeDefaultConfig: () => createRuntimeDefaultConfig,
22777
+ ensureStartupRequirements: () => ensureStartupRequirements,
22777
22778
  formatBanner: () => formatBanner,
22778
22779
  formatLoadedStartMetadata: () => formatLoadedStartMetadata,
22779
22780
  getDefaultNodeName: () => getDefaultNodeName,
@@ -22784,8 +22785,10 @@ __export(main_exports, {
22784
22785
  parseArgs: () => parseArgs,
22785
22786
  promptStartOptions: () => promptStartOptions,
22786
22787
  resolveRuntimeAuthMetadata: () => resolveRuntimeAuthMetadata,
22788
+ resolveStartupRequirementsMetadata: () => resolveStartupRequirementsMetadata,
22787
22789
  shouldCollectStartOptions: () => shouldCollectStartOptions,
22788
- shouldPromptForStartOptions: () => shouldPromptForStartOptions
22790
+ shouldPromptForStartOptions: () => shouldPromptForStartOptions,
22791
+ shouldSkipStartupRequirementsCheck: () => shouldSkipStartupRequirementsCheck
22789
22792
  });
22790
22793
  module.exports = __toCommonJS(main_exports);
22791
22794
  var nodePath = __toESM(require("path"), 1);
@@ -24010,14 +24013,19 @@ async function fetchWithTimeout(url, init, timeoutMs = DEFAULT_NODE_REQUEST_TIME
24010
24013
  cleanup();
24011
24014
  }
24012
24015
  }
24013
- async function fetchNodeWithFallback(node, path16, init, timeoutMs = DEFAULT_NODE_REQUEST_TIMEOUT_MS) {
24016
+ async function fetchNodeWithFallback(node, path17, init, timeoutMs = DEFAULT_NODE_REQUEST_TIMEOUT_MS, trace) {
24014
24017
  let lastError;
24015
- for (const endpoint of getNodeRequestEndpoints(node)) {
24018
+ const endpoints = getNodeRequestEndpoints(node);
24019
+ for (const [index, endpoint] of endpoints.entries()) {
24020
+ const attempt = index + 1;
24021
+ trace?.onAttempt?.({ attempt, endpoint, path: path17, timeoutMs, totalEndpoints: endpoints.length });
24016
24022
  try {
24017
- const response = await fetchWithTimeout(`${endpoint}${path16}`, init, timeoutMs);
24023
+ const response = await fetchWithTimeout(`${endpoint}${path17}`, init, timeoutMs);
24024
+ trace?.onResponse?.({ attempt, endpoint, path: path17, response, timeoutMs, totalEndpoints: endpoints.length });
24018
24025
  return { endpoint, response };
24019
24026
  } catch (error) {
24020
24027
  lastError = error;
24028
+ trace?.onError?.({ attempt, endpoint, error, path: path17, timeoutMs, totalEndpoints: endpoints.length });
24021
24029
  }
24022
24030
  }
24023
24031
  throw lastError instanceof Error ? lastError : new Error("No reachable node endpoint");
@@ -24332,15 +24340,18 @@ var LeaderElection = class {
24332
24340
  if (announce.term < this.currentTerm) {
24333
24341
  return;
24334
24342
  }
24335
- if (announce.term > this.currentTerm) {
24336
- this.currentTerm = announce.term;
24337
- }
24338
- this.leader = false;
24339
- this.nodeRegistry.setLeader(announce.leaderId, announce.term);
24340
- this.eventBus.emit("election.complete", {
24341
- leaderId: announce.leaderId,
24342
- term: announce.term
24343
- });
24343
+ this.adoptLeader(announce);
24344
+ }
24345
+ /**
24346
+ * Explicit cluster joins are operator-driven reconfiguration, not normal
24347
+ * election traffic. Force the local node to follow the joined leader even
24348
+ * if this node previously incremented its term while detached.
24349
+ */
24350
+ adoptLeaderFromJoin(announce) {
24351
+ this.currentTerm = announce.term;
24352
+ this.votedForTerm = announce.term;
24353
+ this.votedForCandidate = announce.leaderId;
24354
+ this.adoptLeader(announce);
24344
24355
  }
24345
24356
  // ── Leader Lease ─────────────────────────────────────────────────────
24346
24357
  isLeader() {
@@ -24352,6 +24363,18 @@ var LeaderElection = class {
24352
24363
  stepDown() {
24353
24364
  this.leader = false;
24354
24365
  }
24366
+ /**
24367
+ * Adopt local leadership without running an election.
24368
+ * Used when a follower explicitly detaches from a leader and becomes
24369
+ * a solo node immediately.
24370
+ */
24371
+ assumeLeadership() {
24372
+ const self = this.nodeRegistry.getSelf();
24373
+ this.currentTerm = Math.max(this.currentTerm, 1);
24374
+ this.votedForTerm = this.currentTerm;
24375
+ this.votedForCandidate = self.id;
24376
+ this.becomeLeader();
24377
+ }
24355
24378
  /** Manually promote this node to leader. */
24356
24379
  async promote() {
24357
24380
  const self = this.nodeRegistry.getSelf();
@@ -24389,6 +24412,15 @@ var LeaderElection = class {
24389
24412
  term: this.currentTerm
24390
24413
  });
24391
24414
  }
24415
+ adoptLeader(announce) {
24416
+ this.currentTerm = announce.term;
24417
+ this.leader = false;
24418
+ this.nodeRegistry.setLeader(announce.leaderId, announce.term);
24419
+ this.eventBus.emit("election.complete", {
24420
+ leaderId: announce.leaderId,
24421
+ term: announce.term
24422
+ });
24423
+ }
24392
24424
  async broadcastAnnounce(leaderId, term) {
24393
24425
  const self = this.nodeRegistry.getSelf();
24394
24426
  const onlineNodes = this.nodeRegistry.getOnlineNodes().filter(
@@ -24990,6 +25022,24 @@ function listWorkDirFolders(workDir) {
24990
25022
 
24991
25023
  // ../../packages/core/src/heartbeat.ts
24992
25024
  var MISSED_HEARTBEAT_THRESHOLD = 3;
25025
+ var DEFAULT_KEEPALIVE_INTERVAL_MS = 3e3;
25026
+ var HEARTBEAT_SUCCESS_LOG_INTERVAL_MS = 6e4;
25027
+ var HEARTBEAT_FAILURE_LOG_INTERVAL_MS = 1e4;
25028
+ function formatError(error) {
25029
+ return error instanceof Error ? error.message : String(error);
25030
+ }
25031
+ function truncateForLog(value, max = 240) {
25032
+ return !value || value.length <= max ? value : `${value.slice(0, max - 3)}...`;
25033
+ }
25034
+ function describeControlRequest(request) {
25035
+ return {
25036
+ requestId: request.requestId,
25037
+ kind: request.kind,
25038
+ taskId: request.taskId,
25039
+ path: request.path,
25040
+ enabled: request.enabled
25041
+ };
25042
+ }
24993
25043
  var HeartbeatMonitor = class {
24994
25044
  constructor(nodeRegistry, election, eventBus, config, logger) {
24995
25045
  this.nodeRegistry = nodeRegistry;
@@ -25042,6 +25092,8 @@ var HeartbeatMonitor = class {
25042
25092
  pendingControlResponses = /* @__PURE__ */ new Map();
25043
25093
  /** Follower-side callback for executing keepalive-delivered control requests. */
25044
25094
  controlRequestHandler;
25095
+ /** Throttle repetitive heartbeat / keepalive logs while preserving periodic visibility. */
25096
+ throttledLogs = /* @__PURE__ */ new Map();
25045
25097
  /** Set the callback used to query tasks assigned to a follower for pull-based dispatch. */
25046
25098
  setGetAssignedTasks(fn) {
25047
25099
  this.getAssignedTasks = fn;
@@ -25082,10 +25134,9 @@ var HeartbeatMonitor = class {
25082
25134
  const queued = this.queuedControlRequests.get(nodeId) ?? [];
25083
25135
  queued.push(fullRequest);
25084
25136
  this.queuedControlRequests.set(nodeId, queued);
25085
- this.log.info("queued control request for keepalive delivery", {
25137
+ this.log.info("queued keepalive control request", {
25086
25138
  nodeId,
25087
- requestId: fullRequest.requestId,
25088
- kind: fullRequest.kind
25139
+ ...describeControlRequest(fullRequest)
25089
25140
  });
25090
25141
  return new Promise((resolve10, reject) => {
25091
25142
  const timeoutId = setTimeout(() => {
@@ -25094,6 +25145,7 @@ var HeartbeatMonitor = class {
25094
25145
  }, timeoutMs);
25095
25146
  this.pendingControlResponses.set(fullRequest.requestId, {
25096
25147
  nodeId,
25148
+ kind: fullRequest.kind,
25097
25149
  resolve: resolve10,
25098
25150
  reject,
25099
25151
  timeoutId
@@ -25111,6 +25163,13 @@ var HeartbeatMonitor = class {
25111
25163
  }
25112
25164
  clearTimeout(pending.timeoutId);
25113
25165
  this.pendingControlResponses.delete(response.requestId);
25166
+ this.log.info("received keepalive control response", {
25167
+ requestId: response.requestId,
25168
+ nodeId: pending.nodeId,
25169
+ kind: pending.kind,
25170
+ ok: response.ok,
25171
+ statusCode: response.statusCode
25172
+ });
25114
25173
  pending.resolve(response);
25115
25174
  return true;
25116
25175
  }
@@ -25167,10 +25226,46 @@ var HeartbeatMonitor = class {
25167
25226
  };
25168
25227
  const promises2 = followers.map(async (node) => {
25169
25228
  try {
25170
- const { response } = await fetchNodeWithFallback(node, "/api/worker/heartbeat", {
25229
+ const { endpoint, response } = await fetchNodeWithFallback(node, "/api/worker/heartbeat", {
25171
25230
  method: "POST",
25172
25231
  headers: { "Content-Type": "application/json" },
25173
25232
  body: JSON.stringify(request)
25233
+ }, void 0, {
25234
+ onAttempt: ({ attempt, endpoint: targetEndpoint, totalEndpoints, timeoutMs }) => {
25235
+ this.log.debug("sending heartbeat request", {
25236
+ nodeId: node.id,
25237
+ endpoint: targetEndpoint,
25238
+ attempt,
25239
+ totalEndpoints,
25240
+ timeoutMs,
25241
+ term: request.term
25242
+ });
25243
+ },
25244
+ onError: ({ attempt, endpoint: targetEndpoint, error, totalEndpoints }) => {
25245
+ this.logWithThrottle(
25246
+ "warn",
25247
+ `heartbeat-attempt-failure:${node.id}`,
25248
+ HEARTBEAT_FAILURE_LOG_INTERVAL_MS,
25249
+ "heartbeat request attempt failed",
25250
+ {
25251
+ nodeId: node.id,
25252
+ endpoint: targetEndpoint,
25253
+ attempt,
25254
+ totalEndpoints,
25255
+ error: formatError(error)
25256
+ }
25257
+ );
25258
+ },
25259
+ onResponse: ({ attempt, endpoint: targetEndpoint, response: heartbeatResponse, totalEndpoints }) => {
25260
+ this.log.debug("heartbeat response received", {
25261
+ nodeId: node.id,
25262
+ endpoint: targetEndpoint,
25263
+ attempt,
25264
+ totalEndpoints,
25265
+ ok: heartbeatResponse.ok,
25266
+ statusCode: heartbeatResponse.status
25267
+ });
25268
+ }
25174
25269
  });
25175
25270
  if (response.ok) {
25176
25271
  const result = await response.json();
@@ -25187,11 +25282,33 @@ var HeartbeatMonitor = class {
25187
25282
  if (result.status !== node.status) {
25188
25283
  this.nodeRegistry.updateStatus(node.id, result.status);
25189
25284
  }
25285
+ this.logWithThrottle(
25286
+ "info",
25287
+ `heartbeat-success:${node.id}`,
25288
+ HEARTBEAT_SUCCESS_LOG_INTERVAL_MS,
25289
+ "heartbeat response applied",
25290
+ {
25291
+ nodeId: node.id,
25292
+ endpoint,
25293
+ reportedStatus: result.status,
25294
+ load: result.load,
25295
+ workDirFolders: result.workDirFolders?.length ?? 0
25296
+ }
25297
+ );
25190
25298
  return;
25191
25299
  }
25192
- this.recordFollowerMiss(node);
25193
- } catch {
25194
- this.recordFollowerMiss(node);
25300
+ const body = truncateForLog(await response.text().catch(() => void 0));
25301
+ this.recordFollowerMiss(node, {
25302
+ endpoint,
25303
+ reason: "non-ok heartbeat response",
25304
+ statusCode: response.status,
25305
+ body
25306
+ });
25307
+ } catch (error) {
25308
+ this.recordFollowerMiss(node, {
25309
+ reason: "heartbeat request threw",
25310
+ error: formatError(error)
25311
+ });
25195
25312
  }
25196
25313
  });
25197
25314
  await Promise.all(promises2);
@@ -25215,7 +25332,7 @@ var HeartbeatMonitor = class {
25215
25332
  term: req.term,
25216
25333
  nodeCount: req.clusterState.nodes.length
25217
25334
  });
25218
- return {
25335
+ const response = {
25219
25336
  nodeId: self.id,
25220
25337
  status: self.status,
25221
25338
  runningTasks: [],
@@ -25227,6 +25344,7 @@ var HeartbeatMonitor = class {
25227
25344
  previewOrigin: self.previewOrigin,
25228
25345
  workDirFolders: self.workDir ? listWorkDirFolders(self.workDir) : void 0
25229
25346
  };
25347
+ return response;
25230
25348
  }
25231
25349
  // ── Leader: handle keepalive from follower ──────────────────────────
25232
25350
  handleKeepalive(req) {
@@ -25250,7 +25368,6 @@ var HeartbeatMonitor = class {
25250
25368
  const normalizedEndpoint = endpoint ?? existingNode?.endpoint ?? devtunnelEndpoint ?? "";
25251
25369
  const normalizedCapabilities = capabilities ?? existingNode?.capabilities ?? [];
25252
25370
  const currentCapabilities = existingNode?.capabilities ?? [];
25253
- this.log.debug("keepalive received", { nodeId, status });
25254
25371
  this.followerMissedMap.set(nodeId, 0);
25255
25372
  let node = existingNode;
25256
25373
  const now = Date.now();
@@ -25317,7 +25434,16 @@ var HeartbeatMonitor = class {
25317
25434
  if (controlRequests.length > 0) {
25318
25435
  this.queuedControlRequests.delete(nodeId);
25319
25436
  }
25320
- return {
25437
+ if (dispatchedTasks.length > 0 || followUpMessages.length > 0 || controlRequests.length > 0) {
25438
+ this.log.info("keepalive delivering queued work", {
25439
+ nodeId,
25440
+ dispatchedTaskCount: dispatchedTasks.length,
25441
+ followUpCount: followUpMessages.length,
25442
+ controlRequestCount: controlRequests.length,
25443
+ controlRequestKinds: controlRequests.map((request) => request.kind)
25444
+ });
25445
+ }
25446
+ const response = {
25321
25447
  leaderId: self.id,
25322
25448
  term: this.election.getCurrentTerm(),
25323
25449
  timestamp: Date.now(),
@@ -25329,6 +25455,7 @@ var HeartbeatMonitor = class {
25329
25455
  followUpMessages: followUpMessages.length > 0 ? followUpMessages : void 0,
25330
25456
  controlRequests: controlRequests.length > 0 ? controlRequests : void 0
25331
25457
  };
25458
+ return response;
25332
25459
  }
25333
25460
  // ── Follower: send keepalive to leader ──────────────────────────────
25334
25461
  async sendKeepalive() {
@@ -25353,6 +25480,13 @@ var HeartbeatMonitor = class {
25353
25480
  previewOrigin: self.previewOrigin,
25354
25481
  workDirFolders: self.workDir ? listWorkDirFolders(self.workDir) : void 0
25355
25482
  };
25483
+ this.log.debug("sending keepalive request", {
25484
+ nodeId: self.id,
25485
+ leaderEndpoint,
25486
+ status: self.status,
25487
+ workDirFolders: request.workDirFolders?.length ?? 0,
25488
+ devtunnelEnabled: request.devtunnelEnabled
25489
+ });
25356
25490
  try {
25357
25491
  const response = await fetch(`${leaderEndpoint}/api/worker/keepalive`, applyRequestAuthHeaders({
25358
25492
  method: "POST",
@@ -25361,7 +25495,22 @@ var HeartbeatMonitor = class {
25361
25495
  }));
25362
25496
  if (response.ok) {
25363
25497
  const result = await response.json();
25364
- this.log.debug("keepalive ok", { nodes: result.clusterState.nodes.length });
25498
+ const hasQueuedWork = (result.dispatchedTasks?.length ?? 0) > 0 || (result.followUpMessages?.length ?? 0) > 0 || (result.controlRequests?.length ?? 0) > 0;
25499
+ this.logWithThrottle(
25500
+ "info",
25501
+ `keepalive-success:${leaderEndpoint}`,
25502
+ HEARTBEAT_SUCCESS_LOG_INTERVAL_MS,
25503
+ "keepalive response received",
25504
+ {
25505
+ leaderId: result.leaderId,
25506
+ term: result.term,
25507
+ nodeCount: result.clusterState.nodes.length,
25508
+ dispatchedTaskCount: result.dispatchedTasks?.length ?? 0,
25509
+ followUpCount: result.followUpMessages?.length ?? 0,
25510
+ controlRequestCount: result.controlRequests?.length ?? 0
25511
+ },
25512
+ hasQueuedWork
25513
+ );
25365
25514
  this.lastHeartbeatTime = Date.now();
25366
25515
  this.missedCount = 0;
25367
25516
  this.nodeRegistry.setLeader(result.leaderId, result.term);
@@ -25386,15 +25535,39 @@ var HeartbeatMonitor = class {
25386
25535
  }
25387
25536
  }
25388
25537
  if (result.controlRequests && result.controlRequests.length > 0) {
25538
+ this.log.info("processing keepalive control requests", {
25539
+ leaderEndpoint,
25540
+ controlRequestCount: result.controlRequests.length,
25541
+ controlRequestKinds: result.controlRequests.map((request2) => request2.kind)
25542
+ });
25389
25543
  await Promise.all(result.controlRequests.map(
25390
25544
  (request2) => this.processControlRequest(leaderEndpoint, request2)
25391
25545
  ));
25392
25546
  }
25393
25547
  } else {
25394
- this.log.warn("keepalive failed", { status: response.status, leaderEndpoint });
25548
+ this.logWithThrottle(
25549
+ "warn",
25550
+ `keepalive-failure:${leaderEndpoint}`,
25551
+ HEARTBEAT_FAILURE_LOG_INTERVAL_MS,
25552
+ "keepalive request returned non-ok response",
25553
+ {
25554
+ leaderEndpoint,
25555
+ statusCode: response.status,
25556
+ body: truncateForLog(await response.text().catch(() => void 0))
25557
+ }
25558
+ );
25395
25559
  }
25396
25560
  } catch (err) {
25397
- this.log.warn("keepalive error", { error: err instanceof Error ? err.message : String(err) });
25561
+ this.logWithThrottle(
25562
+ "warn",
25563
+ `keepalive-failure:${leaderEndpoint}`,
25564
+ HEARTBEAT_FAILURE_LOG_INTERVAL_MS,
25565
+ "keepalive request failed",
25566
+ {
25567
+ leaderEndpoint,
25568
+ error: formatError(err)
25569
+ }
25570
+ );
25398
25571
  }
25399
25572
  }
25400
25573
  // ── Accessors ───────────────────────────────────────────────────────
@@ -25407,13 +25580,30 @@ var HeartbeatMonitor = class {
25407
25580
  canPushToNode(nodeId) {
25408
25581
  const node = this.nodeRegistry.getNode(nodeId);
25409
25582
  if (!node) return false;
25410
- if (node.devtunnelEndpoint) return true;
25411
- return this.followerEverReachedMap.get(nodeId) ?? false;
25583
+ if (node.status === "offline") return false;
25584
+ return !!(node.devtunnelEndpoint || node.endpoint);
25412
25585
  }
25413
25586
  // ── Private helpers ─────────────────────────────────────────────────
25414
25587
  startLeaderMode() {
25415
25588
  this.heartbeatTimer = setInterval(() => {
25416
- void this.sendHeartbeat();
25589
+ const self = this.nodeRegistry.getSelf();
25590
+ if (self.workDir) {
25591
+ this.nodeRegistry.updateFolders(self.id, listWorkDirFolders(self.workDir));
25592
+ }
25593
+ const now = Date.now();
25594
+ const followers = this.nodeRegistry.getAllNodes().filter((node) => node.id !== self.id);
25595
+ for (const node of followers) {
25596
+ if (node.status === "offline") continue;
25597
+ const elapsed = now - node.lastHeartbeat;
25598
+ if (elapsed >= this.config.electionTimeout) {
25599
+ this.log.warn("marking node offline from stale follower keepalive", {
25600
+ nodeId: node.id,
25601
+ lastHeartbeat: node.lastHeartbeat,
25602
+ elapsed
25603
+ });
25604
+ this.nodeRegistry.updateStatus(node.id, "offline");
25605
+ }
25606
+ }
25417
25607
  }, this.config.heartbeatInterval);
25418
25608
  }
25419
25609
  startFollowerMode() {
@@ -25424,9 +25614,10 @@ var HeartbeatMonitor = class {
25424
25614
  this.startKeepalive();
25425
25615
  }
25426
25616
  startKeepalive() {
25617
+ const keepaliveInterval = Math.min(this.config.heartbeatInterval, DEFAULT_KEEPALIVE_INTERVAL_MS);
25427
25618
  this.keepaliveTimer = setInterval(() => {
25428
25619
  void this.sendKeepalive();
25429
- }, this.config.heartbeatInterval);
25620
+ }, keepaliveInterval);
25430
25621
  }
25431
25622
  resetFollowerTimer() {
25432
25623
  if (this.followerTimer !== null) {
@@ -25447,10 +25638,27 @@ var HeartbeatMonitor = class {
25447
25638
  }
25448
25639
  }, this.config.electionTimeout);
25449
25640
  }
25450
- recordFollowerMiss(node) {
25641
+ recordFollowerMiss(node, details) {
25451
25642
  const current = this.followerMissedMap.get(node.id) ?? 0;
25452
25643
  const newCount = current + 1;
25453
25644
  this.followerMissedMap.set(node.id, newCount);
25645
+ const everReached = this.followerEverReachedMap.get(node.id) ?? false;
25646
+ this.logWithThrottle(
25647
+ "warn",
25648
+ `heartbeat-miss:${node.id}`,
25649
+ HEARTBEAT_FAILURE_LOG_INTERVAL_MS,
25650
+ "recorded heartbeat miss",
25651
+ {
25652
+ nodeId: node.id,
25653
+ misses: newCount,
25654
+ everReached,
25655
+ endpoint: details.endpoint,
25656
+ statusCode: details.statusCode,
25657
+ reason: details.reason,
25658
+ error: details.error,
25659
+ body: details.body
25660
+ }
25661
+ );
25454
25662
  if (newCount >= MISSED_HEARTBEAT_THRESHOLD) {
25455
25663
  const freshNode = this.nodeRegistry.getNode(node.id);
25456
25664
  if (freshNode) {
@@ -25459,13 +25667,20 @@ var HeartbeatMonitor = class {
25459
25667
  this.log.debug("miss threshold but lastHeartbeat recent \u2014 skip offline", {
25460
25668
  nodeId: node.id,
25461
25669
  elapsed,
25462
- everReached: this.followerEverReachedMap.get(node.id) ?? false
25670
+ everReached
25463
25671
  });
25464
25672
  return;
25465
25673
  }
25466
25674
  }
25467
- const everReached = this.followerEverReachedMap.get(node.id) ?? false;
25468
- this.log.warn("marking node offline", { nodeId: node.id, everReached, misses: newCount });
25675
+ this.log.warn("marking node offline", {
25676
+ nodeId: node.id,
25677
+ everReached,
25678
+ misses: newCount,
25679
+ endpoint: details.endpoint,
25680
+ statusCode: details.statusCode,
25681
+ reason: details.reason,
25682
+ error: details.error
25683
+ });
25469
25684
  this.nodeRegistry.updateStatus(node.id, "offline");
25470
25685
  }
25471
25686
  }
@@ -25493,6 +25708,10 @@ var HeartbeatMonitor = class {
25493
25708
  }
25494
25709
  }
25495
25710
  async processControlRequest(leaderEndpoint, request) {
25711
+ this.log.info("processing keepalive control request", {
25712
+ leaderEndpoint,
25713
+ ...describeControlRequest(request)
25714
+ });
25496
25715
  let response;
25497
25716
  if (!this.controlRequestHandler) {
25498
25717
  response = {
@@ -25527,6 +25746,12 @@ var HeartbeatMonitor = class {
25527
25746
  };
25528
25747
  }
25529
25748
  }
25749
+ this.log.info("completed keepalive control request", {
25750
+ leaderEndpoint,
25751
+ ...describeControlRequest(request),
25752
+ ok: response.ok,
25753
+ statusCode: response.statusCode
25754
+ });
25530
25755
  try {
25531
25756
  const res = await fetch(`${leaderEndpoint}/api/worker/control-response`, applyRequestAuthHeaders({
25532
25757
  method: "POST",
@@ -25550,6 +25775,21 @@ var HeartbeatMonitor = class {
25550
25775
  });
25551
25776
  }
25552
25777
  }
25778
+ logWithThrottle(level, key, intervalMs, message, data, force = false) {
25779
+ const now = Date.now();
25780
+ const existing = this.throttledLogs.get(key);
25781
+ if (existing && !force && now - existing.lastLoggedAt < intervalMs) {
25782
+ existing.suppressedCount += 1;
25783
+ return;
25784
+ }
25785
+ const mergedData = existing && existing.suppressedCount > 0 ? { ...data, suppressedCount: existing.suppressedCount } : data;
25786
+ this.throttledLogs.set(key, { lastLoggedAt: now, suppressedCount: 0 });
25787
+ if (level === "info") {
25788
+ this.log.info(message, mergedData);
25789
+ return;
25790
+ }
25791
+ this.log.warn(message, mergedData);
25792
+ }
25553
25793
  };
25554
25794
 
25555
25795
  // ../../packages/core/src/data-router.ts
@@ -25577,9 +25817,9 @@ var DataRouter = class {
25577
25817
  /**
25578
25818
  * Returns true if the request should be proxied to the leader.
25579
25819
  */
25580
- shouldProxy(method, path16) {
25820
+ shouldProxy(method, path17) {
25581
25821
  if (this.election.isLeader()) return false;
25582
- if (this.isExcludedPath(path16)) return false;
25822
+ if (this.isExcludedPath(path17)) return false;
25583
25823
  if (method.toUpperCase() === "GET") return false;
25584
25824
  return WRITE_METHODS.has(method.toUpperCase());
25585
25825
  }
@@ -25680,8 +25920,8 @@ var DataRouter = class {
25680
25920
  };
25681
25921
  }
25682
25922
  // ── Helpers ───────────────────────────────────────────────────────────
25683
- isExcludedPath(path16) {
25684
- return EXCLUDED_PATH_PREFIXES.some((prefix) => path16.startsWith(prefix)) || EXCLUDED_PATH_SUFFIXES.some((suffix) => path16.endsWith(suffix));
25923
+ isExcludedPath(path17) {
25924
+ return EXCLUDED_PATH_PREFIXES.some((prefix) => path17.startsWith(prefix)) || EXCLUDED_PATH_SUFFIXES.some((suffix) => path17.endsWith(suffix));
25685
25925
  }
25686
25926
  };
25687
25927
 
@@ -26693,23 +26933,53 @@ ${joinErrors.map((e) => ` - ${e}`).join("\n")}`
26693
26933
  this.started = true;
26694
26934
  }
26695
26935
  async joinCluster(seedUrl) {
26936
+ const log = this.logger.child("node/join-cluster");
26937
+ const tasks = this.taskEngine.listTasks().tasks;
26696
26938
  const joinBody = {
26697
26939
  id: this.selfInfo.id,
26698
26940
  endpoint: this.selfInfo.endpoint,
26699
26941
  name: this.config.node.name,
26700
26942
  capabilities: [],
26701
- tasks: this.taskEngine.listTasks().tasks
26943
+ tasks
26702
26944
  };
26703
26945
  if (this.selfInfo.devtunnelEndpoint) {
26704
26946
  joinBody.devtunnelEndpoint = this.selfInfo.devtunnelEndpoint;
26705
26947
  }
26706
- const response = await fetch(`${seedUrl}/api/cluster/join`, applyRequestAuthHeaders({
26707
- method: "POST",
26708
- headers: { "Content-Type": "application/json" },
26709
- body: JSON.stringify(joinBody)
26710
- }));
26948
+ log.info("sending cluster join request", {
26949
+ seedUrl,
26950
+ nodeId: this.selfInfo.id,
26951
+ endpoint: this.selfInfo.endpoint,
26952
+ taskCount: tasks.length,
26953
+ hasDevTunnel: !!this.selfInfo.devtunnelEndpoint
26954
+ });
26955
+ let response;
26956
+ try {
26957
+ response = await fetch(`${seedUrl}/api/cluster/join`, applyRequestAuthHeaders({
26958
+ method: "POST",
26959
+ headers: { "Content-Type": "application/json" },
26960
+ body: JSON.stringify(joinBody)
26961
+ }));
26962
+ } catch (error) {
26963
+ log.warn("cluster join request failed", {
26964
+ seedUrl,
26965
+ error: error instanceof Error ? error.message : String(error)
26966
+ });
26967
+ throw error;
26968
+ }
26969
+ log.info("received cluster join response", {
26970
+ seedUrl,
26971
+ ok: response.ok,
26972
+ statusCode: response.status
26973
+ });
26711
26974
  if (!response.ok) {
26712
26975
  const body = await response.json?.().catch(() => null);
26976
+ log.warn("cluster join request rejected", {
26977
+ seedUrl,
26978
+ statusCode: response.status,
26979
+ code: body?.error?.code,
26980
+ message: body?.error?.message,
26981
+ leaderEndpoint: body?.error?.leaderEndpoint
26982
+ });
26713
26983
  if (body?.error?.code === "NOT_LEADER") {
26714
26984
  const leader = body.error.leaderEndpoint;
26715
26985
  throw new Error(
@@ -26719,22 +26989,51 @@ ${joinErrors.map((e) => ` - ${e}`).join("\n")}`
26719
26989
  throw new Error(`Failed to join cluster via ${seedUrl}: ${response.status}`);
26720
26990
  }
26721
26991
  const data = await response.json();
26992
+ log.info("applied joined cluster state", {
26993
+ seedUrl,
26994
+ leaderId: data.leaderId,
26995
+ term: data.term,
26996
+ nodeCount: data.nodes.length
26997
+ });
26722
26998
  this.applyClusterState(data.nodes, data.leaderId, data.term);
26723
26999
  }
26724
27000
  async leaveCluster() {
27001
+ const log = this.logger.child("node/leave-cluster");
26725
27002
  const leader = this.nodeRegistry.getLeader();
26726
27003
  const leaderEndpoint = this.nodeRegistry.getLeaderEndpoint();
26727
27004
  const self = this.nodeRegistry.getSelf();
26728
27005
  if (!leader || !leaderEndpoint || leader.id === self.id) {
27006
+ log.info("skipping cluster leave request", {
27007
+ hasLeader: !!leader,
27008
+ leaderEndpoint,
27009
+ selfId: self.id
27010
+ });
26729
27011
  return;
26730
27012
  }
26731
27013
  try {
26732
- await fetch(`${leaderEndpoint}/api/cluster/leave`, applyRequestAuthHeaders({
27014
+ log.info("sending cluster leave request", {
27015
+ leaderId: leader.id,
27016
+ leaderEndpoint,
27017
+ nodeId: self.id
27018
+ });
27019
+ const response = await fetch(`${leaderEndpoint}/api/cluster/leave`, applyRequestAuthHeaders({
26733
27020
  method: "POST",
26734
27021
  headers: { "Content-Type": "application/json" },
26735
27022
  body: JSON.stringify({ nodeId: self.id })
26736
27023
  }));
26737
- } catch {
27024
+ log.info("received cluster leave response", {
27025
+ leaderId: leader.id,
27026
+ leaderEndpoint,
27027
+ ok: response.ok,
27028
+ statusCode: response.status
27029
+ });
27030
+ } catch (error) {
27031
+ log.warn("cluster leave request failed", {
27032
+ leaderId: leader.id,
27033
+ leaderEndpoint,
27034
+ nodeId: self.id,
27035
+ error: error instanceof Error ? error.message : String(error)
27036
+ });
26738
27037
  }
26739
27038
  const refreshedSelf = {
26740
27039
  ...self,
@@ -26747,8 +27046,13 @@ ${joinErrors.map((e) => ` - ${e}`).join("\n")}`
26747
27046
  this.nodeRegistry.setSelf(refreshedSelf);
26748
27047
  this.nodeRegistry.syncFromHeartbeat([refreshedSelf]);
26749
27048
  this.nodeRegistry.clearLeader();
26750
- this.election.stepDown();
26751
- await this.election.startElection();
27049
+ this.election.assumeLeadership();
27050
+ this.syncSelfInfoFromRegistry();
27051
+ log.info("detached from leader and assumed solo leadership", {
27052
+ previousLeaderId: leader.id,
27053
+ nodeId: refreshedSelf.id,
27054
+ term: this.election.getCurrentTerm()
27055
+ });
26752
27056
  }
26753
27057
  async stop() {
26754
27058
  if (!this.started) return;
@@ -26758,13 +27062,26 @@ ${joinErrors.map((e) => ` - ${e}`).join("\n")}`
26758
27062
  try {
26759
27063
  const leaderEndpoint = this.nodeRegistry.getLeaderEndpoint();
26760
27064
  if (leaderEndpoint) {
26761
- await fetch(`${leaderEndpoint}/api/cluster/leave`, applyRequestAuthHeaders({
27065
+ this.logger.info("sending shutdown cluster leave request", {
27066
+ leaderEndpoint,
27067
+ nodeId: this.selfInfo.id
27068
+ });
27069
+ const response = await fetch(`${leaderEndpoint}/api/cluster/leave`, applyRequestAuthHeaders({
26762
27070
  method: "POST",
26763
27071
  headers: { "Content-Type": "application/json" },
26764
27072
  body: JSON.stringify({ nodeId: this.selfInfo.id })
26765
27073
  }));
27074
+ this.logger.info("received shutdown cluster leave response", {
27075
+ leaderEndpoint,
27076
+ ok: response.ok,
27077
+ statusCode: response.status
27078
+ });
26766
27079
  }
26767
- } catch {
27080
+ } catch (error) {
27081
+ this.logger.warn("shutdown cluster leave request failed", {
27082
+ nodeId: this.selfInfo.id,
27083
+ error: error instanceof Error ? error.message : String(error)
27084
+ });
26768
27085
  }
26769
27086
  if (this.devtunnelTransport) {
26770
27087
  await this.devtunnelTransport.stop();
@@ -26925,7 +27242,7 @@ ${joinErrors.map((e) => ` - ${e}`).join("\n")}`
26925
27242
  this.selfInfo = nextSelf;
26926
27243
  this.nodeRegistry.setSelf(nextSelf);
26927
27244
  this.nodeRegistry.syncFromHeartbeat(nodes);
26928
- this.election.handleLeaderAnnounce({ leaderId, term });
27245
+ this.election.adoptLeaderFromJoin({ leaderId, term });
26929
27246
  }
26930
27247
  cloneTransportConfig(config) {
26931
27248
  return {
@@ -27564,8 +27881,8 @@ function getErrorMap() {
27564
27881
 
27565
27882
  // ../../node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/helpers/parseUtil.js
27566
27883
  var makeIssue = (params) => {
27567
- const { data, path: path16, errorMaps, issueData } = params;
27568
- const fullPath = [...path16, ...issueData.path || []];
27884
+ const { data, path: path17, errorMaps, issueData } = params;
27885
+ const fullPath = [...path17, ...issueData.path || []];
27569
27886
  const fullIssue = {
27570
27887
  ...issueData,
27571
27888
  path: fullPath
@@ -27681,11 +27998,11 @@ var errorUtil;
27681
27998
 
27682
27999
  // ../../node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/types.js
27683
28000
  var ParseInputLazyPath = class {
27684
- constructor(parent, value, path16, key) {
28001
+ constructor(parent, value, path17, key) {
27685
28002
  this._cachedPath = [];
27686
28003
  this.parent = parent;
27687
28004
  this.data = value;
27688
- this._path = path16;
28005
+ this._path = path17;
27689
28006
  this._key = key;
27690
28007
  }
27691
28008
  get path() {
@@ -31560,19 +31877,31 @@ function createErrorMiddleware() {
31560
31877
  // ../../packages/api/src/routes/cluster.ts
31561
31878
  var import_express = __toESM(require_express2(), 1);
31562
31879
  var CONNECTIVITY_TIMEOUT_MS = 8e3;
31563
- async function verifyHealth(endpoint) {
31880
+ function formatError2(error) {
31881
+ return error instanceof Error ? error.message : String(error);
31882
+ }
31883
+ async function verifyHealth(endpoint, log) {
31564
31884
  const controller = new AbortController();
31565
31885
  const timer = setTimeout(() => controller.abort(), CONNECTIVITY_TIMEOUT_MS);
31886
+ const url = `${endpoint}/api/system/health`;
31566
31887
  try {
31567
- const response = await fetch(`${endpoint}/api/system/health`, {
31888
+ log.info("sending leader health check", { endpoint, url, timeoutMs: CONNECTIVITY_TIMEOUT_MS });
31889
+ const response = await fetch(url, {
31568
31890
  signal: controller.signal
31569
31891
  });
31892
+ log.info("received leader health check response", {
31893
+ endpoint,
31894
+ url,
31895
+ ok: response.ok,
31896
+ statusCode: response.status
31897
+ });
31570
31898
  if (!response.ok) {
31571
31899
  throw new Error(`health check returned ${response.status}`);
31572
31900
  }
31573
31901
  } catch (err) {
31902
+ log.warn("leader health check failed", { endpoint, url, error: formatError2(err) });
31574
31903
  throw new Error(
31575
- `Failed to verify connectivity for ${endpoint}: ${err instanceof Error ? err.message : String(err)}`
31904
+ `Failed to verify connectivity for ${endpoint}: ${formatError2(err)}`
31576
31905
  );
31577
31906
  } finally {
31578
31907
  clearTimeout(timer);
@@ -31584,9 +31913,11 @@ function asyncHandler(fn) {
31584
31913
  function createClusterRoutes() {
31585
31914
  const router = (0, import_express.Router)();
31586
31915
  router.post("/join", asyncHandler(async (req, res) => {
31587
- const { nodeRegistry, election, taskEngine } = req.app.locals.deps;
31916
+ const { nodeRegistry, election, taskEngine, logger: rootLogger } = req.app.locals.deps;
31917
+ const log = rootLogger.child("cluster/join");
31588
31918
  if (!election.isLeader()) {
31589
31919
  const leaderEndpoint = nodeRegistry.getLeaderEndpoint();
31920
+ log.warn("rejected cluster join because node is not leader", { leaderEndpoint });
31590
31921
  res.status(421).json({
31591
31922
  error: {
31592
31923
  code: "NOT_LEADER",
@@ -31597,10 +31928,19 @@ function createClusterRoutes() {
31597
31928
  return;
31598
31929
  }
31599
31930
  const body = JoinClusterBody.parse(req.body);
31931
+ log.info("received cluster join request", {
31932
+ nodeId: body.id,
31933
+ name: body.name,
31934
+ endpoint: body.endpoint,
31935
+ taskCount: body.tasks?.length ?? 0,
31936
+ hasDevTunnel: !!body.devtunnelEndpoint
31937
+ });
31600
31938
  const existingNodes = nodeRegistry.getAllNodes();
31939
+ let removedCount = 0;
31601
31940
  for (const existing of existingNodes) {
31602
31941
  if (existing.id === body.id || existing.endpoint === body.endpoint || existing.name === body.name) {
31603
31942
  nodeRegistry.removeNode(existing.id);
31943
+ removedCount++;
31604
31944
  }
31605
31945
  }
31606
31946
  const node = {
@@ -31616,6 +31956,12 @@ function createClusterRoutes() {
31616
31956
  };
31617
31957
  nodeRegistry.addNode(node);
31618
31958
  taskEngine.importTasks(body.tasks ?? []);
31959
+ log.info("accepted cluster join request", {
31960
+ nodeId: node.id,
31961
+ endpoint: node.endpoint,
31962
+ removedCount,
31963
+ importedTaskCount: body.tasks?.length ?? 0
31964
+ });
31619
31965
  const clusterState = nodeRegistry.getClusterState();
31620
31966
  res.status(201).json({
31621
31967
  clusterId: clusterState.clusterId ?? nanoid(),
@@ -31627,14 +31973,22 @@ function createClusterRoutes() {
31627
31973
  router.post("/connect", asyncHandler(async (req, res) => {
31628
31974
  const {
31629
31975
  nodeRegistry,
31630
- joinCurrentNodeToCluster
31976
+ joinCurrentNodeToCluster,
31977
+ logger: rootLogger
31631
31978
  } = req.app.locals.deps;
31979
+ const log = rootLogger.child("cluster/connect");
31632
31980
  const body = ConnectClusterBody.parse(req.body);
31981
+ log.info("received cluster connect request", {
31982
+ leaderEndpoint: body.leaderEndpoint,
31983
+ transport: body.transport
31984
+ });
31633
31985
  if (!joinCurrentNodeToCluster) {
31634
31986
  throw new MeshyError("VALIDATION_ERROR", "Join flow is not available on this node", 501);
31635
31987
  }
31636
- await verifyHealth(body.leaderEndpoint);
31988
+ await verifyHealth(body.leaderEndpoint, log);
31989
+ log.info("leader health check succeeded", { leaderEndpoint: body.leaderEndpoint });
31637
31990
  await joinCurrentNodeToCluster(body.leaderEndpoint);
31991
+ log.info("completed cluster connect request", { leaderEndpoint: body.leaderEndpoint });
31638
31992
  const self = nodeRegistry.getSelf();
31639
31993
  const clusterState = nodeRegistry.getClusterState();
31640
31994
  res.json({
@@ -31647,17 +32001,22 @@ function createClusterRoutes() {
31647
32001
  });
31648
32002
  }));
31649
32003
  router.post("/leave", asyncHandler(async (req, res) => {
31650
- const { nodeRegistry } = req.app.locals.deps;
32004
+ const { nodeRegistry, logger: rootLogger } = req.app.locals.deps;
32005
+ const log = rootLogger.child("cluster/leave");
31651
32006
  const { nodeId } = req.body;
32007
+ log.info("received cluster leave request", { nodeId });
31652
32008
  nodeRegistry.removeNode(nodeId);
31653
32009
  res.json({ ok: true });
31654
32010
  }));
31655
32011
  router.post("/disconnect", asyncHandler(async (req, res) => {
31656
- const { leaveCurrentCluster, nodeRegistry } = req.app.locals.deps;
32012
+ const { leaveCurrentCluster, nodeRegistry, logger: rootLogger } = req.app.locals.deps;
32013
+ const log = rootLogger.child("cluster/disconnect");
32014
+ log.info("received cluster disconnect request");
31657
32015
  if (!leaveCurrentCluster) {
31658
32016
  throw new MeshyError("VALIDATION_ERROR", "Leave flow is not available on this node", 501);
31659
32017
  }
31660
32018
  await leaveCurrentCluster();
32019
+ log.info("completed cluster disconnect request");
31661
32020
  const self = nodeRegistry.getSelf();
31662
32021
  const clusterState = nodeRegistry.getClusterState();
31663
32022
  res.json({
@@ -32334,7 +32693,16 @@ async function createPreviewSessionPayload(deps, taskId, entryPath) {
32334
32693
  };
32335
32694
  }
32336
32695
  async function executeWorkerControlRequest(deps, request) {
32696
+ const log = deps.logger.child("worker-control");
32697
+ log.info("received worker control request", {
32698
+ requestId: request.requestId,
32699
+ kind: request.kind,
32700
+ taskId: request.taskId,
32701
+ path: request.path,
32702
+ enabled: request.enabled
32703
+ });
32337
32704
  try {
32705
+ let response;
32338
32706
  switch (request.kind) {
32339
32707
  case "devtunnel": {
32340
32708
  assertCanChangeNodeDevTunnel(deps.taskEngine, deps.nodeRegistry.getSelf().id);
@@ -32343,22 +32711,24 @@ async function executeWorkerControlRequest(deps, request) {
32343
32711
  throw new MeshyError("VALIDATION_ERROR", "DevTunnel control not available", 501);
32344
32712
  }
32345
32713
  const devtunnelEndpoint = await deps.enableDevTunnel();
32346
- return jsonResponse(request.requestId, 200, {
32714
+ response = jsonResponse(request.requestId, 200, {
32347
32715
  ok: true,
32348
32716
  enabled: true,
32349
32717
  devtunnelEndpoint,
32350
32718
  dashboardOrigin: deps.nodeRegistry.getSelf().dashboardOrigin
32351
32719
  });
32720
+ break;
32352
32721
  }
32353
32722
  if (!deps.disableDevTunnel) {
32354
32723
  throw new MeshyError("VALIDATION_ERROR", "DevTunnel control not available", 501);
32355
32724
  }
32356
32725
  await deps.disableDevTunnel();
32357
- return jsonResponse(request.requestId, 200, {
32726
+ response = jsonResponse(request.requestId, 200, {
32358
32727
  ok: true,
32359
32728
  enabled: false,
32360
32729
  dashboardOrigin: deps.nodeRegistry.getSelf().dashboardOrigin
32361
32730
  });
32731
+ break;
32362
32732
  }
32363
32733
  case "task-cancel": {
32364
32734
  const taskId = request.taskId ?? "";
@@ -32370,7 +32740,7 @@ async function executeWorkerControlRequest(deps, request) {
32370
32740
  missingOk: true,
32371
32741
  terminalOk: true
32372
32742
  });
32373
- return jsonResponse(request.requestId, 200, {
32743
+ response = jsonResponse(request.requestId, 200, {
32374
32744
  ok: true,
32375
32745
  taskId,
32376
32746
  cancelled: result.cancelled,
@@ -32378,11 +32748,12 @@ async function executeWorkerControlRequest(deps, request) {
32378
32748
  missing: result.missing,
32379
32749
  terminal: result.terminal
32380
32750
  });
32751
+ break;
32381
32752
  }
32382
32753
  case "task-output-summary": {
32383
32754
  const task = getTask(deps.taskEngine, request.taskId ?? "");
32384
32755
  if (!task.effectiveProjectPath) {
32385
- return jsonResponse(request.requestId, 200, {
32756
+ response = jsonResponse(request.requestId, 200, {
32386
32757
  taskId: task.id,
32387
32758
  requestedProject: task.project,
32388
32759
  effectiveProjectPath: null,
@@ -32390,8 +32761,9 @@ async function executeWorkerControlRequest(deps, request) {
32390
32761
  available: false,
32391
32762
  summary: null
32392
32763
  });
32764
+ break;
32393
32765
  }
32394
- return jsonResponse(request.requestId, 200, {
32766
+ response = jsonResponse(request.requestId, 200, {
32395
32767
  taskId: task.id,
32396
32768
  requestedProject: task.project,
32397
32769
  effectiveProjectPath: task.effectiveProjectPath,
@@ -32399,15 +32771,17 @@ async function executeWorkerControlRequest(deps, request) {
32399
32771
  available: true,
32400
32772
  summary: getOutputSummary(task.effectiveProjectPath)
32401
32773
  });
32774
+ break;
32402
32775
  }
32403
32776
  case "task-output-tree": {
32404
32777
  const rootPath = getTaskOutputRoot(deps.taskEngine, request.taskId ?? "");
32405
32778
  try {
32406
- return jsonResponse(request.requestId, 200, {
32779
+ response = jsonResponse(request.requestId, 200, {
32407
32780
  rootPath,
32408
32781
  currentPath: request.path ?? ".",
32409
32782
  entries: listDirectory(rootPath, request.path ?? ".")
32410
32783
  });
32784
+ break;
32411
32785
  } catch (err) {
32412
32786
  if (err instanceof Error && err.message === "Path traversal detected") {
32413
32787
  throw new MeshyError("VALIDATION_ERROR", "Invalid directory path", 400);
@@ -32419,10 +32793,11 @@ async function executeWorkerControlRequest(deps, request) {
32419
32793
  const rootPath = getTaskOutputRoot(deps.taskEngine, request.taskId ?? "");
32420
32794
  try {
32421
32795
  const filePath = request.path ?? "";
32422
- return jsonResponse(request.requestId, 200, {
32796
+ response = jsonResponse(request.requestId, 200, {
32423
32797
  ...readFileContent(rootPath, filePath),
32424
32798
  downloadUrl: `/api/tasks/${request.taskId}/output/download?path=${encodeURIComponent(filePath)}`
32425
32799
  });
32800
+ break;
32426
32801
  } catch (err) {
32427
32802
  if (err instanceof Error && err.message === "Path traversal detected") {
32428
32803
  throw new MeshyError("VALIDATION_ERROR", "Invalid file path", 400);
@@ -32442,11 +32817,12 @@ async function executeWorkerControlRequest(deps, request) {
32442
32817
  }
32443
32818
  const { mimeType } = classifyFile(absolutePath);
32444
32819
  const fileName = path13.basename(absolutePath).replace(/"/g, "");
32445
- return binaryResponse(request.requestId, 200, fs12.readFileSync(absolutePath), {
32820
+ response = binaryResponse(request.requestId, 200, fs12.readFileSync(absolutePath), {
32446
32821
  "Content-Type": mimeType,
32447
32822
  "Content-Disposition": `inline; filename="${fileName}"`,
32448
32823
  "Cache-Control": "no-cache"
32449
32824
  });
32825
+ break;
32450
32826
  } catch (err) {
32451
32827
  if (err instanceof MeshyError) throw err;
32452
32828
  if (err instanceof Error && err.message === "Path traversal detected") {
@@ -32457,18 +32833,41 @@ async function executeWorkerControlRequest(deps, request) {
32457
32833
  }
32458
32834
  case "task-output-diff": {
32459
32835
  const rootPath = getTaskOutputRoot(deps.taskEngine, request.taskId ?? "");
32460
- return jsonResponse(request.requestId, 200, getGitDiff(rootPath));
32836
+ response = jsonResponse(request.requestId, 200, getGitDiff(rootPath));
32837
+ break;
32461
32838
  }
32462
32839
  case "task-preview-session": {
32463
- return jsonResponse(
32840
+ response = jsonResponse(
32464
32841
  request.requestId,
32465
32842
  200,
32466
32843
  await createPreviewSessionPayload(deps, request.taskId ?? "", request.path)
32467
32844
  );
32845
+ break;
32468
32846
  }
32469
32847
  }
32848
+ log.info("completed worker control request", {
32849
+ requestId: request.requestId,
32850
+ kind: request.kind,
32851
+ taskId: request.taskId,
32852
+ path: request.path,
32853
+ enabled: request.enabled,
32854
+ ok: response.ok,
32855
+ statusCode: response.statusCode
32856
+ });
32857
+ return response;
32470
32858
  } catch (err) {
32471
- return errorResponse(request.requestId, err);
32859
+ const response = errorResponse(request.requestId, err);
32860
+ log.warn("worker control request failed", {
32861
+ requestId: request.requestId,
32862
+ kind: request.kind,
32863
+ taskId: request.taskId,
32864
+ path: request.path,
32865
+ enabled: request.enabled,
32866
+ ok: response.ok,
32867
+ statusCode: response.statusCode,
32868
+ error: err instanceof Error ? err.message : String(err)
32869
+ });
32870
+ return response;
32472
32871
  }
32473
32872
  }
32474
32873
  function sendWorkerControlResponse(res, response) {
@@ -33543,13 +33942,40 @@ function createWorkerRoutes() {
33543
33942
  res.json({ ok: true });
33544
33943
  }));
33545
33944
  router.post("/heartbeat", asyncHandler4(async (req, res) => {
33546
- const { heartbeat } = req.app.locals.deps;
33547
- const response = heartbeat.handleHeartbeat(req.body);
33945
+ const { heartbeat, logger: rootLogger } = req.app.locals.deps;
33946
+ const log = rootLogger.child("worker/heartbeat");
33947
+ const body = req.body;
33948
+ log.debug("received worker heartbeat API call", {
33949
+ leaderId: body.leaderId,
33950
+ term: body.term,
33951
+ nodeCount: body.clusterState?.nodes?.length ?? 0
33952
+ });
33953
+ const response = heartbeat.handleHeartbeat(body);
33954
+ log.debug("completed worker heartbeat API call", {
33955
+ nodeId: response.nodeId,
33956
+ status: response.status,
33957
+ load: response.load
33958
+ });
33548
33959
  res.json(response);
33549
33960
  }));
33550
33961
  router.post("/keepalive", asyncHandler4(async (req, res) => {
33551
- const { heartbeat } = req.app.locals.deps;
33552
- const response = heartbeat.handleKeepalive(req.body);
33962
+ const { heartbeat, logger: rootLogger } = req.app.locals.deps;
33963
+ const log = rootLogger.child("worker/keepalive");
33964
+ const body = req.body;
33965
+ log.debug("received worker keepalive API call", {
33966
+ nodeId: body.nodeId,
33967
+ status: body.status,
33968
+ endpoint: body.endpoint
33969
+ });
33970
+ const response = heartbeat.handleKeepalive(body);
33971
+ log.debug("completed worker keepalive API call", {
33972
+ leaderId: response.leaderId,
33973
+ term: response.term,
33974
+ nodeCount: response.clusterState.nodes.length,
33975
+ dispatchedTaskCount: response.dispatchedTasks?.length ?? 0,
33976
+ followUpCount: response.followUpMessages?.length ?? 0,
33977
+ controlRequestCount: response.controlRequests?.length ?? 0
33978
+ });
33553
33979
  res.json(response);
33554
33980
  }));
33555
33981
  router.post("/vote", asyncHandler4(async (req, res) => {
@@ -33573,8 +33999,19 @@ function createWorkerRoutes() {
33573
33999
  res.json(payload);
33574
34000
  }));
33575
34001
  router.post("/control-response", asyncHandler4(async (req, res) => {
33576
- const { heartbeat } = req.app.locals.deps;
33577
- const handled = heartbeat.handleWorkerControlResponse?.(req.body) ?? false;
34002
+ const { heartbeat, logger: rootLogger } = req.app.locals.deps;
34003
+ const log = rootLogger.child("worker/control-response");
34004
+ const body = req.body;
34005
+ log.info("received worker control response API call", {
34006
+ requestId: body.requestId,
34007
+ ok: body.ok,
34008
+ statusCode: body.statusCode
34009
+ });
34010
+ const handled = heartbeat.handleWorkerControlResponse?.(body) ?? false;
34011
+ log.info("completed worker control response API call", {
34012
+ requestId: body.requestId,
34013
+ handled
34014
+ });
33578
34015
  res.json({ ok: true, handled });
33579
34016
  }));
33580
34017
  return router;
@@ -33587,7 +34024,13 @@ function asyncHandler5(fn) {
33587
34024
  }
33588
34025
  function createSystemRoutes() {
33589
34026
  const router = (0, import_express5.Router)();
33590
- router.get("/health", asyncHandler5(async (_req, res) => {
34027
+ router.get("/health", asyncHandler5(async (req, res) => {
34028
+ const { logger: rootLogger } = req.app.locals.deps;
34029
+ rootLogger.child("system/health").info("received system health API call", {
34030
+ method: req.method,
34031
+ path: req.originalUrl || req.url,
34032
+ remoteAddress: req.ip || req.socket.remoteAddress
34033
+ });
33591
34034
  res.json({ status: "ok", timestamp: Date.now() });
33592
34035
  }));
33593
34036
  router.get("/info", asyncHandler5(async (req, res) => {
@@ -34149,18 +34592,21 @@ function createTransport(config) {
34149
34592
 
34150
34593
  // src/startup.ts
34151
34594
  var fs16 = __toESM(require("fs"), 1);
34595
+ var path16 = __toESM(require("path"), 1);
34152
34596
  var readline = __toESM(require("readline/promises"), 1);
34597
+ var import_node_child_process6 = require("child_process");
34153
34598
  function getDefaultNodeName() {
34154
34599
  return getDeviceNodeName();
34155
34600
  }
34156
34601
  var DEFAULT_NODE_NAME = getDefaultNodeName();
34157
34602
  var DEFAULT_NODE_PORT = 12345;
34158
34603
  var SUPPORTED_TRANSPORTS = ["direct", "devtunnel", "tailscale"];
34604
+ var STARTUP_REQUIREMENTS = ["az", "devtunnel", "claude", "codex"];
34159
34605
  function createDefaultConfig(storagePath = resolveDefaultStoragePath(), nodeName = DEFAULT_NODE_NAME) {
34160
34606
  return {
34161
34607
  node: { name: nodeName, port: DEFAULT_NODE_PORT },
34162
34608
  transport: { type: "direct" },
34163
- cluster: { seeds: [], heartbeatInterval: 3e3, electionTimeout: 9e3, apiKey: "" },
34609
+ cluster: { seeds: [], heartbeatInterval: 3e3, electionTimeout: 36e3, apiKey: "" },
34164
34610
  storage: { path: storagePath }
34165
34611
  };
34166
34612
  }
@@ -34170,6 +34616,257 @@ function createRuntimeDefaultConfig(fileConfig, options = {}) {
34170
34616
  const nodeName = options.ignorePersistedName ? getDeviceNodeName() : resolveDefaultNodeName(storagePath);
34171
34617
  return createDefaultConfig(storagePath, nodeName);
34172
34618
  }
34619
+ function writeLine(stream, message = "") {
34620
+ stream.write(`${message}
34621
+ `);
34622
+ }
34623
+ function createPromptSession(prompt) {
34624
+ if (prompt) {
34625
+ return {
34626
+ ask: prompt,
34627
+ close: async () => void 0
34628
+ };
34629
+ }
34630
+ if (!shouldPromptForStartOptions()) {
34631
+ return void 0;
34632
+ }
34633
+ const rl = readline.createInterface({
34634
+ input: process.stdin,
34635
+ output: process.stdout
34636
+ });
34637
+ return {
34638
+ ask: (question) => rl.question(question),
34639
+ close: async () => rl.close()
34640
+ };
34641
+ }
34642
+ function createDefaultCommandRunner(platform) {
34643
+ return (command, args, interactive = false) => {
34644
+ const result = (0, import_node_child_process6.spawnSync)(command, args, {
34645
+ encoding: "utf-8",
34646
+ shell: platform === "win32",
34647
+ stdio: interactive ? "inherit" : "pipe"
34648
+ });
34649
+ return {
34650
+ ok: result.status === 0 && !result.error,
34651
+ status: result.status,
34652
+ stdout: typeof result.stdout === "string" ? result.stdout : "",
34653
+ stderr: typeof result.stderr === "string" ? result.stderr : "",
34654
+ error: result.error?.message
34655
+ };
34656
+ };
34657
+ }
34658
+ function getNodeMetadataPath2(storagePath) {
34659
+ return path16.join(storagePath, "metadata.json");
34660
+ }
34661
+ function readStartupMetadataFile(storagePath) {
34662
+ try {
34663
+ const raw = JSON.parse(fs16.readFileSync(getNodeMetadataPath2(storagePath), "utf-8"));
34664
+ return typeof raw === "object" && raw !== null ? raw : {};
34665
+ } catch {
34666
+ return {};
34667
+ }
34668
+ }
34669
+ function writeStartupMetadataFile(storagePath, metadata) {
34670
+ fs16.mkdirSync(storagePath, { recursive: true });
34671
+ fs16.writeFileSync(getNodeMetadataPath2(storagePath), JSON.stringify(metadata, null, 2) + "\n", "utf-8");
34672
+ }
34673
+ function formatLocalDate(now) {
34674
+ const year = now.getFullYear();
34675
+ const month = String(now.getMonth() + 1).padStart(2, "0");
34676
+ const day = String(now.getDate()).padStart(2, "0");
34677
+ return `${year}-${month}-${day}`;
34678
+ }
34679
+ function isYesAnswer(value) {
34680
+ const normalized = value.trim().toLowerCase();
34681
+ return normalized === "y" || normalized === "yes";
34682
+ }
34683
+ async function confirm(ask, question) {
34684
+ const answer = await ask(`${question} [y/N]: `);
34685
+ return isYesAnswer(answer);
34686
+ }
34687
+ function isCommandAvailable(commandRunner, platform, command) {
34688
+ const lookupCommand = platform === "win32" ? "where" : "which";
34689
+ return commandRunner(lookupCommand, [command], false).ok;
34690
+ }
34691
+ function buildInstallCommand(requirement, platform) {
34692
+ switch (requirement) {
34693
+ case "az":
34694
+ if (platform === "darwin") {
34695
+ return { command: "brew", args: ["install", "azure-cli"] };
34696
+ }
34697
+ if (platform === "win32") {
34698
+ return { command: "winget", args: ["install", "--exact", "--id", "Microsoft.AzureCLI"] };
34699
+ }
34700
+ return void 0;
34701
+ case "devtunnel":
34702
+ if (platform === "darwin") {
34703
+ return { command: "brew", args: ["install", "--cask", "devtunnel"] };
34704
+ }
34705
+ if (platform === "win32") {
34706
+ return { command: "winget", args: ["install", "--exact", "--id", "Microsoft.devtunnel"] };
34707
+ }
34708
+ return void 0;
34709
+ case "claude":
34710
+ if (platform === "darwin") {
34711
+ return { command: "bash", args: ["-lc", "curl -fsSL https://claude.ai/install.sh | bash"] };
34712
+ }
34713
+ if (platform === "win32") {
34714
+ return {
34715
+ command: "powershell",
34716
+ args: ["-NoProfile", "-ExecutionPolicy", "Bypass", "-Command", "irm https://claude.ai/install.ps1 | iex"]
34717
+ };
34718
+ }
34719
+ return void 0;
34720
+ case "codex":
34721
+ return { command: "npm", args: ["install", "-g", "@openai/codex"] };
34722
+ }
34723
+ }
34724
+ function buildLoginCommand(requirement) {
34725
+ if (requirement === "az") {
34726
+ return { command: "az", args: ["login"] };
34727
+ }
34728
+ return { command: "devtunnel", args: ["user", "login"] };
34729
+ }
34730
+ function isAuthenticated(requirement, commandRunner) {
34731
+ const command = requirement === "az" ? { command: "az", args: ["account", "show"] } : { command: "devtunnel", args: ["user", "show"] };
34732
+ return commandRunner(command.command, command.args, false).ok;
34733
+ }
34734
+ function isRequirementSatisfied(requirement, status) {
34735
+ if (!status || status.installed !== true || status.status !== "ok") {
34736
+ return false;
34737
+ }
34738
+ if (requirement === "az" || requirement === "devtunnel") {
34739
+ return status.authenticated === true;
34740
+ }
34741
+ return true;
34742
+ }
34743
+ function resolveStartupRequirementsMetadata(storagePath) {
34744
+ const metadata = readStartupMetadataFile(storagePath).startupRequirements;
34745
+ return metadata && typeof metadata === "object" ? metadata : {};
34746
+ }
34747
+ function shouldSkipStartupRequirementsCheck(storagePath, now = /* @__PURE__ */ new Date()) {
34748
+ const metadata = resolveStartupRequirementsMetadata(storagePath);
34749
+ if (metadata.lastCheckedOn !== formatLocalDate(now)) {
34750
+ return false;
34751
+ }
34752
+ return STARTUP_REQUIREMENTS.every((requirement) => isRequirementSatisfied(requirement, metadata.components?.[requirement]));
34753
+ }
34754
+ async function ensureRequirementInstalled(requirement, platform, ask, commandRunner, stdout, stderr) {
34755
+ if (isCommandAvailable(commandRunner, platform, requirement)) {
34756
+ return { installed: true, status: "ok" };
34757
+ }
34758
+ if (!ask) {
34759
+ writeLine(stderr, `Missing required component "${requirement}". Start in a TTY to install it automatically.`);
34760
+ return { installed: false, status: "missing" };
34761
+ }
34762
+ const install = buildInstallCommand(requirement, platform);
34763
+ if (!install) {
34764
+ writeLine(stderr, `Missing required component "${requirement}", and automatic install is not supported on ${platform}.`);
34765
+ return { installed: false, status: "unsupported-platform" };
34766
+ }
34767
+ if (!await confirm(ask, `Required component "${requirement}" is missing. Install it now?`)) {
34768
+ return { installed: false, status: "missing" };
34769
+ }
34770
+ writeLine(stdout, `Installing ${requirement}...`);
34771
+ const result = commandRunner(install.command, install.args, true);
34772
+ if (!result.ok) {
34773
+ writeLine(stderr, `Failed to install ${requirement}.`);
34774
+ return { installed: false, status: "install-failed" };
34775
+ }
34776
+ return isCommandAvailable(commandRunner, platform, requirement) ? { installed: true, status: "ok" } : { installed: false, status: "install-failed" };
34777
+ }
34778
+ async function ensureRequirementAuthenticated(requirement, ask, commandRunner, stdout, stderr) {
34779
+ if (isAuthenticated(requirement, commandRunner)) {
34780
+ return { authenticated: true, status: "ok" };
34781
+ }
34782
+ if (!ask) {
34783
+ writeLine(stderr, `${requirement} is installed but not logged in. Start in a TTY to log in automatically.`);
34784
+ return { authenticated: false, status: "not-authenticated" };
34785
+ }
34786
+ if (!await confirm(ask, `"${requirement}" is installed but not logged in. Log in now?`)) {
34787
+ return { authenticated: false, status: "not-authenticated" };
34788
+ }
34789
+ writeLine(stdout, `Opening ${requirement} login...`);
34790
+ const login = buildLoginCommand(requirement);
34791
+ const result = commandRunner(login.command, login.args, true);
34792
+ if (!result.ok) {
34793
+ writeLine(stderr, `Failed to complete ${requirement} login.`);
34794
+ return { authenticated: false, status: "login-failed" };
34795
+ }
34796
+ return isAuthenticated(requirement, commandRunner) ? { authenticated: true, status: "ok" } : { authenticated: false, status: "login-failed" };
34797
+ }
34798
+ async function ensureStartupRequirements(storagePath, options = {}) {
34799
+ const now = options.now ?? /* @__PURE__ */ new Date();
34800
+ if (shouldSkipStartupRequirementsCheck(storagePath, now)) {
34801
+ return {
34802
+ skipped: true,
34803
+ metadata: resolveStartupRequirementsMetadata(storagePath)
34804
+ };
34805
+ }
34806
+ const platform = options.platform ?? process.platform;
34807
+ const stdout = options.stdout ?? process.stdout;
34808
+ const stderr = options.stderr ?? process.stderr;
34809
+ const commandRunner = options.commandRunner ?? createDefaultCommandRunner(platform);
34810
+ const promptSession = createPromptSession(options.prompt);
34811
+ const checkedAt = now.toISOString();
34812
+ const checkedOn = formatLocalDate(now);
34813
+ const components = {};
34814
+ try {
34815
+ for (const requirement of STARTUP_REQUIREMENTS) {
34816
+ const installResult = await ensureRequirementInstalled(
34817
+ requirement,
34818
+ platform,
34819
+ promptSession?.ask,
34820
+ commandRunner,
34821
+ stdout,
34822
+ stderr
34823
+ );
34824
+ if (!installResult.installed) {
34825
+ components[requirement] = {
34826
+ installed: false,
34827
+ status: installResult.status,
34828
+ checkedAt
34829
+ };
34830
+ continue;
34831
+ }
34832
+ if (requirement === "az" || requirement === "devtunnel") {
34833
+ const authResult = await ensureRequirementAuthenticated(
34834
+ requirement,
34835
+ promptSession?.ask,
34836
+ commandRunner,
34837
+ stdout,
34838
+ stderr
34839
+ );
34840
+ components[requirement] = {
34841
+ installed: true,
34842
+ authenticated: authResult.authenticated,
34843
+ status: authResult.status,
34844
+ checkedAt
34845
+ };
34846
+ continue;
34847
+ }
34848
+ components[requirement] = {
34849
+ installed: true,
34850
+ status: "ok",
34851
+ checkedAt
34852
+ };
34853
+ }
34854
+ } finally {
34855
+ await promptSession?.close();
34856
+ }
34857
+ const metadataFile = readStartupMetadataFile(storagePath);
34858
+ const metadata = {
34859
+ lastCheckedAt: checkedAt,
34860
+ lastCheckedOn: checkedOn,
34861
+ components
34862
+ };
34863
+ metadataFile.startupRequirements = metadata;
34864
+ writeStartupMetadataFile(storagePath, metadataFile);
34865
+ return {
34866
+ skipped: false,
34867
+ metadata
34868
+ };
34869
+ }
34173
34870
  function parseArgs(argv) {
34174
34871
  const result = {};
34175
34872
  for (let i = 0; i < argv.length; i++) {
@@ -34216,9 +34913,9 @@ function parseArgs(argv) {
34216
34913
  }
34217
34914
  return result;
34218
34915
  }
34219
- function loadConfigFile(path16) {
34916
+ function loadConfigFile(path17) {
34220
34917
  try {
34221
- const raw = fs16.readFileSync(path16, "utf-8");
34918
+ const raw = fs16.readFileSync(path17, "utf-8");
34222
34919
  return JSON.parse(raw);
34223
34920
  } catch {
34224
34921
  return {};
@@ -34435,6 +35132,11 @@ async function main() {
34435
35132
  if (!resolvedArgs.disableAuth) {
34436
35133
  persistNodeAuthMetadata(config.storage.path, authMetadata);
34437
35134
  }
35135
+ console.log("Checking startup requirements (az, devtunnel, claude, codex)...");
35136
+ const startupRequirements = await ensureStartupRequirements(config.storage.path);
35137
+ console.log(
35138
+ startupRequirements.skipped ? "Startup requirements already verified today; skipping checks." : "Startup requirements check complete."
35139
+ );
34438
35140
  const logDir = nodePath.join(config.storage.path, "logs");
34439
35141
  const logger = createLogger({
34440
35142
  component: "node",
@@ -34763,6 +35465,7 @@ if (isDirectRun) {
34763
35465
  applyStartMetadata,
34764
35466
  createDefaultConfig,
34765
35467
  createRuntimeDefaultConfig,
35468
+ ensureStartupRequirements,
34766
35469
  formatBanner,
34767
35470
  formatLoadedStartMetadata,
34768
35471
  getDefaultNodeName,
@@ -34773,8 +35476,10 @@ if (isDirectRun) {
34773
35476
  parseArgs,
34774
35477
  promptStartOptions,
34775
35478
  resolveRuntimeAuthMetadata,
35479
+ resolveStartupRequirementsMetadata,
34776
35480
  shouldCollectStartOptions,
34777
- shouldPromptForStartOptions
35481
+ shouldPromptForStartOptions,
35482
+ shouldSkipStartupRequirementsCheck
34778
35483
  });
34779
35484
  /*! Bundled license information:
34780
35485