meshy-node 0.3.2 → 0.3.3

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
@@ -34555,6 +34555,7 @@ var NODE_KIND_BY_LEGACY = {
34555
34555
  "node-workdir-branch-create": "node.workdir.branch-create",
34556
34556
  "node-sessions-list": "node.sessions.list",
34557
34557
  devtunnel: "node.transport.set",
34558
+ "node-agent-upgrade": "node.agent.upgrade",
34558
34559
  "task-cancel": "task.cancel",
34559
34560
  "task-logs": "task.logs",
34560
34561
  "task-output-summary": "task.output.summary",
@@ -35601,6 +35602,9 @@ function compareUpdatedSessions(left, right) {
35601
35602
  const rightTime = right.updatedAt ? Date.parse(right.updatedAt) : 0;
35602
35603
  return rightTime - leftTime || left.sessionId.localeCompare(right.sessionId);
35603
35604
  }
35605
+ function getNativeHomeDir() {
35606
+ return process.env.HOME || process.env.USERPROFILE || os4.homedir();
35607
+ }
35604
35608
  function findMatchingFiles(rootDir, predicate) {
35605
35609
  if (!fs7.existsSync(rootDir)) {
35606
35610
  return [];
@@ -35747,7 +35751,7 @@ function findClaudeSessionSummary(entries, sessionId) {
35747
35751
  };
35748
35752
  }
35749
35753
  function loadCodexSessionIndex() {
35750
- const indexPath = path7.join(os4.homedir(), ".codex", "session_index.jsonl");
35754
+ const indexPath = path7.join(getNativeHomeDir(), ".codex", "session_index.jsonl");
35751
35755
  const index = /* @__PURE__ */ new Map();
35752
35756
  for (const entry of readJsonLines(indexPath)) {
35753
35757
  if (typeof entry.id !== "string" || !entry.id.trim()) {
@@ -35797,7 +35801,7 @@ function findCodexSessionSummary(entries, filePath, sessionIndex) {
35797
35801
  };
35798
35802
  }
35799
35803
  function loadClaudeSessionHistory(sessionId) {
35800
- const projectsDir = path7.join(os4.homedir(), ".claude", "projects");
35804
+ const projectsDir = path7.join(getNativeHomeDir(), ".claude", "projects");
35801
35805
  const sessionFile = findFirstMatchingFile(projectsDir, (filePath) => {
35802
35806
  if (filePath.includes(`${path7.sep}subagents${path7.sep}`)) {
35803
35807
  return false;
@@ -35816,7 +35820,7 @@ function loadClaudeSessionHistory(sessionId) {
35816
35820
  return { logs, sourcePath: sessionFile };
35817
35821
  }
35818
35822
  function loadCodexSessionHistory(sessionId) {
35819
- const sessionsDir = path7.join(os4.homedir(), ".codex", "sessions");
35823
+ const sessionsDir = path7.join(getNativeHomeDir(), ".codex", "sessions");
35820
35824
  const sessionFile = findFirstMatchingFile(sessionsDir, (filePath) => path7.basename(filePath).toLowerCase().includes(sessionId.toLowerCase()) && filePath.toLowerCase().endsWith(".jsonl"));
35821
35825
  if (!sessionFile) {
35822
35826
  return { logs: [], sourcePath: null };
@@ -35852,7 +35856,7 @@ function loadNativeSessionHistory(input) {
35852
35856
  function listNativeSessions(input) {
35853
35857
  const limit = Math.min(Math.max(1, input.limit ?? 50), 100);
35854
35858
  if (input.agent === "claudecode") {
35855
- const projectsDir = path7.join(os4.homedir(), ".claude", "projects");
35859
+ const projectsDir = path7.join(getNativeHomeDir(), ".claude", "projects");
35856
35860
  return findMatchingFiles(projectsDir, (filePath) => {
35857
35861
  if (filePath.includes(`${path7.sep}subagents${path7.sep}`)) {
35858
35862
  return false;
@@ -35871,7 +35875,7 @@ function listNativeSessions(input) {
35871
35875
  }).filter((session) => session !== null).sort(compareUpdatedSessions).slice(0, limit);
35872
35876
  }
35873
35877
  const sessionIndex = loadCodexSessionIndex();
35874
- const sessionsDir = path7.join(os4.homedir(), ".codex", "sessions");
35878
+ const sessionsDir = path7.join(getNativeHomeDir(), ".codex", "sessions");
35875
35879
  return findMatchingFiles(sessionsDir, (filePath) => filePath.toLowerCase().endsWith(".jsonl")).map((filePath) => {
35876
35880
  const summary = findCodexSessionSummary(readJsonLines(filePath), filePath, sessionIndex);
35877
35881
  if (!summary) {
@@ -38287,7 +38291,7 @@ async function apiFetch(ctx, port, apiPath) {
38287
38291
  const body = await res.json().catch(() => null);
38288
38292
  throw new Error(body?.error?.message ?? `API error: ${res.status} ${res.statusText}`);
38289
38293
  }
38290
- return res.json();
38294
+ return await res.json();
38291
38295
  }
38292
38296
  async function apiWrite(ctx, method, port, apiPath, body) {
38293
38297
  const res = await ctx.fetchImpl(`http://localhost:${port}${apiPath}`, {
@@ -38299,7 +38303,7 @@ async function apiWrite(ctx, method, port, apiPath, body) {
38299
38303
  const data = await res.json().catch(() => null);
38300
38304
  throw new Error(data?.error?.message ?? `HTTP ${res.status}`);
38301
38305
  }
38302
- return res.json();
38306
+ return await res.json();
38303
38307
  }
38304
38308
  function writeLines(stream, lines) {
38305
38309
  stream.write(`${lines.join("\n")}
@@ -42827,7 +42831,15 @@ var SystemAgentInfoSchema = external_exports.object({
42827
42831
  version: external_exports.string().nullable(),
42828
42832
  checkedAt: external_exports.string(),
42829
42833
  detail: external_exports.string().nullable().optional(),
42830
- metadataStatus: external_exports.string().nullable().optional()
42834
+ metadataStatus: external_exports.string().nullable().optional(),
42835
+ update: external_exports.object({
42836
+ packageName: external_exports.string(),
42837
+ currentVersion: external_exports.string().nullable(),
42838
+ latestVersion: external_exports.string().nullable(),
42839
+ updateAvailable: external_exports.boolean(),
42840
+ checkedAt: external_exports.string(),
42841
+ detail: external_exports.string().nullable().optional()
42842
+ }).optional()
42831
42843
  });
42832
42844
  var NodeSettingsSnapshotSchema = external_exports.object({
42833
42845
  collectedAt: external_exports.number(),
@@ -44152,7 +44164,7 @@ function getGitDiff(dirPath) {
44152
44164
  const branch = git2(["rev-parse", "--abbrev-ref", "HEAD"], dirPath) || null;
44153
44165
  const staged = git2(["diff", "--cached"], dirPath);
44154
44166
  const unstaged = git2(["diff"], dirPath);
44155
- const statusOutput = git2(["status", "--porcelain"], dirPath);
44167
+ const statusOutput = git2(["status", "--porcelain", "-uall"], dirPath);
44156
44168
  const changedFiles = [];
44157
44169
  const untrackedFiles = [];
44158
44170
  for (const line of statusOutput.split("\n")) {
@@ -44385,6 +44397,7 @@ var LEGACY_KIND_BY_NODE_MESSAGE = {
44385
44397
  "node.workdir.branch-create": "node-workdir-branch-create",
44386
44398
  "node.sessions.list": "node-sessions-list",
44387
44399
  "node.transport.set": "devtunnel",
44400
+ "node.agent.upgrade": "node-agent-upgrade",
44388
44401
  "task.cancel": "task-cancel",
44389
44402
  "task.logs": "task-logs",
44390
44403
  "task.output.summary": "task-output-summary",
@@ -45144,6 +45157,289 @@ async function createPreviewSessionPayload(deps, taskId, entryPath, requestOrigi
45144
45157
  };
45145
45158
  }
45146
45159
 
45160
+ // ../../packages/api/src/node/agent-upgrade-service.ts
45161
+ var import_node_child_process9 = require("child_process");
45162
+
45163
+ // ../../packages/api/src/app/system-info.ts
45164
+ var os6 = __toESM(require("os"), 1);
45165
+ var import_node_child_process8 = require("child_process");
45166
+ var RUNTIME_TOOLS = [
45167
+ { id: "claude", label: "Claude Code", command: "claude", packageName: "@anthropic-ai/claude-code" },
45168
+ { id: "codex", label: "Codex", command: "codex", packageName: "@openai/codex" }
45169
+ ];
45170
+ var runtimeToolUpdateCache = /* @__PURE__ */ new Map();
45171
+ function clearRuntimeToolUpdateCache(packageName) {
45172
+ if (packageName) {
45173
+ runtimeToolUpdateCache.delete(packageName);
45174
+ return;
45175
+ }
45176
+ runtimeToolUpdateCache.clear();
45177
+ }
45178
+ function runRuntimeToolCommand(command, args, platform2) {
45179
+ const result = (0, import_node_child_process8.spawnSync)(command, args, {
45180
+ encoding: "utf-8",
45181
+ shell: platform2 === "win32" && command !== "where",
45182
+ windowsHide: true,
45183
+ timeout: 2500
45184
+ });
45185
+ return {
45186
+ status: result.status,
45187
+ stdout: typeof result.stdout === "string" ? result.stdout : "",
45188
+ stderr: typeof result.stderr === "string" ? result.stderr : "",
45189
+ error: result.error?.message ?? null
45190
+ };
45191
+ }
45192
+ function normalizeOutput(output) {
45193
+ if (!output) {
45194
+ return null;
45195
+ }
45196
+ const firstLine = output.split(/\r?\n/).map((line) => line.trim()).find(Boolean);
45197
+ return firstLine ?? null;
45198
+ }
45199
+ function formatLocalDate(date) {
45200
+ const year = date.getFullYear();
45201
+ const month = String(date.getMonth() + 1).padStart(2, "0");
45202
+ const day = String(date.getDate()).padStart(2, "0");
45203
+ return `${year}-${month}-${day}`;
45204
+ }
45205
+ function resolveCommandPath(command, commandRunner, platform2) {
45206
+ const lookupCommand = platform2 === "win32" ? "where" : "which";
45207
+ const result = commandRunner(lookupCommand, [command]);
45208
+ if (result.status === 0) {
45209
+ return { path: normalizeOutput(result.stdout) };
45210
+ }
45211
+ return {
45212
+ path: null,
45213
+ detail: normalizeOutput(result.stderr) ?? result.error ?? null
45214
+ };
45215
+ }
45216
+ function extractSemver(value) {
45217
+ return value?.match(/\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?/)?.[0] ?? null;
45218
+ }
45219
+ function normalizePackageVersion(output) {
45220
+ if (!output) return null;
45221
+ const trimmed = output.trim();
45222
+ try {
45223
+ const parsed = JSON.parse(trimmed);
45224
+ if (typeof parsed === "string") {
45225
+ return parsed.trim() || null;
45226
+ }
45227
+ } catch {
45228
+ }
45229
+ return normalizeOutput(trimmed);
45230
+ }
45231
+ function normalizeInstalledPackageVersion(output, packageName) {
45232
+ if (!output) return null;
45233
+ const trimmed = output.trim();
45234
+ try {
45235
+ const parsed = JSON.parse(trimmed);
45236
+ const version2 = parsed.dependencies?.[packageName]?.version;
45237
+ if (typeof version2 === "string") {
45238
+ return version2.trim() || null;
45239
+ }
45240
+ } catch {
45241
+ }
45242
+ return extractSemver(normalizeOutput(trimmed));
45243
+ }
45244
+ function resolveInstalledPackageVersion(definition, commandRunner) {
45245
+ const result = commandRunner("npm", ["list", "-g", definition.packageName, "--depth=0", "--json"]);
45246
+ return normalizeInstalledPackageVersion(result.stdout ?? null, definition.packageName);
45247
+ }
45248
+ function compareSemver(left, right) {
45249
+ const leftParts = left.split(/[.-]/).map((part) => Number.parseInt(part, 10));
45250
+ const rightParts = right.split(/[.-]/).map((part) => Number.parseInt(part, 10));
45251
+ for (let index = 0; index < 3; index += 1) {
45252
+ const delta = (leftParts[index] || 0) - (rightParts[index] || 0);
45253
+ if (delta !== 0) return delta;
45254
+ }
45255
+ return 0;
45256
+ }
45257
+ function checkToolUpdate(definition, installedVersion, checkedAt, commandRunner, checkedOn, updateCache) {
45258
+ const currentVersion = extractSemver(installedVersion);
45259
+ const cached = updateCache.get(definition.packageName);
45260
+ let latestVersion = cached?.latestVersion ?? null;
45261
+ let detail = cached?.detail ?? null;
45262
+ let updateCheckedAt = cached?.checkedAt ?? checkedAt;
45263
+ if (cached?.checkedOn !== checkedOn) {
45264
+ const latestResult = commandRunner("npm", ["view", definition.packageName, "version", "--json"]);
45265
+ latestVersion = latestResult.status === 0 ? normalizePackageVersion(latestResult.stdout ?? null) : null;
45266
+ detail = latestResult.status === 0 ? null : normalizeOutput(latestResult.stderr) ?? latestResult.error ?? "Update check failed";
45267
+ updateCheckedAt = checkedAt;
45268
+ updateCache.set(definition.packageName, {
45269
+ checkedOn,
45270
+ checkedAt,
45271
+ latestVersion,
45272
+ detail
45273
+ });
45274
+ }
45275
+ return {
45276
+ packageName: definition.packageName,
45277
+ currentVersion,
45278
+ latestVersion,
45279
+ updateAvailable: Boolean(currentVersion && latestVersion && compareSemver(latestVersion, currentVersion) > 0),
45280
+ checkedAt: updateCheckedAt,
45281
+ detail
45282
+ };
45283
+ }
45284
+ function inspectTool(definition, commandRunner, checkedAt, checkedOn, updateCache, platform2) {
45285
+ const resolved = resolveCommandPath(definition.command, commandRunner, platform2);
45286
+ if (!resolved.path) {
45287
+ return {
45288
+ id: definition.id,
45289
+ label: definition.label,
45290
+ command: definition.command,
45291
+ available: false,
45292
+ path: null,
45293
+ version: null,
45294
+ checkedAt,
45295
+ detail: resolved.detail ?? "Command not found on PATH",
45296
+ update: checkToolUpdate(definition, null, checkedAt, commandRunner, checkedOn, updateCache)
45297
+ };
45298
+ }
45299
+ const versionResult = commandRunner(definition.command, ["--version"]);
45300
+ const commandVersion = normalizeOutput(versionResult.stdout) ?? normalizeOutput(versionResult.stderr);
45301
+ const version2 = extractSemver(commandVersion) ? commandVersion : resolveInstalledPackageVersion(definition, commandRunner);
45302
+ const detail = versionResult.status === 0 ? null : normalizeOutput(versionResult.stderr) ?? versionResult.error ?? "Version check failed";
45303
+ return {
45304
+ id: definition.id,
45305
+ label: definition.label,
45306
+ command: definition.command,
45307
+ available: true,
45308
+ path: resolved.path,
45309
+ version: version2,
45310
+ checkedAt,
45311
+ detail,
45312
+ update: checkToolUpdate(definition, version2, checkedAt, commandRunner, checkedOn, updateCache)
45313
+ };
45314
+ }
45315
+ function inspectRuntimeTools(options = {}) {
45316
+ const platform2 = options.platform ?? process.platform;
45317
+ const commandRunner = options.commandRunner ?? ((command, args) => runRuntimeToolCommand(command, args, platform2));
45318
+ const now = options.now ?? /* @__PURE__ */ new Date();
45319
+ const checkedAt = now.toISOString();
45320
+ const checkedOn = formatLocalDate(now);
45321
+ const updateCache = options.updateCache ?? runtimeToolUpdateCache;
45322
+ return RUNTIME_TOOLS.map((definition) => inspectTool(definition, commandRunner, checkedAt, checkedOn, updateCache, platform2));
45323
+ }
45324
+ function getOsSnapshot() {
45325
+ const cpus2 = os6.cpus();
45326
+ return {
45327
+ platform: os6.platform(),
45328
+ release: os6.release(),
45329
+ arch: os6.arch(),
45330
+ hostname: os6.hostname(),
45331
+ cpuModel: cpus2[0]?.model ?? null,
45332
+ cpuCount: cpus2.length,
45333
+ totalMemoryBytes: os6.totalmem(),
45334
+ freeMemoryBytes: os6.freemem()
45335
+ };
45336
+ }
45337
+ function buildNodeSettingsSnapshot(options) {
45338
+ const uptime = process.uptime();
45339
+ const runtimeMetadata = options.runtimeMetadata;
45340
+ const agents = (options.inspectRuntimeTools?.() ?? inspectRuntimeTools()).map((agent) => ({
45341
+ ...agent,
45342
+ metadataStatus: runtimeMetadata?.components?.[agent.id]?.status ?? null
45343
+ }));
45344
+ return {
45345
+ collectedAt: Date.now(),
45346
+ version: runtimeMetadata?.packageVersion ?? "0.1.0",
45347
+ packageName: runtimeMetadata?.packageName ?? "meshy",
45348
+ uptime,
45349
+ auth: options.auth,
45350
+ os: getOsSnapshot(),
45351
+ runtime: {
45352
+ nodeVersion: process.version,
45353
+ pid: process.pid,
45354
+ startedAt: Date.now() - Math.floor(uptime * 1e3),
45355
+ cwd: process.cwd(),
45356
+ workDir: options.workDir ?? null,
45357
+ storagePath: options.storagePath ?? null,
45358
+ localDashboardOrigin: options.localDashboardOrigin ?? null
45359
+ },
45360
+ agents,
45361
+ startupRequirements: {
45362
+ lastCheckedAt: runtimeMetadata?.startupRequirementsLastCheckedAt,
45363
+ lastCheckedOn: runtimeMetadata?.startupRequirementsLastCheckedOn,
45364
+ components: runtimeMetadata?.components ?? {}
45365
+ },
45366
+ components: runtimeMetadata?.components ?? {},
45367
+ repository: runtimeMetadata?.repository ?? {},
45368
+ packages: runtimeMetadata?.packages ?? {}
45369
+ };
45370
+ }
45371
+
45372
+ // ../../packages/api/src/node/agent-upgrade-service.ts
45373
+ var AGENT_PACKAGES = {
45374
+ claude: "@anthropic-ai/claude-code",
45375
+ codex: "@openai/codex"
45376
+ };
45377
+ var OUTPUT_LIMIT = 4e3;
45378
+ function summarizeOutput(value) {
45379
+ const text = value ?? "";
45380
+ return text.length > OUTPUT_LIMIT ? `${text.slice(0, OUTPUT_LIMIT)}\u2026` : text;
45381
+ }
45382
+ function defaultCommandRunner(command, args) {
45383
+ const result = (0, import_node_child_process9.spawnSync)(command, args, {
45384
+ encoding: "utf-8",
45385
+ shell: process.platform === "win32",
45386
+ windowsHide: true,
45387
+ timeout: 18e4
45388
+ });
45389
+ return {
45390
+ status: result.status,
45391
+ stdout: typeof result.stdout === "string" ? result.stdout : "",
45392
+ stderr: typeof result.stderr === "string" ? result.stderr : "",
45393
+ error: result.error?.message ?? null
45394
+ };
45395
+ }
45396
+ function isRuntimeAgentId(value) {
45397
+ return value === "claude" || value === "codex";
45398
+ }
45399
+ function buildUpgradeArgs(agent, packageName) {
45400
+ const args = ["install", "-g", `${packageName}@latest`];
45401
+ if (agent === "claude") {
45402
+ args.push("--include=optional", "--ignore-scripts=false");
45403
+ }
45404
+ return args;
45405
+ }
45406
+ function upgradeRuntimeAgent(agent, options = {}) {
45407
+ const packageName = AGENT_PACKAGES[agent];
45408
+ if (!packageName) {
45409
+ throw new MeshyError("VALIDATION_ERROR", `Unsupported upgrade agent: ${agent}`, 400);
45410
+ }
45411
+ const command = "npm";
45412
+ const args = buildUpgradeArgs(agent, packageName);
45413
+ const result = (options.commandRunner ?? defaultCommandRunner)(command, args);
45414
+ const ok = result.status === 0 && !result.error;
45415
+ if (ok) {
45416
+ clearRuntimeToolUpdateCache(packageName);
45417
+ }
45418
+ const detail = result.error ?? (summarizeOutput(result.stderr) || "Agent upgrade failed");
45419
+ return {
45420
+ ok,
45421
+ agent,
45422
+ packageName,
45423
+ command,
45424
+ args,
45425
+ status: result.status,
45426
+ stdout: summarizeOutput(result.stdout),
45427
+ stderr: summarizeOutput(result.stderr),
45428
+ detail: ok ? null : detail
45429
+ };
45430
+ }
45431
+ function upgradeRuntimeAgentForDeps(deps, agent) {
45432
+ const result = deps.upgradeRuntimeAgent?.(agent) ?? upgradeRuntimeAgent(agent);
45433
+ if (result.ok) {
45434
+ const settingsSnapshot = deps.refreshSettingsSnapshot?.();
45435
+ if (settingsSnapshot) {
45436
+ deps.nodeRegistry.updateSettingsSnapshot(deps.nodeRegistry.getSelf().id, settingsSnapshot);
45437
+ return { ...result, settingsSnapshot };
45438
+ }
45439
+ }
45440
+ return result;
45441
+ }
45442
+
45147
45443
  // ../../packages/api/src/node/worker-control.ts
45148
45444
  function jsonResponse(requestId, statusCode, body) {
45149
45445
  return {
@@ -45303,6 +45599,14 @@ async function executeWorkerControlRequest(deps, request) {
45303
45599
  });
45304
45600
  break;
45305
45601
  }
45602
+ case "node.agent.upgrade": {
45603
+ const agent = payloadValue(request, "agent", "");
45604
+ if (!isRuntimeAgentId(agent)) {
45605
+ throw new MeshyError("VALIDATION_ERROR", `Unsupported upgrade agent: ${agent}`, 400);
45606
+ }
45607
+ response = jsonResponse(request.id, 200, upgradeRuntimeAgentForDeps(deps, agent));
45608
+ break;
45609
+ }
45306
45610
  case "task.cancel": {
45307
45611
  const taskId = payloadValue(request, "taskId", "");
45308
45612
  const result = cancelTaskOnCurrentNode({
@@ -45446,6 +45750,84 @@ function sendLocalNodeNativeSessions(req, res, nodeId, options = {}) {
45446
45750
  res.json(getLocalNodeNativeSessions(self2.id, query.agent, query.limit));
45447
45751
  }
45448
45752
 
45753
+ // ../../packages/api/src/routes/node-agent-upgrade.ts
45754
+ function isRecord5(value) {
45755
+ return typeof value === "object" && value !== null && !Array.isArray(value);
45756
+ }
45757
+ function readSettingsSnapshot(value) {
45758
+ if (!isRecord5(value)) return void 0;
45759
+ const snapshot = value.settingsSnapshot;
45760
+ if (!isRecord5(snapshot)) return void 0;
45761
+ return snapshot;
45762
+ }
45763
+ function applySyncedSettingsSnapshot(deps, nodeId, body) {
45764
+ const settingsSnapshot = readSettingsSnapshot(body);
45765
+ if (settingsSnapshot) {
45766
+ deps.nodeRegistry.updateSettingsSnapshot(nodeId, settingsSnapshot);
45767
+ }
45768
+ }
45769
+ function parseAgent(value) {
45770
+ if (isRuntimeAgentId(value)) return value;
45771
+ throw new MeshyError("VALIDATION_ERROR", `Unsupported upgrade agent: ${value}`, 400);
45772
+ }
45773
+ function upgradeSelfNode(deps, nodeId, agent) {
45774
+ const result = upgradeRuntimeAgentForDeps(deps, agent);
45775
+ applySyncedSettingsSnapshot(deps, nodeId, result);
45776
+ return result;
45777
+ }
45778
+ async function fallbackUpgrade(deps, res, nodeId, agent) {
45779
+ if (!canRequestNodeMessage(deps.heartbeat)) {
45780
+ throw new MeshyError("NODE_OFFLINE", `Cannot reach node ${nodeId} to upgrade ${agent}`, 502);
45781
+ }
45782
+ const controlResponse = await requestFallbackNodeMessage(
45783
+ deps.heartbeat,
45784
+ nodeId,
45785
+ createNodeMessage("node.agent.upgrade", { agent }, { expectsResponse: true })
45786
+ );
45787
+ applySyncedSettingsSnapshot(deps, nodeId, controlResponse.body);
45788
+ sendWorkerControlResponse(res, controlResponse);
45789
+ }
45790
+ async function proxyUpgrade(deps, nodeId, agent) {
45791
+ const node = deps.nodeRegistry.getNode(nodeId);
45792
+ if (!node) {
45793
+ throw new MeshyError("NODE_NOT_FOUND", `Node ${nodeId} not found`, 404);
45794
+ }
45795
+ const { response } = await fetchNodeWithFallback(node, `/api/nodes/${nodeId}/agents/${agent}/upgrade`, {
45796
+ method: "POST",
45797
+ headers: { "Content-Type": "application/json" }
45798
+ }, void 0, void 0, { preferPublicEndpoint: true });
45799
+ const body = await response.json().catch(() => ({ error: response.statusText }));
45800
+ if (!response.ok) {
45801
+ throw new MeshyError("NODE_OFFLINE", `Remote node returned ${response.status}`, response.status, body);
45802
+ }
45803
+ applySyncedSettingsSnapshot(deps, nodeId, body);
45804
+ return body;
45805
+ }
45806
+ async function sendNodeAgentUpgrade(req, res, nodeId, agentParam) {
45807
+ const deps = req.app.locals.deps;
45808
+ const agent = parseAgent(agentParam);
45809
+ const self2 = deps.nodeRegistry.getSelf();
45810
+ const isSelf = nodeId === self2.id;
45811
+ if (!isSelf && !deps.election.isLeader()) {
45812
+ throw new MeshyError("NOT_LEADER", "Only the leader can upgrade agents on other nodes", 403);
45813
+ }
45814
+ if (isSelf) {
45815
+ res.json(upgradeSelfNode(deps, nodeId, agent));
45816
+ return;
45817
+ }
45818
+ const canPushToNode = deps.heartbeat?.canPushToNode?.(nodeId) ?? true;
45819
+ if (!canPushToNode) {
45820
+ await fallbackUpgrade(deps, res, nodeId, agent);
45821
+ return;
45822
+ }
45823
+ try {
45824
+ res.json(await proxyUpgrade(deps, nodeId, agent));
45825
+ } catch (err) {
45826
+ if (!canRequestNodeMessage(deps.heartbeat)) throw err;
45827
+ await fallbackUpgrade(deps, res, nodeId, agent);
45828
+ }
45829
+ }
45830
+
45449
45831
  // ../../packages/api/src/routes/nodes.ts
45450
45832
  var NODE_WORKDIR_PROXY_TIMEOUT_MS = 1e4;
45451
45833
  var NODE_WORKDIR_BRANCH_PROXY_TIMEOUT_MS = 25e3;
@@ -45785,35 +46167,38 @@ function createNodeRoutes() {
45785
46167
  const query = NodeListQuery.parse(req.query);
45786
46168
  let nodes = nodeRegistry.getAllNodes();
45787
46169
  if (query.status) {
45788
- nodes = nodes.filter((n) => n.status === query.status);
46170
+ nodes = nodes.filter((node) => node.status === query.status);
45789
46171
  }
45790
- if (query.capability) {
46172
+ const capability = query.capability;
46173
+ if (capability) {
45791
46174
  nodes = nodes.filter(
45792
- (n) => n.capabilities && n.capabilities.includes(query.capability)
46175
+ (node) => node.capabilities && node.capabilities.includes(capability)
45793
46176
  );
45794
46177
  }
45795
46178
  res.json({ nodes });
45796
46179
  }));
45797
46180
  router.get("/:id", asyncHandler3(async (req, res) => {
45798
46181
  const { nodeRegistry } = req.app.locals.deps;
45799
- const node = nodeRegistry.getNode(req.params.id);
46182
+ const nodeId = req.params.id;
46183
+ const node = nodeRegistry.getNode(nodeId);
45800
46184
  if (!node) {
45801
- throw new MeshyError("NODE_NOT_FOUND", `Node ${req.params.id} not found`, 404);
46185
+ throw new MeshyError("NODE_NOT_FOUND", `Node ${nodeId} not found`, 404);
45802
46186
  }
45803
46187
  res.json(node);
45804
46188
  }));
45805
46189
  router.get("/:id/status", asyncHandler3(async (req, res) => {
45806
46190
  const { nodeRegistry, taskEngine } = req.app.locals.deps;
45807
- const node = nodeRegistry.getNode(req.params.id);
46191
+ const nodeId = req.params.id;
46192
+ const node = nodeRegistry.getNode(nodeId);
45808
46193
  if (!node) {
45809
- throw new MeshyError("NODE_NOT_FOUND", `Node ${req.params.id} not found`, 404);
45810
- }
45811
- const { tasks } = taskEngine.listTasks({ assignedTo: req.params.id });
45812
- const taskSummary = tasks.map((t) => ({
45813
- id: t.id,
45814
- title: t.title,
45815
- status: t.status,
45816
- priority: t.priority
46194
+ throw new MeshyError("NODE_NOT_FOUND", `Node ${nodeId} not found`, 404);
46195
+ }
46196
+ const { tasks } = taskEngine.listTasks({ assignedTo: nodeId });
46197
+ const taskSummary = tasks.map((task) => ({
46198
+ id: task.id,
46199
+ title: task.title,
46200
+ status: task.status,
46201
+ priority: task.priority
45817
46202
  }));
45818
46203
  res.json({ node, tasks: taskSummary });
45819
46204
  }));
@@ -45849,6 +46234,9 @@ function createNodeRoutes() {
45849
46234
  }
45850
46235
  sendLocalNodeWorkDirBranchCreate(req, res, nodeId);
45851
46236
  }));
46237
+ router.post("/:id/agents/:agent/upgrade", asyncHandler3(async (req, res) => {
46238
+ await sendNodeAgentUpgrade(req, res, req.params.id, req.params.agent);
46239
+ }));
45852
46240
  router.patch("/:id", asyncHandler3(async (req, res) => {
45853
46241
  const { nodeRegistry, persistNodeNamePreference } = req.app.locals.deps;
45854
46242
  const updates = UpdateNodeBody.parse(req.body);
@@ -45874,14 +46262,15 @@ function createNodeRoutes() {
45874
46262
  }));
45875
46263
  router.delete("/:id", asyncHandler3(async (req, res) => {
45876
46264
  const { nodeRegistry, election } = req.app.locals.deps;
46265
+ const nodeId = req.params.id;
45877
46266
  if (!election.isLeader()) {
45878
46267
  throw new MeshyError("NOT_LEADER", "Only the leader node can delete nodes", 403);
45879
46268
  }
45880
- const node = nodeRegistry.getNode(req.params.id);
46269
+ const node = nodeRegistry.getNode(nodeId);
45881
46270
  if (!node) {
45882
- throw new MeshyError("NODE_NOT_FOUND", `Node ${req.params.id} not found`, 404);
46271
+ throw new MeshyError("NODE_NOT_FOUND", `Node ${nodeId} not found`, 404);
45883
46272
  }
45884
- nodeRegistry.removeNode(req.params.id);
46273
+ nodeRegistry.removeNode(nodeId);
45885
46274
  res.json({ ok: true });
45886
46275
  }));
45887
46276
  router.post("/:id/devtunnel", asyncHandler3(async (req, res) => {
@@ -46371,14 +46760,15 @@ async function sendTaskLogsResponse(req, res, taskId) {
46371
46760
  selfId
46372
46761
  });
46373
46762
  }
46374
- if (needsProxy) {
46375
- const node = nodeRegistry.getNode(task.assignedTo);
46763
+ const assignedTo = task.assignedTo;
46764
+ if (needsProxy && assignedTo) {
46765
+ const node = nodeRegistry.getNode(assignedTo);
46376
46766
  log2.debug("proxy target", { nodeId: node?.id, endpoint: node?.endpoint, devtunnel: node?.devtunnelEndpoint, status: node?.status });
46377
46767
  if (node) {
46378
46768
  const seededLogs = await seedTaskSnapshotOnWorker(task, [], node, log2).catch((err) => {
46379
46769
  log2.warn("failed to seed task snapshot before task logs proxy", {
46380
46770
  taskId,
46381
- assignedTo: task.assignedTo,
46771
+ assignedTo,
46382
46772
  ...describeProxyError3(err)
46383
46773
  });
46384
46774
  return [];
@@ -46397,24 +46787,24 @@ async function sendTaskLogsResponse(req, res, taskId) {
46397
46787
  const proxyUrl = `${endpoint}${proxyPath}`;
46398
46788
  log2.info("proxying task logs request", {
46399
46789
  taskId,
46400
- assignedTo: task.assignedTo,
46790
+ assignedTo,
46401
46791
  endpoint,
46402
46792
  proxyPath,
46403
46793
  url: proxyUrl
46404
46794
  });
46405
46795
  if (!proxyRes.ok) throw new MeshyError("NODE_OFFLINE", `Worker log proxy failed (${proxyRes.status})`, 502);
46406
46796
  const data = await proxyRes.json();
46407
- log2.debug("proxy returned", { logs: data?.logs?.length ?? 0, total: data?.total });
46797
+ log2.debug("proxy returned", { logs: data.logs?.length ?? 0, total: data.total });
46408
46798
  res.json(data);
46409
46799
  return;
46410
46800
  } catch (err) {
46411
46801
  log2.warn("task logs proxy failed; falling back to keepalive control", {
46412
46802
  taskId,
46413
- assignedTo: task.assignedTo,
46803
+ assignedTo,
46414
46804
  timeoutMs: TASK_LOG_PROXY_TIMEOUT_MS,
46415
46805
  ...describeProxyError3(err)
46416
46806
  });
46417
- const fallback = await requestTaskLogsOverKeepalive(heartbeat, task.assignedTo, task, after);
46807
+ const fallback = await requestTaskLogsOverKeepalive(heartbeat, assignedTo, task, after);
46418
46808
  if (fallback) {
46419
46809
  sendWorkerControlResponse(res, fallback);
46420
46810
  return;
@@ -47062,13 +47452,14 @@ function createTaskRoutes() {
47062
47452
  const { taskEngine, nodeRegistry, logger: rootLogger } = req.app.locals.deps;
47063
47453
  const log2 = rootLogger.child("tasks/patch");
47064
47454
  const updates = UpdateTaskBody.parse(req.body);
47065
- const existing = taskEngine.getTask(req.params.id);
47455
+ const taskId = req.params.id;
47456
+ const existing = taskEngine.getTask(taskId);
47066
47457
  if (!existing) {
47067
- throw new MeshyError("TASK_NOT_FOUND", `Task ${req.params.id} not found`, 404);
47458
+ throw new MeshyError("TASK_NOT_FOUND", `Task ${taskId} not found`, 404);
47068
47459
  }
47069
47460
  if (updates.status !== void 0 && TERMINAL_STATUSES3.has(existing.status) && ACTIVE_STATUSES.has(updates.status)) {
47070
47461
  log2.warn("ignoring stale status regression for terminal task", {
47071
- taskId: req.params.id,
47462
+ taskId,
47072
47463
  currentStatus: existing.status,
47073
47464
  incomingStatus: updates.status
47074
47465
  });
@@ -47084,33 +47475,35 @@ function createTaskRoutes() {
47084
47475
  }
47085
47476
  if (updates.status !== void 0 && existing.status === "archived" && updates.status !== "archived") {
47086
47477
  log2.warn("ignoring status change for archived task", {
47087
- taskId: req.params.id,
47478
+ taskId,
47088
47479
  currentStatus: existing.status,
47089
47480
  incomingStatus: updates.status
47090
47481
  });
47091
47482
  res.json(withAssignedNodeMetadata(existing, nodeRegistry));
47092
47483
  return;
47093
47484
  }
47094
- const task = taskEngine.updateTask(req.params.id, updates);
47485
+ const task = taskEngine.updateTask(taskId, updates);
47095
47486
  if (!task) {
47096
- throw new MeshyError("TASK_NOT_FOUND", `Task ${req.params.id} not found`, 404);
47487
+ throw new MeshyError("TASK_NOT_FOUND", `Task ${taskId} not found`, 404);
47097
47488
  }
47098
47489
  res.json(withAssignedNodeMetadata(task, nodeRegistry));
47099
47490
  }));
47100
47491
  router.delete("/:id", asyncHandler6(async (req, res) => {
47101
47492
  const { taskEngine } = req.app.locals.deps;
47102
- const result = taskEngine.deleteTask(req.params.id);
47493
+ const taskId = req.params.id;
47494
+ const result = taskEngine.deleteTask(taskId);
47103
47495
  if (!result) {
47104
- throw new MeshyError("TASK_NOT_FOUND", `Task ${req.params.id} not found`, 404);
47496
+ throw new MeshyError("TASK_NOT_FOUND", `Task ${taskId} not found`, 404);
47105
47497
  }
47106
47498
  res.json({ ok: true });
47107
47499
  }));
47108
47500
  router.post("/:id/cancel", asyncHandler6(async (req, res) => {
47109
47501
  const { taskEngine, nodeRegistry, engineRegistry, heartbeat, logger: rootLogger } = req.app.locals.deps;
47110
47502
  const log2 = rootLogger.child("tasks/cancel");
47111
- const task = taskEngine.getTask(req.params.id);
47503
+ const taskId = req.params.id;
47504
+ const task = taskEngine.getTask(taskId);
47112
47505
  if (!task) {
47113
- throw new MeshyError("TASK_NOT_FOUND", `Task ${req.params.id} not found`, 404);
47506
+ throw new MeshyError("TASK_NOT_FOUND", `Task ${taskId} not found`, 404);
47114
47507
  }
47115
47508
  if (TERMINAL_STATUSES3.has(task.status)) {
47116
47509
  throw new MeshyError("VALIDATION_ERROR", `Task ${task.id} cannot be cancelled (status: ${task.status})`, 400);
@@ -47160,9 +47553,10 @@ function createTaskRoutes() {
47160
47553
  const { taskEngine, engineRegistry, nodeRegistry, heartbeat, logger: rootLogger } = req.app.locals.deps;
47161
47554
  const log2 = rootLogger.child("tasks/message");
47162
47555
  const body = SendMessageBody.parse(req.body);
47163
- const task = taskEngine.getTask(req.params.id);
47556
+ const taskId = req.params.id;
47557
+ const task = taskEngine.getTask(taskId);
47164
47558
  if (!task) {
47165
- throw new MeshyError("TASK_NOT_FOUND", `Task ${req.params.id} not found`, 404);
47559
+ throw new MeshyError("TASK_NOT_FOUND", `Task ${taskId} not found`, 404);
47166
47560
  }
47167
47561
  if (!supportsFollowUpAgent(task.agent)) {
47168
47562
  throw new MeshyError("VALIDATION_ERROR", `Agent ${task.agent} does not support chat messages`, 400);
@@ -47594,123 +47988,6 @@ function normalizeNodeMessageResponse(value) {
47594
47988
 
47595
47989
  // ../../packages/api/src/routes/system.ts
47596
47990
  var import_express11 = __toESM(require_express2(), 1);
47597
-
47598
- // ../../packages/api/src/app/system-info.ts
47599
- var os6 = __toESM(require("os"), 1);
47600
- var import_node_child_process8 = require("child_process");
47601
- var RUNTIME_TOOLS = [
47602
- { id: "claude", label: "Claude Code", command: "claude" },
47603
- { id: "codex", label: "Codex", command: "codex" }
47604
- ];
47605
- function normalizeOutput(output) {
47606
- if (!output) {
47607
- return null;
47608
- }
47609
- const firstLine = output.split(/\r?\n/).map((line) => line.trim()).find(Boolean);
47610
- return firstLine ?? null;
47611
- }
47612
- function resolveCommandPath(command) {
47613
- const lookupCommand = process.platform === "win32" ? "where" : "which";
47614
- const result = (0, import_node_child_process8.spawnSync)(lookupCommand, [command], {
47615
- encoding: "utf-8",
47616
- shell: false,
47617
- windowsHide: true,
47618
- timeout: 1500
47619
- });
47620
- if (result.status === 0) {
47621
- return { path: normalizeOutput(result.stdout) };
47622
- }
47623
- return {
47624
- path: null,
47625
- detail: normalizeOutput(result.stderr) ?? result.error?.message ?? null
47626
- };
47627
- }
47628
- function inspectTool(definition) {
47629
- const checkedAt = (/* @__PURE__ */ new Date()).toISOString();
47630
- const resolved = resolveCommandPath(definition.command);
47631
- if (!resolved.path) {
47632
- return {
47633
- id: definition.id,
47634
- label: definition.label,
47635
- command: definition.command,
47636
- available: false,
47637
- path: null,
47638
- version: null,
47639
- checkedAt,
47640
- detail: resolved.detail ?? "Command not found on PATH"
47641
- };
47642
- }
47643
- const versionResult = (0, import_node_child_process8.spawnSync)(definition.command, ["--version"], {
47644
- encoding: "utf-8",
47645
- shell: process.platform === "win32",
47646
- windowsHide: true,
47647
- timeout: 2500
47648
- });
47649
- const version2 = normalizeOutput(versionResult.stdout) ?? normalizeOutput(versionResult.stderr);
47650
- const detail = versionResult.status === 0 ? null : normalizeOutput(versionResult.stderr) ?? versionResult.error?.message ?? "Version check failed";
47651
- return {
47652
- id: definition.id,
47653
- label: definition.label,
47654
- command: definition.command,
47655
- available: true,
47656
- path: resolved.path,
47657
- version: version2,
47658
- checkedAt,
47659
- detail
47660
- };
47661
- }
47662
- function inspectRuntimeTools() {
47663
- return RUNTIME_TOOLS.map(inspectTool);
47664
- }
47665
- function getOsSnapshot() {
47666
- const cpus2 = os6.cpus();
47667
- return {
47668
- platform: os6.platform(),
47669
- release: os6.release(),
47670
- arch: os6.arch(),
47671
- hostname: os6.hostname(),
47672
- cpuModel: cpus2[0]?.model ?? null,
47673
- cpuCount: cpus2.length,
47674
- totalMemoryBytes: os6.totalmem(),
47675
- freeMemoryBytes: os6.freemem()
47676
- };
47677
- }
47678
- function buildNodeSettingsSnapshot(options) {
47679
- const uptime = process.uptime();
47680
- const runtimeMetadata = options.runtimeMetadata;
47681
- const agents = (options.inspectRuntimeTools?.() ?? inspectRuntimeTools()).map((agent) => ({
47682
- ...agent,
47683
- metadataStatus: runtimeMetadata?.components?.[agent.id]?.status ?? null
47684
- }));
47685
- return {
47686
- collectedAt: Date.now(),
47687
- version: runtimeMetadata?.packageVersion ?? "0.1.0",
47688
- packageName: runtimeMetadata?.packageName ?? "meshy",
47689
- uptime,
47690
- auth: options.auth,
47691
- os: getOsSnapshot(),
47692
- runtime: {
47693
- nodeVersion: process.version,
47694
- pid: process.pid,
47695
- startedAt: Date.now() - Math.floor(uptime * 1e3),
47696
- cwd: process.cwd(),
47697
- workDir: options.workDir ?? null,
47698
- storagePath: options.storagePath ?? null,
47699
- localDashboardOrigin: options.localDashboardOrigin ?? null
47700
- },
47701
- agents,
47702
- startupRequirements: {
47703
- lastCheckedAt: runtimeMetadata?.startupRequirementsLastCheckedAt,
47704
- lastCheckedOn: runtimeMetadata?.startupRequirementsLastCheckedOn,
47705
- components: runtimeMetadata?.components ?? {}
47706
- },
47707
- components: runtimeMetadata?.components ?? {},
47708
- repository: runtimeMetadata?.repository ?? {},
47709
- packages: runtimeMetadata?.packages ?? {}
47710
- };
47711
- }
47712
-
47713
- // ../../packages/api/src/routes/system.ts
47714
47991
  function asyncHandler9(fn) {
47715
47992
  return (req, res, next) => fn(req, res, next).catch(next);
47716
47993
  }
@@ -47776,8 +48053,8 @@ function createSystemRoutes() {
47776
48053
  return;
47777
48054
  }
47778
48055
  const { type } = req.body;
47779
- if (!type || !["direct", "devtunnel", "tailscale"].includes(type)) {
47780
- res.status(400).json({ error: "Invalid transport type. Must be: direct, devtunnel, or tailscale" });
48056
+ if (!type || !["direct", "devtunnel"].includes(type)) {
48057
+ res.status(400).json({ error: "Invalid transport type. Must be: direct or devtunnel" });
47781
48058
  return;
47782
48059
  }
47783
48060
  const newEndpoint = await switchTransport(type);
@@ -47860,6 +48137,7 @@ function createEventRoutes() {
47860
48137
  const router = (0, import_express12.Router)();
47861
48138
  router.get("/", (req, res) => {
47862
48139
  const { eventBus } = req.app.locals.deps;
48140
+ const stream = res;
47863
48141
  const categories = parseFilter(req.query.filter);
47864
48142
  res.writeHead(200, {
47865
48143
  "Content-Type": "text/event-stream",
@@ -47876,20 +48154,14 @@ data: ${JSON.stringify(data)}
47876
48154
 
47877
48155
  `;
47878
48156
  res.write(payload);
47879
- if (typeof res.flush === "function") {
47880
- ;
47881
- res.flush();
47882
- }
48157
+ stream.flush?.();
47883
48158
  };
47884
48159
  listeners.set(eventName, listener);
47885
48160
  eventBus.on(eventName, listener);
47886
48161
  }
47887
48162
  const heartbeatTimer = setInterval(() => {
47888
48163
  res.write(": heartbeat\n\n");
47889
- if (typeof res.flush === "function") {
47890
- ;
47891
- res.flush();
47892
- }
48164
+ stream.flush?.();
47893
48165
  }, HEARTBEAT_INTERVAL_MS);
47894
48166
  req.on("close", () => {
47895
48167
  clearInterval(heartbeatTimer);
@@ -48141,10 +48413,10 @@ var DirectTransport = class {
48141
48413
  };
48142
48414
 
48143
48415
  // ../../packages/transport/src/devtunnel.ts
48144
- var import_node_child_process9 = require("child_process");
48416
+ var import_node_child_process10 = require("child_process");
48145
48417
  function isInstalled(cmd) {
48146
48418
  try {
48147
- (0, import_node_child_process9.execSync)(process.platform === "win32" ? `where ${cmd}` : `command -v ${cmd}`, { stdio: "pipe" });
48419
+ (0, import_node_child_process10.execSync)(process.platform === "win32" ? `where ${cmd}` : `command -v ${cmd}`, { stdio: "pipe" });
48148
48420
  return true;
48149
48421
  } catch {
48150
48422
  return false;
@@ -48167,14 +48439,14 @@ var DevTunnelTransport = class {
48167
48439
  );
48168
48440
  }
48169
48441
  try {
48170
- (0, import_node_child_process9.execSync)("devtunnel user show", { stdio: "pipe" });
48442
+ (0, import_node_child_process10.execSync)("devtunnel user show", { stdio: "pipe" });
48171
48443
  } catch {
48172
48444
  throw new Error(
48173
48445
  "Not logged in to devtunnel. Run: devtunnel user login"
48174
48446
  );
48175
48447
  }
48176
48448
  const hostArgs = this.buildHostArgs(localPort);
48177
- const child = (0, import_node_child_process9.spawn)("devtunnel", hostArgs, {
48449
+ const child = (0, import_node_child_process10.spawn)("devtunnel", hostArgs, {
48178
48450
  stdio: ["pipe", "pipe", "pipe"]
48179
48451
  });
48180
48452
  this.process = child;
@@ -48279,7 +48551,7 @@ ${lines.join("")}`
48279
48551
  return;
48280
48552
  }
48281
48553
  try {
48282
- (0, import_node_child_process9.execFileSync)("devtunnel", ["access", "create", tunnelId, "--tenant"], { stdio: "pipe" });
48554
+ (0, import_node_child_process10.execFileSync)("devtunnel", ["access", "create", tunnelId, "--tenant"], { stdio: "pipe" });
48283
48555
  } catch (err) {
48284
48556
  if (isExistingTenantAccessError(err)) {
48285
48557
  return;
@@ -48295,7 +48567,7 @@ ${lines.join("")}`
48295
48567
  return void 0;
48296
48568
  }
48297
48569
  try {
48298
- (0, import_node_child_process9.execFileSync)("devtunnel", ["show", tunnelId], { stdio: "pipe" });
48570
+ (0, import_node_child_process10.execFileSync)("devtunnel", ["show", tunnelId], { stdio: "pipe" });
48299
48571
  return tunnelId;
48300
48572
  } catch {
48301
48573
  const createArgs = ["create", tunnelId];
@@ -48303,7 +48575,7 @@ ${lines.join("")}`
48303
48575
  createArgs.push("-a");
48304
48576
  }
48305
48577
  try {
48306
- (0, import_node_child_process9.execFileSync)("devtunnel", createArgs, { stdio: "pipe" });
48578
+ (0, import_node_child_process10.execFileSync)("devtunnel", createArgs, { stdio: "pipe" });
48307
48579
  return tunnelId;
48308
48580
  } catch (err) {
48309
48581
  throw new Error(
@@ -48315,13 +48587,13 @@ ${lines.join("")}`
48315
48587
  ensureTunnelPort(tunnelId, localPort) {
48316
48588
  const ports = this.listTunnelPorts(tunnelId);
48317
48589
  for (const stalePort of ports.filter((p) => p !== localPort)) {
48318
- (0, import_node_child_process9.execFileSync)("devtunnel", ["port", "delete", tunnelId, "-p", String(stalePort)], { stdio: "pipe" });
48590
+ (0, import_node_child_process10.execFileSync)("devtunnel", ["port", "delete", tunnelId, "-p", String(stalePort)], { stdio: "pipe" });
48319
48591
  }
48320
48592
  if (ports.includes(localPort)) {
48321
48593
  return;
48322
48594
  }
48323
48595
  try {
48324
- (0, import_node_child_process9.execFileSync)(
48596
+ (0, import_node_child_process10.execFileSync)(
48325
48597
  "devtunnel",
48326
48598
  ["port", "create", tunnelId, "-p", String(localPort), "--protocol", "http"],
48327
48599
  { stdio: "pipe" }
@@ -48337,7 +48609,7 @@ ${lines.join("")}`
48337
48609
  }
48338
48610
  listTunnelPorts(tunnelId) {
48339
48611
  try {
48340
- const output = (0, import_node_child_process9.execFileSync)("devtunnel", ["port", "list", tunnelId, "-j"], { stdio: "pipe" });
48612
+ const output = (0, import_node_child_process10.execFileSync)("devtunnel", ["port", "list", tunnelId, "-j"], { stdio: "pipe" });
48341
48613
  const parsed = JSON.parse(output.toString());
48342
48614
  const items = Array.isArray(parsed) ? parsed : parsed && typeof parsed === "object" && Array.isArray(parsed.ports) ? parsed.ports : parsed && typeof parsed === "object" && Array.isArray(parsed.value) ? parsed.value : [];
48343
48615
  return items.map((item) => getPortNumber(item)).filter((value) => value !== void 0);
@@ -48349,7 +48621,7 @@ ${lines.join("")}`
48349
48621
  }
48350
48622
  hasTunnelPort(tunnelId, localPort) {
48351
48623
  try {
48352
- (0, import_node_child_process9.execFileSync)(
48624
+ (0, import_node_child_process10.execFileSync)(
48353
48625
  "devtunnel",
48354
48626
  ["port", "show", tunnelId, "-p", String(localPort), "-j"],
48355
48627
  { stdio: "pipe" }
@@ -48426,16 +48698,42 @@ function createTransport(config) {
48426
48698
  }
48427
48699
  }
48428
48700
 
48701
+ // src/bootstrap/terminal-writer.ts
48702
+ function formatDetail(detail) {
48703
+ if (detail instanceof Error) {
48704
+ return detail.stack ?? detail.message;
48705
+ }
48706
+ if (typeof detail === "string") {
48707
+ return detail;
48708
+ }
48709
+ try {
48710
+ return JSON.stringify(detail);
48711
+ } catch {
48712
+ return String(detail);
48713
+ }
48714
+ }
48715
+ var terminalWriter = {
48716
+ error(message, ...details) {
48717
+ const suffix = details.length > 0 ? ` ${details.map(formatDetail).join(" ")}` : "";
48718
+ process.stderr.write(`${message}${suffix}
48719
+ `);
48720
+ },
48721
+ line(message = "") {
48722
+ process.stdout.write(`${message}
48723
+ `);
48724
+ }
48725
+ };
48726
+
48429
48727
  // src/startup.ts
48430
48728
  var fs19 = __toESM(require("fs"), 1);
48431
48729
  var path20 = __toESM(require("path"), 1);
48432
48730
  var readline = __toESM(require("readline/promises"), 1);
48433
- var import_node_child_process10 = require("child_process");
48731
+ var import_node_child_process11 = require("child_process");
48434
48732
  function getDefaultNodeName() {
48435
48733
  return getDeviceNodeName();
48436
48734
  }
48437
48735
  var DEFAULT_NODE_NAME = getDefaultNodeName();
48438
- var SUPPORTED_TRANSPORTS = ["direct", "devtunnel", "tailscale"];
48736
+ var SUPPORTED_TRANSPORTS = ["direct", "devtunnel"];
48439
48737
  var STARTUP_REQUIREMENTS = ["az", "devtunnel", "claude", "codex"];
48440
48738
  function createDefaultConfig(storagePath = resolveDefaultStoragePath(), nodeName = DEFAULT_NODE_NAME) {
48441
48739
  return {
@@ -48476,7 +48774,7 @@ function createPromptSession(prompt) {
48476
48774
  }
48477
48775
  function createDefaultCommandRunner(platform2) {
48478
48776
  return (command, args, interactive = false) => {
48479
- const result = (0, import_node_child_process10.spawnSync)(command, args, {
48777
+ const result = (0, import_node_child_process11.spawnSync)(command, args, {
48480
48778
  encoding: "utf-8",
48481
48779
  shell: platform2 === "win32",
48482
48780
  stdio: interactive ? "inherit" : "pipe"
@@ -48505,7 +48803,7 @@ function writeStartupMetadataFile(storagePath, metadata) {
48505
48803
  fs19.mkdirSync(storagePath, { recursive: true });
48506
48804
  fs19.writeFileSync(getNodeMetadataPath2(storagePath), JSON.stringify(metadata, null, 2) + "\n", "utf-8");
48507
48805
  }
48508
- function formatLocalDate(now) {
48806
+ function formatLocalDate2(now) {
48509
48807
  const year = now.getFullYear();
48510
48808
  const month = String(now.getMonth() + 1).padStart(2, "0");
48511
48809
  const day = String(now.getDate()).padStart(2, "0");
@@ -48596,7 +48894,7 @@ function resolveStartupRequirementsMetadata(storagePath) {
48596
48894
  }
48597
48895
  function shouldSkipStartupRequirementsCheck(storagePath, now = /* @__PURE__ */ new Date()) {
48598
48896
  const metadata = resolveStartupRequirementsMetadata(storagePath);
48599
- if (metadata.lastCheckedOn !== formatLocalDate(now)) {
48897
+ if (metadata.lastCheckedOn !== formatLocalDate2(now)) {
48600
48898
  return false;
48601
48899
  }
48602
48900
  return STARTUP_REQUIREMENTS.every((requirement) => isRequirementSatisfied(requirement, metadata.components?.[requirement]));
@@ -48680,7 +48978,7 @@ async function ensureStartupRequirements(storagePath, options = {}) {
48680
48978
  await current.close();
48681
48979
  };
48682
48980
  const checkedAt = now.toISOString();
48683
- const checkedOn = formatLocalDate(now);
48981
+ const checkedOn = formatLocalDate2(now);
48684
48982
  const components = {};
48685
48983
  try {
48686
48984
  for (const requirement of STARTUP_REQUIREMENTS) {
@@ -48820,13 +49118,13 @@ async function promptStartOptions(args, fileConfig, defaults, prompt) {
48820
49118
  result.port = port;
48821
49119
  break;
48822
49120
  }
48823
- console.log("Invalid port. Enter a positive integer.");
49121
+ terminalWriter.line("Invalid port. Enter a positive integer.");
48824
49122
  }
48825
49123
  }
48826
49124
  if (result.transport === void 0) {
48827
49125
  while (true) {
48828
49126
  const input = normalizePromptValue(
48829
- await ask(formatPrompt("Transport (direct/devtunnel/tailscale)", merged.transport.type))
49127
+ await ask(formatPrompt("Transport (direct/devtunnel)", merged.transport.type))
48830
49128
  );
48831
49129
  if (!input) {
48832
49130
  result.transport = merged.transport.type;
@@ -48836,7 +49134,7 @@ async function promptStartOptions(args, fileConfig, defaults, prompt) {
48836
49134
  result.transport = input;
48837
49135
  break;
48838
49136
  }
48839
- console.log("Invalid transport. Use direct, devtunnel, or tailscale.");
49137
+ terminalWriter.line("Invalid transport. Use direct or devtunnel.");
48840
49138
  }
48841
49139
  }
48842
49140
  if (result.join === void 0) {
@@ -48924,7 +49222,7 @@ function formatLoadedStartMetadata(info, authEnabled) {
48924
49222
  // src/runtime-metadata.ts
48925
49223
  var fs20 = __toESM(require("fs"), 1);
48926
49224
  var path21 = __toESM(require("path"), 1);
48927
- var import_node_child_process11 = require("child_process");
49225
+ var import_node_child_process12 = require("child_process");
48928
49226
  var runtimeDir = resolveRuntimeDir();
48929
49227
  var appRoot = resolveAppRoot(runtimeDir);
48930
49228
  var repoRoot = path21.resolve(appRoot, "../..");
@@ -49020,7 +49318,7 @@ function readRepositoryUrlFromManifest(manifest) {
49020
49318
  }
49021
49319
  function readGitValue(args) {
49022
49320
  try {
49023
- const output = (0, import_node_child_process11.execFileSync)("git", args, {
49321
+ const output = (0, import_node_child_process12.execFileSync)("git", args, {
49024
49322
  cwd: repoRoot,
49025
49323
  encoding: "utf-8",
49026
49324
  stdio: ["ignore", "pipe", "ignore"],
@@ -49069,11 +49367,7 @@ function buildRuntimeMetadata(storagePath) {
49069
49367
  function createSettingsSnapshotProvider(options) {
49070
49368
  let cachedSnapshot = null;
49071
49369
  let cachedSnapshotAt = 0;
49072
- return function getSettingsSnapshot() {
49073
- const now = Date.now();
49074
- if (cachedSnapshot && now - cachedSnapshotAt < 3e4) {
49075
- return structuredClone(cachedSnapshot);
49076
- }
49370
+ function buildSnapshot() {
49077
49371
  cachedSnapshot = buildNodeSettingsSnapshot({
49078
49372
  auth: options.authMetadata,
49079
49373
  localDashboardOrigin: options.localDashboardOrigin,
@@ -49081,8 +49375,23 @@ function createSettingsSnapshotProvider(options) {
49081
49375
  storagePath: options.storagePath,
49082
49376
  workDir: options.workDir
49083
49377
  });
49084
- cachedSnapshotAt = now;
49378
+ cachedSnapshotAt = Date.now();
49085
49379
  return structuredClone(cachedSnapshot);
49380
+ }
49381
+ function getSettingsSnapshot() {
49382
+ const now = Date.now();
49383
+ if (cachedSnapshot && now - cachedSnapshotAt < 3e4) {
49384
+ return structuredClone(cachedSnapshot);
49385
+ }
49386
+ return buildSnapshot();
49387
+ }
49388
+ return {
49389
+ getSettingsSnapshot,
49390
+ refreshSettingsSnapshot: () => {
49391
+ cachedSnapshot = null;
49392
+ cachedSnapshotAt = 0;
49393
+ return buildSnapshot();
49394
+ }
49086
49395
  };
49087
49396
  }
49088
49397
  async function resolveStartConfig(args) {
@@ -49369,13 +49678,13 @@ function registerShutdownHandlers(options) {
49369
49678
  let shuttingDown = false;
49370
49679
  async function shutdown() {
49371
49680
  if (shuttingDown) {
49372
- console.log("Force exit.");
49681
+ terminalWriter.line("Force exit.");
49373
49682
  process.exit(130);
49374
49683
  }
49375
49684
  shuttingDown = true;
49376
- console.log("\nShutting down...");
49685
+ terminalWriter.line("\nShutting down...");
49377
49686
  const forceExitTimer = setTimeout(() => {
49378
- console.log("Force exit.");
49687
+ terminalWriter.line("Force exit.");
49379
49688
  process.exit(0);
49380
49689
  }, 1e4);
49381
49690
  forceExitTimer.unref?.();
@@ -49385,10 +49694,10 @@ function registerShutdownHandlers(options) {
49385
49694
  await options.tunnelManager.stop();
49386
49695
  await options.meshyNode.stop();
49387
49696
  clearTimeout(forceExitTimer);
49388
- console.log("Goodbye!");
49697
+ terminalWriter.line("Goodbye!");
49389
49698
  process.exit(0);
49390
49699
  } catch (err) {
49391
- console.error("Error during shutdown:", err);
49700
+ terminalWriter.error("Error during shutdown:", err);
49392
49701
  process.exit(1);
49393
49702
  }
49394
49703
  }
@@ -49397,9 +49706,9 @@ function registerShutdownHandlers(options) {
49397
49706
  }
49398
49707
  async function startNode(args) {
49399
49708
  const { authMetadata, config, hydratedArgs, runtimeMetadata, storageRoot } = await resolveStartConfig(args);
49400
- console.log("Checking startup requirements (az, devtunnel, claude, codex)...");
49709
+ terminalWriter.line("Checking startup requirements (az, devtunnel, claude, codex)...");
49401
49710
  const startupRequirements = await ensureStartupRequirements(config.storage.path);
49402
- console.log(
49711
+ terminalWriter.line(
49403
49712
  startupRequirements.skipped ? "Startup requirements already verified today; skipping checks." : "Startup requirements check complete."
49404
49713
  );
49405
49714
  const localDashboardOrigin = `http://localhost:${config.node.port}`;
@@ -49413,9 +49722,9 @@ async function startNode(args) {
49413
49722
  settingsProvider: () => authMetadata
49414
49723
  });
49415
49724
  if (hydratedArgs.loaded) {
49416
- console.log("");
49417
- console.log(formatLoadedStartMetadata(hydratedArgs.loaded, authMetadata.enabled));
49418
- console.log("");
49725
+ terminalWriter.line("");
49726
+ terminalWriter.line(formatLoadedStartMetadata(hydratedArgs.loaded, authMetadata.enabled));
49727
+ terminalWriter.line("");
49419
49728
  }
49420
49729
  if (authMetadata.enabled) {
49421
49730
  await nodeAuth.warmup();
@@ -49423,13 +49732,14 @@ async function startNode(args) {
49423
49732
  } else {
49424
49733
  setRequestAuthHeadersProvider(null);
49425
49734
  }
49426
- const getSettingsSnapshot = createSettingsSnapshotProvider({
49735
+ const settingsSnapshotProvider = createSettingsSnapshotProvider({
49427
49736
  authMetadata,
49428
49737
  localDashboardOrigin,
49429
49738
  runtimeMetadata,
49430
49739
  storagePath: config.storage.path,
49431
49740
  workDir: nodePath2.resolve(config.node.workDir ?? process.cwd())
49432
49741
  });
49742
+ const { getSettingsSnapshot, refreshSettingsSnapshot } = settingsSnapshotProvider;
49433
49743
  const meshyNode = new MeshyNode(config, {
49434
49744
  logger: logger27,
49435
49745
  transportFactory: createTransport,
@@ -49512,6 +49822,8 @@ async function startNode(args) {
49512
49822
  },
49513
49823
  isDevTunnelEnabled: () => meshyNode.isDevTunnelEnabled(),
49514
49824
  localDashboardOrigin,
49825
+ upgradeRuntimeAgent,
49826
+ refreshSettingsSnapshot,
49515
49827
  dashboardOrigin: tunnelManager.getDashboardOrigin(),
49516
49828
  shareOrigin: tunnelManager.getShareOrigin(),
49517
49829
  ensureShareTunnel: () => tunnelManager.ensureShareTunnel(),
@@ -49539,9 +49851,9 @@ async function startNode(args) {
49539
49851
  port: config.node.port,
49540
49852
  dashboardOrigin: tunnelManager.getDashboardOrigin()
49541
49853
  });
49542
- console.log("");
49543
- console.log(banner);
49544
- console.log("");
49854
+ terminalWriter.line("");
49855
+ terminalWriter.line(banner);
49856
+ terminalWriter.line("");
49545
49857
  });
49546
49858
  registerShutdownHandlers({
49547
49859
  server,