@ran-sh/dsh-crew 2.2.4 → 2.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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-crew",
3
- "version": "2.2.4",
3
+ "version": "2.2.5",
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.4",
3
+ "version": "2.2.5",
4
4
  "type": "module",
5
5
  "main": "./src/hub/entry.mjs",
6
6
  "bin": {
@@ -14,6 +14,15 @@ function plan(home, root) {
14
14
  ['official-web-bridge/lib/client.js', readFileSync(join(bridge, 'lib', 'client.js'))],
15
15
  ['src/local-request-guard.mjs', readFileSync(join(root, 'src', 'local-request-guard.mjs'))],
16
16
  ]);
17
+ // The snapshot is a canonical artifact: these sources are text that either line
18
+ // ending serves, so its bytes and its revision both normalize to LF. Otherwise a
19
+ // `core.autocrlf=true` checkout hashes to a different revision than the npm
20
+ // payload it installs, which made readiness look for a snapshot that does not
21
+ // exist and reported "needs repair" on a correct install (and made every payload
22
+ // update look like a stale desktop revision).
23
+ const canonicalBytes = (bytes) => (bytes.includes(0) ? bytes
24
+ : Buffer.from(bytes.toString('utf8').replace(/\r\n/g, '\n'), 'utf8'));
25
+ for (const [name, bytes] of files) files.set(name, canonicalBytes(bytes));
17
26
  const hash = createHash('sha256').update(JSON.stringify(metadata));
18
27
  for (const [name, bytes] of files) hash.update(name).update(bytes);
19
28
  const revision = hash.digest('hex');
@@ -30,17 +39,29 @@ function plan(home, root) {
30
39
  return { frontendRoot, snapshotRoot, revision, overlayFile, overlay, files };
31
40
  }
32
41
 
42
+ // Git checks a `core.autocrlf=true` Windows tree out as CRLF while the installed
43
+ // snapshot was written from the npm payload (LF). The bridge sources are text that
44
+ // either line ending serves, so a CRLF checkout must not read as a drifted snapshot;
45
+ // bytes are still compared first, and the tolerant path only applies to text.
46
+ function sameTextContent(installed, expected) {
47
+ if (installed.equals(expected)) return true;
48
+ if (installed.includes(0) || expected.includes(0)) return false;
49
+ const normalize = (buffer) => buffer.toString('utf8').replace(/\r\n/g, '\n');
50
+ return normalize(installed) === normalize(expected);
51
+ }
52
+
33
53
  function matches(p) {
34
54
  try { return [...p.files].every(([name, bytes]) => {
35
55
  const file = join(p.snapshotRoot, name);
36
- return lstatSync(file).isFile() && !lstatSync(file).isSymbolicLink() && readFileSync(file).equals(bytes);
56
+ return lstatSync(file).isFile() && !lstatSync(file).isSymbolicLink() && sameTextContent(readFileSync(file), bytes);
37
57
  }); } catch { return false; }
38
58
  }
39
59
 
40
60
  export function officialFrontendAssetsReady({ home = homedir(), root } = {}) {
41
61
  try {
42
62
  const p = plan(home, root);
43
- return matches(p) && readFileSync(p.overlayFile, 'utf8') === p.overlay;
63
+ const overlay = readFileSync(p.overlayFile, 'utf8');
64
+ return matches(p) && (overlay === p.overlay || overlay.replace(/\r\n/g, '\n') === p.overlay.replace(/\r\n/g, '\n'));
44
65
  } catch { return false; }
45
66
  }
46
67
 
@@ -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.4';
39
+ export const RUNTIME_VERSION = '2.2.5';
40
40
  export const HUB_PROTOCOL_VERSION = 1;
41
41
 
42
42
  export const HUB_CAPABILITIES = Object.freeze([