@ran-sh/dsh-crew 2.0.13 → 2.0.14

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.0.13",
3
+ "version": "2.0.14",
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.0.13",
3
+ "version": "2.0.14",
4
4
  "type": "module",
5
5
  "main": "./src/hub/entry.mjs",
6
6
  "bin": {
@@ -4,7 +4,7 @@
4
4
 
5
5
  import { readFileSync, writeFileSync, mkdirSync, copyFileSync, existsSync, readdirSync, rmSync, statSync, lstatSync, opendirSync } from 'node:fs';
6
6
  import { createHash } from 'node:crypto';
7
- import { createRequire } from 'node:module';
7
+ import { createRequire, isBuiltin } from 'node:module';
8
8
  import { spawnSync } from 'node:child_process';
9
9
  import { dirname, join, resolve, relative } from 'node:path';
10
10
  import { fileURLToPath } from 'node:url';
@@ -593,25 +593,58 @@ export function claudeSnapshotSettleMs(err) {
593
593
  * present is what *it* imports. The manifest is not a substitute: it declares 28
594
594
  * dependencies, including meta-packages the server never imports from here, and
595
595
  * requiring all of them to resolve read a working install as broken. Resolution
596
- * is asked from the entry itself, and subpath specifiers are kept as they are —
596
+ * follows local modules from the entry, and subpath specifiers are kept as they are —
597
597
  * `@modelcontextprotocol/sdk/server/mcp.js` resolves through its package's
598
598
  * `exports`, which the bare package name does not.
599
599
  */
600
600
  function claudeSnapshotResolvable(snapshotRoot) {
601
601
  if (typeof snapshotRoot !== 'string' || !snapshotRoot.trim()) return false;
602
- const entry = join(snapshotRoot, 'src', 'server.mjs');
603
- if (!existsSync(entry)) return false;
604
- let source;
605
- try { source = readFileSync(entry, 'utf8'); } catch { return false; }
606
- const specifiers = [...source.matchAll(/(?:from|import)\s*\(?\s*['"]([^'"]+)['"]/g)]
607
- .map((match) => match[1])
608
- .filter((specifier) => !specifier.startsWith('.') && !specifier.startsWith('node:'));
609
- if (!specifiers.length) return true;
610
- let fromEntry;
611
- try { fromEntry = createRequire(entry); } catch { return false; }
612
- return specifiers.every((specifier) => {
613
- try { return existsSync(fromEntry.resolve(specifier)); } catch { return false; }
614
- });
602
+ const root = resolve(snapshotRoot);
603
+ const visited = new Set();
604
+ let bytes = 0;
605
+ const inspect = (entry) => {
606
+ if (visited.has(entry)) return true;
607
+ if (visited.size >= 512) return false;
608
+ const local = relative(root, entry);
609
+ if (local.startsWith('..') || resolve(root, local) !== entry) return false;
610
+ visited.add(entry);
611
+ try {
612
+ const info = lstatSync(entry);
613
+ if (!info.isFile() || info.isSymbolicLink() || bytes + info.size > 8 * 1024 * 1024) return false;
614
+ bytes += info.size;
615
+ if (!/\.[cm]?js$/i.test(entry)) return true;
616
+ const source = readFileSync(entry, 'utf8');
617
+ const fromEntry = createRequire(entry);
618
+ // Consume comments and literals as complete tokens before recognizing declarations.
619
+ const tokens = (source.match(/\/\*[\s\S]*?\*\/|\/\/[^\r\n]*|"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'|`(?:\\.|[^`\\])*`|[A-Za-z_$][\w$]*|[^\s]/g) ?? []).filter((token) => !token.startsWith('/*') && !token.startsWith('//'));
620
+ const specifiers = [];
621
+ const literal = (token) => token && /^['"]/.test(token);
622
+ for (let i = 0; i < tokens.length; i += 1) {
623
+ if (!['import', 'export', 'require'].includes(tokens[i])) continue;
624
+ if (tokens[i - 1] === '.') continue;
625
+ let next = i + 1;
626
+ while (tokens[next]?.startsWith('/*') || tokens[next]?.startsWith('//')) next += 1;
627
+ if (['import', 'require'].includes(tokens[i]) && tokens[next] === '(') {
628
+ if (literal(tokens[next + 1])) specifiers.push(tokens[next + 1].slice(1, -1));
629
+ continue;
630
+ }
631
+ if (tokens[i] === 'require') continue;
632
+ if (literal(tokens[next])) { specifiers.push(tokens[next].slice(1, -1)); continue; }
633
+ // Only import/export declarations can introduce a `from` clause.
634
+ if (tokens[next] === '.' || tokens[next] === '(' || ['const', 'let', 'var', 'function', 'class', 'default', 'async'].includes(tokens[next])) continue;
635
+ for (let j = next; j < tokens.length && tokens[j] !== ';'; j += 1) {
636
+ if (tokens[j] === 'from' && literal(tokens[j + 1])) { specifiers.push(tokens[j + 1].slice(1, -1)); break; }
637
+ }
638
+ }
639
+ return specifiers.every((specifier) => {
640
+ if (isBuiltin(specifier)) return true;
641
+ const target = fromEntry.resolve(specifier);
642
+ if (!existsSync(target)) return false;
643
+ return specifier.startsWith('.') ? inspect(target) : true;
644
+ });
645
+ } catch { return false; }
646
+ };
647
+ return inspect(join(root, 'src', 'server.mjs'));
615
648
  }
616
649
 
617
650
  /**
@@ -1181,9 +1181,15 @@ export function defaultPayloadSmoke(dir, { nodePath = process.execPath, runner =
1181
1181
  // Keyword boundaries reject identifiers containing the keywords
1182
1182
  // ('legacy-import') and member calls (.from); captures stay on one line and
1183
1183
  // bounded — real specifiers never span lines.
1184
- const IMPORT_SPECIFIER_RE = /(?<![\w.\-])(?:from|import|require)\b\s*\(?\s*["']([^"'\n]{1,200})["']/g;
1185
-
1186
- function collectExternalSpecifiers(dir) {
1184
+ //
1185
+ // A quote before the keyword is rejected too. Writing the keyword as a string —
1186
+ // `['import', 'export', 'require'].includes(word)` — is ordinary JavaScript, and
1187
+ // without this the closing quote of `'import'` was taken for the opening quote of
1188
+ // a specifier: it captured the text between two keywords and the payload failed to
1189
+ // stage on code that imports nothing at all.
1190
+ const IMPORT_SPECIFIER_RE = /(?<![\w.\-'"`])(?:from|import|require)\b\s*\(?\s*["']([^"'\n]{1,200})["']/g;
1191
+
1192
+ export function collectExternalSpecifiers(dir) {
1187
1193
  const specifiers = new Set();
1188
1194
  const walk = (root) => {
1189
1195
  if (!existsSync(root)) return;
@@ -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.0.13';
39
+ export const RUNTIME_VERSION = '2.0.14';
40
40
  export const HUB_PROTOCOL_VERSION = 1;
41
41
 
42
42
  export const HUB_CAPABILITIES = Object.freeze([