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/README.md +1 -1
- package/dashboard/assets/{DashboardPage-vJKpJw48.js → DashboardPage-D5VZZBFL.js} +4 -4
- package/dashboard/assets/{DiffTab-RBHpHnzj.js → DiffTab-DHa2twrY.js} +1 -1
- package/dashboard/assets/{FilesTab-tWHzvDgF.js → FilesTab-DI35HxWK.js} +1 -1
- package/dashboard/assets/{PreviewTab-DUfl_tjX.js → PreviewTab-CAEInFgg.js} +1 -1
- package/dashboard/assets/{folder-DLwF1-Yt.js → folder-0eOoupgZ.js} +1 -1
- package/dashboard/assets/index-Bx5CT0VB.css +1 -0
- package/dashboard/assets/{index-B-YQV-_s.js → index-CRdTxsjS.js} +1 -1
- package/dashboard/index.html +2 -2
- package/main.cjs +952 -247
- package/package.json +1 -1
- package/dashboard/assets/index-bj-TBurH.css +0 -1
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(
|
|
14255
|
-
if (!
|
|
14254
|
+
function lookup(path17) {
|
|
14255
|
+
if (!path17 || typeof path17 !== "string") {
|
|
14256
14256
|
return false;
|
|
14257
14257
|
}
|
|
14258
|
-
var extension2 = extname3("x." +
|
|
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(
|
|
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 (
|
|
17791
|
-
while (m = MATCHING_GROUP_REGEXP.exec(
|
|
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
|
|
17799
|
+
return path17;
|
|
17800
17800
|
}
|
|
17801
|
-
if (Array.isArray(
|
|
17802
|
-
|
|
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(
|
|
17805
|
+
return new RegExp(path17.join("|"), flags);
|
|
17806
17806
|
}
|
|
17807
|
-
if (typeof
|
|
17807
|
+
if (typeof path17 !== "string") {
|
|
17808
17808
|
throw new TypeError("path must be a string, array of strings, or regular expression");
|
|
17809
17809
|
}
|
|
17810
|
-
|
|
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 +=
|
|
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(
|
|
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
|
-
|
|
17869
|
+
path17 += strict ? "" : path17[path17.length - 1] === "/" ? "?" : "/?";
|
|
17870
17870
|
if (end) {
|
|
17871
|
-
|
|
17872
|
-
} else if (
|
|
17873
|
-
|
|
17871
|
+
path17 += "$";
|
|
17872
|
+
} else if (path17[path17.length - 1] !== "/") {
|
|
17873
|
+
path17 += lookahead ? "(?=/|$)" : "(?:/|$)";
|
|
17874
17874
|
}
|
|
17875
|
-
return new RegExp("^" +
|
|
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(
|
|
17888
|
+
function Layer(path17, options, fn) {
|
|
17889
17889
|
if (!(this instanceof Layer)) {
|
|
17890
|
-
return new Layer(
|
|
17890
|
+
return new Layer(path17, options, fn);
|
|
17891
17891
|
}
|
|
17892
|
-
debug("new %o",
|
|
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(
|
|
17899
|
-
this.regexp.fast_star =
|
|
17900
|
-
this.regexp.fast_slash =
|
|
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(
|
|
17924
|
+
Layer.prototype.match = function match(path17) {
|
|
17925
17925
|
var match2;
|
|
17926
|
-
if (
|
|
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(
|
|
17934
|
-
this.path =
|
|
17933
|
+
this.params = { "0": decode_param(path17) };
|
|
17934
|
+
this.path = path17;
|
|
17935
17935
|
return true;
|
|
17936
17936
|
}
|
|
17937
|
-
match2 = this.regexp.exec(
|
|
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(
|
|
18031
|
-
this.path =
|
|
18030
|
+
function Route(path17) {
|
|
18031
|
+
this.path = path17;
|
|
18032
18032
|
this.stack = [];
|
|
18033
|
-
debug("new %o",
|
|
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
|
|
18247
|
-
if (
|
|
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,
|
|
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,
|
|
18293
|
+
trim_prefix(layer, layerError, layerPath, path17);
|
|
18294
18294
|
}
|
|
18295
18295
|
sync = 0;
|
|
18296
18296
|
});
|
|
18297
18297
|
}
|
|
18298
|
-
function trim_prefix(layer, layerError, layerPath,
|
|
18298
|
+
function trim_prefix(layer, layerError, layerPath, path17) {
|
|
18299
18299
|
if (layerPath.length !== 0) {
|
|
18300
|
-
if (layerPath !==
|
|
18300
|
+
if (layerPath !== path17.slice(0, layerPath.length)) {
|
|
18301
18301
|
next(layerError);
|
|
18302
18302
|
return;
|
|
18303
18303
|
}
|
|
18304
|
-
var c =
|
|
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
|
|
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
|
-
|
|
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",
|
|
18403
|
-
var layer = new Layer(
|
|
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(
|
|
18414
|
-
var route2 = new Route(
|
|
18415
|
-
var layer = new Layer(
|
|
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(
|
|
18426
|
-
var route = this.route(
|
|
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,
|
|
18462
|
+
function matchLayer(layer, path17) {
|
|
18463
18463
|
try {
|
|
18464
|
-
return layer.match(
|
|
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
|
|
18582
|
+
var path17 = require("path");
|
|
18583
18583
|
var fs17 = require("fs");
|
|
18584
|
-
var dirname3 =
|
|
18585
|
-
var basename4 =
|
|
18586
|
-
var extname3 =
|
|
18587
|
-
var
|
|
18588
|
-
var resolve10 =
|
|
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
|
|
18617
|
+
var path18;
|
|
18618
18618
|
var roots = [].concat(this.root);
|
|
18619
18619
|
debug('lookup "%s"', name);
|
|
18620
|
-
for (var i = 0; i < roots.length && !
|
|
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
|
-
|
|
18625
|
+
path18 = this.resolve(dir, file);
|
|
18626
18626
|
}
|
|
18627
|
-
return
|
|
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
|
|
18636
|
-
var stat = tryStat(
|
|
18635
|
+
var path18 = join13(dir, file);
|
|
18636
|
+
var stat = tryStat(path18);
|
|
18637
18637
|
if (stat && stat.isFile()) {
|
|
18638
|
-
return
|
|
18638
|
+
return path18;
|
|
18639
18639
|
}
|
|
18640
|
-
|
|
18641
|
-
stat = tryStat(
|
|
18640
|
+
path18 = join13(dir, basename4(file, ext), "index" + ext);
|
|
18641
|
+
stat = tryStat(path18);
|
|
18642
18642
|
if (stat && stat.isFile()) {
|
|
18643
|
-
return
|
|
18643
|
+
return path18;
|
|
18644
18644
|
}
|
|
18645
18645
|
};
|
|
18646
|
-
function tryStat(
|
|
18647
|
-
debug('stat "%s"',
|
|
18646
|
+
function tryStat(path18) {
|
|
18647
|
+
debug('stat "%s"', path18);
|
|
18648
18648
|
try {
|
|
18649
|
-
return fs17.statSync(
|
|
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
|
|
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(
|
|
19036
|
-
var ext =
|
|
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
|
|
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 =
|
|
19276
|
-
var
|
|
19277
|
-
var normalize =
|
|
19278
|
-
var resolve10 =
|
|
19279
|
-
var sep3 =
|
|
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,
|
|
19286
|
-
return new SendStream(req,
|
|
19285
|
+
function send(req, path18, options) {
|
|
19286
|
+
return new SendStream(req, path18, options);
|
|
19287
19287
|
}
|
|
19288
|
-
function SendStream(req,
|
|
19288
|
+
function SendStream(req, path18, options) {
|
|
19289
19289
|
Stream.call(this);
|
|
19290
19290
|
var opts = options || {};
|
|
19291
19291
|
this.options = opts;
|
|
19292
|
-
this.path =
|
|
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(
|
|
19339
|
-
this._root = resolve10(String(
|
|
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(
|
|
19452
|
+
SendStream.prototype.redirect = function redirect(path18) {
|
|
19453
19453
|
var res = this.res;
|
|
19454
19454
|
if (hasListeners(this, "directory")) {
|
|
19455
|
-
this.emit("directory", res,
|
|
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
|
|
19476
|
-
if (
|
|
19475
|
+
var path18 = decode(this.path);
|
|
19476
|
+
if (path18 === -1) {
|
|
19477
19477
|
this.error(400);
|
|
19478
19478
|
return res;
|
|
19479
19479
|
}
|
|
19480
|
-
if (~
|
|
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 (
|
|
19487
|
-
|
|
19486
|
+
if (path18) {
|
|
19487
|
+
path18 = normalize("." + sep3 + path18);
|
|
19488
19488
|
}
|
|
19489
|
-
if (UP_PATH_REGEXP.test(
|
|
19490
|
-
debug('malicious path "%s"',
|
|
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 =
|
|
19495
|
-
|
|
19494
|
+
parts = path18.split(sep3);
|
|
19495
|
+
path18 = normalize(join13(root, path18));
|
|
19496
19496
|
} else {
|
|
19497
|
-
if (UP_PATH_REGEXP.test(
|
|
19498
|
-
debug('malicious path "%s"',
|
|
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(
|
|
19503
|
-
|
|
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,
|
|
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(
|
|
19524
|
+
this.sendIndex(path18);
|
|
19525
19525
|
return res;
|
|
19526
19526
|
}
|
|
19527
|
-
this.sendFile(
|
|
19527
|
+
this.sendFile(path18);
|
|
19528
19528
|
return res;
|
|
19529
19529
|
};
|
|
19530
|
-
SendStream.prototype.send = function send2(
|
|
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"',
|
|
19543
|
-
this.setHeader(
|
|
19544
|
-
this.type(
|
|
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(
|
|
19593
|
+
this.stream(path18, opts);
|
|
19594
19594
|
};
|
|
19595
|
-
SendStream.prototype.sendFile = function sendFile(
|
|
19595
|
+
SendStream.prototype.sendFile = function sendFile(path18) {
|
|
19596
19596
|
var i = 0;
|
|
19597
19597
|
var self = this;
|
|
19598
|
-
debug('stat "%s"',
|
|
19599
|
-
fs17.stat(
|
|
19600
|
-
if (err && err.code === "ENOENT" && !extname3(
|
|
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(
|
|
19605
|
-
self.emit("file",
|
|
19606
|
-
self.send(
|
|
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 =
|
|
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(
|
|
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 =
|
|
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(
|
|
19641
|
+
SendStream.prototype.stream = function stream(path18, options) {
|
|
19642
19642
|
var self = this;
|
|
19643
19643
|
var res = this.res;
|
|
19644
|
-
var stream2 = fs17.createReadStream(
|
|
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(
|
|
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(
|
|
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(
|
|
19671
|
+
SendStream.prototype.setHeader = function setHeader(path18, stat) {
|
|
19672
19672
|
var res = this.res;
|
|
19673
|
-
this.emit("headers", res,
|
|
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(
|
|
19732
|
+
function decode(path18) {
|
|
19733
19733
|
try {
|
|
19734
|
-
return decodeURIComponent(
|
|
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(
|
|
20645
|
-
if ("/" ===
|
|
20646
|
-
if (":" ===
|
|
20647
|
-
if ("\\\\" ===
|
|
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
|
|
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
|
-
|
|
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(
|
|
20877
|
+
return router.use(path17, fn2);
|
|
20878
20878
|
}
|
|
20879
|
-
debug(".use app under %s",
|
|
20880
|
-
fn2.mountpath =
|
|
20879
|
+
debug(".use app under %s", path17);
|
|
20880
|
+
fn2.mountpath = path17;
|
|
20881
20881
|
fn2.parent = this;
|
|
20882
|
-
router.use(
|
|
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(
|
|
20894
|
+
app.route = function route(path17) {
|
|
20895
20895
|
this.lazyrouter();
|
|
20896
|
-
return this._router.route(
|
|
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
|
|
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(
|
|
20963
|
+
app[method] = function(path17) {
|
|
20964
20964
|
if (method === "get" && arguments.length === 1) {
|
|
20965
|
-
return this.set(
|
|
20965
|
+
return this.set(path17);
|
|
20966
20966
|
}
|
|
20967
20967
|
this.lazyrouter();
|
|
20968
|
-
var route = this._router.route(
|
|
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(
|
|
20973
|
+
app.all = function all(path17) {
|
|
20974
20974
|
this.lazyrouter();
|
|
20975
|
-
var route = this._router.route(
|
|
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
|
|
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
|
|
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 =
|
|
22066
|
+
var extname3 = path17.extname;
|
|
22067
22067
|
var mime = send.mime;
|
|
22068
|
-
var resolve10 =
|
|
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(
|
|
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 (!
|
|
22251
|
+
if (!path18) {
|
|
22252
22252
|
throw new TypeError("path argument is required to res.sendFile");
|
|
22253
22253
|
}
|
|
22254
|
-
if (typeof
|
|
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(
|
|
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(
|
|
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(
|
|
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,
|
|
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(
|
|
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 ||
|
|
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(
|
|
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
|
|
22629
|
-
if (
|
|
22630
|
-
|
|
22628
|
+
var path17 = parseUrl(req).pathname;
|
|
22629
|
+
if (path17 === "/" && originalUrl.pathname.substr(-1) !== "/") {
|
|
22630
|
+
path17 = "";
|
|
22631
22631
|
}
|
|
22632
|
-
var stream = send(req,
|
|
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,
|
|
24016
|
+
async function fetchNodeWithFallback(node, path17, init, timeoutMs = DEFAULT_NODE_REQUEST_TIMEOUT_MS, trace) {
|
|
24014
24017
|
let lastError;
|
|
24015
|
-
|
|
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}${
|
|
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
|
-
|
|
24336
|
-
|
|
24337
|
-
|
|
24338
|
-
|
|
24339
|
-
|
|
24340
|
-
|
|
24341
|
-
|
|
24342
|
-
|
|
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
|
|
25137
|
+
this.log.info("queued keepalive control request", {
|
|
25086
25138
|
nodeId,
|
|
25087
|
-
|
|
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
|
-
|
|
25193
|
-
|
|
25194
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
25411
|
-
return
|
|
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
|
-
|
|
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
|
-
},
|
|
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
|
|
25670
|
+
everReached
|
|
25463
25671
|
});
|
|
25464
25672
|
return;
|
|
25465
25673
|
}
|
|
25466
25674
|
}
|
|
25467
|
-
|
|
25468
|
-
|
|
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,
|
|
25820
|
+
shouldProxy(method, path17) {
|
|
25581
25821
|
if (this.election.isLeader()) return false;
|
|
25582
|
-
if (this.isExcludedPath(
|
|
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(
|
|
25684
|
-
return EXCLUDED_PATH_PREFIXES.some((prefix) =>
|
|
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
|
|
26943
|
+
tasks
|
|
26702
26944
|
};
|
|
26703
26945
|
if (this.selfInfo.devtunnelEndpoint) {
|
|
26704
26946
|
joinBody.devtunnelEndpoint = this.selfInfo.devtunnelEndpoint;
|
|
26705
26947
|
}
|
|
26706
|
-
|
|
26707
|
-
|
|
26708
|
-
|
|
26709
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
26751
|
-
|
|
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
|
-
|
|
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.
|
|
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:
|
|
27568
|
-
const fullPath = [...
|
|
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,
|
|
28001
|
+
constructor(parent, value, path17, key) {
|
|
27685
28002
|
this._cachedPath = [];
|
|
27686
28003
|
this.parent = parent;
|
|
27687
28004
|
this.data = value;
|
|
27688
|
-
this._path =
|
|
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
|
-
|
|
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
|
-
|
|
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}: ${
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
32836
|
+
response = jsonResponse(request.requestId, 200, getGitDiff(rootPath));
|
|
32837
|
+
break;
|
|
32461
32838
|
}
|
|
32462
32839
|
case "task-preview-session": {
|
|
32463
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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 (
|
|
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:
|
|
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(
|
|
34916
|
+
function loadConfigFile(path17) {
|
|
34220
34917
|
try {
|
|
34221
|
-
const raw = fs16.readFileSync(
|
|
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
|
|