@ran-sh/dsh-crew 2.2.4 → 2.2.6

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.6",
4
4
  "description": "Dispatch subtasks to DeepSeek Harness (DSH) agents as native subagents with live progress",
5
5
  "author": {
6
6
  "name": "ZSeven-W"
@@ -80,6 +80,7 @@ const T = {
80
80
  openDiag: "打开诊断",
81
81
  crew: "Crew",
82
82
  enabled: "启用子 Agent",
83
+ frontendAutostart: "登录时启动 3080 前端(不打开浏览器)",
83
84
  flash: "Worker / Flash",
84
85
  pro: "Reviewer / Pro",
85
86
  addModel: "+ 添加模型",
@@ -107,6 +108,7 @@ const T = {
107
108
  openDiag: "Open diagnostics",
108
109
  crew: "Crew",
109
110
  enabled: "Enable sub-agents",
111
+ frontendAutostart: "Start the 3080 frontend at login (no browser)",
110
112
  flash: "Worker / Flash",
111
113
  pro: "Reviewer / Pro",
112
114
  addModel: "+ Add model",
@@ -439,19 +441,32 @@ function QuickPanel({ ctx }) {
439
441
  }),
440
442
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
441
443
  className: "crew-quick-row crew-quick-master",
442
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
443
- className: "crew-quick-section",
444
- children: t.crew
445
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", { children: [
446
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
447
- type: "checkbox",
448
- disabled: busy,
449
- checked: config.subagents_enabled !== false,
450
- onChange: (e) => toggle("subagents_enabled", e.target.checked)
444
+ children: [
445
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
446
+ className: "crew-quick-section",
447
+ children: t.crew
451
448
  }),
452
- " ",
453
- t.enabled
454
- ] })]
449
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", { children: [
450
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
451
+ type: "checkbox",
452
+ disabled: busy,
453
+ checked: config.subagents_enabled !== false,
454
+ onChange: (e) => toggle("subagents_enabled", e.target.checked)
455
+ }),
456
+ " ",
457
+ t.enabled
458
+ ] }),
459
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", { children: [
460
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
461
+ type: "checkbox",
462
+ disabled: busy,
463
+ checked: config.frontend_autostart === true,
464
+ onChange: (e) => toggle("frontend_autostart", e.target.checked)
465
+ }),
466
+ " ",
467
+ t.frontendAutostart
468
+ ] })
469
+ ]
455
470
  }),
456
471
  modelList("flash_model_priority", t.flash),
457
472
  modelList("pro_model_priority", t.pro),
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.6",
4
4
  "type": "module",
5
5
  "main": "./src/hub/entry.mjs",
6
6
  "bin": {
@@ -19,6 +19,7 @@ const FULL = 'http://127.0.0.1:3210/_dsh/dsh-crew/control-plane';
19
19
  type ModelEntry = { provider: string; model: string };
20
20
  type QuickConfig = {
21
21
  subagents_enabled?: boolean;
22
+ frontend_autostart?: boolean;
22
23
  flash_model_priority?: ModelEntry[];
23
24
  pro_model_priority?: ModelEntry[];
24
25
  };
@@ -41,6 +42,7 @@ const T = {
41
42
  openDiag: '打开诊断',
42
43
  crew: 'Crew',
43
44
  enabled: '启用子 Agent',
45
+ frontendAutostart: '登录时启动 3080 前端(不打开浏览器)',
44
46
  flash: 'Worker / Flash',
45
47
  pro: 'Reviewer / Pro',
46
48
  addModel: '+ 添加模型',
@@ -66,6 +68,7 @@ const T = {
66
68
  openDiag: 'Open diagnostics',
67
69
  crew: 'Crew',
68
70
  enabled: 'Enable sub-agents',
71
+ frontendAutostart: 'Start the 3080 frontend at login (no browser)',
69
72
  flash: 'Worker / Flash',
70
73
  pro: 'Reviewer / Pro',
71
74
  addModel: '+ Add model',
@@ -157,7 +160,7 @@ export function QuickPanel({ ctx }: { ctx: any }) {
157
160
  } finally { setBusy(false); }
158
161
  }, [t]);
159
162
 
160
- const toggle = (key: 'subagents_enabled', value: boolean) => {
163
+ const toggle = (key: 'subagents_enabled' | 'frontend_autostart', value: boolean) => {
161
164
  setConfig((c) => ({ ...(c ?? {}), [key]: value }));
162
165
  void patch({ [key]: value } as QuickConfig);
163
166
  };
@@ -251,6 +254,8 @@ export function QuickPanel({ ctx }: { ctx: any }) {
251
254
  <span className="crew-quick-section">{t.crew}</span>
252
255
  <label><input type="checkbox" disabled={busy} checked={config.subagents_enabled !== false}
253
256
  onChange={(e) => toggle('subagents_enabled', e.target.checked)} /> {t.enabled}</label>
257
+ <label><input type="checkbox" disabled={busy} checked={config.frontend_autostart === true}
258
+ onChange={(e) => toggle('frontend_autostart', e.target.checked)} /> {t.frontendAutostart}</label>
254
259
  </div>
255
260
  {modelList('flash_model_priority', t.flash)}
256
261
  {modelList('pro_model_priority', t.pro)}
package/src/hub/index.mjs CHANGED
@@ -114,6 +114,7 @@ const ROUTE_BASE = '/_dsh/dsh-crew';
114
114
  // canonical authority; quick-config is a projection, never a second store.
115
115
  export const QUICK_CONFIG_KEYS = Object.freeze([
116
116
  'subagents_enabled',
117
+ 'frontend_autostart',
117
118
  'flash_model_priority',
118
119
  'pro_model_priority',
119
120
  ]);
@@ -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.6';
40
40
  export const HUB_PROTOCOL_VERSION = 1;
41
41
 
42
42
  export const HUB_CAPABILITIES = Object.freeze([