meshy-node 0.2.4 → 0.2.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/main.cjs CHANGED
@@ -25760,7 +25760,7 @@ var require_application = __commonJS({
25760
25760
  "../../node_modules/.pnpm/express@4.22.1/node_modules/express/lib/application.js"(exports2, module2) {
25761
25761
  "use strict";
25762
25762
  var finalhandler = require_finalhandler();
25763
- var Router11 = require_router();
25763
+ var Router12 = require_router();
25764
25764
  var methods = require_methods();
25765
25765
  var middleware = require_init();
25766
25766
  var query = require_query();
@@ -25825,7 +25825,7 @@ var require_application = __commonJS({
25825
25825
  };
25826
25826
  app.lazyrouter = function lazyrouter() {
25827
25827
  if (!this._router) {
25828
- this._router = new Router11({
25828
+ this._router = new Router12({
25829
25829
  caseSensitive: this.enabled("case sensitive routing"),
25830
25830
  strict: this.enabled("strict routing")
25831
25831
  });
@@ -27690,7 +27690,7 @@ var require_express = __commonJS({
27690
27690
  var mixin = require_merge_descriptors();
27691
27691
  var proto = require_application();
27692
27692
  var Route = require_route();
27693
- var Router11 = require_router();
27693
+ var Router12 = require_router();
27694
27694
  var req = require_request();
27695
27695
  var res = require_response();
27696
27696
  exports2 = module2.exports = createApplication;
@@ -27713,7 +27713,7 @@ var require_express = __commonJS({
27713
27713
  exports2.request = req;
27714
27714
  exports2.response = res;
27715
27715
  exports2.Route = Route;
27716
- exports2.Router = Router11;
27716
+ exports2.Router = Router12;
27717
27717
  exports2.json = bodyParser.json;
27718
27718
  exports2.query = require_query();
27719
27719
  exports2.raw = bodyParser.raw;
@@ -32335,6 +32335,7 @@ var fs3 = __toESM(require("fs"), 1);
32335
32335
  var path3 = __toESM(require("path"), 1);
32336
32336
 
32337
32337
  // ../../packages/core/src/shared/types.ts
32338
+ var DEFAULT_TASK_SHARE_TENANT = "72f988bf-86f1-41af-91ab-2d7cd011db47";
32338
32339
  var TASK_EXECUTION_MODES = ["bypass", "plan", "edit", "dangerous"];
32339
32340
  var MeshyError = class extends Error {
32340
32341
  constructor(code, message, statusCode, details) {
@@ -32387,6 +32388,7 @@ var FILE_NAMES = {
32387
32388
  cluster: "cluster.json",
32388
32389
  nodes: "nodes.json",
32389
32390
  tasks: "tasks.json",
32391
+ shares: "shares.json",
32390
32392
  election: "election.json"
32391
32393
  };
32392
32394
  var FileStore = class {
@@ -32395,6 +32397,7 @@ var FileStore = class {
32395
32397
  cluster = null;
32396
32398
  nodes = /* @__PURE__ */ new Map();
32397
32399
  tasks = /* @__PURE__ */ new Map();
32400
+ shares = /* @__PURE__ */ new Map();
32398
32401
  election = /* @__PURE__ */ new Map();
32399
32402
  dirtySections = /* @__PURE__ */ new Set();
32400
32403
  constructor(options = {}) {
@@ -32410,6 +32413,7 @@ var FileStore = class {
32410
32413
  this.cluster = this.loadCluster();
32411
32414
  this.replaceMap(this.nodes, this.loadNodes());
32412
32415
  this.replaceMap(this.tasks, this.loadTasks());
32416
+ this.replaceMap(this.shares, this.loadTaskShares());
32413
32417
  this.replaceMap(this.election, this.loadElection());
32414
32418
  this.flushDirtySections();
32415
32419
  }
@@ -32422,6 +32426,7 @@ var FileStore = class {
32422
32426
  this.cluster = null;
32423
32427
  this.nodes.clear();
32424
32428
  this.tasks.clear();
32429
+ this.shares.clear();
32425
32430
  this.election.clear();
32426
32431
  this.dirtySections.clear();
32427
32432
  }
@@ -32562,6 +32567,46 @@ var FileStore = class {
32562
32567
  }
32563
32568
  return metrics;
32564
32569
  }
32570
+ createTaskShare(share) {
32571
+ this.ensureOpen();
32572
+ const copy = clone(share);
32573
+ this.shares.set(copy.id, copy);
32574
+ this.persistSection("shares");
32575
+ return clone(copy);
32576
+ }
32577
+ getTaskShare(id) {
32578
+ this.ensureOpen();
32579
+ const share = this.shares.get(id);
32580
+ return share === void 0 ? null : clone(share);
32581
+ }
32582
+ getTaskShareByTaskId(taskId) {
32583
+ this.ensureOpen();
32584
+ for (const share of this.shares.values()) {
32585
+ if (share.taskId === taskId && share.revokedAt === void 0) {
32586
+ return clone(share);
32587
+ }
32588
+ }
32589
+ return null;
32590
+ }
32591
+ getAllTaskShares() {
32592
+ this.ensureOpen();
32593
+ return Array.from(this.shares.values(), (share) => clone(share));
32594
+ }
32595
+ updateTaskShare(id, updates) {
32596
+ this.ensureOpen();
32597
+ const current = this.shares.get(id);
32598
+ if (current === void 0) {
32599
+ return null;
32600
+ }
32601
+ const merged = {
32602
+ ...current,
32603
+ ...clone(updates),
32604
+ id: current.id
32605
+ };
32606
+ this.shares.set(id, merged);
32607
+ this.persistSection("shares");
32608
+ return clone(merged);
32609
+ }
32565
32610
  getElectionValue(key) {
32566
32611
  this.ensureOpen();
32567
32612
  return this.election.get(key) ?? null;
@@ -32633,6 +32678,26 @@ var FileStore = class {
32633
32678
  }
32634
32679
  return tasks;
32635
32680
  }
32681
+ loadTaskShares() {
32682
+ const raw = this.readJsonFile("shares");
32683
+ if (!isPlainObject(raw)) {
32684
+ this.markDirty("shares");
32685
+ return /* @__PURE__ */ new Map();
32686
+ }
32687
+ const shares = /* @__PURE__ */ new Map();
32688
+ for (const [key, value] of Object.entries(raw)) {
32689
+ const share = parseTaskShare(value);
32690
+ if (share === null) {
32691
+ this.markDirty("shares");
32692
+ continue;
32693
+ }
32694
+ if (share.id !== key) {
32695
+ this.markDirty("shares");
32696
+ }
32697
+ shares.set(share.id, share);
32698
+ }
32699
+ return shares;
32700
+ }
32636
32701
  loadElection() {
32637
32702
  const raw = this.readJsonFile("election");
32638
32703
  if (!isPlainObject(raw)) {
@@ -32698,6 +32763,8 @@ var FileStore = class {
32698
32763
  return Object.fromEntries(sortEntries(this.nodes));
32699
32764
  case "tasks":
32700
32765
  return Object.fromEntries(sortEntries(this.tasks));
32766
+ case "shares":
32767
+ return Object.fromEntries(sortEntries(this.shares));
32701
32768
  case "election":
32702
32769
  return Object.fromEntries(sortEntries(this.election));
32703
32770
  }
@@ -32824,6 +32891,29 @@ function parseTask(value) {
32824
32891
  updatedAt: value.updatedAt
32825
32892
  };
32826
32893
  }
32894
+ function parseTaskShare(value) {
32895
+ if (!isPlainObject(value)) {
32896
+ return null;
32897
+ }
32898
+ if (typeof value.id !== "string" || typeof value.taskId !== "string" || typeof value.createdAt !== "number" || !Number.isFinite(value.createdAt) || value.revokedAt !== void 0 && (typeof value.revokedAt !== "number" || !Number.isFinite(value.revokedAt))) {
32899
+ return null;
32900
+ }
32901
+ return {
32902
+ id: value.id,
32903
+ taskId: value.taskId,
32904
+ tenant: asTaskShareTenant(value.tenant) ?? DEFAULT_TASK_SHARE_TENANT,
32905
+ scope: asTaskShareScope(value.scope) ?? "chat",
32906
+ createdAt: value.createdAt,
32907
+ revokedAt: typeof value.revokedAt === "number" ? value.revokedAt : void 0
32908
+ };
32909
+ }
32910
+ function asTaskShareTenant(value) {
32911
+ if (value === "microsoft") return DEFAULT_TASK_SHARE_TENANT;
32912
+ return typeof value === "string" && value.trim().length > 0 ? value.trim() : null;
32913
+ }
32914
+ function asTaskShareScope(value) {
32915
+ return value === "chat" || value === "filesPreview" ? value : null;
32916
+ }
32827
32917
  function asNodeRole(value) {
32828
32918
  return isNodeRole(value) ? value : null;
32829
32919
  }
@@ -33887,6 +33977,19 @@ var TaskEngine = class {
33887
33977
  getMetrics() {
33888
33978
  return this.store.getTaskMetrics();
33889
33979
  }
33980
+ // ── Task Shares ─────────────────────────────────────────────────────
33981
+ createTaskShare(share) {
33982
+ return this.store.createTaskShare(share);
33983
+ }
33984
+ getTaskShare(id) {
33985
+ return this.store.getTaskShare(id);
33986
+ }
33987
+ getTaskShareByTaskId(taskId) {
33988
+ return this.store.getTaskShareByTaskId(taskId);
33989
+ }
33990
+ updateTaskShare(id, updates) {
33991
+ return this.store.updateTaskShare(id, updates);
33992
+ }
33890
33993
  // ── Helpers ──────────────────────────────────────────────────────────
33891
33994
  /** Calculate exponential backoff: min(2^retryCount * 1000, 30000) ms */
33892
33995
  getRetryBackoff(retryCount) {
@@ -41487,6 +41590,10 @@ var SendMessageBody = external_exports.union([
41487
41590
  var TaskLogsQuery = external_exports.object({
41488
41591
  after: external_exports.coerce.number().int().min(0).optional()
41489
41592
  });
41593
+ var CreateTaskShareBody = external_exports.object({
41594
+ tenant: external_exports.string().trim().min(1).max(128).default(DEFAULT_TASK_SHARE_TENANT),
41595
+ scope: external_exports.enum(["chat", "filesPreview"]).default("chat")
41596
+ }).default({});
41490
41597
  var TaskOutputTreeQuery = external_exports.object({
41491
41598
  path: external_exports.string().default(".")
41492
41599
  });
@@ -41504,7 +41611,7 @@ var BatchTaskIdsBody = external_exports.object({
41504
41611
  // ../../packages/api/src/app/server.ts
41505
41612
  var path17 = __toESM(require("path"), 1);
41506
41613
  var fs15 = __toESM(require("fs"), 1);
41507
- var import_express11 = __toESM(require_express2(), 1);
41614
+ var import_express12 = __toESM(require_express2(), 1);
41508
41615
 
41509
41616
  // ../../packages/api/src/middleware/auth.ts
41510
41617
  var SKIP_AUTH_PATHS = ["/api/system/health"];
@@ -44033,6 +44140,7 @@ async function handleTaskMessage(deps, message) {
44033
44140
 
44034
44141
  // ../../packages/api/src/routes/tasks.ts
44035
44142
  var import_express7 = __toESM(require_express2(), 1);
44143
+ var import_node_crypto7 = require("crypto");
44036
44144
 
44037
44145
  // ../../packages/api/src/tasks/task-remote-cancellation.ts
44038
44146
  async function cancelRemoteTaskExecution(deps) {
@@ -44595,6 +44703,25 @@ function withAssignedNodeMetadata(task, nodeRegistry) {
44595
44703
  assignedNodeAvailable: node?.status !== "offline" && Boolean(node)
44596
44704
  };
44597
44705
  }
44706
+ function withShareMetadata(task, taskEngine, shareOrigin) {
44707
+ const share = taskEngine.getTaskShareByTaskId?.(task.id) ?? null;
44708
+ if (!share) return task;
44709
+ return {
44710
+ ...task,
44711
+ activeShare: {
44712
+ shareId: share.id,
44713
+ tenant: share.tenant,
44714
+ scope: share.scope,
44715
+ ...shareOrigin ? { url: buildShareUrl(shareOrigin, share.id) } : {}
44716
+ }
44717
+ };
44718
+ }
44719
+ function toTaskResponse(task, nodeRegistry, taskEngine, shareOrigin) {
44720
+ return withShareMetadata(withAssignedNodeMetadata(task, nodeRegistry), taskEngine, shareOrigin);
44721
+ }
44722
+ function buildShareUrl(origin, shareId) {
44723
+ return `${origin.replace(/\/+$/, "")}/shared/tasks/${encodeURIComponent(shareId)}`;
44724
+ }
44598
44725
  function createTaskRoutes() {
44599
44726
  const router = (0, import_express7.Router)();
44600
44727
  router.post("/", asyncHandler6(async (req, res) => {
@@ -44620,7 +44747,7 @@ function createTaskRoutes() {
44620
44747
  res.status(201).json(withAssignedNodeMetadata(task, nodeRegistry));
44621
44748
  }));
44622
44749
  router.get("/", asyncHandler6(async (req, res) => {
44623
- const { taskEngine, nodeRegistry } = req.app.locals.deps;
44750
+ const { taskEngine, nodeRegistry, shareOrigin } = req.app.locals.deps;
44624
44751
  const query = TaskListQuery.parse(req.query);
44625
44752
  const filter = {};
44626
44753
  if (query.status) filter.status = query.status;
@@ -44630,7 +44757,7 @@ function createTaskRoutes() {
44630
44757
  if (query.offset) filter.offset = query.offset;
44631
44758
  const result = taskEngine.listTasks(filter);
44632
44759
  res.json({
44633
- tasks: result.tasks.map((task) => withAssignedNodeMetadata(task, nodeRegistry)),
44760
+ tasks: result.tasks.map((task) => toTaskResponse(task, nodeRegistry, taskEngine, shareOrigin)),
44634
44761
  total: result.total
44635
44762
  });
44636
44763
  }));
@@ -44666,12 +44793,59 @@ function createTaskRoutes() {
44666
44793
  res.json({ results });
44667
44794
  }));
44668
44795
  router.get("/:id", asyncHandler6(async (req, res) => {
44669
- const { taskEngine, nodeRegistry } = req.app.locals.deps;
44670
- const task = taskEngine.getTask(req.params.id);
44796
+ const { taskEngine, nodeRegistry, shareOrigin } = req.app.locals.deps;
44797
+ const taskId = req.params.id;
44798
+ const task = taskEngine.getTask(taskId);
44671
44799
  if (!task) {
44672
- throw new MeshyError("TASK_NOT_FOUND", `Task ${req.params.id} not found`, 404);
44800
+ throw new MeshyError("TASK_NOT_FOUND", `Task ${taskId} not found`, 404);
44673
44801
  }
44674
- res.json(withAssignedNodeMetadata(task, nodeRegistry));
44802
+ res.json(toTaskResponse(task, nodeRegistry, taskEngine, shareOrigin));
44803
+ }));
44804
+ router.post("/:id/share", asyncHandler6(async (req, res) => {
44805
+ const { taskEngine, ensureShareTunnel } = req.app.locals.deps;
44806
+ const body = CreateTaskShareBody.parse(req.body ?? {});
44807
+ const taskId = req.params.id;
44808
+ const task = taskEngine.getTask(taskId);
44809
+ if (!task) {
44810
+ throw new MeshyError("TASK_NOT_FOUND", `Task ${taskId} not found`, 404);
44811
+ }
44812
+ if (!ensureShareTunnel) {
44813
+ throw new MeshyError("VALIDATION_ERROR", "Share tunnel is not available", 503);
44814
+ }
44815
+ const shareOrigin = await ensureShareTunnel();
44816
+ const existing = taskEngine.getTaskShareByTaskId(task.id);
44817
+ if (existing) {
44818
+ const updated = taskEngine.updateTaskShare(existing.id, {
44819
+ tenant: body.tenant,
44820
+ scope: body.scope
44821
+ }) ?? existing;
44822
+ res.json({ shareId: updated.id, tenant: updated.tenant, scope: updated.scope, url: buildShareUrl(shareOrigin, updated.id) });
44823
+ return;
44824
+ }
44825
+ const share = taskEngine.createTaskShare({
44826
+ id: (0, import_node_crypto7.randomUUID)(),
44827
+ taskId: task.id,
44828
+ tenant: body.tenant,
44829
+ scope: body.scope,
44830
+ createdAt: Date.now()
44831
+ });
44832
+ res.status(201).json({ shareId: share.id, tenant: share.tenant, scope: share.scope, url: buildShareUrl(shareOrigin, share.id) });
44833
+ }));
44834
+ router.delete("/:id/share", asyncHandler6(async (req, res) => {
44835
+ const { taskEngine } = req.app.locals.deps;
44836
+ const taskId = req.params.id;
44837
+ const task = taskEngine.getTask(taskId);
44838
+ if (!task) {
44839
+ throw new MeshyError("TASK_NOT_FOUND", `Task ${taskId} not found`, 404);
44840
+ }
44841
+ const existing = taskEngine.getTaskShareByTaskId(task.id);
44842
+ if (!existing) {
44843
+ res.json({ ok: true, stopped: false });
44844
+ return;
44845
+ }
44846
+ const revokedAt = Date.now();
44847
+ const updated = taskEngine.updateTaskShare(existing.id, { revokedAt }) ?? { ...existing, revokedAt };
44848
+ res.json({ ok: true, stopped: true, shareId: updated.id });
44675
44849
  }));
44676
44850
  router.patch("/:id", asyncHandler6(async (req, res) => {
44677
44851
  const { taskEngine, nodeRegistry, logger: rootLogger } = req.app.locals.deps;
@@ -44914,20 +45088,174 @@ function createTaskRoutes() {
44914
45088
  return router;
44915
45089
  }
44916
45090
 
44917
- // ../../packages/api/src/routes/worker.ts
45091
+ // ../../packages/api/src/routes/shared.ts
44918
45092
  var import_express8 = __toESM(require_express2(), 1);
45093
+ function asyncHandler7(fn) {
45094
+ return (req, res, next) => fn(req, res, next).catch(next);
45095
+ }
45096
+ function assertActiveShare(share) {
45097
+ if (!share) {
45098
+ throw new MeshyError("TASK_NOT_FOUND", "Shared conversation not found", 404);
45099
+ }
45100
+ if (share.revokedAt !== void 0) {
45101
+ throw new MeshyError("VALIDATION_ERROR", "Shared conversation has been revoked", 410);
45102
+ }
45103
+ return share;
45104
+ }
45105
+ function assertFilesPreviewShare(share) {
45106
+ if (share.scope !== "filesPreview") {
45107
+ throw new MeshyError("VALIDATION_ERROR", "Shared conversation does not include files or preview", 403);
45108
+ }
45109
+ }
45110
+ function getTaskPayloadForShare(task) {
45111
+ return task.payload.initialMessage === void 0 ? {} : { initialMessage: task.payload.initialMessage };
45112
+ }
45113
+ function toSharedConversation(share, task) {
45114
+ return {
45115
+ id: task.id,
45116
+ shareId: share.id,
45117
+ title: task.title,
45118
+ description: task.description,
45119
+ agent: task.agent,
45120
+ status: task.status,
45121
+ priority: task.priority,
45122
+ assignedNodeName: task.assignedNodeName ?? null,
45123
+ tenant: share.tenant,
45124
+ scope: share.scope,
45125
+ payload: getTaskPayloadForShare(task),
45126
+ createdAt: task.createdAt,
45127
+ updatedAt: task.updatedAt
45128
+ };
45129
+ }
45130
+ function createSharedRoutes() {
45131
+ const router = (0, import_express8.Router)();
45132
+ router.get("/conversations/:shareId", asyncHandler7(async (req, res) => {
45133
+ const { taskEngine } = req.app.locals.deps;
45134
+ const shareId = req.params.shareId;
45135
+ const share = assertActiveShare(taskEngine.getTaskShare(shareId));
45136
+ const task = taskEngine.getTask(share.taskId);
45137
+ if (!task) {
45138
+ throw new MeshyError("TASK_NOT_FOUND", "Shared conversation task not found", 404);
45139
+ }
45140
+ res.json(toSharedConversation(share, task));
45141
+ }));
45142
+ router.get("/conversations/:shareId/logs", asyncHandler7(async (req, res) => {
45143
+ const { taskEngine, engineRegistry } = req.app.locals.deps;
45144
+ const query = TaskLogsQuery.parse(req.query);
45145
+ const shareId = req.params.shareId;
45146
+ const share = assertActiveShare(taskEngine.getTaskShare(shareId));
45147
+ const task = taskEngine.getTask(share.taskId);
45148
+ if (!task) {
45149
+ throw new MeshyError("TASK_NOT_FOUND", "Shared conversation task not found", 404);
45150
+ }
45151
+ res.json(readLocalTaskLogs(engineRegistry, task.id, query.after ?? 0, task.agent));
45152
+ }));
45153
+ router.get("/conversations/:shareId/output", asyncHandler7(async (req, res) => {
45154
+ const { taskEngine } = req.app.locals.deps;
45155
+ const shareId = req.params.shareId;
45156
+ const share = assertActiveShare(taskEngine.getTaskShare(shareId));
45157
+ assertFilesPreviewShare(share);
45158
+ const handled = await maybeHandleRemoteTaskOutputRequest(
45159
+ req,
45160
+ res,
45161
+ share.taskId,
45162
+ "",
45163
+ void 0,
45164
+ createNodeMessage("task.output.summary", { taskId: share.taskId }, { expectsResponse: true })
45165
+ );
45166
+ if (handled) return;
45167
+ res.json(getLocalTaskOutputSummary(req.app.locals.deps.taskEngine, share.taskId));
45168
+ }));
45169
+ router.get("/conversations/:shareId/output/tree", asyncHandler7(async (req, res) => {
45170
+ const { taskEngine } = req.app.locals.deps;
45171
+ const query = TaskOutputTreeQuery.parse(req.query);
45172
+ const shareId = req.params.shareId;
45173
+ const share = assertActiveShare(taskEngine.getTaskShare(shareId));
45174
+ assertFilesPreviewShare(share);
45175
+ const handled = await maybeHandleRemoteTaskOutputRequest(
45176
+ req,
45177
+ res,
45178
+ share.taskId,
45179
+ "/tree",
45180
+ void 0,
45181
+ createNodeMessage("task.output.tree", { taskId: share.taskId, path: query.path }, { expectsResponse: true })
45182
+ );
45183
+ if (handled) return;
45184
+ res.json(getLocalTaskOutputTree(req.app.locals.deps.taskEngine, share.taskId, query.path));
45185
+ }));
45186
+ router.get("/conversations/:shareId/output/content", asyncHandler7(async (req, res) => {
45187
+ const { taskEngine } = req.app.locals.deps;
45188
+ const query = TaskOutputContentQuery.parse(req.query);
45189
+ const shareId = req.params.shareId;
45190
+ const share = assertActiveShare(taskEngine.getTaskShare(shareId));
45191
+ assertFilesPreviewShare(share);
45192
+ const handled = await maybeHandleRemoteTaskOutputRequest(
45193
+ req,
45194
+ res,
45195
+ share.taskId,
45196
+ "/content",
45197
+ void 0,
45198
+ createNodeMessage("task.output.content", { taskId: share.taskId, path: query.path }, { expectsResponse: true })
45199
+ );
45200
+ if (handled) return;
45201
+ const content = getLocalTaskOutputContent(req.app.locals.deps.taskEngine, share.taskId, query.path);
45202
+ res.json({
45203
+ ...content,
45204
+ downloadUrl: `/api/shared/conversations/${encodeURIComponent(share.id)}/output/download?path=${encodeURIComponent(query.path)}`
45205
+ });
45206
+ }));
45207
+ router.get("/conversations/:shareId/output/download", asyncHandler7(async (req, res) => {
45208
+ const { taskEngine } = req.app.locals.deps;
45209
+ const query = TaskOutputDownloadQuery.parse(req.query);
45210
+ const shareId = req.params.shareId;
45211
+ const share = assertActiveShare(taskEngine.getTaskShare(shareId));
45212
+ assertFilesPreviewShare(share);
45213
+ const handled = await maybeHandleRemoteTaskOutputRequest(
45214
+ req,
45215
+ res,
45216
+ share.taskId,
45217
+ "/download",
45218
+ void 0,
45219
+ createNodeMessage("task.output.download", { taskId: share.taskId, path: query.path }, { expectsResponse: true })
45220
+ );
45221
+ if (handled) return;
45222
+ const download = getLocalTaskOutputDownload(req.app.locals.deps.taskEngine, share.taskId, query.path);
45223
+ for (const [key, value] of Object.entries(download.headers)) {
45224
+ res.setHeader(key, value);
45225
+ }
45226
+ res.send(download.content);
45227
+ }));
45228
+ router.post("/conversations/:shareId/output/preview-sessions", asyncHandler7(async (req, res) => {
45229
+ const { taskEngine } = req.app.locals.deps;
45230
+ const body = TaskPreviewSessionBody.parse(req.body ?? {});
45231
+ const shareId = req.params.shareId;
45232
+ const share = assertActiveShare(taskEngine.getTaskShare(shareId));
45233
+ assertFilesPreviewShare(share);
45234
+ const handled = await maybeHandleRemoteTaskOutputRequest(req, res, share.taskId, "/preview-sessions", {
45235
+ method: "POST",
45236
+ headers: { "Content-Type": "application/json" },
45237
+ body: JSON.stringify(body)
45238
+ }, createNodeMessage("task.preview.create", { taskId: share.taskId, path: body.path }, { expectsResponse: true }));
45239
+ if (handled) return;
45240
+ res.json(await createPreviewSessionPayload(req.app.locals.deps, share.taskId, body.path, resolveRequestOrigin(req)));
45241
+ }));
45242
+ return router;
45243
+ }
45244
+
45245
+ // ../../packages/api/src/routes/worker.ts
45246
+ var import_express9 = __toESM(require_express2(), 1);
44919
45247
  var WorkerMessageBody = external_exports.object({
44920
45248
  taskId: external_exports.string().min(1)
44921
45249
  }).and(SendMessageBody);
44922
45250
  var WorkerTaskBody = external_exports.object({
44923
45251
  taskId: external_exports.string().min(1)
44924
45252
  });
44925
- function asyncHandler7(fn) {
45253
+ function asyncHandler8(fn) {
44926
45254
  return (req, res, next) => fn(req, res, next).catch(next);
44927
45255
  }
44928
45256
  function createWorkerRoutes() {
44929
- const router = (0, import_express8.Router)();
44930
- router.post("/execute", asyncHandler7(async (req, res) => {
45257
+ const router = (0, import_express9.Router)();
45258
+ router.post("/execute", asyncHandler8(async (req, res) => {
44931
45259
  const { taskEngine, engineRegistry, nodeRegistry, eventBus, logger: rootLogger, workDir } = req.app.locals.deps;
44932
45260
  const log2 = rootLogger.child("worker/execute");
44933
45261
  const task = req.body;
@@ -44944,7 +45272,7 @@ function createWorkerRoutes() {
44944
45272
  });
44945
45273
  res.json({ ok: true });
44946
45274
  }));
44947
- router.post("/message", asyncHandler7(async (req, res) => {
45275
+ router.post("/message", asyncHandler8(async (req, res) => {
44948
45276
  const { engineRegistry, taskEngine, nodeRegistry, eventBus, logger: rootLogger } = req.app.locals.deps;
44949
45277
  const log2 = rootLogger.child("worker/message");
44950
45278
  const body = WorkerMessageBody.parse(req.body);
@@ -44994,7 +45322,7 @@ function createWorkerRoutes() {
44994
45322
  }
44995
45323
  res.json({ ok: true });
44996
45324
  }));
44997
- router.post("/cancel", asyncHandler7(async (req, res) => {
45325
+ router.post("/cancel", asyncHandler8(async (req, res) => {
44998
45326
  const { taskEngine, engineRegistry, logger: rootLogger } = req.app.locals.deps;
44999
45327
  const log2 = rootLogger.child("worker/cancel");
45000
45328
  const body = WorkerTaskBody.parse(req.body);
@@ -45014,7 +45342,7 @@ function createWorkerRoutes() {
45014
45342
  terminal: result.terminal
45015
45343
  });
45016
45344
  }));
45017
- router.post("/output", asyncHandler7(async (req, res) => {
45345
+ router.post("/output", asyncHandler8(async (req, res) => {
45018
45346
  const { eventBus, engineRegistry, taskEngine, logger: rootLogger } = req.app.locals.deps;
45019
45347
  const log2 = rootLogger.child("worker/output");
45020
45348
  const { taskId, event } = req.body;
@@ -45029,7 +45357,7 @@ function createWorkerRoutes() {
45029
45357
  }
45030
45358
  res.json({ ok: true });
45031
45359
  }));
45032
- router.post("/heartbeat", asyncHandler7(async (req, res) => {
45360
+ router.post("/heartbeat", asyncHandler8(async (req, res) => {
45033
45361
  const { heartbeat, logger: rootLogger } = req.app.locals.deps;
45034
45362
  const log2 = rootLogger.child("worker/heartbeat");
45035
45363
  const body = req.body;
@@ -45046,7 +45374,7 @@ function createWorkerRoutes() {
45046
45374
  });
45047
45375
  res.json(response);
45048
45376
  }));
45049
- router.post("/keepalive", asyncHandler7(async (req, res) => {
45377
+ router.post("/keepalive", asyncHandler8(async (req, res) => {
45050
45378
  const { heartbeat, logger: rootLogger } = req.app.locals.deps;
45051
45379
  const log2 = rootLogger.child("worker/keepalive");
45052
45380
  const body = req.body;
@@ -45064,17 +45392,17 @@ function createWorkerRoutes() {
45064
45392
  });
45065
45393
  res.json(response);
45066
45394
  }));
45067
- router.post("/vote", asyncHandler7(async (req, res) => {
45395
+ router.post("/vote", asyncHandler8(async (req, res) => {
45068
45396
  const { election } = req.app.locals.deps;
45069
45397
  const response = election.handleVoteRequest(req.body);
45070
45398
  res.json(response);
45071
45399
  }));
45072
- router.post("/announce", asyncHandler7(async (req, res) => {
45400
+ router.post("/announce", asyncHandler8(async (req, res) => {
45073
45401
  const { election } = req.app.locals.deps;
45074
45402
  election.handleLeaderAnnounce(req.body);
45075
45403
  res.json({ ok: true });
45076
45404
  }));
45077
- router.post("/preview-session", asyncHandler7(async (req, res) => {
45405
+ router.post("/preview-session", asyncHandler8(async (req, res) => {
45078
45406
  const { logger: rootLogger } = req.app.locals.deps;
45079
45407
  const log2 = rootLogger.child("worker/preview-session");
45080
45408
  const body = TaskPreviewSessionBody.extend({
@@ -45089,7 +45417,7 @@ function createWorkerRoutes() {
45089
45417
  log2.info("created preview session", { taskId: body.taskId, entryPath: payload.entryPath });
45090
45418
  res.json(payload);
45091
45419
  }));
45092
- router.get("/preview-asset", asyncHandler7(async (req, res) => {
45420
+ router.get("/preview-asset", asyncHandler8(async (req, res) => {
45093
45421
  const { previewSessionManager } = req.app.locals.deps;
45094
45422
  if (!previewSessionManager) {
45095
45423
  throw new MeshyError("VALIDATION_ERROR", "Preview not available on this node", 400);
@@ -45098,7 +45426,7 @@ function createWorkerRoutes() {
45098
45426
  const requestedPath = typeof req.query.path === "string" && req.query.path.length > 0 ? req.query.path : void 0;
45099
45427
  sendPreviewAssetResponse(previewSessionManager, token, requestedPath, res);
45100
45428
  }));
45101
- router.post("/control-response", asyncHandler7(async (req, res) => {
45429
+ router.post("/control-response", asyncHandler8(async (req, res) => {
45102
45430
  const { heartbeat, logger: rootLogger } = req.app.locals.deps;
45103
45431
  const log2 = rootLogger.child("worker/control-response");
45104
45432
  const body = normalizeNodeMessageResponse(req.body);
@@ -45125,7 +45453,7 @@ function normalizeNodeMessageResponse(value) {
45125
45453
  }
45126
45454
 
45127
45455
  // ../../packages/api/src/routes/system.ts
45128
- var import_express9 = __toESM(require_express2(), 1);
45456
+ var import_express10 = __toESM(require_express2(), 1);
45129
45457
 
45130
45458
  // ../../packages/api/src/app/system-info.ts
45131
45459
  var os5 = __toESM(require("os"), 1);
@@ -45243,12 +45571,12 @@ function buildNodeSettingsSnapshot(options) {
45243
45571
  }
45244
45572
 
45245
45573
  // ../../packages/api/src/routes/system.ts
45246
- function asyncHandler8(fn) {
45574
+ function asyncHandler9(fn) {
45247
45575
  return (req, res, next) => fn(req, res, next).catch(next);
45248
45576
  }
45249
45577
  function createSystemRoutes() {
45250
- const router = (0, import_express9.Router)();
45251
- router.get("/health", asyncHandler8(async (req, res) => {
45578
+ const router = (0, import_express10.Router)();
45579
+ router.get("/health", asyncHandler9(async (req, res) => {
45252
45580
  const { logger: rootLogger } = req.app.locals.deps;
45253
45581
  rootLogger.child("system/health").info("received system health API call", {
45254
45582
  method: req.method,
@@ -45257,7 +45585,7 @@ function createSystemRoutes() {
45257
45585
  });
45258
45586
  res.json({ status: "ok", timestamp: Date.now() });
45259
45587
  }));
45260
- router.get("/info", asyncHandler8(async (req, res) => {
45588
+ router.get("/info", asyncHandler9(async (req, res) => {
45261
45589
  const {
45262
45590
  nodeRegistry,
45263
45591
  getTransportType,
@@ -45301,7 +45629,7 @@ function createSystemRoutes() {
45301
45629
  packages: settingsSnapshot.packages
45302
45630
  });
45303
45631
  }));
45304
- router.post("/transport", asyncHandler8(async (req, res) => {
45632
+ router.post("/transport", asyncHandler9(async (req, res) => {
45305
45633
  const { switchTransport } = req.app.locals.deps;
45306
45634
  if (!switchTransport) {
45307
45635
  res.status(501).json({ error: "Transport switching not available" });
@@ -45315,7 +45643,7 @@ function createSystemRoutes() {
45315
45643
  const newEndpoint = await switchTransport(type);
45316
45644
  res.json({ ok: true, transportType: type, endpoint: newEndpoint });
45317
45645
  }));
45318
- router.post("/devtunnel", asyncHandler8(async (req, res) => {
45646
+ router.post("/devtunnel", asyncHandler9(async (req, res) => {
45319
45647
  const { enableDevTunnel, disableDevTunnel, nodeRegistry, taskEngine } = req.app.locals.deps;
45320
45648
  if (!enableDevTunnel || !disableDevTunnel) {
45321
45649
  res.status(501).json({ error: "DevTunnel control not available" });
@@ -45344,7 +45672,7 @@ function createSystemRoutes() {
45344
45672
  });
45345
45673
  }
45346
45674
  }));
45347
- router.get("/metrics", asyncHandler8(async (req, res) => {
45675
+ router.get("/metrics", asyncHandler9(async (req, res) => {
45348
45676
  const { taskEngine } = req.app.locals.deps;
45349
45677
  const metrics = taskEngine.getMetrics();
45350
45678
  res.json(metrics);
@@ -45353,7 +45681,7 @@ function createSystemRoutes() {
45353
45681
  }
45354
45682
 
45355
45683
  // ../../packages/api/src/routes/events.ts
45356
- var import_express10 = __toESM(require_express2(), 1);
45684
+ var import_express11 = __toESM(require_express2(), 1);
45357
45685
  var HEARTBEAT_INTERVAL_MS = 15e3;
45358
45686
  var CATEGORY_MAP = {
45359
45687
  tasks: "task.",
@@ -45389,7 +45717,7 @@ function shouldIncludeEvent(eventName, categories) {
45389
45717
  });
45390
45718
  }
45391
45719
  function createEventRoutes() {
45392
- const router = (0, import_express10.Router)();
45720
+ const router = (0, import_express11.Router)();
45393
45721
  router.get("/", (req, res) => {
45394
45722
  const { eventBus } = req.app.locals.deps;
45395
45723
  const categories = parseFilter(req.query.filter);
@@ -45437,6 +45765,40 @@ data: ${JSON.stringify(data)}
45437
45765
  // ../../packages/api/src/app/server.ts
45438
45766
  var JSON_BODY_LIMIT = "1mb";
45439
45767
  var JSON_BODY_LIMIT_LARGE = "25mb";
45768
+ var TRUSTED_LOCAL_HOSTS2 = /* @__PURE__ */ new Set(["localhost", "127.0.0.1", "[::1]", "::1"]);
45769
+ function normalizeHost2(value) {
45770
+ const trimmed = value?.trim().toLowerCase();
45771
+ return trimmed ? trimmed.replace(/:\d+$/, "") : void 0;
45772
+ }
45773
+ function collectForwardedHosts(value) {
45774
+ if (typeof value !== "string") {
45775
+ return [];
45776
+ }
45777
+ return value.split(",").map((item) => normalizeHost2(item)).filter((host) => Boolean(host));
45778
+ }
45779
+ function getRequestHosts2(req) {
45780
+ const forwardedHosts = collectForwardedHosts(req.headers["x-forwarded-host"]);
45781
+ if (forwardedHosts.length > 0) {
45782
+ return forwardedHosts;
45783
+ }
45784
+ return [normalizeHost2(req.hostname), normalizeHost2(typeof req.headers.host === "string" ? req.headers.host : void 0)].filter((host) => Boolean(host));
45785
+ }
45786
+ function getOriginHost(origin) {
45787
+ if (!origin) return void 0;
45788
+ try {
45789
+ return normalizeHost2(new URL(origin).host);
45790
+ } catch {
45791
+ return void 0;
45792
+ }
45793
+ }
45794
+ function isShareHostRequest(req, shareOrigin) {
45795
+ const shareHost = getOriginHost(shareOrigin);
45796
+ if (!shareHost) return false;
45797
+ return getRequestHosts2(req).some((host) => !TRUSTED_LOCAL_HOSTS2.has(host) && host === shareHost);
45798
+ }
45799
+ function isAllowedSharePath(pathname) {
45800
+ return pathname === "/favicon.ico" || pathname === "/api/shared" || pathname.startsWith("/api/shared/") || pathname === "/preview" || pathname.startsWith("/preview/") || pathname === "/shared" || pathname.startsWith("/shared/") || pathname === "/assets" || pathname.startsWith("/assets/");
45801
+ }
45440
45802
  function resolveRuntimeBaseDir() {
45441
45803
  const entryPath = process.argv[1];
45442
45804
  if (typeof entryPath === "string" && entryPath.length > 0) {
@@ -45467,7 +45829,7 @@ function resolveStaticDir(baseDir) {
45467
45829
  return null;
45468
45830
  }
45469
45831
  function createServer2(deps) {
45470
- const app = (0, import_express11.default)();
45832
+ const app = (0, import_express12.default)();
45471
45833
  app.locals.deps = deps;
45472
45834
  if (typeof deps.heartbeat.setNodeMessageHandler === "function") {
45473
45835
  deps.heartbeat.setNodeMessageHandler((message) => handleNodeMessage(deps, message));
@@ -45483,15 +45845,32 @@ function createServer2(deps) {
45483
45845
  } catch {
45484
45846
  }
45485
45847
  }
45848
+ if (deps.shareOrigin) {
45849
+ try {
45850
+ trustedHosts.push(new URL(deps.shareOrigin).host);
45851
+ } catch {
45852
+ }
45853
+ }
45486
45854
  return trustedHosts;
45487
45855
  }
45488
45856
  };
45489
45857
  const isApiRequest = (req) => req.path === "/api" || req.path.startsWith("/api/");
45490
45858
  const canServeDashboard = (req) => !deps.config.validateBearerToken || isTrustedDashboardRequest(req, authConfig);
45859
+ app.use((req, res, next) => {
45860
+ if (!isShareHostRequest(req, deps.shareOrigin)) {
45861
+ next();
45862
+ return;
45863
+ }
45864
+ if (isAllowedSharePath(req.path)) {
45865
+ next();
45866
+ return;
45867
+ }
45868
+ res.status(404).type("text/plain").send("Not Found");
45869
+ });
45491
45870
  const runtimeBaseDir = resolveRuntimeBaseDir();
45492
45871
  const staticDir = resolveStaticDir(runtimeBaseDir);
45493
45872
  if (staticDir) {
45494
- const staticMiddleware = import_express11.default.static(staticDir);
45873
+ const staticMiddleware = import_express12.default.static(staticDir);
45495
45874
  app.use((req, res, next) => {
45496
45875
  if (isApiRequest(req)) {
45497
45876
  next();
@@ -45504,7 +45883,7 @@ function createServer2(deps) {
45504
45883
  staticMiddleware(req, res, next);
45505
45884
  });
45506
45885
  }
45507
- app.use(import_express11.default.json({ limit: JSON_BODY_LIMIT }));
45886
+ app.use(import_express12.default.json({ limit: JSON_BODY_LIMIT }));
45508
45887
  app.use(createAuthMiddleware(authConfig));
45509
45888
  app.use((req, res, next) => {
45510
45889
  void (async () => {
@@ -45526,12 +45905,13 @@ function createServer2(deps) {
45526
45905
  })().catch(next);
45527
45906
  });
45528
45907
  app.use(createRoutingMiddleware(deps.dataRouter));
45529
- const largeBodyParser = import_express11.default.json({ limit: JSON_BODY_LIMIT_LARGE });
45908
+ const largeBodyParser = import_express12.default.json({ limit: JSON_BODY_LIMIT_LARGE });
45530
45909
  app.use("/api/cluster", createClusterRoutes());
45531
45910
  app.use("/api/cluster-control", createClusterControlRoutes());
45532
45911
  app.use("/api/node", largeBodyParser, createNodeMessageRoutes());
45533
45912
  app.use("/api/nodes", createNodeRoutes());
45534
45913
  app.use("/api/tasks", largeBodyParser, createTaskRoutes());
45914
+ app.use("/api/shared", createSharedRoutes());
45535
45915
  app.use("/api/worker", largeBodyParser, createWorkerRoutes());
45536
45916
  app.use("/api/system", createSystemRoutes());
45537
45917
  app.use("/api/events", createEventRoutes());
@@ -45751,8 +46131,24 @@ ${lines.join("")}`
45751
46131
  return args;
45752
46132
  }
45753
46133
  this.ensureTunnelPort(tunnelId, localPort);
46134
+ this.ensureTunnelAccess(tunnelId);
45754
46135
  return ["host", tunnelId];
45755
46136
  }
46137
+ ensureTunnelAccess(tunnelId) {
46138
+ if (this.config.access !== "tenant") {
46139
+ return;
46140
+ }
46141
+ try {
46142
+ (0, import_node_child_process9.execFileSync)("devtunnel", ["access", "create", tunnelId, "--tenant"], { stdio: "pipe" });
46143
+ } catch (err) {
46144
+ if (isExistingTenantAccessError(err)) {
46145
+ return;
46146
+ }
46147
+ throw new Error(
46148
+ `Failed to configure tenant access for devtunnel "${tunnelId}": ${formatCommandError(err)}`
46149
+ );
46150
+ }
46151
+ }
45756
46152
  ensureTunnelExists() {
45757
46153
  const tunnelId = this.config.id.trim();
45758
46154
  if (!tunnelId) {
@@ -45847,6 +46243,10 @@ function normalizeDevTunnelUrl(value) {
45847
46243
  return null;
45848
46244
  }
45849
46245
  }
46246
+ function isExistingTenantAccessError(error) {
46247
+ const message = formatCommandError(error).toLowerCase();
46248
+ return message.includes("already exists") || message.includes("conflict") || message.includes("duplicate");
46249
+ }
45850
46250
  function formatCommandError(error) {
45851
46251
  if (!error || typeof error !== "object") {
45852
46252
  return String(error);
@@ -46624,7 +47024,9 @@ async function main() {
46624
47024
  const previewSessionManager = new PreviewSessionManager();
46625
47025
  const previewProxyManager = new PreviewProxyManager();
46626
47026
  let dashboardTransport = null;
47027
+ let shareTransport = null;
46627
47028
  let dashboardOrigin;
47029
+ let shareOrigin;
46628
47030
  let deps;
46629
47031
  async function closeServer(server2) {
46630
47032
  if (!server2) {
@@ -46653,11 +47055,29 @@ async function main() {
46653
47055
  }
46654
47056
  };
46655
47057
  }
47058
+ function createShareTransportConfig() {
47059
+ return {
47060
+ type: "devtunnel",
47061
+ devtunnel: {
47062
+ id: resolveOrCreateDevTunnelId(
47063
+ config.storage.path,
47064
+ meshyNode.getNodeRegistry().getSelf().id,
47065
+ "share"
47066
+ ),
47067
+ allowAnonymous: false,
47068
+ access: "tenant"
47069
+ }
47070
+ };
47071
+ }
46656
47072
  function setAdvertisedDashboardOrigin(origin) {
46657
47073
  dashboardOrigin = origin;
46658
47074
  deps.dashboardOrigin = origin;
46659
47075
  meshyNode.getNodeRegistry().updateDashboardOrigin(meshyNode.getNodeRegistry().getSelf().id, origin);
46660
47076
  }
47077
+ function setShareOrigin(origin) {
47078
+ shareOrigin = origin;
47079
+ deps.shareOrigin = origin;
47080
+ }
46661
47081
  function shouldPublishDashboardTunnel() {
46662
47082
  return authMetadata.enabled && (meshyNode.getTransportType() === "devtunnel" || meshyNode.isDevTunnelEnabled());
46663
47083
  }
@@ -46739,6 +47159,27 @@ async function main() {
46739
47159
  }
46740
47160
  await restartDashboardTransport(reason);
46741
47161
  }
47162
+ async function ensureShareTunnel() {
47163
+ if (shareTransport && shareOrigin && await shareTransport.isHealthy().catch(() => false)) {
47164
+ return shareOrigin;
47165
+ }
47166
+ if (shareTransport) {
47167
+ await shareTransport.stop().catch(() => void 0);
47168
+ shareTransport = null;
47169
+ setShareOrigin(void 0);
47170
+ }
47171
+ const nextTransport = createTransport(createShareTransportConfig());
47172
+ await nextTransport.start(config.node.port);
47173
+ const nextOrigin = await nextTransport.getEndpoint();
47174
+ shareTransport = nextTransport;
47175
+ setShareOrigin(nextOrigin);
47176
+ meshyNode.getLogger().info("started share tunnel", {
47177
+ shareOrigin: nextOrigin,
47178
+ tenantAccess: true,
47179
+ stableUrl: true
47180
+ });
47181
+ return nextOrigin;
47182
+ }
46742
47183
  deps = {
46743
47184
  dataRouter: meshyNode.getDataRouter(),
46744
47185
  taskEngine: meshyNode.getTaskEngine(),
@@ -46791,11 +47232,14 @@ async function main() {
46791
47232
  isDevTunnelEnabled: () => meshyNode.isDevTunnelEnabled(),
46792
47233
  localDashboardOrigin,
46793
47234
  dashboardOrigin,
47235
+ shareOrigin,
47236
+ ensureShareTunnel,
46794
47237
  runtimeMetadata
46795
47238
  };
46796
47239
  deps.previewSessionManager = previewSessionManager;
46797
47240
  deps.previewProxyManager = previewProxyManager;
46798
47241
  deps.dashboardOrigin = dashboardOrigin;
47242
+ deps.shareOrigin = shareOrigin;
46799
47243
  meshyNode.getLogger().info("configured node auth mode", {
46800
47244
  authEnabled: authMetadata.enabled,
46801
47245
  allowSameTenant: authMetadata.allowSameTenant,
@@ -46869,6 +47313,9 @@ async function main() {
46869
47313
  if (dashboardTransport) {
46870
47314
  await dashboardTransport.stop();
46871
47315
  }
47316
+ if (shareTransport) {
47317
+ await shareTransport.stop();
47318
+ }
46872
47319
  await meshyNode.stop();
46873
47320
  await closeServer(server);
46874
47321
  console.log("Goodbye!");