@sparkelf/dsh-plus 0.2.2-alpha.2.30 → 0.2.2-alpha.2.32

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.
@@ -9,8 +9,9 @@
9
9
  * bundle's own list.
10
10
  */
11
11
  import { spawnSync } from 'node:child_process';
12
- import { existsSync, mkdirSync, readdirSync, readFileSync, renameSync, symlinkSync, writeFileSync } from 'node:fs';
12
+ import { existsSync, mkdirSync, readdirSync, readFileSync, renameSync, rmSync, symlinkSync, writeFileSync } from 'node:fs';
13
13
  import { createRequire } from 'node:module';
14
+ import { fileURLToPath } from 'node:url';
14
15
  import { homedir } from 'node:os';
15
16
  import { dirname, join, posix, resolve, win32 } from 'node:path';
16
17
  import { parseDocument } from 'yaml';
@@ -98,6 +99,12 @@ function requireStringArray(value, label) {
98
99
  throw new Error(label + ' must be a string array');
99
100
  return value;
100
101
  }
102
+ /** Read one non-empty string field. */
103
+ function requireString(value, label) {
104
+ if (typeof value !== 'string' || value.trim() === '')
105
+ throw new Error(label + ' must be a non-empty string');
106
+ return value;
107
+ }
101
108
  /** Resolve the DSH home, honouring the environment override the launcher itself reads. */
