@ganglion/xacpx 0.17.0-beta.8 → 0.17.0-beta.9

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/dist/cli.js CHANGED
@@ -35409,7 +35409,7 @@ import { execFile as execFile2 } from "node:child_process";
35409
35409
  import { createHash as createHash4 } from "node:crypto";
35410
35410
  import { lstat, mkdir as mkdir10, realpath as realpath2 } from "node:fs/promises";
35411
35411
  import { homedir as homedir13 } from "node:os";
35412
- import { basename as basename4, isAbsolute as isAbsolute4, join as join23, resolve as resolve4, sep as sep2 } from "node:path";
35412
+ import { basename as basename4, isAbsolute as isAbsolute4, join as join23, resolve as resolve4, sep as sep2, win32 as win322 } from "node:path";
35413
35413
  import { promisify as promisify2 } from "node:util";
35414
35414
  function expandHome3(path14) {
35415
35415
  if (path14 === "~")
@@ -35418,14 +35418,48 @@ function expandHome3(path14) {
35418
35418
  return resolve4(homedir13(), path14.slice(2));
35419
35419
  return path14;
35420
35420
  }
35421
+ function worktreePathsEqual(left, right, platform = process.platform) {
35422
+ if (platform === "win32") {
35423
+ return normalizeWindowsWorktreePath(left) === normalizeWindowsWorktreePath(right);
35424
+ }
35425
+ return left === right;
35426
+ }
35427
+ function worktreePathIsWithin(parent, child, platform = process.platform) {
35428
+ const normalizedParent = platform === "win32" ? normalizeWindowsWorktreePath(parent) : parent;
35429
+ const normalizedChild = platform === "win32" ? normalizeWindowsWorktreePath(child) : child;
35430
+ if (normalizedParent === normalizedChild)
35431
+ return false;
35432
+ const separator = platform === "win32" ? win322.sep : sep2;
35433
+ const parentPrefix = normalizedParent.endsWith(separator) ? normalizedParent : normalizedParent + separator;
35434
+ return normalizedChild.startsWith(parentPrefix);
35435
+ }
35436
+ function normalizeWindowsWorktreePath(path14) {
35437
+ let normalized = win322.normalize(path14);
35438
+ const folded = normalized.toLowerCase();
35439
+ if (folded.startsWith("\\\\?\\unc\\")) {
35440
+ normalized = `\\\\${normalized.slice(8)}`;
35441
+ } else if (folded.startsWith("\\\\?\\")) {
35442
+ normalized = normalized.slice(4);
35443
+ }
35444
+ normalized = win322.normalize(normalized);
35445
+ const root = win322.parse(normalized).root;
35446
+ while (normalized.length > root.length && normalized.endsWith(win322.sep)) {
35447
+ normalized = normalized.slice(0, -1);
35448
+ }
35449
+ return normalized.toLowerCase();
35450
+ }
35421
35451
 
35422
35452
  class WorkspaceGit {
35423
35453
  listWorkspaces;
35424
35454
  managedWorktreesRoot;
35455
+ platform;
35456
+ runGitOverride;
35425
35457
  writeTails = new Map;
35426
35458
  constructor(listWorkspaces, options = {}) {
35427
35459
  this.listWorkspaces = listWorkspaces;
35428
35460
  this.managedWorktreesRoot = options.managedWorktreesRoot ?? join23(homedir13(), ".xacpx", "worktrees");
35461
+ this.platform = options.platform ?? process.platform;
35462
+ this.runGitOverride = options.runGit;
35429
35463
  }
35430
35464
  async rootFor(workspace3) {
35431
35465
  const ref = this.listWorkspaces().find((item) => item.name === workspace3);
@@ -35441,6 +35475,8 @@ class WorkspaceGit {
35441
35475
  return (await this.runRaw(root, args)).trim();
35442
35476
  }
35443
35477
  async runRaw(root, args) {
35478
+ if (this.runGitOverride)
35479
+ return await this.runGitOverride(root, args);
35444
35480
  const result = await execFileAsync2("git", ["-C", root, ...args], {
35445
35481
  timeout: GIT_TIMEOUT_MS2,
35446
35482
  killSignal: "SIGKILL",
@@ -35624,7 +35660,7 @@ class WorkspaceGit {
35624
35660
  await mkdir10(repoDir);
35625
35661
  }
35626
35662
  const realRepoDir = await realpath2(repoDir);
35627
- if (!realRepoDir.startsWith(managedRoot + sep2))
35663
+ if (!worktreePathIsWithin(managedRoot, realRepoDir, this.platform))
35628
35664
  throw new Error("worktree-path-unsafe");
35629
35665
  const target = join23(realRepoDir, branchKey);
35630
35666
  try {
@@ -35640,7 +35676,7 @@ class WorkspaceGit {
35640
35676
  await this.run(root, ["worktree", "add", target, branch]);
35641
35677
  }
35642
35678
  const createdPath = await realpath2(target);
35643
- if (!createdPath.startsWith(managedRoot + sep2))
35679
+ if (!worktreePathIsWithin(managedRoot, createdPath, this.platform))
35644
35680
  throw new Error("worktree-path-unsafe");
35645
35681
  return { path: createdPath, branch, linked: true };
35646
35682
  });
@@ -35650,10 +35686,10 @@ class WorkspaceGit {
35650
35686
  const root = await this.rootFor(workspace3);
35651
35687
  const managedRoot = await realpath2(this.managedWorktreesRoot);
35652
35688
  const target = await realpath2(path14);
35653
- if (!target.startsWith(managedRoot + sep2))
35689
+ if (!worktreePathIsWithin(managedRoot, target, this.platform))
35654
35690
  throw new Error("worktree-outside-managed-root");
35655
35691
  const worktrees = await this.readWorktrees(root);
35656
- const worktree = worktrees.find((item) => item.path === target);
35692
+ const worktree = worktrees.find((item) => worktreePathsEqual(item.path, target, this.platform));
35657
35693
  if (!worktree)
35658
35694
  throw new Error("worktree-not-found");
35659
35695
  if (worktree.current)
@@ -35767,7 +35803,7 @@ class WorkspaceGit {
35767
35803
  path: item.path,
35768
35804
  ...item.branch ? { branch: item.branch } : {},
35769
35805
  ...item.detached ? { detached: true } : {},
35770
- current: item.path === currentRoot,
35806
+ current: worktreePathsEqual(item.path, currentRoot, this.platform),
35771
35807
  linked: index !== 0
35772
35808
  }));
35773
35809
  }
@@ -57,13 +57,20 @@ export interface GitWorktreeCreateResult {
57
57
  branch: string;
58
58
  linked: true;
59
59
  }
60
+ export declare function worktreePathsEqual(left: string, right: string, platform?: NodeJS.Platform): boolean;
61
+ export declare function worktreePathIsWithin(parent: string, child: string, platform?: NodeJS.Platform): boolean;
62
+ export interface WorkspaceGitOptions {
63
+ managedWorktreesRoot?: string;
64
+ platform?: NodeJS.Platform;
65
+ runGit?: (root: string, args: string[]) => Promise<string>;
66
+ }
60
67
  export declare class WorkspaceGit {
61
68
  private readonly listWorkspaces;
62
69
  private readonly managedWorktreesRoot;
70
+ private readonly platform;
71
+ private readonly runGitOverride;
63
72
  private readonly writeTails;
64
- constructor(listWorkspaces: () => GitWorkspaceRef[], options?: {
65
- managedWorktreesRoot?: string;
66
- });
73
+ constructor(listWorkspaces: () => GitWorkspaceRef[], options?: WorkspaceGitOptions);
67
74
  private rootFor;
68
75
  private run;
69
76
  private runRaw;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ganglion/xacpx",
3
- "version": "0.17.0-beta.8",
3
+ "version": "0.17.0-beta.9",
4
4
  "description": "随时随地通过聊天频道(微信 / 飞书 / 元宝等)远程控制 `acpx` 上的 Claude Code、Codex 等 Agents。",
5
5
  "keywords": [
6
6
  "acpx",