meshy-node 0.4.3 → 0.4.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/main.cjs CHANGED
@@ -6629,8 +6629,8 @@ var require_node2 = __commonJS({
6629
6629
  }
6630
6630
  break;
6631
6631
  case "FILE":
6632
- var fs22 = require("fs");
6633
- stream2 = new fs22.SyncWriteStream(fd2, { autoClose: false });
6632
+ var fs23 = require("fs");
6633
+ stream2 = new fs23.SyncWriteStream(fd2, { autoClose: false });
6634
6634
  stream2._type = "fs";
6635
6635
  break;
6636
6636
  case "PIPE":
@@ -23749,8 +23749,8 @@ var require_view = __commonJS({
23749
23749
  "use strict";
23750
23750
  var debug = require_src2()("express:view");
23751
23751
  var path23 = require("path");
23752
- var fs22 = require("fs");
23753
- var dirname7 = path23.dirname;
23752
+ var fs23 = require("fs");
23753
+ var dirname8 = path23.dirname;
23754
23754
  var basename5 = path23.basename;
23755
23755
  var extname3 = path23.extname;
23756
23756
  var join17 = path23.join;
@@ -23789,7 +23789,7 @@ var require_view = __commonJS({
23789
23789
  for (var i = 0; i < roots.length && !path24; i++) {
23790
23790
  var root = roots[i];
23791
23791
  var loc = resolve15(root, name2);
23792
- var dir = dirname7(loc);
23792
+ var dir = dirname8(loc);
23793
23793
  var file = basename5(loc);
23794
23794
  path24 = this.resolve(dir, file);
23795
23795
  }
@@ -23815,7 +23815,7 @@ var require_view = __commonJS({
23815
23815
  function tryStat(path24) {
23816
23816
  debug('stat "%s"', path24);
23817
23817
  try {
23818
- return fs22.statSync(path24);
23818
+ return fs23.statSync(path24);
23819
23819
  } catch (e) {
23820
23820
  return void 0;
23821
23821
  }
@@ -24113,7 +24113,7 @@ var require_mime = __commonJS({
24113
24113
  "../../node_modules/.pnpm/mime@1.6.0/node_modules/mime/mime.js"(exports2, module2) {
24114
24114
  "use strict";
24115
24115
  var path23 = require("path");
24116
- var fs22 = require("fs");
24116
+ var fs23 = require("fs");
24117
24117
  function Mime() {
24118
24118
  this.types = /* @__PURE__ */ Object.create(null);
24119
24119
  this.extensions = /* @__PURE__ */ Object.create(null);
@@ -24134,7 +24134,7 @@ var require_mime = __commonJS({
24134
24134
  };
24135
24135
  Mime.prototype.load = function(file) {
24136
24136
  this._loading = file;
24137
- var map = {}, content = fs22.readFileSync(file, "ascii"), lines = content.split(/[\r\n]+/);
24137
+ var map = {}, content = fs23.readFileSync(file, "ascii"), lines = content.split(/[\r\n]+/);
24138
24138
  lines.forEach(function(line) {
24139
24139
  var fields = line.replace(/\s*#.*|^\s*|\s*$/g, "").split(/\s+/);
24140
24140
  map[fields.shift()] = fields;
@@ -24256,7 +24256,7 @@ var require_send = __commonJS({
24256
24256
  var escapeHtml2 = require_escape_html();
24257
24257
  var etag = require_etag();
24258
24258
  var fresh = require_fresh();
24259
- var fs22 = require("fs");
24259
+ var fs23 = require("fs");
24260
24260
  var mime = require_mime();
24261
24261
  var ms = require_ms();
24262
24262
  var onFinished = require_on_finished();
@@ -24589,7 +24589,7 @@ var require_send = __commonJS({
24589
24589
  var i = 0;
24590
24590
  var self2 = this;
24591
24591
  debug('stat "%s"', path24);
24592
- fs22.stat(path24, function onstat(err, stat) {
24592
+ fs23.stat(path24, function onstat(err, stat) {
24593
24593
  if (err && err.code === "ENOENT" && !extname3(path24) && path24[path24.length - 1] !== sep4) {
24594
24594
  return next(err);
24595
24595
  }
@@ -24604,7 +24604,7 @@ var require_send = __commonJS({
24604
24604
  }
24605
24605
  var p = path24 + "." + self2._extensions[i++];
24606
24606
  debug('stat "%s"', p);
24607
- fs22.stat(p, function(err2, stat) {
24607
+ fs23.stat(p, function(err2, stat) {
24608
24608
  if (err2) return next(err2);
24609
24609
  if (stat.isDirectory()) return next();
24610
24610
  self2.emit("file", p, stat);
@@ -24622,7 +24622,7 @@ var require_send = __commonJS({
24622
24622
  }
24623
24623
  var p = join17(path24, self2._index[i]);
24624
24624
  debug('stat "%s"', p);
24625
- fs22.stat(p, function(err2, stat) {
24625
+ fs23.stat(p, function(err2, stat) {
24626
24626
  if (err2) return next(err2);
24627
24627
  if (stat.isDirectory()) return next();
24628
24628
  self2.emit("file", p, stat);
@@ -24634,7 +24634,7 @@ var require_send = __commonJS({
24634
24634
  SendStream.prototype.stream = function stream(path24, options) {
24635
24635
  var self2 = this;
24636
24636
  var res = this.res;
24637
- var stream2 = fs22.createReadStream(path24, options);
24637
+ var stream2 = fs23.createReadStream(path24, options);
24638
24638
  this.emit("stream", stream2);
24639
24639
  stream2.pipe(res);
24640
24640
  function cleanup() {
@@ -31894,11 +31894,11 @@ var processUtils = {
31894
31894
  // ../../node_modules/.pnpm/@azure+identity@4.13.1/node_modules/@azure/identity/dist/esm/credentials/azurePowerShellCredential.js
31895
31895
  var logger18 = credentialLogger("AzurePowerShellCredential");
31896
31896
  var isWindows = process.platform === "win32";
31897
- function formatCommand(commandName) {
31897
+ function formatCommand(commandName2) {
31898
31898
  if (isWindows) {
31899
- return `${commandName}.exe`;
31899
+ return `${commandName2}.exe`;
31900
31900
  } else {
31901
- return commandName;
31901
+ return commandName2;
31902
31902
  }
31903
31903
  }
31904
31904
  async function runCommands(commands, timeout) {
@@ -33245,8 +33245,11 @@ var DEFAULT_NODE_REQUEST_TIMEOUT_MS = 1500;
33245
33245
  function isDevTunnelTransport(transportType) {
33246
33246
  return transportType?.toLowerCase() === "devtunnel";
33247
33247
  }
33248
+ function getPublishedNodeDevTunnelEndpoint(node) {
33249
+ return isDevTunnelTransport(node.transportType) ? node.endpoint : node.devtunnelEndpoint;
33250
+ }
33248
33251
  function hasNodeDevTunnel(node) {
33249
- return isDevTunnelTransport(node.transportType) || !!node.devtunnelEndpoint;
33252
+ return getPublishedNodeDevTunnelEndpoint(node) !== void 0;
33250
33253
  }
33251
33254
  function getNodePublicEndpoint(node) {
33252
33255
  return node.devtunnelEndpoint ?? node.endpoint;
@@ -34948,8 +34951,8 @@ var HeartbeatMonitor = class {
34948
34951
  // simplified — real impl would query task engine
34949
34952
  load: 0,
34950
34953
  supportedAgents: self2.supportedAgents,
34951
- devtunnelEnabled: self2.devtunnelEndpoint !== void 0,
34952
- devtunnelEndpoint: self2.devtunnelEndpoint,
34954
+ devtunnelEnabled: getPublishedNodeDevTunnelEndpoint(self2) !== void 0,
34955
+ devtunnelEndpoint: getPublishedNodeDevTunnelEndpoint(self2),
34953
34956
  devtunnelHealth: self2.devtunnelHealth,
34954
34957
  dashboardOrigin: self2.dashboardOrigin,
34955
34958
  workDirFolders: self2.workDir ? listWorkDirFolders(self2.workDir) : void 0,
@@ -35083,8 +35086,8 @@ var HeartbeatMonitor = class {
35083
35086
  supportedAgents: self2.supportedAgents,
35084
35087
  transportType: self2.transportType,
35085
35088
  workDir: self2.workDir,
35086
- devtunnelEnabled: self2.devtunnelEndpoint !== void 0,
35087
- devtunnelEndpoint: self2.devtunnelEndpoint,
35089
+ devtunnelEnabled: getPublishedNodeDevTunnelEndpoint(self2) !== void 0,
35090
+ devtunnelEndpoint: getPublishedNodeDevTunnelEndpoint(self2),
35088
35091
  devtunnelHealth: self2.devtunnelHealth,
35089
35092
  dashboardOrigin: self2.dashboardOrigin,
35090
35093
  workDirFolders: self2.workDir ? listWorkDirFolders(self2.workDir) : void 0,
@@ -37931,7 +37934,7 @@ ${joinErrors.map((e) => ` - ${e}`).join("\n")}`
37931
37934
  this.election.stop();
37932
37935
  try {
37933
37936
  const leaderEndpoint = this.nodeRegistry.getLeaderEndpoint();
37934
- if (leaderEndpoint) {
37937
+ if (leaderEndpoint && !this.nodeRegistry.isLeader()) {
37935
37938
  this.logger.info("sending shutdown cluster leave request", {
37936
37939
  leaderEndpoint,
37937
37940
  nodeId: this.selfInfo.id
@@ -38832,7 +38835,7 @@ function isDirectRunPath(entryPath) {
38832
38835
  }
38833
38836
 
38834
38837
  // src/bootstrap/start-node.ts
38835
- var nodePath2 = __toESM(require("path"), 1);
38838
+ var nodePath3 = __toESM(require("path"), 1);
38836
38839
 
38837
38840
  // ../../node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/external.js
38838
38841
  var external_exports = {};
@@ -42948,6 +42951,15 @@ var SystemAgentInfoSchema = external_exports.object({
42948
42951
  detail: external_exports.string().nullable().optional()
42949
42952
  }).optional()
42950
42953
  });
42954
+ var SystemRuntimeUpdateInfoSchema = external_exports.object({
42955
+ packageName: external_exports.string(),
42956
+ currentVersion: external_exports.string().nullable(),
42957
+ latestVersion: external_exports.string().nullable(),
42958
+ updateAvailable: external_exports.boolean(),
42959
+ checkedAt: external_exports.string(),
42960
+ restartMode: external_exports.enum(["package", "development"]),
42961
+ detail: external_exports.string().nullable().optional()
42962
+ });
42951
42963
  var NodeSettingsSnapshotSchema = external_exports.object({
42952
42964
  collectedAt: external_exports.number(),
42953
42965
  version: external_exports.string(),
@@ -42956,6 +42968,7 @@ var NodeSettingsSnapshotSchema = external_exports.object({
42956
42968
  auth: SystemAuthInfoSchema.optional(),
42957
42969
  os: SystemOsInfoSchema.optional(),
42958
42970
  runtime: SystemRuntimeInfoSchema.optional(),
42971
+ runtimeUpdate: SystemRuntimeUpdateInfoSchema.optional(),
42959
42972
  agents: external_exports.array(SystemAgentInfoSchema).optional(),
42960
42973
  startupRequirements: external_exports.object({
42961
42974
  lastCheckedAt: external_exports.string().optional(),
@@ -44752,6 +44765,7 @@ function buildNodeSettingsSnapshot(options) {
44752
44765
  storagePath: options.storagePath ?? null,
44753
44766
  localDashboardOrigin: options.localDashboardOrigin ?? null
44754
44767
  },
44768
+ runtimeUpdate: options.runtimeUpdate,
44755
44769
  agents,
44756
44770
  startupRequirements: {
44757
44771
  lastCheckedAt: runtimeMetadata?.startupRequirementsLastCheckedAt,
@@ -44837,6 +44851,7 @@ function upgradeRuntimeAgentForDeps(deps, agent) {
44837
44851
 
44838
44852
  // ../../packages/api/src/node/node-operation-service.ts
44839
44853
  var LEADER_REPORT_RETRY_MS = 5e3;
44854
+ var OPERATION_COMMIT_EFFECT = /* @__PURE__ */ Symbol("operationCommitEffect");
44840
44855
  var NodeOperationFailure = class extends Error {
44841
44856
  constructor(message, result) {
44842
44857
  super(message);
@@ -44870,6 +44885,7 @@ var NodeOperationService = class {
44870
44885
  this.deps = deps;
44871
44886
  this.store = store;
44872
44887
  this.registerHandler("agent.upgrade", runAgentUpgradeOperation);
44888
+ this.registerHandler("runtime.restart", runRuntimeRestartOperation);
44873
44889
  this.registerHandler("workdir.branch-create", runWorkDirBranchCreateOperation);
44874
44890
  }
44875
44891
  handlers = /* @__PURE__ */ new Map();
@@ -44940,8 +44956,14 @@ var NodeOperationService = class {
44940
44956
  try {
44941
44957
  const handler = this.handlers.get(running.kind);
44942
44958
  if (!handler) throw new Error(`No node operation handler registered for kind: ${running.kind}`);
44943
- const result = await handler(running, this.deps);
44944
- this.update(id, { status: "succeeded", result, completedAt: Date.now() });
44959
+ const handled = readOperationCommitEffect(await handler(running, this.deps));
44960
+ const saved = this.update(id, { status: "succeeded", result: handled.result, completedAt: Date.now() }, {
44961
+ reportToLeader: handled.afterCommit ? false : true
44962
+ });
44963
+ if (handled.afterCommit) {
44964
+ if (saved) await this.reportToLeader(saved);
44965
+ await handled.afterCommit();
44966
+ }
44945
44967
  } catch (err) {
44946
44968
  this.update(id, {
44947
44969
  status: "failed",
@@ -44951,12 +44973,12 @@ var NodeOperationService = class {
44951
44973
  });
44952
44974
  }
44953
44975
  }
44954
- update(id, updates) {
44976
+ update(id, updates, options = {}) {
44955
44977
  const saved = this.store.update(id, { ...updates, updatedAt: Date.now() });
44956
44978
  if (!saved) return null;
44957
44979
  this.applyOperationSideEffects(saved);
44958
44980
  this.deps.eventBus.emit("node.operation.updated", { operation: saved });
44959
- void this.reportToLeader(saved);
44981
+ if (options.reportToLeader !== false) void this.reportToLeader(saved);
44960
44982
  return saved;
44961
44983
  }
44962
44984
  applyOperationSideEffects(operation) {
@@ -45037,6 +45059,23 @@ function runAgentUpgradeOperation(operation, deps) {
45037
45059
  }
45038
45060
  return result;
45039
45061
  }
45062
+ function runRuntimeRestartOperation(operation, deps) {
45063
+ if (!deps.restartRuntime) throw new Error("Runtime restart is not available for this node");
45064
+ const reason = readPayloadString(operation, "reason") === "update" ? "update" : "restart";
45065
+ return deps.restartRuntime({ reason, deferShutdown: true }).then(
45066
+ ({ completeShutdown, ...result }) => operationCommitEffect(result, completeShutdown)
45067
+ );
45068
+ }
45069
+ function operationCommitEffect(result, afterCommit) {
45070
+ return { [OPERATION_COMMIT_EFFECT]: true, result, afterCommit };
45071
+ }
45072
+ function readOperationCommitEffect(value) {
45073
+ if (value && typeof value === "object" && value[OPERATION_COMMIT_EFFECT]) {
45074
+ const effect = value;
45075
+ return { result: effect.result, afterCommit: effect.afterCommit };
45076
+ }
45077
+ return { result: value };
45078
+ }
45040
45079
  function runWorkDirBranchCreateOperation(operation, deps) {
45041
45080
  const self2 = deps.nodeRegistry.getSelf();
45042
45081
  return createLocalNodeWorkDirBranch(
@@ -47122,6 +47161,37 @@ async function sendNodeAgentUpgrade(req, res, nodeId, agentParam) {
47122
47161
  res.status(202).json(operation);
47123
47162
  }
47124
47163
 
47164
+ // ../../packages/api/src/routes/node-runtime.ts
47165
+ function parseRestartReason(value) {
47166
+ return value === "update" ? "update" : "restart";
47167
+ }
47168
+ async function sendNodeRuntimeRestart(req, res, nodeId) {
47169
+ const deps = req.app.locals.deps;
47170
+ const self2 = deps.nodeRegistry.getSelf();
47171
+ const isSelf = nodeId === self2.id;
47172
+ if (!isSelf && !deps.election.isLeader()) {
47173
+ throw new MeshyError("NOT_LEADER", "Only the leader can restart other nodes", 403);
47174
+ }
47175
+ const node = isSelf ? self2 : deps.nodeRegistry.getNode(nodeId);
47176
+ if (!node) {
47177
+ throw new MeshyError("NODE_NOT_FOUND", `Node ${nodeId} not found`, 404);
47178
+ }
47179
+ const reason = parseRestartReason(req.body?.reason);
47180
+ const operations = getNodeOperationService(deps);
47181
+ const operation = operations.create("runtime.restart", nodeId, { reason });
47182
+ if (isSelf) {
47183
+ operations.runLocal(operation.id);
47184
+ } else {
47185
+ try {
47186
+ await dispatchNodeOperation(deps, operation, node);
47187
+ } catch (err) {
47188
+ operations.markFailed(operation.id, err instanceof Error ? err.message : String(err));
47189
+ throw new MeshyError("NODE_OFFLINE", `Cannot reach node ${nodeId} to restart runtime`, 502);
47190
+ }
47191
+ }
47192
+ res.status(202).json(operation);
47193
+ }
47194
+
47125
47195
  // ../../packages/api/src/routes/node-terminal.ts
47126
47196
  var TERMINAL_SESSION_PROXY_TIMEOUT_MS = 1e4;
47127
47197
  function getTerminalSessionService2(deps) {
@@ -47545,6 +47615,9 @@ function createNodeRoutes() {
47545
47615
  router.post("/:id/agents/:agent/upgrade", asyncHandler3(async (req, res) => {
47546
47616
  await sendNodeAgentUpgrade(req, res, req.params.id, req.params.agent);
47547
47617
  }));
47618
+ router.post("/:id/runtime/restart", asyncHandler3(async (req, res) => {
47619
+ await sendNodeRuntimeRestart(req, res, req.params.id);
47620
+ }));
47548
47621
  router.patch("/:id", asyncHandler3(async (req, res) => {
47549
47622
  const { nodeRegistry, persistNodeNamePreference } = req.app.locals.deps;
47550
47623
  const updates = UpdateNodeBody.parse(req.body);
@@ -49375,7 +49448,8 @@ function createSystemRoutes() {
49375
49448
  localDashboardOrigin,
49376
49449
  storagePath,
49377
49450
  inspectRuntimeTools: inspectTools,
49378
- runtimeMetadata
49451
+ runtimeMetadata,
49452
+ runtimeUpdate
49379
49453
  } = req.app.locals.deps;
49380
49454
  const self2 = nodeRegistry.getSelf();
49381
49455
  const auth = config.authMetadata ?? {
@@ -49388,6 +49462,7 @@ function createSystemRoutes() {
49388
49462
  inspectRuntimeTools: inspectTools,
49389
49463
  localDashboardOrigin,
49390
49464
  runtimeMetadata,
49465
+ runtimeUpdate,
49391
49466
  storagePath,
49392
49467
  workDir: self2.workDir
49393
49468
  });
@@ -49403,6 +49478,7 @@ function createSystemRoutes() {
49403
49478
  auth: settingsSnapshot.auth,
49404
49479
  os: settingsSnapshot.os,
49405
49480
  runtime: settingsSnapshot.runtime,
49481
+ runtimeUpdate: settingsSnapshot.runtimeUpdate,
49406
49482
  agents: settingsSnapshot.agents,
49407
49483
  startupRequirements: settingsSnapshot.startupRequirements,
49408
49484
  components: settingsSnapshot.components,
@@ -49410,6 +49486,24 @@ function createSystemRoutes() {
49410
49486
  packages: settingsSnapshot.packages
49411
49487
  });
49412
49488
  }));
49489
+ router.post("/runtime/restart", asyncHandler9(async (req, res) => {
49490
+ const { restartRuntime } = req.app.locals.deps;
49491
+ if (!restartRuntime) {
49492
+ throw new MeshyError("VALIDATION_ERROR", "Runtime restart is not available for this node", 501);
49493
+ }
49494
+ const body = req.body;
49495
+ const reason = body.reason === "update" ? "update" : "restart";
49496
+ const result = await restartRuntime({ reason });
49497
+ res.status(202).json(result);
49498
+ }));
49499
+ router.post("/runtime/stop", asyncHandler9(async (req, res) => {
49500
+ const { stopRuntime } = req.app.locals.deps;
49501
+ if (!stopRuntime) {
49502
+ throw new MeshyError("VALIDATION_ERROR", "Runtime stop is not available for this node", 501);
49503
+ }
49504
+ const result = await stopRuntime();
49505
+ res.status(202).json(result);
49506
+ }));
49413
49507
  router.post("/transport", asyncHandler9(async (req, res) => {
49414
49508
  const { switchTransport } = req.app.locals.deps;
49415
49509
  if (!switchTransport) {
@@ -50777,6 +50871,343 @@ function buildRuntimeMetadata(storagePath) {
50777
50871
  };
50778
50872
  }
50779
50873
 
50874
+ // src/bootstrap/runtime-restart.ts
50875
+ var fs22 = __toESM(require("fs"), 1);
50876
+ var nodePath2 = __toESM(require("path"), 1);
50877
+ var import_node_child_process14 = require("child_process");
50878
+ var runtimeUpdateCache = /* @__PURE__ */ new Map();
50879
+ var RESTARTER_SOURCE = String.raw`
50880
+ const { spawn } = require('node:child_process');
50881
+ const http = require('node:http');
50882
+
50883
+ const plan = JSON.parse(process.env.MESHY_RESTART_PLAN || '{}');
50884
+ const parentPid = Number(process.env.MESHY_RESTART_PARENT_PID || '0');
50885
+
50886
+ function log(message) {
50887
+ process.stdout.write('[' + new Date().toISOString() + '] ' + message + '\n');
50888
+ }
50889
+
50890
+ function sleep(ms) {
50891
+ return new Promise(resolve => setTimeout(resolve, ms));
50892
+ }
50893
+
50894
+ function isProcessAlive(pid) {
50895
+ if (!pid) return false;
50896
+ try {
50897
+ process.kill(pid, 0);
50898
+ return true;
50899
+ } catch {
50900
+ return false;
50901
+ }
50902
+ }
50903
+
50904
+ function requestHealth(url) {
50905
+ return new Promise(resolve => {
50906
+ const req = http.get(url, res => {
50907
+ res.resume();
50908
+ res.on('end', () => resolve(res.statusCode >= 200 && res.statusCode < 500));
50909
+ });
50910
+ req.setTimeout(1000, () => {
50911
+ req.destroy();
50912
+ resolve(false);
50913
+ });
50914
+ req.on('error', () => resolve(false));
50915
+ });
50916
+ }
50917
+
50918
+ async function waitForParentExit() {
50919
+ const deadline = Date.now() + 30000;
50920
+ while (isProcessAlive(parentPid) && Date.now() < deadline) {
50921
+ await sleep(250);
50922
+ }
50923
+ }
50924
+
50925
+ async function waitForEndpointDown() {
50926
+ if (!plan.healthUrl) return;
50927
+ const deadline = Date.now() + 30000;
50928
+ while (Date.now() < deadline) {
50929
+ if (!await requestHealth(plan.healthUrl)) return;
50930
+ await sleep(250);
50931
+ }
50932
+ }
50933
+
50934
+ (async () => {
50935
+ log('waiting to restart ' + (plan.displayCommand || plan.command));
50936
+ await waitForParentExit();
50937
+ await waitForEndpointDown();
50938
+ log('starting ' + (plan.displayCommand || plan.command));
50939
+ const child = spawn(plan.command, plan.args || [], {
50940
+ cwd: plan.cwd,
50941
+ env: { ...process.env, ...(plan.env || {}), MESHY_RESTART_CHILD: '1' },
50942
+ detached: true,
50943
+ stdio: ['ignore', 'inherit', 'inherit'],
50944
+ windowsHide: true,
50945
+ });
50946
+ child.on('error', error => {
50947
+ log('failed to launch restart command: ' + (error && error.message ? error.message : String(error)));
50948
+ process.exitCode = 1;
50949
+ });
50950
+ child.unref();
50951
+ await sleep(500);
50952
+ })().catch(error => {
50953
+ log('restart helper failed: ' + (error && error.message ? error.message : String(error)));
50954
+ process.exit(1);
50955
+ });
50956
+ `;
50957
+ function formatLocalDate3(date) {
50958
+ const year = date.getFullYear();
50959
+ const month = String(date.getMonth() + 1).padStart(2, "0");
50960
+ const day = String(date.getDate()).padStart(2, "0");
50961
+ return `${year}-${month}-${day}`;
50962
+ }
50963
+ function normalizeOutput2(output) {
50964
+ if (!output) return null;
50965
+ const firstLine = output.split(/\r?\n/).map((line) => line.trim()).find(Boolean);
50966
+ return firstLine ?? null;
50967
+ }
50968
+ function normalizePackageVersion2(output) {
50969
+ const normalized = normalizeOutput2(output);
50970
+ if (!normalized) return null;
50971
+ try {
50972
+ const parsed = JSON.parse(normalized);
50973
+ return typeof parsed === "string" ? parsed.trim() || null : normalized;
50974
+ } catch {
50975
+ return normalized;
50976
+ }
50977
+ }
50978
+ function compareSemver2(left, right) {
50979
+ const leftParts = left.split(/[.-]/).map((part) => Number.parseInt(part, 10));
50980
+ const rightParts = right.split(/[.-]/).map((part) => Number.parseInt(part, 10));
50981
+ for (let index = 0; index < 3; index += 1) {
50982
+ const delta = (leftParts[index] || 0) - (rightParts[index] || 0);
50983
+ if (delta !== 0) return delta;
50984
+ }
50985
+ return 0;
50986
+ }
50987
+ function runRuntimePackageCommand(command, args) {
50988
+ const result = (0, import_node_child_process14.spawnSync)(command, args, {
50989
+ encoding: "utf-8",
50990
+ windowsHide: true,
50991
+ timeout: 2500
50992
+ });
50993
+ return {
50994
+ status: result.status,
50995
+ stdout: typeof result.stdout === "string" ? result.stdout : "",
50996
+ stderr: typeof result.stderr === "string" ? result.stderr : "",
50997
+ error: result.error?.message ?? null
50998
+ };
50999
+ }
51000
+ function commandName(base, platform2) {
51001
+ return platform2 === "win32" ? `${base}.cmd` : base;
51002
+ }
51003
+ function pathForPlatform(platform2) {
51004
+ return platform2 === "win32" ? nodePath2.win32 : nodePath2;
51005
+ }
51006
+ function resolveDevelopmentRepoRoot(cwd, env, platform2) {
51007
+ if (env.MESHY_STORAGE_BASE_DIR) return env.MESHY_STORAGE_BASE_DIR;
51008
+ const pathApi = pathForPlatform(platform2);
51009
+ const packageDir = pathApi.basename(cwd);
51010
+ const packageParent = pathApi.basename(pathApi.dirname(cwd));
51011
+ return packageDir === "node" && packageParent === "apps" ? pathApi.dirname(pathApi.dirname(cwd)) : cwd;
51012
+ }
51013
+ function windowsShellQuote(value) {
51014
+ return /[\s&()^|<>"]/.test(value) ? `"${value.replace(/"/g, '\\"')}"` : value;
51015
+ }
51016
+ function createSpawnableCommand(command, args, platform2) {
51017
+ const displayCommand = [command, ...args].map(shellQuote).join(" ");
51018
+ if (platform2 !== "win32") return { command, args, displayCommand };
51019
+ return {
51020
+ command: "cmd.exe",
51021
+ args: ["/d", "/s", "/c", [command, ...args].map(windowsShellQuote).join(" ")],
51022
+ displayCommand
51023
+ };
51024
+ }
51025
+ function readProcessCommand(pid, platform2) {
51026
+ if (platform2 === "win32") return null;
51027
+ const result = (0, import_node_child_process14.spawnSync)("ps", ["-p", String(pid), "-o", "args="], {
51028
+ encoding: "utf-8",
51029
+ windowsHide: true,
51030
+ timeout: 1e3
51031
+ });
51032
+ return result.status === 0 && typeof result.stdout === "string" ? result.stdout.trim() || null : null;
51033
+ }
51034
+ function isNodeWatchCommand(command) {
51035
+ return /(?:^|[/\\])node(?:\.exe)?(?:\s|$)/.test(command) && /(?:^|\s)--watch(?:=|\s|$)/.test(command);
51036
+ }
51037
+ function terminateRuntimeWatchParent(options) {
51038
+ if (options.mode !== "development") return { terminated: false, reason: "not-development" };
51039
+ const parentPid = options.parentPid ?? process.ppid;
51040
+ if (!Number.isInteger(parentPid) || parentPid <= 1 || parentPid === process.pid) {
51041
+ return { terminated: false, reason: "invalid-parent" };
51042
+ }
51043
+ const commandReader = options.commandReader ?? ((pid) => readProcessCommand(pid, options.platform ?? process.platform));
51044
+ const parentCommand = commandReader(parentPid);
51045
+ const parentIsWatch = parentCommand !== null ? isNodeWatchCommand(parentCommand) : options.env?.WATCH_REPORT_DEPENDENCIES === "1";
51046
+ if (!parentIsWatch) return { terminated: false, reason: "parent-not-watch" };
51047
+ try {
51048
+ const kill = options.kill ?? process.kill;
51049
+ kill(parentPid, "SIGINT");
51050
+ return { terminated: true, pid: parentPid };
51051
+ } catch {
51052
+ return { terminated: false, reason: "kill-failed" };
51053
+ }
51054
+ }
51055
+ function compactEnv(env) {
51056
+ const result = {};
51057
+ for (const [key, value] of Object.entries(env)) {
51058
+ if (typeof value === "string") result[key] = value;
51059
+ }
51060
+ return result;
51061
+ }
51062
+ function shellQuote(value) {
51063
+ return /\s/.test(value) ? JSON.stringify(value) : value;
51064
+ }
51065
+ function formatStartArgs(args) {
51066
+ const result = ["start"];
51067
+ if (args.port !== void 0) result.push("--port", String(args.port));
51068
+ if (args.name) result.push("--name", args.name);
51069
+ if (args.join) result.push("--join", args.join);
51070
+ if (args.transport) result.push("--transport", args.transport);
51071
+ if (args.workDir) result.push("--work-dir", args.workDir);
51072
+ if (args.config) result.push("--config", args.config);
51073
+ if (args.disableAuth) result.push("--disable-auth");
51074
+ return result;
51075
+ }
51076
+ function detectRuntimeRestartMode(env = process.env, entryPath = process.argv[1]) {
51077
+ if (env.MESHY_RESTART_MODE === "package" || env.MESHY_RESTART_MODE === "development") {
51078
+ return env.MESHY_RESTART_MODE;
51079
+ }
51080
+ if (env.MESHY_STORAGE_MODE === "cwd" && env.MESHY_STORAGE_BASE_DIR) {
51081
+ return "development";
51082
+ }
51083
+ return typeof entryPath === "string" && /src[/\\]main\.ts$/.test(entryPath) ? "development" : "package";
51084
+ }
51085
+ function createRuntimeRestartLaunchPlan(options) {
51086
+ const platform2 = options.platform ?? process.platform;
51087
+ const startArgs = formatStartArgs(options.startArgs);
51088
+ const env = compactEnv(options.env);
51089
+ const logPath = pathForPlatform(platform2).join(options.storagePath, "logs", "restart.log");
51090
+ const healthUrl = new URL("/api/system/health", options.localDashboardOrigin).toString();
51091
+ if (options.mode === "development") {
51092
+ const repoRoot2 = resolveDevelopmentRepoRoot(options.cwd, options.env, platform2);
51093
+ const launch2 = createSpawnableCommand(commandName("pnpm", platform2), ["dev:node", "--", ...startArgs], platform2);
51094
+ return {
51095
+ mode: "development",
51096
+ command: launch2.command,
51097
+ args: launch2.args,
51098
+ cwd: repoRoot2,
51099
+ env,
51100
+ displayCommand: launch2.displayCommand,
51101
+ logPath,
51102
+ healthUrl
51103
+ };
51104
+ }
51105
+ const launch = createSpawnableCommand(commandName("npx", platform2), ["-y", "meshy-node@latest", ...startArgs], platform2);
51106
+ return {
51107
+ mode: "package",
51108
+ command: launch.command,
51109
+ args: launch.args,
51110
+ cwd: options.cwd,
51111
+ env,
51112
+ displayCommand: launch.displayCommand,
51113
+ logPath,
51114
+ healthUrl
51115
+ };
51116
+ }
51117
+ function scheduleRuntimeRestart(plan, options = {}) {
51118
+ fs22.mkdirSync(nodePath2.dirname(plan.logPath), { recursive: true });
51119
+ const logFd = fs22.openSync(plan.logPath, "a");
51120
+ const spawnImpl = options.spawnImpl ?? import_node_child_process14.spawn;
51121
+ const env = {
51122
+ ...process.env,
51123
+ MESHY_RESTART_PARENT_PID: String(options.parentPid ?? process.pid),
51124
+ MESHY_RESTART_PLAN: JSON.stringify(plan)
51125
+ };
51126
+ const child = spawnImpl(process.execPath, ["-e", RESTARTER_SOURCE], {
51127
+ detached: true,
51128
+ env,
51129
+ stdio: ["ignore", logFd, logFd],
51130
+ windowsHide: true
51131
+ });
51132
+ child.unref();
51133
+ }
51134
+ function createRuntimeUpdateSnapshot(options) {
51135
+ const now = options.now ?? /* @__PURE__ */ new Date();
51136
+ const checkedAt = now.toISOString();
51137
+ const currentVersion = options.runtimeMetadata?.packageVersion ?? null;
51138
+ const packageName = "meshy-node";
51139
+ if (options.mode === "development") {
51140
+ return {
51141
+ packageName,
51142
+ currentVersion,
51143
+ latestVersion: null,
51144
+ updateAvailable: false,
51145
+ checkedAt,
51146
+ restartMode: "development",
51147
+ detail: "Development runtime restarts from the local workspace."
51148
+ };
51149
+ }
51150
+ const cache = options.updateCache ?? runtimeUpdateCache;
51151
+ const checkedOn = formatLocalDate3(now);
51152
+ const cached = cache.get(packageName);
51153
+ let latestVersion = cached?.latestVersion ?? null;
51154
+ let detail = cached?.detail ?? null;
51155
+ let updateCheckedAt = cached?.checkedAt ?? checkedAt;
51156
+ if (cached?.checkedOn !== checkedOn) {
51157
+ const runner = options.commandRunner ?? runRuntimePackageCommand;
51158
+ const result = runner("npm", ["view", `${packageName}@latest`, "version", "--json"]);
51159
+ latestVersion = result.status === 0 ? normalizePackageVersion2(result.stdout) : null;
51160
+ detail = result.status === 0 ? null : normalizeOutput2(result.stderr) ?? result.error ?? "Runtime update check failed";
51161
+ updateCheckedAt = checkedAt;
51162
+ cache.set(packageName, { checkedOn, checkedAt, latestVersion, detail });
51163
+ }
51164
+ return {
51165
+ packageName,
51166
+ currentVersion,
51167
+ latestVersion,
51168
+ updateAvailable: Boolean(currentVersion && latestVersion && compareSemver2(latestVersion, currentVersion) > 0),
51169
+ checkedAt: updateCheckedAt,
51170
+ restartMode: "package",
51171
+ detail
51172
+ };
51173
+ }
51174
+
51175
+ // src/bootstrap/settings-snapshot.ts
51176
+ function createSettingsSnapshotProvider(options) {
51177
+ let cachedSnapshot = null;
51178
+ let cachedSnapshotAt = 0;
51179
+ function buildSnapshot() {
51180
+ cachedSnapshot = buildNodeSettingsSnapshot({
51181
+ auth: options.authMetadata,
51182
+ localDashboardOrigin: options.localDashboardOrigin,
51183
+ runtimeMetadata: options.runtimeMetadata,
51184
+ runtimeUpdate: createRuntimeUpdateSnapshot({
51185
+ mode: options.runtimeRestartMode,
51186
+ runtimeMetadata: options.runtimeMetadata
51187
+ }),
51188
+ storagePath: options.storagePath,
51189
+ workDir: options.workDir
51190
+ });
51191
+ cachedSnapshotAt = Date.now();
51192
+ return structuredClone(cachedSnapshot);
51193
+ }
51194
+ function getSettingsSnapshot() {
51195
+ const now = Date.now();
51196
+ if (cachedSnapshot && now - cachedSnapshotAt < 3e4) {
51197
+ return structuredClone(cachedSnapshot);
51198
+ }
51199
+ return buildSnapshot();
51200
+ }
51201
+ return {
51202
+ getSettingsSnapshot,
51203
+ refreshSettingsSnapshot: () => {
51204
+ cachedSnapshot = null;
51205
+ cachedSnapshotAt = 0;
51206
+ return buildSnapshot();
51207
+ }
51208
+ };
51209
+ }
51210
+
50780
51211
  // src/bootstrap/tunnel-health.ts
50781
51212
  var DEFAULT_TUNNEL_REACHABILITY_INTERVAL_MS = 6e4;
50782
51213
  var DEFAULT_TUNNEL_REACHABILITY_TIMEOUT_MS = 5e3;
@@ -50854,36 +51285,6 @@ async function fetchWithTimeout2(fetchImpl, url, timeoutMs) {
50854
51285
  }
50855
51286
 
50856
51287
  // src/bootstrap/start-node.ts
50857
- function createSettingsSnapshotProvider(options) {
50858
- let cachedSnapshot = null;
50859
- let cachedSnapshotAt = 0;
50860
- function buildSnapshot() {
50861
- cachedSnapshot = buildNodeSettingsSnapshot({
50862
- auth: options.authMetadata,
50863
- localDashboardOrigin: options.localDashboardOrigin,
50864
- runtimeMetadata: options.runtimeMetadata,
50865
- storagePath: options.storagePath,
50866
- workDir: options.workDir
50867
- });
50868
- cachedSnapshotAt = Date.now();
50869
- return structuredClone(cachedSnapshot);
50870
- }
50871
- function getSettingsSnapshot() {
50872
- const now = Date.now();
50873
- if (cachedSnapshot && now - cachedSnapshotAt < 3e4) {
50874
- return structuredClone(cachedSnapshot);
50875
- }
50876
- return buildSnapshot();
50877
- }
50878
- return {
50879
- getSettingsSnapshot,
50880
- refreshSettingsSnapshot: () => {
50881
- cachedSnapshot = null;
50882
- cachedSnapshotAt = 0;
50883
- return buildSnapshot();
50884
- }
50885
- };
50886
- }
50887
51288
  async function resolveStartConfig(args) {
50888
51289
  const configPath = args.config ?? "./config.json";
50889
51290
  const fileConfig = loadConfigFile(configPath);
@@ -50913,7 +51314,7 @@ async function resolveStartConfig(args) {
50913
51314
  ignorePersistedName: args.reset === true
50914
51315
  });
50915
51316
  const config = mergeConfig(defaults, scopedFileConfig, resolvedArgs);
50916
- setStartupFatalLogPath(nodePath2.join(config.storage.path, "logs", "startup-fatal.log"));
51317
+ setStartupFatalLogPath(nodePath3.join(config.storage.path, "logs", "startup-fatal.log"));
50917
51318
  persistDefaultNodeName(storageRoot, config.node.name);
50918
51319
  persistDefaultNodeName(config.storage.path, config.node.name);
50919
51320
  persistNodeStartupMetadata(config.storage.path, {
@@ -51268,15 +51669,20 @@ function createTunnelManager(options) {
51268
51669
  }
51269
51670
  function registerShutdownHandlers(options) {
51270
51671
  let shuttingDown = false;
51271
- async function shutdown() {
51672
+ function stopDevelopmentWatchParent(runtimeMode) {
51673
+ if (runtimeMode) terminateRuntimeWatchParent({ mode: runtimeMode, env: process.env });
51674
+ }
51675
+ async function shutdown(reason = "shutdown", runtimeMode) {
51272
51676
  if (shuttingDown) {
51273
51677
  terminalWriter.line("Force exit.");
51678
+ stopDevelopmentWatchParent(runtimeMode);
51274
51679
  process.exit(130);
51275
51680
  }
51276
51681
  shuttingDown = true;
51277
- terminalWriter.line("\nShutting down...");
51682
+ terminalWriter.line(reason === "restart" ? "\nRestarting..." : "\nShutting down...");
51278
51683
  const forceExitTimer = setTimeout(() => {
51279
51684
  terminalWriter.line("Force exit.");
51685
+ stopDevelopmentWatchParent(runtimeMode);
51280
51686
  process.exit(0);
51281
51687
  }, 1e4);
51282
51688
  forceExitTimer.unref?.();
@@ -51286,15 +51692,21 @@ function registerShutdownHandlers(options) {
51286
51692
  await options.tunnelManager.stop();
51287
51693
  await options.meshyNode.stop();
51288
51694
  clearTimeout(forceExitTimer);
51289
- terminalWriter.line("Goodbye!");
51695
+ terminalWriter.line(reason === "restart" ? "Restart handoff complete." : "Goodbye!");
51696
+ stopDevelopmentWatchParent(runtimeMode);
51290
51697
  process.exit(0);
51291
51698
  } catch (err) {
51292
51699
  terminalWriter.error("Error during shutdown:", err);
51293
51700
  process.exit(1);
51294
51701
  }
51295
51702
  }
51296
- process.on("SIGINT", shutdown);
51297
- process.on("SIGTERM", shutdown);
51703
+ process.on("SIGINT", () => void shutdown("shutdown"));
51704
+ process.on("SIGTERM", () => void shutdown("shutdown"));
51705
+ return {
51706
+ requestShutdown: (reason = "shutdown", runtimeMode) => {
51707
+ void shutdown(reason, runtimeMode);
51708
+ }
51709
+ };
51298
51710
  }
51299
51711
  async function startNode(args) {
51300
51712
  const { authMetadata, config, hydratedArgs, runtimeMetadata, storageRoot } = await resolveStartConfig(args);
@@ -51306,7 +51718,7 @@ async function startNode(args) {
51306
51718
  const localDashboardOrigin = `http://localhost:${config.node.port}`;
51307
51719
  const logger27 = createLogger({
51308
51720
  component: "node",
51309
- logDir: nodePath2.join(config.storage.path, "logs"),
51721
+ logDir: nodePath3.join(config.storage.path, "logs"),
51310
51722
  console: true
51311
51723
  });
51312
51724
  const nodeAuth = new AzureCliNodeAuth(config.storage.path, {
@@ -51328,8 +51740,9 @@ async function startNode(args) {
51328
51740
  authMetadata,
51329
51741
  localDashboardOrigin,
51330
51742
  runtimeMetadata,
51743
+ runtimeRestartMode: detectRuntimeRestartMode(),
51331
51744
  storagePath: config.storage.path,
51332
- workDir: nodePath2.resolve(config.node.workDir ?? process.cwd())
51745
+ workDir: nodePath3.resolve(config.node.workDir ?? process.cwd())
51333
51746
  });
51334
51747
  const { getSettingsSnapshot, refreshSettingsSnapshot } = settingsSnapshotProvider;
51335
51748
  const meshyNode = new MeshyNode(config, {
@@ -51345,6 +51758,7 @@ async function startNode(args) {
51345
51758
  const previewSessionManager = new PreviewSessionManager();
51346
51759
  const previewProxyManager = new PreviewProxyManager();
51347
51760
  let deps;
51761
+ let requestShutdownForRestart;
51348
51762
  const tunnelManager = createTunnelManager({
51349
51763
  authMetadata,
51350
51764
  config,
@@ -51421,7 +51835,39 @@ async function startNode(args) {
51421
51835
  isDevTunnelEnabled: () => meshyNode.isDevTunnelEnabled(),
51422
51836
  localDashboardOrigin,
51423
51837
  upgradeRuntimeAgent,
51838
+ restartRuntime: async (request) => {
51839
+ const mode = detectRuntimeRestartMode();
51840
+ const plan = createRuntimeRestartLaunchPlan({
51841
+ mode,
51842
+ cwd: process.cwd(),
51843
+ env: process.env,
51844
+ localDashboardOrigin,
51845
+ startArgs: hydratedArgs.args,
51846
+ storagePath: config.storage.path
51847
+ });
51848
+ scheduleRuntimeRestart(plan, { parentPid: process.pid });
51849
+ const completeShutdown = () => setTimeout(() => requestShutdownForRestart?.("restart", mode), 250).unref?.();
51850
+ if (!request.deferShutdown) completeShutdown();
51851
+ return {
51852
+ ok: true,
51853
+ mode: plan.mode,
51854
+ command: plan.displayCommand,
51855
+ logPath: plan.logPath,
51856
+ pid: process.pid,
51857
+ completeShutdown
51858
+ };
51859
+ },
51860
+ stopRuntime: async () => {
51861
+ const pid = process.pid;
51862
+ const mode = detectRuntimeRestartMode();
51863
+ setTimeout(() => requestShutdownForRestart?.("shutdown", mode), 250).unref?.();
51864
+ return { ok: true, pid };
51865
+ },
51424
51866
  refreshSettingsSnapshot,
51867
+ runtimeUpdate: createRuntimeUpdateSnapshot({
51868
+ mode: detectRuntimeRestartMode(),
51869
+ runtimeMetadata
51870
+ }),
51425
51871
  dashboardOrigin: tunnelManager.getDashboardOrigin(),
51426
51872
  shareOrigin: tunnelManager.getShareOrigin(),
51427
51873
  ensureShareTunnel: () => tunnelManager.ensureShareTunnel(),
@@ -51453,11 +51899,12 @@ async function startNode(args) {
51453
51899
  terminalWriter.line(banner);
51454
51900
  terminalWriter.line("");
51455
51901
  });
51456
- registerShutdownHandlers({
51902
+ const shutdownHandle = registerShutdownHandlers({
51457
51903
  server,
51458
51904
  meshyNode,
51459
51905
  tunnelManager
51460
51906
  });
51907
+ requestShutdownForRestart = shutdownHandle.requestShutdown;
51461
51908
  }
51462
51909
 
51463
51910
  // src/main.ts