meshy-node 1.0.2 → 1.0.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.
@@ -5,7 +5,7 @@
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-Cw9E6sQy.js"></script>
8
+ <script type="module" crossorigin src="/assets/index-DQFY3oGe.js"></script>
9
9
  <link rel="stylesheet" crossorigin href="/assets/index-DPc1_Q2V.css">
10
10
  </head>
11
11
  <body>
package/main.cjs CHANGED
@@ -148597,6 +148597,7 @@ var DEFAULT_PORT_CHECK_TIMEOUT_MS = 750;
148597
148597
  var DEFAULT_POLL_INTERVAL_MS = 1e3;
148598
148598
  var DEFAULT_STARTUP_TIMEOUT_MS = 15 * 60 * 1e3;
148599
148599
  var DEFAULT_STOP_TIMEOUT_MS = 3e3;
148600
+ var DEVICE_AUTH_EXPIRED_DETAIL = "GitHub device authorization expired; enable the tunnel again to request a new code";
148600
148601
  var GHC_TUNNEL_ARGS = ["-y", "ghc-tunnel@latest", "-d"];
148601
148602
  var GHC_TUNNEL_SETUP_ARGS = ["-y", "ghc-tunnel@latest", "--setup", "-d"];
148602
148603
  var GHC_TUNNEL_READY_PATTERN = /GitHub Copilot API Proxy/i;
