meshy-node 0.1.10 → 0.2.1

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
@@ -32727,7 +32727,7 @@ function parseTask(value) {
32727
32727
  }
32728
32728
  const status = asTaskStatus(value.status);
32729
32729
  const priority = asTaskPriority(value.priority);
32730
- if (typeof value.id !== "string" || typeof value.title !== "string" || typeof value.description !== "string" || typeof value.agent !== "string" || value.project !== null && typeof value.project !== "string" || value.effectiveProjectPath !== null && value.effectiveProjectPath !== void 0 && typeof value.effectiveProjectPath !== "string" || !isPlainObject(value.payload) || status === null || priority === null || value.assignedTo !== null && typeof value.assignedTo !== "string" || value.assignedNodeName !== void 0 && value.assignedNodeName !== null && typeof value.assignedNodeName !== "string" || typeof value.createdBy !== "string" || value.result !== null && !isPlainObject(value.result) || value.error !== null && typeof value.error !== "string" || typeof value.retryCount !== "number" || !Number.isFinite(value.retryCount) || typeof value.maxRetries !== "number" || !Number.isFinite(value.maxRetries) || typeof value.createdAt !== "number" || !Number.isFinite(value.createdAt) || typeof value.updatedAt !== "number" || !Number.isFinite(value.updatedAt)) {
32730
+ if (typeof value.id !== "string" || typeof value.title !== "string" || typeof value.description !== "string" || typeof value.agent !== "string" || value.project !== null && typeof value.project !== "string" || value.effectiveProjectPath !== null && value.effectiveProjectPath !== void 0 && typeof value.effectiveProjectPath !== "string" || value.branch !== null && value.branch !== void 0 && typeof value.branch !== "string" || !isPlainObject(value.payload) || status === null || priority === null || value.assignedTo !== null && typeof value.assignedTo !== "string" || value.assignedNodeName !== void 0 && value.assignedNodeName !== null && typeof value.assignedNodeName !== "string" || typeof value.createdBy !== "string" || value.result !== null && !isPlainObject(value.result) || value.error !== null && typeof value.error !== "string" || typeof value.retryCount !== "number" || !Number.isFinite(value.retryCount) || typeof value.maxRetries !== "number" || !Number.isFinite(value.maxRetries) || typeof value.createdAt !== "number" || !Number.isFinite(value.createdAt) || typeof value.updatedAt !== "number" || !Number.isFinite(value.updatedAt)) {
32731
32731
  return null;
32732
32732
  }
32733
32733
  return {
@@ -32737,6 +32737,7 @@ function parseTask(value) {
32737
32737
  agent: value.agent,
32738
32738
  project: value.project ?? null,
32739
32739
  effectiveProjectPath: typeof value.effectiveProjectPath === "string" ? value.effectiveProjectPath : null,
32740
+ branch: value.branch === void 0 ? void 0 : typeof value.branch === "string" ? value.branch : null,
32740
32741
  payload: clone(value.payload),
32741
32742
  status,
32742
32743
  priority,
@@ -33350,6 +33351,7 @@ var TaskEngine = class {
33350
33351
  agent: input.agent,
33351
33352
  project: input.project ?? null,
33352
33353
  effectiveProjectPath: null,
33354
+ branch: input.branch ?? null,
33353
33355
  payload: input.payload,
33354
33356
  status: "pending",
33355
33357
  priority: input.priority ?? "normal",
@@ -33593,10 +33595,12 @@ var TaskEngine = class {
33593
33595
  const now = Date.now();
33594
33596
  const existing = this.store.getTask(task.id);
33595
33597
  const effectiveProjectPath = task.effectiveProjectPath ?? existing?.effectiveProjectPath ?? null;
33598
+ const branch = task.branch ?? existing?.branch ?? null;
33596
33599
  if (!existing) {
33597
33600
  this.store.createTask({
33598
33601
  ...task,
33599
33602
  effectiveProjectPath,
33603
+ branch,
33600
33604
  status: "running",
33601
33605
  updatedAt: now
33602
33606
  });
@@ -33604,6 +33608,7 @@ var TaskEngine = class {
33604
33608
  this.store.updateTask(task.id, {
33605
33609
  status: "running",
33606
33610
  effectiveProjectPath,
33611
+ branch,
33607
33612
  updatedAt: now
33608
33613
  });
33609
33614
  }
@@ -34951,6 +34956,7 @@ function buildLeaderTaskPatch(data) {
34951
34956
  if (data.result !== void 0) body.result = data.result;
34952
34957
  if (data.error !== void 0) body.error = data.error;
34953
34958
  if (data.effectiveProjectPath !== void 0) body.effectiveProjectPath = data.effectiveProjectPath;
34959
+ if (data.branch !== void 0) body.branch = data.branch;
34954
34960
  if (data.status === "pending") body.assignedTo = null;
34955
34961
  return body;
34956
34962
  }
@@ -34992,9 +34998,10 @@ function forwardLeaderTaskPatch(log2, nodeRegistry, taskId, data, options) {
34992
34998
  });
34993
34999
  });
34994
35000
  }
34995
- function forwardRunningStatusToLeader(log2, nodeRegistry, taskId, effectiveProjectPath) {
35001
+ function forwardRunningStatusToLeader(log2, nodeRegistry, taskId, effectiveProjectPath, branch) {
34996
35002
  const data = { status: "running" };
34997
35003
  if (effectiveProjectPath !== void 0) data.effectiveProjectPath = effectiveProjectPath;
35004
+ if (branch !== void 0) data.branch = branch;
34998
35005
  forwardLeaderTaskPatch(log2, nodeRegistry, taskId, data, {
34999
35006
  successMessage: "forwarded running status to leader",
35000
35007
  rejectedMessage: "leader rejected running status update",
@@ -40783,6 +40790,12 @@ var NodeWorkDirEntrySchema = external_exports.object({
40783
40790
  mimeType: external_exports.string().optional(),
40784
40791
  kind: external_exports.enum(["text", "json", "markdown", "image", "html", "pdf", "binary"]).optional()
40785
40792
  });
40793
+ var GitRemoteBranchOptionSchema = external_exports.object({
40794
+ name: external_exports.string(),
40795
+ remote: external_exports.enum(["origin", "upstream"]),
40796
+ branch: external_exports.string(),
40797
+ ref: external_exports.string()
40798
+ });
40786
40799
  var NodeListQuery = external_exports.object({
40787
40800
  status: external_exports.enum(["online", "busy", "offline"]).optional(),
40788
40801
  capability: external_exports.string().optional()
@@ -40806,6 +40819,12 @@ var NodeWorkDirTreeQuery = external_exports.object({
40806
40819
  directoriesOnly: QueryBoolean,
40807
40820
  allowAbsolute: QueryBoolean
40808
40821
  });
40822
+ var NodeWorkDirBranchQuery = external_exports.object({
40823
+ path: external_exports.string().default("."),
40824
+ limit: external_exports.coerce.number().int().min(1).max(20).default(20),
40825
+ offset: external_exports.coerce.number().int().min(0).default(0),
40826
+ allowAbsolute: QueryBoolean
40827
+ });
40809
40828
  var NodeWorkDirTreeResponse = external_exports.object({
40810
40829
  nodeId: external_exports.string(),
40811
40830
  rootPath: external_exports.string(),
@@ -40819,6 +40838,29 @@ var NodeWorkDirTreeResponse = external_exports.object({
40819
40838
  isAbsolute: external_exports.boolean(),
40820
40839
  parentPath: external_exports.string().nullable()
40821
40840
  });
40841
+ var NodeWorkDirBranchResponse = external_exports.object({
40842
+ nodeId: external_exports.string(),
40843
+ path: external_exports.string(),
40844
+ available: external_exports.boolean(),
40845
+ repoRoot: external_exports.string().nullable(),
40846
+ currentBranch: external_exports.string().nullable(),
40847
+ branches: external_exports.array(GitRemoteBranchOptionSchema),
40848
+ limit: external_exports.number().int().min(1),
40849
+ offset: external_exports.number().int().min(0),
40850
+ total: external_exports.number().int().min(0),
40851
+ hasMore: external_exports.boolean()
40852
+ });
40853
+ var CreateNodeWorkDirBranchBody = external_exports.object({
40854
+ path: external_exports.string().default("."),
40855
+ branchName: external_exports.string().trim().min(1),
40856
+ startPoint: external_exports.string().trim().min(1),
40857
+ limit: external_exports.coerce.number().int().min(1).max(20).default(20),
40858
+ offset: external_exports.coerce.number().int().min(0).default(0),
40859
+ allowAbsolute: external_exports.boolean().default(true)
40860
+ });
40861
+ var CreateNodeWorkDirBranchResponse = NodeWorkDirBranchResponse.extend({
40862
+ createdBranch: external_exports.string()
40863
+ });
40822
40864
  var UpdateNodeBody = external_exports.object({
40823
40865
  name: external_exports.string().min(1).optional(),
40824
40866
  capabilities: external_exports.array(external_exports.string()).optional()
@@ -40842,6 +40884,7 @@ var CreateTaskBody = external_exports.object({
40842
40884
  description: external_exports.string().default(""),
40843
40885
  agent: external_exports.enum(AGENT_OPTIONS),
40844
40886
  project: external_exports.string().nullable().optional().default(null),
40887
+ branch: external_exports.string().nullable().optional().default(null),
40845
40888
  payload: TaskPayload.default({}),
40846
40889
  priority: external_exports.enum(["low", "normal", "high", "critical"]).default("normal"),
40847
40890
  assignTo: external_exports.string().optional()
@@ -40861,6 +40904,7 @@ var TaskListResponse = external_exports.object({
40861
40904
  agent: external_exports.string(),
40862
40905
  project: external_exports.string().nullable(),
40863
40906
  effectiveProjectPath: external_exports.string().nullable(),
40907
+ branch: external_exports.string().nullable().optional(),
40864
40908
  payload: external_exports.record(external_exports.unknown()),
40865
40909
  status: external_exports.enum(["pending", "assigned", "running", "completed", "failed", "cancelled", "archived"]),
40866
40910
  priority: external_exports.enum(["low", "normal", "high", "critical"]),
@@ -40883,7 +40927,8 @@ var UpdateTaskBody = external_exports.object({
40883
40927
  status: external_exports.enum(["pending", "assigned", "running", "completed", "failed", "cancelled", "archived"]).optional(),
40884
40928
  result: external_exports.record(external_exports.unknown()).nullable().optional(),
40885
40929
  error: external_exports.string().nullable().optional(),
40886
- effectiveProjectPath: external_exports.string().nullable().optional()
40930
+ effectiveProjectPath: external_exports.string().nullable().optional(),
40931
+ branch: external_exports.string().nullable().optional()
40887
40932
  });
40888
40933
  var AssignTaskBody = external_exports.object({
40889
40934
  nodeId: external_exports.string().min(1)
@@ -41636,7 +41681,7 @@ function readFileContent(root, relativePath) {
41636
41681
  };
41637
41682
  }
41638
41683
 
41639
- // ../../packages/api/src/output/git-diff.ts
41684
+ // ../../packages/api/src/output/git-branches.ts
41640
41685
  var import_node_child_process4 = require("child_process");
41641
41686
  function git(args, cwd) {
41642
41687
  try {
@@ -41649,19 +41694,192 @@ function git(args, cwd) {
41649
41694
  return "";
41650
41695
  }
41651
41696
  }
41697
+ function gitSucceeds(args, cwd) {
41698
+ try {
41699
+ (0, import_node_child_process4.execFileSync)("git", ["-C", cwd, ...args], {
41700
+ encoding: "utf-8",
41701
+ timeout: 1e4,
41702
+ stdio: ["pipe", "pipe", "pipe"]
41703
+ });
41704
+ return true;
41705
+ } catch {
41706
+ return false;
41707
+ }
41708
+ }
41709
+ function runGit(args, cwd) {
41710
+ return (0, import_node_child_process4.execFileSync)("git", ["-C", cwd, ...args], {
41711
+ encoding: "utf-8",
41712
+ timeout: 1e4,
41713
+ stdio: ["pipe", "pipe", "pipe"]
41714
+ }).trim();
41715
+ }
41652
41716
  function isGitRepo(dirPath) {
41653
- const result = git(["rev-parse", "--is-inside-work-tree"], dirPath);
41717
+ return git(["rev-parse", "--is-inside-work-tree"], dirPath) === "true";
41718
+ }
41719
+ function normalizeCurrentBranch(branch) {
41720
+ if (!branch || branch === "HEAD") {
41721
+ return null;
41722
+ }
41723
+ return branch;
41724
+ }
41725
+ function parseRemoteBranch(ref) {
41726
+ const normalized = ref.trim();
41727
+ if (!normalized || normalized.includes(" -> ")) {
41728
+ return null;
41729
+ }
41730
+ const [remote, ...branchParts] = normalized.split("/");
41731
+ if (remote !== "origin" && remote !== "upstream" || branchParts.length === 0) {
41732
+ return null;
41733
+ }
41734
+ return {
41735
+ name: normalized,
41736
+ remote,
41737
+ branch: branchParts.join("/"),
41738
+ ref: normalized
41739
+ };
41740
+ }
41741
+ function branchPriority(branch) {
41742
+ const name2 = branch.branch.toLowerCase();
41743
+ const mainScore = name2 === "main" ? 0 : name2 === "master" ? 1 : 2;
41744
+ const remoteScore = branch.remote === "upstream" ? 0 : 1;
41745
+ return [mainScore, remoteScore, branch.branch];
41746
+ }
41747
+ function compareRemoteBranches(left, right) {
41748
+ const [leftMain, leftRemote, leftName] = branchPriority(left);
41749
+ const [rightMain, rightRemote, rightName] = branchPriority(right);
41750
+ if (leftMain !== rightMain) {
41751
+ return leftMain - rightMain;
41752
+ }
41753
+ if (leftRemote !== rightRemote) {
41754
+ return leftRemote - rightRemote;
41755
+ }
41756
+ const branchCompare = leftName.localeCompare(rightName);
41757
+ if (branchCompare !== 0) {
41758
+ return branchCompare;
41759
+ }
41760
+ return left.remote.localeCompare(right.remote);
41761
+ }
41762
+ function listRemoteBranches(repoRoot2) {
41763
+ const output = git([
41764
+ "for-each-ref",
41765
+ "--format=%(refname:short)",
41766
+ "refs/remotes/origin",
41767
+ "refs/remotes/upstream"
41768
+ ], repoRoot2);
41769
+ if (!output) {
41770
+ return [];
41771
+ }
41772
+ return output.split("\n").map(parseRemoteBranch).filter((value) => value !== null).sort(compareRemoteBranches);
41773
+ }
41774
+ function getRemoteStartPoint(repoRoot2, value) {
41775
+ const trimmed = value.trim();
41776
+ if (!trimmed) {
41777
+ throw new Error("A base branch is required");
41778
+ }
41779
+ const directRemote = parseRemoteBranch(trimmed);
41780
+ if (directRemote) {
41781
+ if (!gitSucceeds(["show-ref", "--verify", "--quiet", `refs/remotes/${directRemote.ref}`], repoRoot2)) {
41782
+ throw new Error(`Remote branch ${directRemote.ref} not found`);
41783
+ }
41784
+ return { ref: directRemote.ref, track: true };
41785
+ }
41786
+ for (const remote of ["upstream", "origin"]) {
41787
+ const remoteRef = `${remote}/${trimmed}`;
41788
+ if (gitSucceeds(["show-ref", "--verify", "--quiet", `refs/remotes/${remoteRef}`], repoRoot2)) {
41789
+ return { ref: remoteRef, track: true };
41790
+ }
41791
+ }
41792
+ if (gitSucceeds(["show-ref", "--verify", "--quiet", `refs/heads/${trimmed}`], repoRoot2)) {
41793
+ return { ref: trimmed, track: false };
41794
+ }
41795
+ throw new Error(`Branch ${trimmed} not found on upstream/origin or locally`);
41796
+ }
41797
+ function getGitBranchInfo(dirPath, options) {
41798
+ const limit = Math.max(1, Math.min(20, Math.trunc(options.limit)));
41799
+ const offset = Math.max(0, Math.trunc(options.offset));
41800
+ if (!isGitRepo(dirPath)) {
41801
+ return {
41802
+ available: false,
41803
+ repoRoot: null,
41804
+ currentBranch: null,
41805
+ branches: [],
41806
+ limit,
41807
+ offset,
41808
+ total: 0,
41809
+ hasMore: false
41810
+ };
41811
+ }
41812
+ const repoRoot2 = git(["rev-parse", "--show-toplevel"], dirPath);
41813
+ const currentBranch = normalizeCurrentBranch(git(["rev-parse", "--abbrev-ref", "HEAD"], dirPath));
41814
+ const remoteBranches = listRemoteBranches(repoRoot2);
41815
+ const branches = remoteBranches.slice(offset, offset + limit);
41816
+ return {
41817
+ available: true,
41818
+ repoRoot: repoRoot2,
41819
+ currentBranch,
41820
+ branches,
41821
+ limit,
41822
+ offset,
41823
+ total: remoteBranches.length,
41824
+ hasMore: offset + branches.length < remoteBranches.length
41825
+ };
41826
+ }
41827
+ function createGitBranch(dirPath, branchName, startPoint, options) {
41828
+ if (!isGitRepo(dirPath)) {
41829
+ throw new Error("Selected working directory is not inside a git repository");
41830
+ }
41831
+ const repoRoot2 = git(["rev-parse", "--show-toplevel"], dirPath);
41832
+ const name2 = branchName.trim();
41833
+ if (!name2) {
41834
+ throw new Error("A new branch name is required");
41835
+ }
41836
+ try {
41837
+ runGit(["check-ref-format", "--branch", name2], repoRoot2);
41838
+ } catch {
41839
+ throw new Error(`Invalid branch name: ${name2}`);
41840
+ }
41841
+ if (gitSucceeds(["show-ref", "--verify", "--quiet", `refs/heads/${name2}`], repoRoot2)) {
41842
+ throw new Error(`Local branch ${name2} already exists`);
41843
+ }
41844
+ const target = getRemoteStartPoint(repoRoot2, startPoint);
41845
+ if (target.track) {
41846
+ runGit(["switch", "-c", name2, "--track", target.ref], repoRoot2);
41847
+ } else {
41848
+ runGit(["switch", "-c", name2, target.ref], repoRoot2);
41849
+ }
41850
+ return {
41851
+ createdBranch: name2,
41852
+ ...getGitBranchInfo(dirPath, options),
41853
+ currentBranch: name2
41854
+ };
41855
+ }
41856
+
41857
+ // ../../packages/api/src/output/git-diff.ts
41858
+ var import_node_child_process5 = require("child_process");
41859
+ function git2(args, cwd) {
41860
+ try {
41861
+ return (0, import_node_child_process5.execFileSync)("git", ["-C", cwd, ...args], {
41862
+ encoding: "utf-8",
41863
+ timeout: 1e4,
41864
+ stdio: ["pipe", "pipe", "pipe"]
41865
+ }).trim();
41866
+ } catch {
41867
+ return "";
41868
+ }
41869
+ }
41870
+ function isGitRepo2(dirPath) {
41871
+ const result = git2(["rev-parse", "--is-inside-work-tree"], dirPath);
41654
41872
  return result === "true";
41655
41873
  }
41656
41874
  function getGitDiff(dirPath) {
41657
- if (!isGitRepo(dirPath)) {
41875
+ if (!isGitRepo2(dirPath)) {
41658
41876
  return { available: false };
41659
41877
  }
41660
- const repoRoot2 = git(["rev-parse", "--show-toplevel"], dirPath);
41661
- const branch = git(["rev-parse", "--abbrev-ref", "HEAD"], dirPath) || null;
41662
- const staged = git(["diff", "--cached"], dirPath);
41663
- const unstaged = git(["diff"], dirPath);
41664
- const statusOutput = git(["status", "--porcelain"], dirPath);
41878
+ const repoRoot2 = git2(["rev-parse", "--show-toplevel"], dirPath);
41879
+ const branch = git2(["rev-parse", "--abbrev-ref", "HEAD"], dirPath) || null;
41880
+ const staged = git2(["diff", "--cached"], dirPath);
41881
+ const unstaged = git2(["diff"], dirPath);
41882
+ const statusOutput = git2(["status", "--porcelain"], dirPath);
41665
41883
  const changedFiles = [];
41666
41884
  const untrackedFiles = [];
41667
41885
  for (const line of statusOutput.split("\n")) {
@@ -41689,20 +41907,33 @@ function getGitDiff(dirPath) {
41689
41907
  function isAbsolutePath(p) {
41690
41908
  return path12.isAbsolute(p) || /^[A-Za-z]:[\\/]/.test(p);
41691
41909
  }
41692
- function getLocalNodeWorkDirTree(nodeId, rootPath, currentPath, options) {
41910
+ function resolveNodeWorkDirTarget(nodeId, rootPath, currentPath, allowAbsolute) {
41693
41911
  if (!rootPath) {
41694
41912
  throw new MeshyError("VALIDATION_ERROR", `Node ${nodeId} does not expose a working directory`, 400);
41695
41913
  }
41696
- const useAbsolute = options.allowAbsolute && isAbsolutePath(currentPath);
41914
+ const useAbsolute = allowAbsolute && isAbsolutePath(currentPath);
41697
41915
  const resolvedPath = useAbsolute ? path12.resolve(currentPath) : currentPath;
41916
+ return {
41917
+ rootPath,
41918
+ resolvedPath,
41919
+ useAbsolute
41920
+ };
41921
+ }
41922
+ function getLocalNodeWorkDirTree(nodeId, rootPath, currentPath, options) {
41923
+ const { rootPath: resolvedRootPath, resolvedPath, useAbsolute } = resolveNodeWorkDirTarget(
41924
+ nodeId,
41925
+ rootPath,
41926
+ currentPath,
41927
+ options.allowAbsolute
41928
+ );
41698
41929
  try {
41699
- const entries = useAbsolute ? listAbsoluteDirectory(resolvedPath) : listDirectory(rootPath, resolvedPath);
41930
+ const entries = useAbsolute ? listAbsoluteDirectory(resolvedPath) : listDirectory(resolvedRootPath, resolvedPath);
41700
41931
  const visibleEntries = options.directoriesOnly ? entries.filter((entry) => entry.type === "directory") : entries;
41701
41932
  const pagedEntries = visibleEntries.slice(options.offset, options.offset + options.limit);
41702
- const parentPath = computeParentPath(rootPath, resolvedPath, useAbsolute);
41933
+ const parentPath = computeParentPath(resolvedRootPath, resolvedPath, useAbsolute);
41703
41934
  return {
41704
41935
  nodeId,
41705
- rootPath,
41936
+ rootPath: resolvedRootPath,
41706
41937
  currentPath: resolvedPath,
41707
41938
  entries: pagedEntries,
41708
41939
  limit: options.limit,
@@ -41720,6 +41951,50 @@ function getLocalNodeWorkDirTree(nodeId, rootPath, currentPath, options) {
41720
41951
  throw err;
41721
41952
  }
41722
41953
  }
41954
+ function getLocalNodeWorkDirBranchInfo(nodeId, rootPath, currentPath, options) {
41955
+ const { resolvedPath } = resolveNodeWorkDirTarget(
41956
+ nodeId,
41957
+ rootPath,
41958
+ currentPath,
41959
+ options.allowAbsolute
41960
+ );
41961
+ const branchInfo = getGitBranchInfo(resolvedPath, {
41962
+ limit: options.limit,
41963
+ offset: options.offset
41964
+ });
41965
+ return {
41966
+ nodeId,
41967
+ path: resolvedPath,
41968
+ ...branchInfo
41969
+ };
41970
+ }
41971
+ function createLocalNodeWorkDirBranch(nodeId, rootPath, currentPath, options) {
41972
+ const { resolvedPath } = resolveNodeWorkDirTarget(
41973
+ nodeId,
41974
+ rootPath,
41975
+ currentPath,
41976
+ options.allowAbsolute
41977
+ );
41978
+ try {
41979
+ const result = createGitBranch(
41980
+ resolvedPath,
41981
+ options.branchName,
41982
+ options.startPoint,
41983
+ { limit: options.limit, offset: options.offset }
41984
+ );
41985
+ return {
41986
+ nodeId,
41987
+ path: resolvedPath,
41988
+ ...result
41989
+ };
41990
+ } catch (err) {
41991
+ throw new MeshyError(
41992
+ "VALIDATION_ERROR",
41993
+ err instanceof Error ? err.message : String(err),
41994
+ 400
41995
+ );
41996
+ }
41997
+ }
41723
41998
  function computeParentPath(rootPath, currentPath, useAbsolute) {
41724
41999
  if (useAbsolute) {
41725
42000
  const parent = path12.dirname(currentPath);
@@ -41755,6 +42030,44 @@ function sendLocalNodeWorkDirTree(req, res, nodeId, options = {}) {
41755
42030
  }
41756
42031
  ));
41757
42032
  }
42033
+ function sendLocalNodeWorkDirBranchInfo(req, res, nodeId, options = {}) {
42034
+ const query = NodeWorkDirBranchQuery.parse(req.query);
42035
+ const { nodeRegistry, workDir } = req.app.locals.deps;
42036
+ const self2 = nodeRegistry.getSelf();
42037
+ if (options.requireSelfNode && nodeId !== self2.id) {
42038
+ throw new MeshyError("NODE_NOT_FOUND", `Node ${nodeId} not found`, 404);
42039
+ }
42040
+ res.json(getLocalNodeWorkDirBranchInfo(
42041
+ self2.id,
42042
+ self2.workDir ?? workDir,
42043
+ query.path,
42044
+ {
42045
+ limit: query.limit,
42046
+ offset: query.offset,
42047
+ allowAbsolute: query.allowAbsolute
42048
+ }
42049
+ ));
42050
+ }
42051
+ function sendLocalNodeWorkDirBranchCreate(req, res, nodeId, options = {}) {
42052
+ const body = CreateNodeWorkDirBranchBody.parse(req.body);
42053
+ const { nodeRegistry, workDir } = req.app.locals.deps;
42054
+ const self2 = nodeRegistry.getSelf();
42055
+ if (options.requireSelfNode && nodeId !== self2.id) {
42056
+ throw new MeshyError("NODE_NOT_FOUND", `Node ${nodeId} not found`, 404);
42057
+ }
42058
+ res.json(createLocalNodeWorkDirBranch(
42059
+ self2.id,
42060
+ self2.workDir ?? workDir,
42061
+ body.path,
42062
+ {
42063
+ branchName: body.branchName,
42064
+ startPoint: body.startPoint,
42065
+ limit: body.limit,
42066
+ offset: body.offset,
42067
+ allowAbsolute: body.allowAbsolute
42068
+ }
42069
+ ));
42070
+ }
41758
42071
 
41759
42072
  // ../../packages/api/src/task-route-utils.ts
41760
42073
  var fs11 = __toESM(require("fs"), 1);
@@ -42287,6 +42600,44 @@ async function executeWorkerControlRequest(deps, request) {
42287
42600
  );
42288
42601
  break;
42289
42602
  }
42603
+ case "node-workdir-branch-info": {
42604
+ const self2 = deps.nodeRegistry.getSelf();
42605
+ response = jsonResponse(
42606
+ request.requestId,
42607
+ 200,
42608
+ getLocalNodeWorkDirBranchInfo(
42609
+ self2.id,
42610
+ self2.workDir ?? deps.workDir,
42611
+ request.path ?? ".",
42612
+ {
42613
+ limit: request.limit ?? 20,
42614
+ offset: request.offset ?? 0,
42615
+ allowAbsolute: request.allowAbsolute ?? true
42616
+ }
42617
+ )
42618
+ );
42619
+ break;
42620
+ }
42621
+ case "node-workdir-branch-create": {
42622
+ const self2 = deps.nodeRegistry.getSelf();
42623
+ response = jsonResponse(
42624
+ request.requestId,
42625
+ 200,
42626
+ createLocalNodeWorkDirBranch(
42627
+ self2.id,
42628
+ self2.workDir ?? deps.workDir,
42629
+ request.path ?? ".",
42630
+ {
42631
+ branchName: request.branchName ?? "",
42632
+ startPoint: request.startPoint ?? "",
42633
+ limit: request.limit ?? 20,
42634
+ offset: request.offset ?? 0,
42635
+ allowAbsolute: request.allowAbsolute ?? true
42636
+ }
42637
+ )
42638
+ );
42639
+ break;
42640
+ }
42290
42641
  case "devtunnel": {
42291
42642
  assertCanChangeNodeDevTunnel(deps.taskEngine, deps.nodeRegistry.getSelf().id);
42292
42643
  if (request.enabled) {
@@ -42551,6 +42902,148 @@ async function maybeHandleRemoteNodeWorkDirRequest(req, res, nodeId) {
42551
42902
  throw new MeshyError("NODE_OFFLINE", `Cannot reach node ${nodeId} to browse its working directory`, 502);
42552
42903
  }
42553
42904
  }
42905
+ async function maybeHandleRemoteNodeWorkDirBranchInfoRequest(req, res, nodeId) {
42906
+ const query = NodeWorkDirBranchQuery.parse(req.query);
42907
+ const { nodeRegistry, heartbeat, logger: rootLogger } = req.app.locals.deps;
42908
+ const selfId = nodeRegistry.getSelf().id;
42909
+ if (nodeId === selfId) {
42910
+ return false;
42911
+ }
42912
+ const node = nodeRegistry.getNode(nodeId);
42913
+ if (!node) {
42914
+ throw new MeshyError("NODE_NOT_FOUND", `Node ${nodeId} not found`, 404);
42915
+ }
42916
+ const log2 = rootLogger.child("nodes/workdir-branch");
42917
+ const proxyPath = req.originalUrl ?? `/api/nodes/${nodeId}/workdir/branch`;
42918
+ const fallbackRequest = {
42919
+ kind: "node-workdir-branch-info",
42920
+ path: query.path,
42921
+ limit: query.limit,
42922
+ offset: query.offset,
42923
+ allowAbsolute: query.allowAbsolute
42924
+ };
42925
+ const canPushToNode = heartbeat?.canPushToNode?.(nodeId) ?? true;
42926
+ if (!canPushToNode && heartbeat?.requestWorkerControl) {
42927
+ log2.warn("node workdir branch request falling back to keepalive control", {
42928
+ nodeId,
42929
+ proxyPath
42930
+ });
42931
+ const controlResponse = await heartbeat.requestWorkerControl(nodeId, fallbackRequest);
42932
+ sendWorkerControlResponse(res, controlResponse);
42933
+ return true;
42934
+ }
42935
+ try {
42936
+ const { endpoint, response } = await fetchNodeWithFallback(
42937
+ node,
42938
+ proxyPath,
42939
+ void 0,
42940
+ NODE_WORKDIR_PROXY_TIMEOUT_MS,
42941
+ createNodeWorkdirProxyTrace(log2, nodeId, proxyPath),
42942
+ { preferPublicEndpoint: true }
42943
+ );
42944
+ log2.debug("proxying node workdir branch request", {
42945
+ nodeId,
42946
+ endpoint,
42947
+ proxyPath
42948
+ });
42949
+ await sendProxyResponse(res, response);
42950
+ return true;
42951
+ } catch (err) {
42952
+ const errorDetails = describeProxyError(err);
42953
+ log2.warn("node workdir branch proxy error", {
42954
+ nodeId,
42955
+ proxyPath,
42956
+ timeoutMs: NODE_WORKDIR_PROXY_TIMEOUT_MS,
42957
+ ...errorDetails
42958
+ });
42959
+ if (heartbeat?.requestWorkerControl) {
42960
+ log2.warn("node workdir branch proxy failed, falling back to keepalive control", {
42961
+ nodeId,
42962
+ proxyPath,
42963
+ timeoutMs: NODE_WORKDIR_PROXY_TIMEOUT_MS,
42964
+ ...errorDetails
42965
+ });
42966
+ const controlResponse = await heartbeat.requestWorkerControl(nodeId, fallbackRequest);
42967
+ sendWorkerControlResponse(res, controlResponse);
42968
+ return true;
42969
+ }
42970
+ throw new MeshyError("NODE_OFFLINE", `Cannot reach node ${nodeId} to inspect its git branch`, 502);
42971
+ }
42972
+ }
42973
+ async function maybeHandleRemoteNodeWorkDirBranchCreateRequest(req, res, nodeId) {
42974
+ const body = CreateNodeWorkDirBranchBody.parse(req.body);
42975
+ const { nodeRegistry, heartbeat, logger: rootLogger } = req.app.locals.deps;
42976
+ const selfId = nodeRegistry.getSelf().id;
42977
+ if (nodeId === selfId) {
42978
+ return false;
42979
+ }
42980
+ const node = nodeRegistry.getNode(nodeId);
42981
+ if (!node) {
42982
+ throw new MeshyError("NODE_NOT_FOUND", `Node ${nodeId} not found`, 404);
42983
+ }
42984
+ const log2 = rootLogger.child("nodes/workdir-branch-create");
42985
+ const proxyPath = req.originalUrl ?? `/api/nodes/${nodeId}/workdir/branch`;
42986
+ const fallbackRequest = {
42987
+ kind: "node-workdir-branch-create",
42988
+ path: body.path,
42989
+ branchName: body.branchName,
42990
+ startPoint: body.startPoint,
42991
+ limit: body.limit,
42992
+ offset: body.offset,
42993
+ allowAbsolute: body.allowAbsolute
42994
+ };
42995
+ const canPushToNode = heartbeat?.canPushToNode?.(nodeId) ?? true;
42996
+ if (!canPushToNode && heartbeat?.requestWorkerControl) {
42997
+ log2.warn("node workdir branch create request falling back to keepalive control", {
42998
+ nodeId,
42999
+ proxyPath
43000
+ });
43001
+ const controlResponse = await heartbeat.requestWorkerControl(nodeId, fallbackRequest);
43002
+ sendWorkerControlResponse(res, controlResponse);
43003
+ return true;
43004
+ }
43005
+ try {
43006
+ const { endpoint, response } = await fetchNodeWithFallback(
43007
+ node,
43008
+ proxyPath,
43009
+ {
43010
+ method: "POST",
43011
+ headers: { "Content-Type": "application/json" },
43012
+ body: JSON.stringify(body)
43013
+ },
43014
+ NODE_WORKDIR_PROXY_TIMEOUT_MS,
43015
+ createNodeWorkdirProxyTrace(log2, nodeId, proxyPath),
43016
+ { preferPublicEndpoint: true }
43017
+ );
43018
+ log2.debug("proxying node workdir branch create request", {
43019
+ nodeId,
43020
+ endpoint,
43021
+ proxyPath
43022
+ });
43023
+ await sendProxyResponse(res, response);
43024
+ return true;
43025
+ } catch (err) {
43026
+ const errorDetails = describeProxyError(err);
43027
+ log2.warn("node workdir branch create proxy error", {
43028
+ nodeId,
43029
+ proxyPath,
43030
+ timeoutMs: NODE_WORKDIR_PROXY_TIMEOUT_MS,
43031
+ ...errorDetails
43032
+ });
43033
+ if (heartbeat?.requestWorkerControl) {
43034
+ log2.warn("node workdir branch create proxy failed, falling back to keepalive control", {
43035
+ nodeId,
43036
+ proxyPath,
43037
+ timeoutMs: NODE_WORKDIR_PROXY_TIMEOUT_MS,
43038
+ ...errorDetails
43039
+ });
43040
+ const controlResponse = await heartbeat.requestWorkerControl(nodeId, fallbackRequest);
43041
+ sendWorkerControlResponse(res, controlResponse);
43042
+ return true;
43043
+ }
43044
+ throw new MeshyError("NODE_OFFLINE", `Cannot reach node ${nodeId} to create a git branch`, 502);
43045
+ }
43046
+ }
42554
43047
  function createNodeRoutes() {
42555
43048
  const router = (0, import_express3.Router)();
42556
43049
  router.get("/", asyncHandler2(async (req, res) => {
@@ -42598,6 +43091,22 @@ function createNodeRoutes() {
42598
43091
  }
42599
43092
  sendLocalNodeWorkDirTree(req, res, nodeId);
42600
43093
  }));
43094
+ router.get("/:id/workdir/branch", asyncHandler2(async (req, res) => {
43095
+ const nodeId = req.params.id;
43096
+ const handled = await maybeHandleRemoteNodeWorkDirBranchInfoRequest(req, res, nodeId);
43097
+ if (handled) {
43098
+ return;
43099
+ }
43100
+ sendLocalNodeWorkDirBranchInfo(req, res, nodeId);
43101
+ }));
43102
+ router.post("/:id/workdir/branch", asyncHandler2(async (req, res) => {
43103
+ const nodeId = req.params.id;
43104
+ const handled = await maybeHandleRemoteNodeWorkDirBranchCreateRequest(req, res, nodeId);
43105
+ if (handled) {
43106
+ return;
43107
+ }
43108
+ sendLocalNodeWorkDirBranchCreate(req, res, nodeId);
43109
+ }));
42601
43110
  router.patch("/:id", asyncHandler2(async (req, res) => {
42602
43111
  const { nodeRegistry, persistNodeNamePreference } = req.app.locals.deps;
42603
43112
  const updates = UpdateNodeBody.parse(req.body);
@@ -43089,6 +43598,7 @@ function createTaskRoutes() {
43089
43598
  description: body.description ?? "",
43090
43599
  agent: body.agent,
43091
43600
  project: body.project ?? null,
43601
+ branch: body.branch ?? null,
43092
43602
  payload: body.payload ?? {},
43093
43603
  priority: body.priority,
43094
43604
  createdBy: "api"
@@ -43430,10 +43940,11 @@ function createWorkerRoutes() {
43430
43940
  const task = req.body;
43431
43941
  const effectiveWorkDir = workDir ?? nodeRegistry.getSelf().workDir ?? process.cwd();
43432
43942
  task.effectiveProjectPath = resolveTaskWorkDir(task.project, effectiveWorkDir);
43943
+ task.branch = getGitBranchInfo(task.effectiveProjectPath, { limit: 20, offset: 0 }).currentBranch;
43433
43944
  await taskEngine.executeTask(task);
43434
43945
  res.json({ ok: true });
43435
43946
  if (!nodeRegistry.isLeader()) {
43436
- forwardRunningStatusToLeader(log2, nodeRegistry, task.id, task.effectiveProjectPath);
43947
+ forwardRunningStatusToLeader(log2, nodeRegistry, task.id, task.effectiveProjectPath, task.branch);
43437
43948
  }
43438
43949
  const engine = engineRegistry.get(task.agent);
43439
43950
  if (engine) {
@@ -43515,7 +44026,7 @@ function createWorkerRoutes() {
43515
44026
  }
43516
44027
  taskEngine.updateTask(body.taskId, { status: "running" });
43517
44028
  if (!nodeRegistry.isLeader()) {
43518
- forwardRunningStatusToLeader(log2, nodeRegistry, body.taskId);
44029
+ forwardRunningStatusToLeader(log2, nodeRegistry, body.taskId, void 0, task.branch);
43519
44030
  }
43520
44031
  let cleanupForwarding = null;
43521
44032
  if (!nodeRegistry.isLeader()) {
@@ -43670,7 +44181,7 @@ var import_express7 = __toESM(require_express2(), 1);
43670
44181
 
43671
44182
  // ../../packages/api/src/system-info.ts
43672
44183
  var os5 = __toESM(require("os"), 1);
43673
- var import_node_child_process5 = require("child_process");
44184
+ var import_node_child_process6 = require("child_process");
43674
44185
  var RUNTIME_TOOLS = [
43675
44186
  { id: "claude", label: "Claude Code", command: "claude" },
43676
44187
  { id: "codex", label: "Codex", command: "codex" }
@@ -43684,7 +44195,7 @@ function normalizeOutput(output) {
43684
44195
  }
43685
44196
  function resolveCommandPath(command) {
43686
44197
  const lookupCommand = process.platform === "win32" ? "where" : "which";
43687
- const result = (0, import_node_child_process5.spawnSync)(lookupCommand, [command], {
44198
+ const result = (0, import_node_child_process6.spawnSync)(lookupCommand, [command], {
43688
44199
  encoding: "utf-8",
43689
44200
  shell: false,
43690
44201
  windowsHide: true,
@@ -43713,7 +44224,7 @@ function inspectTool(definition) {
43713
44224
  detail: resolved.detail ?? "Command not found on PATH"
43714
44225
  };
43715
44226
  }
43716
- const versionResult = (0, import_node_child_process5.spawnSync)(definition.command, ["--version"], {
44227
+ const versionResult = (0, import_node_child_process6.spawnSync)(definition.command, ["--version"], {
43717
44228
  encoding: "utf-8",
43718
44229
  shell: process.platform === "win32",
43719
44230
  windowsHide: true,
@@ -44159,10 +44670,10 @@ var DirectTransport = class {
44159
44670
  };
44160
44671
 
44161
44672
  // ../../packages/transport/src/devtunnel.ts
44162
- var import_node_child_process6 = require("child_process");
44673
+ var import_node_child_process7 = require("child_process");
44163
44674
  function isInstalled(cmd) {
44164
44675
  try {
44165
- (0, import_node_child_process6.execSync)(process.platform === "win32" ? `where ${cmd}` : `command -v ${cmd}`, { stdio: "pipe" });
44676
+ (0, import_node_child_process7.execSync)(process.platform === "win32" ? `where ${cmd}` : `command -v ${cmd}`, { stdio: "pipe" });
44166
44677
  return true;
44167
44678
  } catch {
44168
44679
  return false;
@@ -44185,14 +44696,14 @@ var DevTunnelTransport = class {
44185
44696
  );
44186
44697
  }
44187
44698
  try {
44188
- (0, import_node_child_process6.execSync)("devtunnel user show", { stdio: "pipe" });
44699
+ (0, import_node_child_process7.execSync)("devtunnel user show", { stdio: "pipe" });
44189
44700
  } catch {
44190
44701
  throw new Error(
44191
44702
  "Not logged in to devtunnel. Run: devtunnel user login"
44192
44703
  );
44193
44704
  }
44194
44705
  const hostArgs = this.buildHostArgs(localPort);
44195
- const child = (0, import_node_child_process6.spawn)("devtunnel", hostArgs, {
44706
+ const child = (0, import_node_child_process7.spawn)("devtunnel", hostArgs, {
44196
44707
  stdio: ["pipe", "pipe", "pipe"]
44197
44708
  });
44198
44709
  this.process = child;
@@ -44297,7 +44808,7 @@ ${lines.join("")}`
44297
44808
  return void 0;
44298
44809
  }
44299
44810
  try {
44300
- (0, import_node_child_process6.execFileSync)("devtunnel", ["show", tunnelId], { stdio: "pipe" });
44811
+ (0, import_node_child_process7.execFileSync)("devtunnel", ["show", tunnelId], { stdio: "pipe" });
44301
44812
  return tunnelId;
44302
44813
  } catch {
44303
44814
  const createArgs = ["create", tunnelId];
@@ -44305,7 +44816,7 @@ ${lines.join("")}`
44305
44816
  createArgs.push("-a");
44306
44817
  }
44307
44818
  try {
44308
- (0, import_node_child_process6.execFileSync)("devtunnel", createArgs, { stdio: "pipe" });
44819
+ (0, import_node_child_process7.execFileSync)("devtunnel", createArgs, { stdio: "pipe" });
44309
44820
  return tunnelId;
44310
44821
  } catch (err) {
44311
44822
  throw new Error(
@@ -44320,7 +44831,7 @@ ${lines.join("")}`
44320
44831
  return;
44321
44832
  }
44322
44833
  try {
44323
- (0, import_node_child_process6.execFileSync)(
44834
+ (0, import_node_child_process7.execFileSync)(
44324
44835
  "devtunnel",
44325
44836
  ["port", "create", tunnelId, "-p", String(localPort), "--protocol", "http"],
44326
44837
  { stdio: "pipe" }
@@ -44336,7 +44847,7 @@ ${lines.join("")}`
44336
44847
  }
44337
44848
  listTunnelPorts(tunnelId) {
44338
44849
  try {
44339
- const output = (0, import_node_child_process6.execFileSync)("devtunnel", ["port", "list", tunnelId, "-j"], { stdio: "pipe" });
44850
+ const output = (0, import_node_child_process7.execFileSync)("devtunnel", ["port", "list", tunnelId, "-j"], { stdio: "pipe" });
44340
44851
  const parsed = JSON.parse(output.toString());
44341
44852
  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 : [];
44342
44853
  return items.map((item) => getPortNumber(item)).filter((value) => value !== void 0);
@@ -44348,7 +44859,7 @@ ${lines.join("")}`
44348
44859
  }
44349
44860
  hasTunnelPort(tunnelId, localPort) {
44350
44861
  try {
44351
- (0, import_node_child_process6.execFileSync)(
44862
+ (0, import_node_child_process7.execFileSync)(
44352
44863
  "devtunnel",
44353
44864
  ["port", "show", tunnelId, "-p", String(localPort), "-j"],
44354
44865
  { stdio: "pipe" }
@@ -44406,7 +44917,7 @@ function createTransport(config) {
44406
44917
  var fs15 = __toESM(require("fs"), 1);
44407
44918
  var path16 = __toESM(require("path"), 1);
44408
44919
  var readline = __toESM(require("readline/promises"), 1);
44409
- var import_node_child_process7 = require("child_process");
44920
+ var import_node_child_process8 = require("child_process");
44410
44921
  function getDefaultNodeName() {
44411
44922
  return getDeviceNodeName();
44412
44923
  }
@@ -44453,7 +44964,7 @@ function createPromptSession(prompt) {
44453
44964
  }
44454
44965
  function createDefaultCommandRunner(platform2) {
44455
44966
  return (command, args, interactive = false) => {
44456
- const result = (0, import_node_child_process7.spawnSync)(command, args, {
44967
+ const result = (0, import_node_child_process8.spawnSync)(command, args, {
44457
44968
  encoding: "utf-8",
44458
44969
  shell: platform2 === "win32",
44459
44970
  stdio: interactive ? "inherit" : "pipe"
@@ -44894,7 +45405,7 @@ function formatLoadedStartMetadata(info, authEnabled) {
44894
45405
  // src/runtime-metadata.ts
44895
45406
  var fs16 = __toESM(require("fs"), 1);
44896
45407
  var path17 = __toESM(require("path"), 1);
44897
- var import_node_child_process8 = require("child_process");
45408
+ var import_node_child_process9 = require("child_process");
44898
45409
  var runtimeDir = resolveRuntimeDir();
44899
45410
  var appRoot = resolveAppRoot(runtimeDir);
44900
45411
  var repoRoot = path17.resolve(appRoot, "../..");
@@ -44989,7 +45500,7 @@ function readRepositoryUrlFromManifest(manifest) {
44989
45500
  }
44990
45501
  function readGitValue(args) {
44991
45502
  try {
44992
- const output = (0, import_node_child_process8.execFileSync)("git", args, {
45503
+ const output = (0, import_node_child_process9.execFileSync)("git", args, {
44993
45504
  cwd: repoRoot,
44994
45505
  encoding: "utf-8",
44995
45506
  stdio: ["ignore", "pipe", "ignore"],