meshy-node 0.5.6 → 0.5.8

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.
@@ -1,4 +1,4 @@
1
- import{c as a}from"./index-nG4cAWuh.js";/**
1
+ import{c as a}from"./index-C8QVuMfU.js";/**
2
2
  * @license lucide-react v1.7.0 - ISC
3
3
  *
4
4
  * This source code is licensed under the ISC license.
@@ -5,8 +5,8 @@
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
6
  <title>Meshy Dashboard</title>
7
7
  <link rel="icon" type="image/svg+xml" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>&#x1F578;</text></svg>" />
8
- <script type="module" crossorigin src="/assets/index-nG4cAWuh.js"></script>
9
- <link rel="stylesheet" crossorigin href="/assets/index-DgDWjVGo.css">
8
+ <script type="module" crossorigin src="/assets/index-C8QVuMfU.js"></script>
9
+ <link rel="stylesheet" crossorigin href="/assets/index-BlCzIaoB.css">
10
10
  </head>
11
11
  <body>
12
12
  <div id="root"></div>
package/main.cjs CHANGED
@@ -52275,6 +52275,7 @@ function normalizeDevBoxMetadata(devbox) {
52275
52275
  locationInUri: normalizeString(devbox?.locationInUri),
52276
52276
  poolName: normalizeString(devbox?.poolName),
52277
52277
  scope: normalizeString(devbox?.scope),
52278
+ alwaysOnline: devbox?.alwaysOnline === true,
52278
52279
  lastStatus: normalizeString(devbox?.lastStatus),
52279
52280
  checkedAt: typeof devbox?.checkedAt === "number" && Number.isFinite(devbox.checkedAt) ? devbox.checkedAt : void 0,
52280
52281
  operationId: normalizeString(devbox?.operationId)
@@ -52356,6 +52357,7 @@ function pickDevBoxConfig(devbox) {
52356
52357
  if (devbox.locationInUri) subset.locationInUri = devbox.locationInUri;
52357
52358
  if (devbox.poolName) subset.poolName = devbox.poolName;
52358
52359
  if (devbox.scope) subset.scope = devbox.scope;
52360
+ if (devbox.alwaysOnline !== void 0) subset.alwaysOnline = devbox.alwaysOnline;
52359
52361
  return subset;
52360
52362
  }
52361
52363
  function persistNodeStartupTransport(storagePath, transport) {
@@ -54039,6 +54041,7 @@ function getPublicTaskUpdates(updates, updatedAt) {
54039
54041
  if (updates.description !== void 0) publicUpdates.description = updates.description;
54040
54042
  if (updates.priority !== void 0) publicUpdates.priority = updates.priority;
54041
54043
  if (updates.status !== void 0) publicUpdates.status = updates.status;
54044
+ if (updates.queuedMessages !== void 0) publicUpdates.queuedMessages = updates.queuedMessages;
54042
54045
  if (updates.assignedTo !== void 0) publicUpdates.assignedTo = updates.assignedTo;
54043
54046
  if (updates.assignedNodeName !== void 0) publicUpdates.assignedNodeName = updates.assignedNodeName;
54044
54047
  if (updates.result !== void 0) publicUpdates.result = updates.result;
@@ -54221,6 +54224,61 @@ var TaskEngine = class {
54221
54224
  });
54222
54225
  return true;
54223
54226
  }
54227
+ enqueueTaskMessage(taskId, content) {
54228
+ const task = this.store.getTask(taskId);
54229
+ if (!task) {
54230
+ throw new MeshyError("TASK_NOT_FOUND", `Task ${taskId} not found`, 404);
54231
+ }
54232
+ if (task.status !== "running") {
54233
+ throw new MeshyError(
54234
+ "VALIDATION_ERROR",
54235
+ `Task ${taskId} is not running (status: ${task.status})`,
54236
+ 400
54237
+ );
54238
+ }
54239
+ const now = Date.now();
54240
+ const queuedMessages = [
54241
+ ...task.queuedMessages ?? [],
54242
+ { id: (0, import_node_crypto5.randomUUID)(), content, createdAt: now }
54243
+ ];
54244
+ const updated = this.store.updateTask(taskId, { queuedMessages, updatedAt: now });
54245
+ this.eventBus.emit("task.updated", {
54246
+ taskId,
54247
+ updates: { queuedMessages, updatedAt: now }
54248
+ });
54249
+ return updated;
54250
+ }
54251
+ dequeueTaskMessage(taskId) {
54252
+ const task = this.store.getTask(taskId);
54253
+ if (!task) {
54254
+ throw new MeshyError("TASK_NOT_FOUND", `Task ${taskId} not found`, 404);
54255
+ }
54256
+ const [message, ...queuedMessages] = task.queuedMessages ?? [];
54257
+ if (!message) return null;
54258
+ const now = Date.now();
54259
+ const updated = this.store.updateTask(taskId, { queuedMessages, updatedAt: now });
54260
+ this.eventBus.emit("task.updated", {
54261
+ taskId,
54262
+ updates: { queuedMessages, updatedAt: now }
54263
+ });
54264
+ return { task: updated, message };
54265
+ }
54266
+ removeQueuedTaskMessage(taskId, messageId) {
54267
+ const task = this.store.getTask(taskId);
54268
+ if (!task) {
54269
+ throw new MeshyError("TASK_NOT_FOUND", `Task ${taskId} not found`, 404);
54270
+ }
54271
+ const currentQueue = task.queuedMessages ?? [];
54272
+ const queuedMessages = currentQueue.filter((message) => message.id !== messageId);
54273
+ if (queuedMessages.length === currentQueue.length) return null;
54274
+ const now = Date.now();
54275
+ const updated = this.store.updateTask(taskId, { queuedMessages, updatedAt: now });
54276
+ this.eventBus.emit("task.updated", {
54277
+ taskId,
54278
+ updates: { queuedMessages, updatedAt: now }
54279
+ });
54280
+ return updated;
54281
+ }
54224
54282
  // ── Dispatch failure ─────────────────────────────────────────────────
54225
54283
  /**
54226
54284
  * Handle a dispatch failure for a task that is still in `assigned` status.
@@ -58240,6 +58298,7 @@ var NodeDevBoxInfoSchema = external_exports.object({
58240
58298
  locationInUri: external_exports.string().optional(),
58241
58299
  poolName: external_exports.string().optional(),
58242
58300
  scope: external_exports.string().optional(),
58301
+ alwaysOnline: external_exports.boolean().optional(),
58243
58302
  lastStatus: external_exports.string().optional(),
58244
58303
  checkedAt: external_exports.number().optional(),
58245
58304
  operationId: external_exports.string().optional()
@@ -58535,6 +58594,11 @@ var TaskListResponse = external_exports.object({
58535
58594
  branch: external_exports.string().nullable().optional(),
58536
58595
  conversationKind: external_exports.enum(TASK_CONVERSATION_KIND_OPTIONS).optional(),
58537
58596
  payload: external_exports.record(external_exports.unknown()),
58597
+ queuedMessages: external_exports.array(external_exports.object({
58598
+ id: external_exports.string(),
58599
+ content: external_exports.array(external_exports.record(external_exports.unknown())).min(1),
58600
+ createdAt: external_exports.number()
58601
+ })).optional(),
58538
58602
  status: external_exports.enum(["pending", "assigned", "running", "completed", "failed", "cancelled", "archived"]),
58539
58603
  priority: external_exports.enum(["low", "normal", "high", "critical"]),
58540
58604
  assignedTo: external_exports.string().nullable(),
@@ -58937,6 +59001,7 @@ var import_express = __toESM(require_express2(), 1);
58937
59001
  var sharedMcpControl;
58938
59002
  var DEFAULT_START_POLL_INTERVAL_MS = 1e4;
58939
59003
  var DEFAULT_START_TIMEOUT_MS = 15 * 6e4;
59004
+ var ALWAYS_ONLINE_STARTABLE_DEVBOX_STATUSES = /* @__PURE__ */ new Set(["hibernated", "stopped"]);
58940
59005
  function getDevBoxControl(deps) {
58941
59006
  if (deps.devBoxControl) return deps.devBoxControl;
58942
59007
  sharedMcpControl ??= new McpDevBoxControl(deps.logger);
@@ -58952,6 +59017,7 @@ function mergeDevBoxInfo(current, next) {
58952
59017
  locationInUri: merged.locationInUri,
58953
59018
  poolName: merged.poolName,
58954
59019
  scope: merged.scope,
59020
+ alwaysOnline: merged.alwaysOnline,
58955
59021
  lastStatus: merged.lastStatus,
58956
59022
  checkedAt: merged.checkedAt,
58957
59023
  operationId: merged.operationId
@@ -59022,7 +59088,14 @@ async function waitForNodeDevBoxRunning(deps, nodeId, initial, options) {
59022
59088
  }
59023
59089
  }
59024
59090
  function isRunningDevBoxStatus(status) {
59025
- return status?.trim().toLowerCase().replace(/[\s_-]+/gu, "") === "running";
59091
+ return normalizeDevBoxStatus(status) === "running";
59092
+ }
59093
+ function isAlwaysOnlineStartableDevBoxStatus(status) {
59094
+ const normalized = normalizeDevBoxStatus(status);
59095
+ return Boolean(normalized && ALWAYS_ONLINE_STARTABLE_DEVBOX_STATUSES.has(normalized));
59096
+ }
59097
+ function normalizeDevBoxStatus(status) {
59098
+ return status?.trim().toLowerCase().replace(/[\s_-]+/gu, "");
59026
59099
  }
59027
59100
  function sleep(ms) {
59028
59101
  return new Promise((resolve14) => {
@@ -63666,29 +63739,6 @@ async function cancelRemoteTaskExecution(deps) {
63666
63739
  return response.ok ? null : response;
63667
63740
  }
63668
63741
 
63669
- // ../../packages/api/src/tasks/task-follow-up.ts
63670
- function startLocalTaskFollowUp(deps, task, content, assignedTo, metadata) {
63671
- const engine = deps.engineRegistry.get(task.agent);
63672
- if (!engine) {
63673
- throw new MeshyError("VALIDATION_ERROR", `Engine not registered for agent: ${task.agent}`, 400);
63674
- }
63675
- deps.taskEngine.updateTask(task.id, {
63676
- status: "running",
63677
- assignedTo
63678
- });
63679
- try {
63680
- engine.sendMessage(task.id, content);
63681
- } catch (err) {
63682
- restoreTaskState(deps.taskEngine, task);
63683
- deps.logger.warn("failed to start local follow-up execution", {
63684
- taskId: task.id,
63685
- ...metadata,
63686
- error: err instanceof Error ? err.message : String(err)
63687
- });
63688
- throw err;
63689
- }
63690
- }
63691
-
63692
63742
  // ../../packages/api/src/tasks/task-session-import.ts
63693
63743
  var TASK_IMPORT_PROXY_TIMEOUT_MS = 1e4;
63694
63744
  function buildImportedSessionTitle(agent, sessionId) {
@@ -63992,6 +64042,153 @@ async function sendTaskLogsResponse(req, res, taskId) {
63992
64042
  res.json(local);
63993
64043
  }
63994
64044
 
64045
+ // ../../packages/api/src/tasks/task-follow-up.ts
64046
+ function startLocalTaskFollowUp(deps, task, content, assignedTo, metadata) {
64047
+ const engine = deps.engineRegistry.get(task.agent);
64048
+ if (!engine) {
64049
+ throw new MeshyError("VALIDATION_ERROR", `Engine not registered for agent: ${task.agent}`, 400);
64050
+ }
64051
+ deps.taskEngine.updateTask(task.id, {
64052
+ status: "running",
64053
+ assignedTo
64054
+ });
64055
+ try {
64056
+ engine.sendMessage(task.id, content);
64057
+ } catch (err) {
64058
+ restoreTaskState(deps.taskEngine, task);
64059
+ deps.logger.warn("failed to start local follow-up execution", {
64060
+ taskId: task.id,
64061
+ ...metadata,
64062
+ error: err instanceof Error ? err.message : String(err)
64063
+ });
64064
+ throw err;
64065
+ }
64066
+ }
64067
+
64068
+ // ../../packages/api/src/tasks/task-message-queue.ts
64069
+ var registeredDrainers = /* @__PURE__ */ new WeakSet();
64070
+ var drainingTaskIds = /* @__PURE__ */ new Set();
64071
+ var QUEUE_DRAIN_STATUSES = /* @__PURE__ */ new Set(["completed", "cancelled"]);
64072
+ function restoreQueuedMessage(taskEngine, taskId, message) {
64073
+ const current = taskEngine.getTask(taskId);
64074
+ if (!current) return;
64075
+ taskEngine.updateTask(taskId, {
64076
+ queuedMessages: [message, ...current.queuedMessages ?? []]
64077
+ });
64078
+ }
64079
+ async function sendTaskFollowUpMessage(deps, task, content, metadata) {
64080
+ if (!supportsFollowUpAgent(task.agent)) {
64081
+ throw new MeshyError("VALIDATION_ERROR", `Agent ${task.agent} does not support chat messages`, 400);
64082
+ }
64083
+ if (task.assignedTo && task.assignedTo !== deps.nodeRegistry.getSelf()?.id) {
64084
+ const node = deps.nodeRegistry.getNode(task.assignedTo);
64085
+ const log2 = deps.logger.child("tasks/message");
64086
+ if (!node || node.status === "offline") {
64087
+ log2.warn("assigned worker unavailable for follow-up", {
64088
+ taskId: task.id,
64089
+ assignedTo: task.assignedTo,
64090
+ nodeFound: Boolean(node),
64091
+ nodeStatus: node?.status,
64092
+ ...metadata
64093
+ });
64094
+ throw new MeshyError(
64095
+ "NODE_OFFLINE",
64096
+ `Task ${task.id} is unavailable because ${task.assignedNodeName ?? task.assignedTo} is no longer reachable`,
64097
+ 409
64098
+ );
64099
+ }
64100
+ deps.taskEngine.updateTask(task.id, { status: "running" });
64101
+ log2.info("proxying follow-up message to worker", {
64102
+ taskId: task.id,
64103
+ assignedTo: task.assignedTo,
64104
+ ...metadata
64105
+ });
64106
+ try {
64107
+ const client = new NodeMessageClient({ heartbeat: deps.heartbeat, logger: deps.logger });
64108
+ const delivery = await client.send(node, createNodeMessage("task.message", { taskId: task.id, content }));
64109
+ return delivery.queued ? { queued: true } : {};
64110
+ } catch (err) {
64111
+ restoreTaskState(deps.taskEngine, task);
64112
+ const message = err instanceof Error ? err.message : String(err);
64113
+ log2.warn("failed to proxy follow-up message to worker", {
64114
+ taskId: task.id,
64115
+ assignedTo: task.assignedTo,
64116
+ error: message,
64117
+ ...metadata
64118
+ });
64119
+ throw err instanceof MeshyError ? err : new MeshyError("NODE_OFFLINE", `Failed to proxy follow-up message to worker: ${message}`, 502);
64120
+ }
64121
+ }
64122
+ startLocalTaskFollowUp({
64123
+ taskEngine: deps.taskEngine,
64124
+ engineRegistry: deps.engineRegistry,
64125
+ logger: deps.logger.child("tasks/message")
64126
+ }, task, content, task.assignedTo, metadata);
64127
+ return {};
64128
+ }
64129
+ function queueRunningTaskMessage(deps, task, content) {
64130
+ if (!supportsFollowUpAgent(task.agent)) {
64131
+ throw new MeshyError("VALIDATION_ERROR", `Agent ${task.agent} does not support chat messages`, 400);
64132
+ }
64133
+ const queued = deps.taskEngine.enqueueTaskMessage(task.id, content);
64134
+ deps.logger.child("tasks/message").info("queued follow-up message for running task", {
64135
+ taskId: task.id,
64136
+ queuedCount: queued.queuedMessages?.length ?? 0
64137
+ });
64138
+ return queued;
64139
+ }
64140
+ function cancelQueuedTaskMessage(deps, taskId, messageId) {
64141
+ const task = deps.taskEngine.removeQueuedTaskMessage(taskId, messageId);
64142
+ if (!task) {
64143
+ throw new MeshyError("TASK_NOT_FOUND", `Queued message ${messageId} not found for task ${taskId}`, 404);
64144
+ }
64145
+ deps.logger.child("tasks/message").info("cancelled queued follow-up message", {
64146
+ taskId,
64147
+ queuedMessageId: messageId,
64148
+ queuedCount: task.queuedMessages?.length ?? 0
64149
+ });
64150
+ return task;
64151
+ }
64152
+ function registerTaskMessageQueueDrainer(deps) {
64153
+ if (registeredDrainers.has(deps.eventBus)) return;
64154
+ registeredDrainers.add(deps.eventBus);
64155
+ deps.eventBus.on("task.status", (data) => {
64156
+ if (!QUEUE_DRAIN_STATUSES.has(data.status)) return;
64157
+ void drainNextQueuedTaskMessage(deps, data.taskId);
64158
+ });
64159
+ }
64160
+ async function drainNextQueuedTaskMessage(deps, taskId) {
64161
+ if (drainingTaskIds.has(taskId)) return false;
64162
+ const task = deps.taskEngine.getTask(taskId);
64163
+ if (!task || !QUEUE_DRAIN_STATUSES.has(task.status) || !task.queuedMessages?.length) {
64164
+ return false;
64165
+ }
64166
+ const dequeued = deps.taskEngine.dequeueTaskMessage(taskId);
64167
+ if (!dequeued) return false;
64168
+ drainingTaskIds.add(taskId);
64169
+ try {
64170
+ await sendTaskFollowUpMessage(deps, dequeued.task, dequeued.message.content, {
64171
+ queuedMessageId: dequeued.message.id
64172
+ });
64173
+ deps.logger.child("tasks/message").info("started queued follow-up message", {
64174
+ taskId,
64175
+ queuedMessageId: dequeued.message.id,
64176
+ remainingQueuedCount: dequeued.task.queuedMessages?.length ?? 0
64177
+ });
64178
+ return true;
64179
+ } catch (err) {
64180
+ restoreQueuedMessage(deps.taskEngine, taskId, dequeued.message);
64181
+ deps.logger.child("tasks/message").warn("failed to start queued follow-up message", {
64182
+ taskId,
64183
+ queuedMessageId: dequeued.message.id,
64184
+ error: err instanceof Error ? err.message : String(err)
64185
+ });
64186
+ return false;
64187
+ } finally {
64188
+ drainingTaskIds.delete(taskId);
64189
+ }
64190
+ }
64191
+
63995
64192
  // ../../packages/api/src/routes/task-output.ts
63996
64193
  var import_express8 = __toESM(require_express2(), 1);
63997
64194
 
@@ -64820,63 +65017,39 @@ function createTaskRoutes() {
64820
65017
  router.get("/:id/logs", asyncHandler7(async (req, res) => {
64821
65018
  await sendTaskLogsResponse(req, res, req.params.id);
64822
65019
  }));
65020
+ router.delete("/:id/queued-messages/:messageId", asyncHandler7(async (req, res) => {
65021
+ const { taskEngine, logger: rootLogger } = req.app.locals.deps;
65022
+ const task = cancelQueuedTaskMessage(
65023
+ { taskEngine, logger: rootLogger },
65024
+ req.params.id,
65025
+ req.params.messageId
65026
+ );
65027
+ res.json({ ok: true, queuedMessages: task.queuedMessages ?? [] });
65028
+ }));
64823
65029
  router.post("/:id/message", asyncHandler7(async (req, res) => {
64824
65030
  const { taskEngine, engineRegistry, nodeRegistry, heartbeat, logger: rootLogger } = req.app.locals.deps;
64825
- const log2 = rootLogger.child("tasks/message");
64826
65031
  const body = SendMessageBody.parse(req.body);
64827
65032
  const taskId = req.params.id;
64828
65033
  const task = taskEngine.getTask(taskId);
64829
65034
  if (!task) {
64830
65035
  throw new MeshyError("TASK_NOT_FOUND", `Task ${taskId} not found`, 404);
64831
65036
  }
64832
- if (!supportsFollowUpAgent(task.agent)) {
64833
- throw new MeshyError("VALIDATION_ERROR", `Agent ${task.agent} does not support chat messages`, 400);
64834
- }
64835
65037
  if (!["running", "completed", "cancelled"].includes(task.status)) {
64836
65038
  throw new MeshyError("VALIDATION_ERROR", `Cannot send message to task in ${task.status} status`, 400);
64837
65039
  }
64838
- if (task.assignedTo && task.assignedTo !== nodeRegistry.getSelf()?.id) {
64839
- const node = nodeRegistry.getNode(task.assignedTo);
64840
- if (!node || node.status === "offline") {
64841
- log2.warn("assigned worker unavailable for follow-up", {
64842
- taskId: task.id,
64843
- assignedTo: task.assignedTo,
64844
- nodeFound: Boolean(node),
64845
- nodeStatus: node?.status
64846
- });
64847
- throw new MeshyError(
64848
- "NODE_OFFLINE",
64849
- `Task ${task.id} is unavailable because ${task.assignedNodeName ?? task.assignedTo} is no longer reachable`,
64850
- 409
64851
- );
64852
- }
64853
- taskEngine.updateTask(task.id, { status: "running" });
64854
- log2.info("proxying follow-up message to worker", {
64855
- taskId: task.id,
64856
- assignedTo: task.assignedTo
64857
- });
64858
- try {
64859
- const client = new NodeMessageClient({ heartbeat, logger: rootLogger });
64860
- const delivery = await client.send(node, createNodeMessage("task.message", { taskId: task.id, content: body.content }));
64861
- res.json(delivery.queued ? { ok: true, queued: true } : { ok: true });
64862
- return;
64863
- } catch (err) {
64864
- restoreTaskState(taskEngine, task);
64865
- const message = err instanceof Error ? err.message : String(err);
64866
- log2.warn("failed to proxy follow-up message to worker", {
64867
- taskId: task.id,
64868
- assignedTo: task.assignedTo,
64869
- error: message
64870
- });
64871
- throw err instanceof MeshyError ? err : new MeshyError("NODE_OFFLINE", `Failed to proxy follow-up message to worker: ${message}`, 502);
64872
- }
65040
+ if (task.status === "running") {
65041
+ const queued = queueRunningTaskMessage({ taskEngine, logger: rootLogger }, task, body.content);
65042
+ res.json({ ok: true, queued: true, queuedMessages: queued.queuedMessages ?? [] });
65043
+ return;
64873
65044
  }
64874
- startLocalTaskFollowUp({
65045
+ const result = await sendTaskFollowUpMessage({
64875
65046
  taskEngine,
64876
65047
  engineRegistry,
64877
- logger: log2
64878
- }, task, body.content, task.assignedTo);
64879
- res.json({ ok: true });
65048
+ nodeRegistry,
65049
+ heartbeat,
65050
+ logger: rootLogger
65051
+ }, task, body.content);
65052
+ res.json(result.queued ? { ok: true, queued: true } : { ok: true });
64880
65053
  }));
64881
65054
  router.use(createTaskOutputRoutes());
64882
65055
  return router;
@@ -65283,7 +65456,7 @@ function createSystemRoutes() {
65283
65456
  path: req.originalUrl || req.url,
65284
65457
  remoteAddress: req.ip || req.socket.remoteAddress
65285
65458
  });
65286
- res.json({ status: "ok", timestamp: Date.now() });
65459
+ res.json({ status: "ok", timestamp: Date.now(), pid: process.pid });
65287
65460
  }));
65288
65461
  router.get("/info", asyncHandler10(async (req, res) => {
65289
65462
  const {
@@ -65295,6 +65468,7 @@ function createSystemRoutes() {
65295
65468
  storagePath,
65296
65469
  inspectRuntimeTools: inspectTools,
65297
65470
  runtimeMetadata,
65471
+ getRuntimeUpdate,
65298
65472
  runtimeUpdate
65299
65473
  } = req.app.locals.deps;
65300
65474
  const self2 = nodeRegistry.getSelf();
@@ -65308,7 +65482,7 @@ function createSystemRoutes() {
65308
65482
  inspectRuntimeTools: inspectTools,
65309
65483
  localDashboardOrigin,
65310
65484
  runtimeMetadata,
65311
- runtimeUpdate,
65485
+ runtimeUpdate: getRuntimeUpdate?.() ?? runtimeUpdate,
65312
65486
  storagePath,
65313
65487
  workDir: self2.workDir
65314
65488
  });
@@ -65592,6 +65766,7 @@ function resolveStaticDir(baseDir) {
65592
65766
  function createServer2(deps) {
65593
65767
  const app = (0, import_express15.default)();
65594
65768
  app.locals.deps = deps;
65769
+ registerTaskMessageQueueDrainer(deps);
65595
65770
  if (typeof deps.heartbeat.setNodeMessageHandler === "function") {
65596
65771
  deps.heartbeat.setNodeMessageHandler((message) => handleNodeMessage(deps, message));
65597
65772
  }
@@ -65730,11 +65905,25 @@ function createDevBoxStatusPoller(deps, options = {}) {
65730
65905
  const checkedAt = node.devbox?.checkedAt;
65731
65906
  return typeof checkedAt !== "number" || now - checkedAt >= staleAfterMs;
65732
65907
  }
65908
+ function hasActiveStartOperation(nodeId) {
65909
+ const operationService = getNodeOperationService(deps);
65910
+ return operationService.list({ kind: "devbox.start", status: "queued" }).some((operation) => operation.nodeId === nodeId) || operationService.list({ kind: "devbox.start", status: "running" }).some((operation) => operation.nodeId === nodeId);
65911
+ }
65912
+ function maybeStartAlwaysOnlineDevBox(node, refreshedStatus) {
65913
+ if (node.status !== "offline") return;
65914
+ if (node.devbox?.alwaysOnline !== true) return;
65915
+ if (!isAlwaysOnlineStartableDevBoxStatus(refreshedStatus)) return;
65916
+ if (hasActiveStartOperation(node.id)) return;
65917
+ const operationService = getNodeOperationService(deps);
65918
+ const operation = operationService.create("devbox.start", node.id, { previousStatus: refreshedStatus ?? "" });
65919
+ operationService.runLocal(operation.id);
65920
+ }
65733
65921
  async function refreshNode(node) {
65734
65922
  if (inFlight.has(node.id)) return;
65735
65923
  inFlight.add(node.id);
65736
65924
  try {
65737
- await refreshNodeDevBoxStatus(deps, node);
65925
+ const refreshed = await refreshNodeDevBoxStatus(deps, node);
65926
+ maybeStartAlwaysOnlineDevBox(node, refreshed.lastStatus);
65738
65927
  } catch (err) {
65739
65928
  logger27.debug(`refresh failed for ${node.name}: ${err instanceof Error ? err.message : String(err)}`);
65740
65929
  } finally {
@@ -66804,6 +66993,7 @@ function buildRuntimeMetadata(storagePath) {
66804
66993
  var fs22 = __toESM(require("fs"), 1);
66805
66994
  var nodePath2 = __toESM(require("path"), 1);
66806
66995
  var import_node_child_process13 = require("child_process");
66996
+ var RUNTIME_UPDATE_CHECK_INTERVAL_MS = 6 * 60 * 60 * 1e3;
66807
66997
  function resolveRuntimeRestartStartArgs(hydratedArgs, restartArgs) {
66808
66998
  return restartArgs ? { ...restartArgs } : hydratedArgs;
66809
66999
  }
@@ -66886,12 +67076,6 @@ async function waitForEndpointDown() {
66886
67076
  process.exit(1);
66887
67077
  });
66888
67078
  `;
66889
- function formatLocalDate3(date3) {
66890
- const year = date3.getFullYear();
66891
- const month = String(date3.getMonth() + 1).padStart(2, "0");
66892
- const day = String(date3.getDate()).padStart(2, "0");
66893
- return `${year}-${month}-${day}`;
66894
- }
66895
67079
  function normalizeOutput2(output) {
66896
67080
  if (!output) return null;
66897
67081
  const firstLine = output.split(/\r?\n/).map((line) => line.trim()).find(Boolean);
@@ -67080,18 +67264,18 @@ function createRuntimeUpdateSnapshot(options) {
67080
67264
  };
67081
67265
  }
67082
67266
  const cache = options.updateCache ?? runtimeUpdateCache;
67083
- const checkedOn = formatLocalDate3(now);
67267
+ const nowMs = now.getTime();
67084
67268
  const cached2 = cache.get(packageName);
67085
67269
  let latestVersion = cached2?.latestVersion ?? null;
67086
67270
  let detail = cached2?.detail ?? null;
67087
67271
  let updateCheckedAt = cached2?.checkedAt ?? checkedAt;
67088
- if (cached2?.checkedOn !== checkedOn) {
67272
+ if (!cached2 || nowMs - cached2.checkedAtMs >= RUNTIME_UPDATE_CHECK_INTERVAL_MS) {
67089
67273
  const runner = options.commandRunner ?? runRuntimePackageCommand;
67090
67274
  const result = runner("npm", ["view", `${packageName}@latest`, "version", "--json"]);
67091
67275
  latestVersion = result.status === 0 ? normalizePackageVersion2(result.stdout) : null;
67092
67276
  detail = result.status === 0 ? null : normalizeOutput2(result.stderr) ?? result.error ?? "Runtime update check failed";
67093
67277
  updateCheckedAt = checkedAt;
67094
- cache.set(packageName, { checkedOn, checkedAt, latestVersion, detail });
67278
+ cache.set(packageName, { checkedAt, checkedAtMs: nowMs, latestVersion, detail });
67095
67279
  }
67096
67280
  return {
67097
67281
  packageName,
@@ -67669,11 +67853,12 @@ async function startNode(args) {
67669
67853
  } else {
67670
67854
  setRequestAuthHeadersProvider(null);
67671
67855
  }
67856
+ const runtimeRestartMode = detectRuntimeRestartMode();
67672
67857
  const settingsSnapshotProvider = createSettingsSnapshotProvider({
67673
67858
  authMetadata,
67674
67859
  localDashboardOrigin,
67675
67860
  runtimeMetadata,
67676
- runtimeRestartMode: detectRuntimeRestartMode(),
67861
+ runtimeRestartMode,
67677
67862
  storagePath: config2.storage.path,
67678
67863
  workDir: nodePath3.resolve(config2.node.workDir ?? process.cwd())
67679
67864
  });
@@ -67797,8 +67982,8 @@ async function startNode(args) {
67797
67982
  return { ok: true, pid };
67798
67983
  },
67799
67984
  refreshSettingsSnapshot,
67800
- runtimeUpdate: createRuntimeUpdateSnapshot({
67801
- mode: detectRuntimeRestartMode(),
67985
+ getRuntimeUpdate: () => createRuntimeUpdateSnapshot({
67986
+ mode: runtimeRestartMode,
67802
67987
  runtimeMetadata
67803
67988
  }),
67804
67989
  dashboardOrigin: tunnelManager.getDashboardOrigin(),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "meshy-node",
3
- "version": "0.5.6",
3
+ "version": "0.5.8",
4
4
  "private": false,
5
5
  "description": "Standalone Meshy node package with bundled runtime and dashboard assets.",
6
6
  "type": "commonjs",
@@ -1,14 +1,14 @@
1
1
  {
2
2
  "packageName": "meshy-node",
3
- "packageVersion": "0.5.6",
3
+ "packageVersion": "0.5.8",
4
4
  "packages": {
5
5
  "workspace": {
6
6
  "name": "meshy",
7
- "version": "0.5.6"
7
+ "version": "0.5.8"
8
8
  },
9
9
  "node": {
10
10
  "name": "meshy-node",
11
- "version": "0.5.6"
11
+ "version": "0.5.8"
12
12
  },
13
13
  "core": {
14
14
  "name": "@meshy/core",
@@ -25,7 +25,7 @@
25
25
  },
26
26
  "repository": {
27
27
  "url": "https://github.com/ai-microsoft/meshy",
28
- "branch": "FixDevBoxNaming",
29
- "commit": "e313b30"
28
+ "branch": "RestartOpt",
29
+ "commit": "160a1d3"
30
30
  }
31
31
  }