@ran-sh/dsh-crew 2.2.11 → 2.2.12

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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-crew",
3
- "version": "2.2.11",
3
+ "version": "2.2.12",
4
4
  "description": "Dispatch subtasks to DeepSeek Harness (DSH) agents as native subagents with live progress",
5
5
  "author": {
6
6
  "name": "ZSeven-W"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ran-sh/dsh-crew",
3
- "version": "2.2.11",
3
+ "version": "2.2.12",
4
4
  "type": "module",
5
5
  "main": "./src/hub/entry.mjs",
6
6
  "bin": {
@@ -36,7 +36,7 @@ export {
36
36
  // included in the identity contract.
37
37
  const RUNTIME_ID = randomUUID();
38
38
 
39
- export const RUNTIME_VERSION = '2.2.11';
39
+ export const RUNTIME_VERSION = '2.2.12';
40
40
  export const HUB_PROTOCOL_VERSION = 1;
41
41
 
42
42
  export const HUB_CAPABILITIES = Object.freeze([
@@ -25,6 +25,8 @@ export const NOT_GIT_REPOSITORY = 'NOT_GIT_REPOSITORY';
25
25
  export const GIT_NOT_FOUND = 'GIT_NOT_FOUND';
26
26
  export const GIT_TIMEOUT = 'GIT_TIMEOUT';
27
27
  export const GIT_ERROR = 'GIT_ERROR';
28
+ /** Git refused to read a repository owned by another account; the trust decision is the operator's. */
29
+ export const WORKTREE_GIT_OWNERSHIP_UNSAFE = 'WORKTREE_GIT_OWNERSHIP_UNSAFE';
28
30
  /** A valid repository whose HEAD does not resolve because nothing is committed. */
29
31
  export const REPOSITORY_HAS_NO_COMMITS = 'REPOSITORY_HAS_NO_COMMITS';
30
32
  export const WORKTREE_LOCKED = 'WORKTREE_LOCKED';
@@ -67,6 +69,12 @@ async function runGit(runner, args, opts) {
67
69
  const r = await runner(args, opts);
68
70
  const stderr = r.stderr ?? '';
69
71
  if (/not a git repository/i.test(stderr)) return { ok: false, reason: NOT_GIT_REPOSITORY, error: stderr.trim() };
72
+ // Git's ownership refusal is its own condition, distinct from a broken
73
+ // repository: the tree exists, Crew can see it, and the remedy is a trust
74
+ // decision that belongs to the operator, not a repair step. Naming it keeps
75
+ // "the worktree looks unowned" (WORKSPACE_CONFLICT) and generic GIT_ERROR
76
+ // from misdescribing a shared checkout.
77
+ if (/dubious ownership/i.test(stderr)) return { ok: false, reason: WORKTREE_GIT_OWNERSHIP_UNSAFE, code: r.code, error: stderr.trim() };
70
78
  if (r.code != null && r.code !== 0) return { ok: false, reason: r.code === -1 ? GIT_NOT_FOUND : GIT_ERROR, code: r.code, error: stderr.trim() || 'git exited non-zero' };
71
79
  return { ok: true, code: r.code, stdout: r.stdout ?? '', stderr };
72
80
  } catch (err) {
@@ -101,6 +109,13 @@ export function sameRepoPath(left, right) {
101
109
  export function withSafeDirectory(git, trustedRepoRoot) {
102
110
  const run = git ?? defaultRunner;
103
111
  const safe = resolve(String(trustedRepoRoot ?? ''));
112
+ // Git reads a trailing `/*` as a prefix wildcard, and on POSIX `*` is a legal
113
+ // filename, so a value that could outlive "exactly one repository" is refused
114
+ // here rather than handed to Git and discovered later.
115
+ if (safe.includes('*')) {
116
+ const error = `Crew trust refusal: the safe.directory anchor must be one exact path without wildcards, got ${safe}`;
117
+ return async () => ({ code: 1, stdout: '', stderr: error });
118
+ }
104
119
  return (args, opts) => run(['-c', `safe.directory=${safe}`, ...args], opts);
105
120
  }
106
121