@@ -148656,19 +148657,20 @@ function appendTail(tail, chunk, limitBytes) {
148656
148657
  function sleep2(ms) {
148657
148658
  return new Promise((resolve24) => setTimeout(resolve24, ms));
148658
148659
  }
148659
- function createDetachedOutputLog() {
148660
+ function createTunnelOutputLog() {
148660
148661
  const dir = import_node_fs.default.mkdtempSync(import_node_path.default.join(import_node_os4.default.tmpdir(), "meshy-ghc-tunnel-"));
148661
148662
  const logPath = import_node_path.default.join(dir, "output.log");
148662
148663
  const fd = import_node_fs.default.openSync(logPath, "a");
148663
148664
  return { dir, logPath, fd };
148664
148665
  }
148665
148666
  function defaultSpawnTunnel(command, args, options) {
148666
- const output = createDetachedOutputLog();
148667
+ const output = createTunnelOutputLog();
148667
148668
  let child;
148668
148669
  try {
148670
+ const detached = process.platform !== "win32";
148669
148671
  child = (0, import_node_child_process10.spawn)(command, args, {
148670
148672
  ...options,
148671
- detached: true,
148673
+ detached,
148672
148674
  stdio: ["ignore", output.fd, output.fd]
148673
148675
  });
148674
148676
  } finally {
@@ -148803,6 +148805,36 @@ function parseGithubDeviceFlow(output) {
148803
148805
  ...expires ? { expiresInSeconds: Number(expires) } : {}
148804
148806
  };
148805
148807
  }
148808
+ function updateAuthPrompt(current, output, now = Date.now()) {
148809
+ const parsed = parseGithubDeviceFlow(output);
148810
+ if (!parsed) return current;
148811
+ if (current?.code === parsed.code && current.expiresAt) return current;
148812
+ return addAuthPromptTiming(parsed, now);
148813
+ }
148814
+ function addAuthPromptTiming(auth, now = Date.now()) {
148815
+ return {
148816
+ ...auth,
148817
+ issuedAt: now,
148818
+ ...auth.expiresInSeconds !== void 0 ? { expiresAt: now + auth.expiresInSeconds * 1e3 } : {}
148819
+ };
148820
+ }
148821
+ function normalizeCopilotTunnelResult(value, now = Date.now()) {
148822
+ if (!value || typeof value !== "object") return value;
148823
+ const result = value;
148824
+ if (result.auth || result.status !== "starting" && result.status !== "auth-required") return value;
148825
+ const output = `${typeof result.stdout === "string" ? result.stdout : ""}
148826
+ ${typeof result.stderr === "string" ? result.stderr : ""}`;
148827
+ const auth = parseGithubDeviceFlow(output);
148828
+ if (!auth) return value;
148829
+ return {
148830
+ ...result,
148831
+ status: "auth-required",
148832
+ auth: addAuthPromptTiming(auth, now)
148833
+ };
148834
+ }
148835
+ function isAuthPromptExpired(auth, now = Date.now()) {
148836
+ return typeof auth?.expiresAt === "number" && now >= auth.expiresAt;
148837
+ }
148806
148838
  function hasGithubCopilotTunnelReadyOutput(output) {
148807
148839
  return GHC_TUNNEL_READY_PATTERN.test(output) && GHC_TUNNEL_API_PATTERN.test(output);
148808
148840
  }
@@ -148987,8 +149019,8 @@ async function enableGithubCopilotTunnel(options = {}) {
148987
149019
  };
148988
149020
  const handleOutput = (target, chunk) => {
148989
149021
  appendTail(target, chunk, outputLimitBytes);
148990
- auth = parseGithubDeviceFlow(`${stdout.text}
148991
- ${stderr.text}`) ?? auth;
149022
+ auth = updateAuthPrompt(auth, `${stdout.text}
149023
+ ${stderr.text}`);
148992
149024
  publish2(auth ? "auth-required" : "starting");
148993
149025
  };
148994
149026
  const stdoutListener = (chunk) => handleOutput(stdout, Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk)));
@@ -149026,6 +149058,10 @@ ${stderr.text}`) ?? auth;
149026
149058
  if (childState.spawnError) {
149027
149059
  return buildResult({ host, port, command, args, pid, status: "failed", stdout, stderr, auth, detail: childState.spawnError.message });
149028
149060
  }
149061
+ if (isAuthPromptExpired(auth)) {
149062
+ await terminateChildProcess(child, stopTimeoutMs);
149063
+ return buildResult({ host, port, command, args, pid, status: "failed", stdout, stderr, auth, detail: DEVICE_AUTH_EXPIRED_DETAIL });
149064
+ }
149029
149065
  if (childState.exit) {
149030
149066
  const detail = childState.exit.code === 0 ? `ghc-tunnel exited before port ${port} became reachable` : `ghc-tunnel exited with code ${childState.exit.code ?? "null"}${childState.exit.signal ? ` (${childState.exit.signal})` : ""}`;
149031
149067
  return buildResult({ host, port, command, args, pid, status: "failed", stdout, stderr, auth, detail });
@@ -149073,8 +149109,8 @@ async function runGithubCopilotTunnelSetup(options = {}) {
149073
149109
  };
149074
149110
  const handleOutput = (target, chunk) => {
149075
149111
  appendTail(target, chunk, outputLimitBytes);
149076
- auth = parseGithubDeviceFlow(`${stdout.text}
149077
- ${stderr.text}`) ?? auth;
149112
+ auth = updateAuthPrompt(auth, `${stdout.text}
149113
+ ${stderr.text}`);
149078
149114
  publish2(auth ? "auth-required" : "starting");
149079
149115
  if (options.resolveOnReadyOutput && hasGithubCopilotTunnelReadyOutput(`${stdout.text}
149080
149116
  ${stderr.text}`)) {
@@ -149108,8 +149144,9 @@ ${stderr.text}`)) {
149108
149144
 
149109
149145
  // ../../packages/api/src/node/node-operation-service.ts
149110
149146
  var LEADER_REPORT_RETRY_MS = 5e3;
149111
- var REMOTE_OPERATION_REFRESH_TIMEOUT_MS = 2e3;
149147
+ var REMOTE_OPERATION_REFRESH_TIMEOUT_MS = 7e3;
149112
149148
  var OPERATION_COMMIT_EFFECT = /* @__PURE__ */ Symbol("operationCommitEffect");
149149
+ var REMOTE_OPERATION_MISSING_STATUS = 404;
149113
149150
  function sleep3(ms) {
149114
149151
  return new Promise((resolve24) => setTimeout(resolve24, ms));
149115
149152
  }
@@ -149209,16 +149246,19 @@ var NodeOperationService = class {
149209
149246
  this.scheduleRun(id);
149210
149247
  }
149211
149248
  applyRemoteUpdate(operation) {
149249
+ const normalized = normalizeNodeOperation(operation);
149212
149250
  const current = this.store.get(operation.id);
149213
- if (current && isTerminalStatus(current.status) && !isTerminalStatus(operation.status)) {
149251
+ if (current && isTerminalStatus(current.status) && !isTerminalStatus(normalized.status)) {
149214
149252
  return current;
149215
149253
  }
149216
- const saved = this.store.upsert(current ? { ...current, ...operation } : operation);
149254
+ const saved = this.store.upsert(current ? { ...current, ...normalized } : normalized);
149217
149255
  this.applyOperationSideEffects(saved);
149218
149256
  this.deps.eventBus.emit("node.operation.updated", { operation: saved });
149219
149257
  return saved;
149220
149258
  }
149221
149259
  markFailed(id, error2) {
149260
+ const current = this.store.get(id);
149261
+ if (!current || isTerminalStatus(current.status)) return current;
149222
149262
  return this.update(id, {
149223
149263
  status: "failed",
149224
149264
  error: error2,
@@ -149331,10 +149371,11 @@ var NodeOperationService = class {
149331
149371
  }
149332
149372
  }
149333
149373
  scheduleLeaderReportRetry(operation) {
149334
- if (!isTerminalStatus(operation.status) || this.leaderReportRetryTimers.has(operation.id)) return;
149374
+ if (this.leaderReportRetryTimers.has(operation.id)) return;
149335
149375
  const timer = setTimeout(() => {
149336
149376
  this.leaderReportRetryTimers.delete(operation.id);
149337
- void this.reportToLeader(operation);
149377
+ const latest = this.store.get(operation.id) ?? operation;
149378
+ void this.reportToLeader(latest);
149338
149379
  }, LEADER_REPORT_RETRY_MS);
149339
149380
  this.leaderReportRetryTimers.set(operation.id, timer);
149340
149381
  }
@@ -149358,7 +149399,8 @@ async function refreshRemoteNodeOperation(deps, operation) {
149358
149399
  const self2 = deps.nodeRegistry.getSelf();
149359
149400
  if (operation.nodeId === self2.id) return operation;
149360
149401
  const node2 = deps.nodeRegistry.getNode(operation.nodeId);
149361
- if (!node2) return operation;
149402
+ if (!node2) return failUnavailableCopilotTunnelOperation(deps, operation, `Remote node ${operation.nodeId} is no longer in the cluster`);
149403
+ if (node2.status === "offline") return failUnavailableCopilotTunnelOperation(deps, operation, `Remote node ${node2.name} is offline while ${operation.kind} is running`);
149362
149404
  const message = createNodeMessage("node.operation.get", { operationId: operation.id });
149363
149405
  const heartbeat = deps.heartbeat;
149364
149406
  try {
@@ -149367,6 +149409,15 @@ async function refreshRemoteNodeOperation(deps, operation) {
149367
149409
  timeoutMs: REMOTE_OPERATION_REFRESH_TIMEOUT_MS,
149368
149410
  logger: deps.logger
149369
149411
  }).request(node2, message);
149412
+ if (!response.ok) {
149413
+ if (response.statusCode === REMOTE_OPERATION_MISSING_STATUS) {
149414
+ return getNodeOperationService(deps).markFailed(
149415
+ operation.id,
149416
+ `Remote node no longer has operation ${operation.id}; it may have restarted`
149417
+ ) ?? operation;
149418
+ }
149419
+ return operation;
149420
+ }
149370
149421
  const remote = readOperationFromResponse(response);
149371
149422
  return remote ? getNodeOperationService(deps).applyRemoteUpdate(remote) : operation;
149372
149423
  } catch (err) {
@@ -149379,6 +149430,10 @@ async function refreshRemoteNodeOperation(deps, operation) {
149379
149430
  return operation;
149380
149431
  }
149381
149432
  }
149433
+ function failUnavailableCopilotTunnelOperation(deps, operation, error2) {
149434
+ if (!operation.kind.startsWith("copilot.tunnel")) return operation;
149435
+ return getNodeOperationService(deps).markFailed(operation.id, error2) ?? operation;
149436
+ }
149382
149437
  async function dispatchNodeOperation(deps, operation, node2, options = {}) {
149383
149438
  const message = createNodeMessage("node.operation.execute", {
149384
149439
  operation,
@@ -149417,6 +149472,13 @@ function readOperationFromResponse(response) {
149417
149472
  const operation = response.body && typeof response.body === "object" ? response.body.operation : void 0;
149418
149473
  return operation && typeof operation === "object" ? operation : null;
149419
149474
  }
149475
+ function normalizeNodeOperation(operation) {
149476
+ if (!operation.kind.startsWith("copilot.tunnel") || !operation.result) return operation;
149477
+ return {
149478
+ ...operation,
149479
+ result: normalizeCopilotTunnelResult(operation.result)
149480
+ };
149481
+ }
149420
149482
  function runAgentUpgradeOperation(operation, deps) {
149421
149483
  const agent = readPayloadString(operation, "agent");
149422
149484
  if (!isRuntimeAgentId(agent)) throw new Error(`Unsupported upgrade agent: ${agent}`);
@@ -177699,6 +177761,15 @@ async function cancelRemoteTaskExecution(deps) {
177699
177761
  // ../../packages/api/src/tasks/task-session-import.ts
177700
177762
  init_cjs_shims();
177701
177763
  var TASK_IMPORT_PROXY_TIMEOUT_MS = 1e4;
177764
+ var IMPORT_STATUS_SCORE = {
177765
+ running: 50,
177766
+ assigned: 40,
177767
+ pending: 30,
177768
+ completed: 20,
177769
+ failed: 10,
177770
+ cancelled: 10,
177771
+ archived: 0
177772
+ };
177702
177773
  function buildImportedSessionTitle(agent, sessionId) {
177703
177774
  const label = agent === "codex" ? "Codex" : agent === "copilot" ? "Copilot CLI" : "Claude Code";
177704
177775
  const suffix = sessionId.trim().slice(0, 12);
@@ -177723,6 +177794,38 @@ function resolveTargetImportNode(nodeRegistry, nodeId, agent) {
177723
177794
  }
177724
177795
  return node2;
177725
177796
  }
177797
+ function sameNativeAgent(taskAgent, nativeAgent) {
177798
+ if (nativeAgent === "claudecode") return taskAgent === "claudecode" || taskAgent === "claude";
177799
+ return taskAgent === nativeAgent;
177800
+ }
177801
+ function taskNativeSessionIds(task) {
177802
+ const ids = [];
177803
+ const add = (value) => {
177804
+ if (typeof value === "string" && value.trim() && !ids.includes(value.trim())) ids.push(value.trim());
177805
+ };
177806
+ add(task.payload?.nativeSessionId);
177807
+ add(task.payload?.sessionId);
177808
+ const agentSessions = task.payload?.agentSessions;
177809
+ if (agentSessions && typeof agentSessions === "object") {
177810
+ for (const session of Object.values(agentSessions)) {
177811
+ add(session?.sessionId);
177812
+ }
177813
+ }
177814
+ return ids;
177815
+ }
177816
+ function taskMatchesImportTarget(task, targetNode) {
177817
+ if (task.assignedTo === targetNode.id || task.assignedNodeName === targetNode.name) return true;
177818
+ return !task.assignedTo && !task.assignedNodeName;
177819
+ }
177820
+ function compareImportCandidates(left, right, targetNode) {
177821
+ const score = (task) => IMPORT_STATUS_SCORE[task.status] + (task.conversationKind === "nativeSession" ? 5 : 0) + (task.assignedTo === targetNode.id ? 3 : 0);
177822
+ return score(right) - score(left) || right.updatedAt - left.updatedAt || right.createdAt - left.createdAt;
177823
+ }
177824
+ function findExistingImportedSessionTask(taskEngine, body3, targetNode) {
177825
+ const sessionId = body3.sessionId.trim();
177826
+ const candidates = taskEngine.listTasks().tasks.filter((task) => task.status !== "archived" && sameNativeAgent(task.agent, body3.agent) && taskMatchesImportTarget(task, targetNode) && taskNativeSessionIds(task).includes(sessionId));
177827
+ return candidates.sort((left, right) => compareImportCandidates(left, right, targetNode))[0] ?? null;
177828
+ }
177726
177829
  async function attachNativeSession(deps, body3) {
177727
177830
  const log2 = deps.logger.child("tasks/import-session");
177728
177831
  const selfId = deps.nodeRegistry.getSelf()?.id;
@@ -177731,6 +177834,15 @@ async function attachNativeSession(deps, body3) {
177731
177834
  throw new MeshyError("VALIDATION_ERROR", "No target node is available for session import", 400);
177732
177835
  }
177733
177836
  const targetNode = resolveTargetImportNode(deps.nodeRegistry, targetNodeId, body3.agent);
177837
+ const existingTask = findExistingImportedSessionTask(deps.taskEngine, body3, targetNode);
177838
+ if (existingTask) {
177839
+ log2.info("native session already attached to task", {
177840
+ taskId: existingTask.id,
177841
+ assignedTo: existingTask.assignedTo,
177842
+ sessionId: body3.sessionId
177843
+ });
177844
+ return { task: existingTask, created: false };
177845
+ }
177734
177846
  const history = loadNativeSessionHistory({
177735
177847
  agent: body3.agent,
177736
177848
  sessionId: body3.sessionId
@@ -177795,7 +177907,7 @@ async function attachNativeSession(deps, body3) {
177795
177907
  sourcePath: history.sourcePath
177796
177908
  });
177797
177909
  }
177798
- return importedTask;
177910
+ return { task: importedTask, created: true };
177799
177911
  }
177800
177912
 
177801
177913
  // ../../packages/api/src/tasks/task-log-response.ts
@@ -178938,13 +179050,13 @@ function createTaskRoutes() {
178938
179050
  router.post("/import-session", asyncHandler8(async (req, res) => {
178939
179051
  const { taskEngine, nodeRegistry, engineRegistry, logger: rootLogger, shareOrigin } = req.app.locals.deps;
178940
179052
  const body3 = AttachNativeSessionBody.parse(req.body);
178941
- const importedTask = await attachNativeSession({
179053
+ const imported = await attachNativeSession({
178942
179054
  taskEngine,
178943
179055
  nodeRegistry,
178944
179056
  engineRegistry,
178945
179057
  logger: rootLogger
178946
179058
  }, body3);
178947
- res.status(201).json(toTaskResponse(importedTask, nodeRegistry, taskEngine, shareOrigin));
179059
+ res.status(imported.created ? 201 : 200).json(toTaskResponse(imported.task, nodeRegistry, taskEngine, shareOrigin));
178948
179060
  }));
178949
179061
  router.get("/", asyncHandler8(async (req, res) => {
178950
179062
  const { taskEngine, nodeRegistry, shareOrigin } = req.app.locals.deps;
@@ -179914,6 +180026,7 @@ data: ${JSON.stringify(data)}
179914
180026
  // ../../packages/api/src/routes/node-operations.ts
179915
180027
  init_cjs_shims();
179916
180028
  var import_express15 = __toESM(require_express2(), 1);
180029
+ var TERMINABLE_COPILOT_TUNNEL_KINDS = /* @__PURE__ */ new Set(["copilot.tunnel", "copilot.tunnel.disable"]);
179917
180030
  function asyncHandler12(fn) {
179918
180031
  return (req, res, next2) => fn(req, res, next2).catch(next2);
179919
180032
  }
@@ -179921,6 +180034,13 @@ function parseStatus(value) {
179921
180034
  if (value === "queued" || value === "running" || value === "succeeded" || value === "failed") return value;
179922
180035
  return void 0;
179923
180036
  }
180037
+ function readTerminateReason(body3) {
180038
+ if (body3 && typeof body3 === "object") {
180039
+ const reason = body3.reason;
180040
+ if (typeof reason === "string" && reason.trim()) return reason.trim();
180041
+ }
180042
+ return "Terminated by Copilot tunnel retry";
180043
+ }
179924
180044
  function createNodeOperationRoutes() {
179925
180045
  const router = (0, import_express15.Router)();
179926
180046
  router.get("/", asyncHandler12(async (req, res) => {
@@ -179943,6 +180063,20 @@ function createNodeOperationRoutes() {
179943
180063
  }
179944
180064
  res.json(operation);
179945
180065
  }));
180066
+ router.post("/:id/terminate", asyncHandler12(async (req, res) => {
180067
+ if (await maybeProxyReadToLeader(req, res, { methods: ["POST"] })) return;
180068
+ const deps = req.app.locals.deps;
180069
+ const operations = getNodeOperationService(deps);
180070
+ const current = operations.get(req.params.id);
180071
+ if (!current) {
180072
+ throw new MeshyError("NODE_NOT_FOUND", `Node operation ${req.params.id} not found`, 404);
180073
+ }
180074
+ if (!TERMINABLE_COPILOT_TUNNEL_KINDS.has(current.kind)) {
180075
+ throw new MeshyError("VALIDATION_ERROR", `Node operation ${current.kind} cannot be terminated from this route`, 400);
180076
+ }
180077
+ const operation = operations.markFailed(current.id, readTerminateReason(req.body)) ?? current;
180078
+ res.json(operation);
180079
+ }));
179946
180080
  return router;
179947
180081
  }
179948
180082
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "meshy-node",
3
- "version": "1.0.2",
3
+ "version": "1.0.3",
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": "1.0.2",
3
+ "packageVersion": "1.0.3",
4
4
  "packages": {
5
5
  "workspace": {
6
6
  "name": "meshy",
7
- "version": "1.0.2"
7
+ "version": "1.0.3"
8
8
  },
9
9
  "node": {
10
10
  "name": "meshy-node",
11
- "version": "1.0.2"
11
+ "version": "1.0.3"
12
12
  },
13
13
  "core": {
14
14
  "name": "@meshy/core",
@@ -26,6 +26,6 @@
26
26
  "repository": {
27
27
  "url": "https://github.com/gim-home/meshy",
28
28
  "branch": "main",
29
- "commit": "8b895af"
29
+ "commit": "1e2b6f3"
30
30
  }
31
31
  }