102
109
  export function resolveHome(env = process.env) {
103
110
  const configured = env.DSH_HOME;
@@ -136,12 +143,15 @@ export function isWithin(candidate, parent, platform = process.platform) {
136
143
  * @returns absolute path to the installed distribution.
137
144
  */
138
145
  export function resolveDistributionDirectory(anchor) {
146
+ const ownDirectory = dirname(dirname(fileURLToPath(import.meta.url)));
139
147
  let resolved;
140
148
  try {
141
149
  resolved = dirname(createRequire(anchor).resolve('@sparkelf/dsh-plus/package.json'));
142
150
  }
143
151
  catch {
144
- throw new Error('@sparkelf/dsh-plus is not installed in ' + dirname(anchor) + '; run this command from the directory that installed it');
152
+ // `npx @sparkelf/dsh-plus ...` runs the CLI from a temporary install, so the consumer
153
+ // tree has no copy to resolve. The running module's own package is the distribution.
154
+ return ownDirectory;
145
155
  }
146
156
  // A resolution that leaves the consumer tree means the module answered from its own
147
157
  // installation, which reports health for a distribution the consumer never installed.
@@ -157,21 +167,14 @@ export function resolveDistributionDirectory(anchor) {
157
167
  break;
158
168
  current = parent;
159
169
  }
160
- throw new Error('@sparkelf/dsh-plus resolved outside ' + resolve(dirname(anchor)) + ' (found ' + resolved + '); run this command from the directory that installed it');
170
+ return ownDirectory;
161
171
  }
162
- /** The reviewed bundle order and pins the installed distribution declares. */
163
172
  export function readDistributionProfile(distributionDirectory) {
164
173
  const manifest = requireRecord(JSON.parse(readFileSync(join(distributionDirectory, 'package.json'), 'utf8')), 'Plus distribution manifest');
165
174
  const plus = requireRecord(manifest.dshPlus, 'dshPlus');
166
175
  const profile = requireRecord(plus.profile, 'dshPlus.profile');
167
- const rawDependencies = requireRecord(profile.dependencies, 'dshPlus.profile.dependencies');
176
+ const dependencies = requireDependencyMap(requireRecord(profile.dependencies, 'dshPlus.profile.dependencies'), 'dshPlus.profile.dependencies');
168
177
  const rawAllowBuilds = requireRecord(profile.allowBuilds, 'dshPlus.profile.allowBuilds');
169
- const dependencies = {};
170
- for (const [name, spec] of Object.entries(rawDependencies)) {
171
- if (typeof spec !== 'string' || spec === '')
172
- throw new Error('dshPlus.profile.dependencies.' + name + ' must be a non-empty string');
173
- dependencies[name] = spec;
174
- }
175
178
  const allowBuilds = {};
176
179
  for (const [name, allowed] of Object.entries(rawAllowBuilds)) {
177
180
  if (typeof allowed !== 'boolean')
@@ -190,6 +193,16 @@ export function readDistributionProfile(distributionDirectory) {
190
193
  throw new Error('dshPlus.profile.overrides.' + name + ' must be a non-empty string');
191
194
  overrides[name] = spec;
192
195
  }
196
+ const editions = {};
197
+ const rawEditions = plus.editions === undefined ? {} : requireRecord(plus.editions, 'dshPlus.editions');
198
+ for (const [id, value] of Object.entries(rawEditions)) {
199
+ const edition = requireRecord(value, 'dshPlus.editions.' + id);
200
+ editions[id] = {
201
+ label: requireString(edition.label, 'dshPlus.editions.' + id + '.label'),
202
+ bundles: requireStringArray(edition.bundles, 'dshPlus.editions.' + id + '.bundles'),
203
+ packages: requireDependencyMap(requireRecord(edition.packages, 'dshPlus.editions.' + id + '.packages'), 'dshPlus.editions.' + id + '.packages'),
204
+ };
205
+ }
193
206
  const compatibility = requireRecord(plus.compatibility, 'dshPlus.compatibility');
194
207
  return {
195
208
  name: String(manifest.name),
@@ -197,10 +210,21 @@ export function readDistributionProfile(distributionDirectory) {
197
210
  dependencies,
198
211
  allowBuilds,
199
212
  overrides,
213
+ editions,
200
214
  dshRange: String(compatibility.dsh),
201
215
  version: String(manifest.version),
202
216
  };
203
217
  }
218
+ /** Read one package-name-to-spec map, rejecting empty entries. */
219
+ function requireDependencyMap(value, label) {
220
+ const dependencies = {};
221
+ for (const [name, spec] of Object.entries(value)) {
222
+ if (typeof spec !== 'string' || spec === '')
223
+ throw new Error(label + '.' + name + ' must be a non-empty string');
224
+ dependencies[name] = spec;
225
+ }
226
+ return dependencies;
227
+ }
204
228
  /**
205
229
  * Give the profile its own installed tree, built from the distribution's declarations.
206
230
  *
@@ -222,7 +246,12 @@ export function readDistributionProfile(distributionDirectory) {
222
246
  export function installProfilePackages(paths, consumerDirectory) {
223
247
  const consumerModules = join(consumerDirectory, 'node_modules');
224
248
  if (!existsSync(consumerModules)) {
225
- throw new Error('no node_modules in ' + consumerDirectory + '; run npm install there first');
249
+ // `npx @sparkelf/dsh-plus` runs from a temporary package tree that has no launcher
250
+ // beside it. The profile installs its own tree, so there is nothing to link from
251
+ // the caller; only the profile's packages matter.
252
+ installWithRegistryFallback(paths);
253
+ alignReplacedPackageNames(join(paths.profileDirectory, 'node_modules'));
254
+ return;
226
255
  }
227
256
  const profileModules = join(paths.profileDirectory, 'node_modules');
228
257
  if (existsSync(profileModules)) {
@@ -247,10 +276,18 @@ export function installProfilePackages(paths, consumerDirectory) {
247
276
  function installWithRegistryFallback(paths) {
248
277
  const registries = registryOrder();
249
278
  for (const [index, registry] of registries.entries()) {
279
+ // A lockfile written against the previous registry keeps its resolved URLs, so the
280
+ // second attempt would fetch from the registry that just failed. Drop it and resolve
281
+ // against the registry being tried.
282
+ if (index > 0)
283
+ rmSync(join(paths.profileDirectory, 'pnpm-lock.yaml'), { force: true });
250
284
  const result = spawnSync('pnpm', ['install', '--no-frozen-lockfile', '--registry', registry], {
251
285
  cwd: paths.profileDirectory,
252
286
  stdio: 'inherit',
253
287
  shell: process.platform === 'win32',
288
+ // A registry that accepts the connection but never answers would otherwise hold the
289
+ // first start for pnpm's full retry budget before the fallback got a chance.
290
+ timeout: 5 * 60_000,
254
291
  });
255
292
  if (result.error !== undefined)
256
293
  throw result.error;
@@ -313,8 +350,12 @@ const MAINLAND_REGISTRY = 'https://registry.npmmirror.com';
313
350
  */
314
351
  export function registryOrder() {
315
352
  const configured = process.env.DSH_PLUS_INSTALL_REGISTRY;
316
- if (configured !== undefined && configured !== '')
317
- return [configured, MAINLAND_REGISTRY];
353
+ if (configured !== undefined && configured !== '') {
354
+ // The fallback must be the other registry, not the configured one again: an override
355
+ // set to the mirror used to fall back to the same mirror and fail twice.
356
+ const fallback = configured === MAINLAND_REGISTRY ? OFFICIAL_REGISTRY : MAINLAND_REGISTRY;
357
+ return [configured, fallback];
358
+ }
318
359
  // Intl reports the system locale on every platform. The POSIX variables are empty on
319
360
  // Windows, so reading them alone classified every Windows console as non-mainland and
320
361
  // reached the origin first — measured in a consumer's log, which showed npmjs failing
@@ -431,12 +472,113 @@ export function resolvePaths(anchor, env = process.env) {
431
472
  *
432
473
  * @param paths - resolved standalone paths.
433
474
  * @param consumerDirectory - directory whose `node_modules` holds the installed packages.
475
+ * @param edition - edition id whose package list to install, or undefined for the default.
434
476
  * @returns whether this call created the manifest.
435
477
  */
436
- export function ensureProfile(paths, consumerDirectory) {
478
+ export function ensureProfile(paths, consumerDirectory, edition) {
437
479
  const manifestPath = join(paths.profileDirectory, 'package.json');
438
480
  const distribution = readDistributionProfile(paths.distributionDirectory);
481
+ const composition = edition === undefined ? undefined : distribution.editions[edition];
482
+ if (edition !== undefined && composition === undefined) {
483
+ const offered = Object.keys(distribution.editions);
484
+ throw new Error('unknown edition "' + edition + '"; this release offers '
485
+ + (offered.length === 0 ? '(none)' : offered.join(', ')));
486
+ }
487
+ // An edition lists only third-party, fork, and self-developed plugins. The official
488
+ // runtime is the CLI's responsibility and must not be duplicated in that list.
489
+ for (const name of Object.keys(composition?.packages ?? {})) {
490
+ if (name.startsWith('@deepseek-ai/')) {
491
+ throw new Error('edition "' + String(edition) + '" lists the official package ' + name
492
+ + '; editions contain only third-party or self-developed plugins');
493
+ }
494
+ }
495
+ const runtime = distribution.dshRange.replace(/^>=?/u, '');
496
+ const baseBundles = composition === undefined
497
+ ? distribution.bundles
498
+ : distribution.bundles.filter(bundle => bundle.startsWith('@deepseek-ai/') || bundle === '@sparkelf/dsh-plus');
499
+ const basePackages = composition === undefined
500
+ ? distribution.dependencies
501
+ : {
502
+ '@deepseek-ai/dsh': distribution.dshRange,
503
+ '@sparkelf/dsh-plus': distribution.version,
504
+ // `dsh-app-boot` declares its launcher as peer dependencies, and pnpm does not
505
+ // install a profile's transitive peers. Without these the boot loader resolves
506
+ // `@deepseek-ai/cordis-plugin-group` and fails before any plugin mounts.
507
+ '@deepseek-ai/cordis': '^4.0.6-alpha.1',
508
+ '@deepseek-ai/cordis-plugin-group': '^1.0.6-alpha.1',
509
+ '@deepseek-ai/cordis-plugin-include': '^1.0.11-alpha.1',
510
+ '@deepseek-ai/cordis-plugin-loader': '^1.0.7-alpha.1',
511
+ '@deepseek-ai/dsh-home-paths': runtime,
512
+ '@deepseek-ai/dsh-launch-environment': runtime,
513
+ // `dsh-session` / `dsh-tools` declare the rest of the runtime as peers; pnpm leaves
514
+ // them out of a profile install for the same reason as the launcher peers above.
515
+ '@deepseek-ai/dsh-agent': runtime,
516
+ '@deepseek-ai/dsh-llm': runtime,
517
+ '@deepseek-ai/dsh-scope': runtime,
518
+ '@deepseek-ai/dsh-session': runtime,
519
+ '@deepseek-ai/dsh-system-prompt': runtime,
520
+ '@deepseek-ai/dsh-user-approval': runtime,
521
+ // Capability packages that the official plugins import but declare only as peers.
522
+ // A profile install leaves them out and boot then fails inside an otherwise
523
+ // healthy plugin's import with ERR_MODULE_NOT_FOUND.
524
+ '@deepseek-ai/dsh-attachment': runtime,
525
+ '@deepseek-ai/dsh-compaction': runtime,
526
+ '@deepseek-ai/dsh-fs': runtime,
527
+ '@deepseek-ai/dsh-shell': runtime,
528
+ '@deepseek-ai/dsh-spill': runtime,
529
+ '@deepseek-ai/dsh-sandbox': runtime,
530
+ '@deepseek-ai/dsh-ptc-runtime': runtime,
531
+ '@deepseek-ai/dsh-llm-deepseek': runtime,
532
+ '@deepseek-ai/dsh-deepseek-account': runtime,
533
+ '@deepseek-ai/dsh-anonymous-user-id': runtime,
534
+ '@deepseek-ai/dsh-bash-local': runtime,
535
+ '@deepseek-ai/dsh-client-store': runtime,
536
+ '@deepseek-ai/dsh-jobs': runtime,
537
+ '@deepseek-ai/dsh-sdk-protocol': runtime,
538
+ '@deepseek-ai/dsh-session-persistence': runtime,
539
+ '@deepseek-ai/dsh-session-telemetry': runtime,
540
+ '@deepseek-ai/dsh-session-title-llm': runtime,
541
+ '@deepseek-ai/dsh-util-workspace-path': runtime,
542
+ '@deepseek-ai/dsh-workflow': runtime,
543
+ ...Object.fromEntries(baseBundles
544
+ .filter(bundle => bundle.startsWith('@deepseek-ai/dsh-'))
545
+ .map(bundle => [bundle, runtime])),
546
+ };
547
+ const compositionPackages = composition === undefined
548
+ ? basePackages
549
+ : { ...basePackages, ...composition.packages };
550
+ // Official cordis rows name the historical unscoped packages. The fork ships under
551
+ // `@sparkelf/*`, so the row would fail to resolve unless the profile also carries the
552
+ // historical name as an alias to the same version.
553
+ const rowAliases = {
554
+ 'dsh-better-sidebar': '@sparkelf/dsh-better-sidebar',
555
+ 'dsh-sql-workbench': '@sparkelf/dsh-sql-workbench',
556
+ };
557
+ for (const [alias, target] of Object.entries(rowAliases)) {
558
+ const spec = compositionPackages[target];
559
+ if (spec !== undefined && compositionPackages[alias] === undefined) {
560
+ compositionPackages[alias] = 'npm:' + target + '@' + spec;
561
+ }
562
+ }
563
+ const compositionBundles = composition === undefined
564
+ ? baseBundles
565
+ : [...baseBundles, ...composition.bundles];
439
566
  if (existsSync(manifestPath)) {
567
+ // A selected edition is an explicit install choice, so its packages and bundle order
568
+ // replace the profile's rather than being ignored. Other profile decisions stay.
569
+ if (composition !== undefined) {
570
+ const manifest = requireRecord(JSON.parse(readFileSync(manifestPath, 'utf8')), 'Plus profile manifest');
571
+ manifest.dependencies = { ...manifest.dependencies, ...compositionPackages };
572
+ manifest.dsh = {
573
+ ...manifest.dsh,
574
+ profile: {
575
+ ...manifest.dsh?.profile,
576
+ bundles: [...compositionBundles],
577
+ ...(edition === undefined ? {} : { plusEdition: edition }),
578
+ },
579
+ };
580
+ writeFileSync(manifestPath, JSON.stringify(manifest, null, 2) + '\n');
581
+ }
440
582
  // The workspace carries decisions the distribution owns — overrides and the build
441
583
  // script allowlist — and a distribution release changes them. Rewriting on every
442
584
  // start is what lets an upgraded installation receive the new values; a profile
@@ -450,18 +592,12 @@ export function ensureProfile(paths, consumerDirectory) {
450
592
  name: 'dsh-profile-' + STANDALONE_PROFILE,
451
593
  private: true,
452
594
  type: 'module',
453
- dependencies: {
454
- '@sparkelf/dsh-plus': distribution.version,
455
- // The launcher's own tree supplies every service package the bundles mount. A
456
- // profile that lists only the distribution's plugins installs a partial tree and
457
- // fails at load with a module the launcher would have carried.
458
- '@deepseek-ai/dsh': distribution.dshRange,
459
- ...distribution.dependencies,
460
- },
595
+ dependencies: compositionPackages,
461
596
  dsh: {
462
597
  profile: {
463
- bundles: distribution.bundles,
598
+ bundles: [...compositionBundles],
464
599
  patchReload: 'live',
600
+ ...(edition === undefined ? {} : { plusEdition: edition }),
465
601
  },
466
602
  },
467
603
  };
@@ -506,7 +642,7 @@ function writeProfileOverrides(profileDirectory, overrides, allowBuilds, omitted
506
642
  if (document.get('nodeLinker') === undefined)
507
643
  document.set('nodeLinker', 'hoisted');
508
644
  if (document.get('autoInstallPeers') === undefined)
509
- document.set('autoInstallPeers', false);
645
+ document.set('autoInstallPeers', true);
510
646
  writeFileSync(workspacePath, String(document));
511
647
  }
512
648
  /** Run git in one directory, returning undefined instead of throwing when asked to. */
@@ -540,6 +676,8 @@ export function applyProfileNpmPatches(distributionDirectory, profileDirectory)
540
676
  const patchSet = readPatchSet(distributionDirectory, manifest);
541
677
  const applied = [];
542
678
  for (const entry of [...patchSet.entries, ...patchSet.thirdPartyPatches]) {
679
+ // Only npm targets reach an installed package; a source target needs the
680
+ // official checkout, which a standalone installation does not have.
543
681
  if (entry.target.kind !== 'npm')
544
682
  continue;
545
683
  const targetName = entry.target.name;
@@ -560,7 +698,22 @@ function resolveInstalledPackage(from, packageName) {
560
698
  manifestPath = requireFrom.resolve(packageName + '/package.json');
561
699
  }
562
700
  catch {
563
- throw new Error(packageName + ' is not installed under ' + from);
701
+ // Some packages do not export `./package.json`; resolve the entry module instead and walk
702
+ // up to the directory that holds the manifest.
703
+ let directory;
704
+ try {
705
+ directory = dirname(requireFrom.resolve(packageName));
706
+ }
707
+ catch {
708
+ throw new Error(packageName + ' is not installed under ' + from);
709
+ }
710
+ while (!existsSync(resolve(directory, 'package.json'))) {
711
+ const parent = dirname(directory);
712
+ if (parent === directory)
713
+ throw new Error(packageName + ' has no resolvable package manifest under ' + from);
714
+ directory = parent;
715
+ }
716
+ manifestPath = resolve(directory, 'package.json');
564
717
  }
565
718
  const manifest = requireRecord(JSON.parse(readFileSync(manifestPath, 'utf8')), packageName + ' manifest');
566
719
  return { name: packageName, version: String(manifest.version), directory: dirname(manifestPath), manifest };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@sparkelf/dsh-plus",
3
3
  "description": "DeepSeek Harness Plus official-base Web distribution profile and ranged compatibility patchset",
4
- "version": "0.2.2-alpha.2.30",
4
+ "version": "0.2.2-alpha.2.32",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
@@ -82,7 +82,7 @@
82
82
  "@deepseek-ai/dsh-experimental-agent-team-profile": "0.2.2-alpha.2",
83
83
  "@sparkelf/dsh-mobile-bridge": "0.2.18",
84
84
  "@sparkelf/dsh-quant": "0.91.0-sparkelf.13",
85
- "@sparkelf/dsh-task-bus": "0.1.2",
85
+ "@sparkelf/dsh-task-bus": "0.1.3",
86
86
  "@deepseek-ai/dsh-experimental-auto-review": "0.2.2-alpha.2",
87
87
  "@sparkelf/dsh-genui": "0.15.0-sparkelf.1",
88
88
  "@deepseek-ai/dsh-anonymous-user-id": "0.2.2-alpha.2",
@@ -620,7 +620,6 @@
620
620
  "bundles": [
621
621
  "@sparkelf/dsh-mineru",
622
622
  "@sparkelf/dsh-officecli",
623
- "dshmarket",
624
623
  "@sparkelf/dsh-office-viewer-fonts",
625
624
  "@sparkelf/dsh-genui",
626
625
  "@sparkelf/dsh-plugin-supervisor",
@@ -642,7 +641,7 @@
642
641
  ],
643
642
  "packages": {
644
643
  "@sparkelf/dsh-api-client": "0.5.3",
645
- "@sparkelf/dsh-dataops-managed": "0.3.22",
644
+ "@sparkelf/dsh-dataops-managed": "0.3.23",
646
645
  "@sparkelf/dsh-genui": "0.15.0-sparkelf.1",
647
646
  "@sparkelf/dsh-knowledge-base": "0.1.24",
648
647
  "@sparkelf/dsh-mineru": "0.1.4",
@@ -652,7 +651,7 @@
652
651
  "@sparkelf/dsh-ssh-manager": "0.7.5",
653
652
  "@sparkelf/dsh-workbench-vault": "0.1.2",
654
653
  "dsh-right-bg-anim": "1.1.0",
655
- "@sparkelf/dsh-task-bus": "0.1.2",
654
+ "@sparkelf/dsh-task-bus": "0.1.3",
656
655
  "@sparkelf/dsh-credential-manager": "0.1.3",
657
656
  "@sparkelf/dsh-dataops-account": "0.2.0",
658
657
  "@sparkelf/dsh-group-chat": "0.5.1",
@@ -667,14 +666,14 @@
667
666
  }
668
667
  },
669
668
  "dependencies": {
670
- "@deepseek-ai/dsh-computer-use": "workspace:>=0.2.2-alpha.2",
671
- "@deepseek-ai/dsh-experimental-computer-use-cua-driver-mcp": "workspace:>=0.2.2-alpha.2",
672
- "@deepseek-ai/dsh-tool-session-query": "workspace:>=0.2.2-alpha.2",
673
- "@deepseek-ai/dsh-web-search-exa": "workspace:>=0.1.6-alpha.1",
674
- "@sparkelf/dsh-client-ui-skill-center": "workspace:>=0.2.1-alpha.1",
669
+ "@deepseek-ai/dsh-computer-use": ">=0.2.2-alpha.2",
670
+ "@deepseek-ai/dsh-experimental-computer-use-cua-driver-mcp": ">=0.2.2-alpha.2",
671
+ "@deepseek-ai/dsh-tool-session-query": ">=0.2.2-alpha.2",
672
+ "@deepseek-ai/dsh-web-search-exa": ">=0.1.6-alpha.1",
673
+ "@sparkelf/dsh-client-ui-skill-center": ">=0.2.1-alpha.1",
675
674
  "@sparkelf/dsh-image-hoist": ">=0.1.3",
676
- "@sparkelf/dsh-plugin-backup": "workspace:>=0.2.1-alpha.1",
677
- "@sparkelf/dsh-plugin-mcp-credentials": "workspace:>=0.2.1-alpha.1",
675
+ "@sparkelf/dsh-plugin-backup": ">=0.2.1-alpha.1",
676
+ "@sparkelf/dsh-plugin-mcp-credentials": ">=0.2.1-alpha.1",
678
677
  "dshmarket": ">=1.66.7",
679
678
  "semver": ">=7.7.3",
680
679
  "yaml": ">=2.9.0"
@@ -684,8 +683,8 @@
684
683
  },
685
684
  "devDependencies": {
686
685
  "@changfenhuang/dsh-genui": "0.9.9",
687
- "@deepseek-ai/cordis": "workspace:^",
688
- "@types/semver": ">=7.7.1"
686
+ "@types/semver": ">=7.7.1",
687
+ "@deepseek-ai/cordis": "^4.0.2"
689
688
  },
690
689
  "bin": {
691
690
  "dsh-plus": "lib/bin.js"
@@ -693,6 +692,6 @@
693
692
  "optionalDependencies": {
694
693
  "@deepseek-ai/dsh-computer-use": ">=0.2.2-alpha.2",
695
694
  "@deepseek-ai/dsh-experimental-computer-use-cua-driver-mcp": ">=0.2.2-alpha.2",
696
- "@deepseek-ai/dsh-web-search-exa": "workspace:>=0.1.6-alpha.1"
695
+ "@deepseek-ai/dsh-web-search-exa": ">=0.1.6-alpha.1"
697
696
  }
698
- }
697
+ }