@dsh-plugin/dsh-loader 1.0.0

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.
package/src/setup.mjs ADDED
@@ -0,0 +1,125 @@
1
+ // One-shot profile injection script (design.md §3.6 / §6 / M6).
2
+ //
3
+ // `dshloader setup <profile>`:
4
+ // - adds `@dsh-plugin/dsh-loader` to the profile package.json dependencies
5
+ // (if missing);
6
+ // - appends the dshloader `insert` entry to cordis.patch.yml (if missing);
7
+ // - does NOT reorder the insert list — cordis is reactive DI, so position
8
+ // does not affect whether service aliases / module redirects take effect
9
+ // (design.md §1.2 / §6.2).
10
+ //
11
+ // `dshloader dump-config <profile>`: best-effort validation that runs
12
+ // `dsh --profile <name> --dump-config` when the dsh CLI is available.
13
+ //
14
+ // `dshloader info [profile]`: prints dshloader version, detected dsh version,
15
+ // selected adapter, and registered aliases (AC-OB-03, P2 — minimal).
16
+ import { existsSync, readFileSync, writeFileSync } from 'node:fs';
17
+ import { join, resolve } from 'node:path';
18
+ import { spawnSync } from 'node:child_process';
19
+ import { LOADER_VERSION, LOG_PREFIX } from './version.js';
20
+ import { detectDshVersion, AdapterRegistry, UnsupportedDshVersionError } from './registry.js';
21
+ import { registerHostAdapters } from './adapters/index.js';
22
+
23
+ const LOADER_PKG = '@dsh-plugin/dsh-loader';
24
+ const PATCH_ENTRY = `- id: dsh-loader\n name: '${LOADER_PKG}'`;
25
+
26
+ export function dshHome() {
27
+ const env = process.env.DSH_HOME?.trim();
28
+ return env ? resolve(env) : join(process.env.HOME ?? '~', '.dsh');
29
+ }
30
+
31
+ export function profileDir(profileName) {
32
+ return join(dshHome(), 'profiles', profileName);
33
+ }
34
+
35
+ function readJson(path) {
36
+ try {
37
+ return JSON.parse(readFileSync(path, 'utf8'));
38
+ } catch {
39
+ return null;
40
+ }
41
+ }
42
+
43
+ function writeJson(path, data) {
44
+ writeFileSync(path, JSON.stringify(data, undefined, 2) + '\n');
45
+ }
46
+
47
+ /** Ensure dshloader is listed in the profile package.json dependencies. */
48
+ export function injectDependency(pkgPath) {
49
+ const manifest = readJson(pkgPath) ?? { name: '', dependencies: {} };
50
+ manifest.dependencies = manifest.dependencies ?? {};
51
+ if (manifest.dependencies[LOADER_PKG] === undefined) {
52
+ manifest.dependencies[LOADER_PKG] = '^' + LOADER_VERSION;
53
+ writeJson(pkgPath, manifest);
54
+ return { added: true, manifest };
55
+ }
56
+ return { added: false, manifest };
57
+ }
58
+
59
+ /** Ensure the dshloader insert entry exists in cordis.patch.yml (no reorder). */
60
+ export function injectPatch(patchPath) {
61
+ let text = '';
62
+ try {
63
+ text = readFileSync(patchPath, 'utf8');
64
+ } catch {
65
+ text = '';
66
+ }
67
+ if (text.includes("id: dsh-loader")) {
68
+ return { added: false, text };
69
+ }
70
+ const insertion = `- insert:\n ${PATCH_ENTRY}\n`;
71
+ const next = text.length === 0 ? insertion : text.endsWith('\n') ? text + insertion : text + '\n' + insertion;
72
+ writeFileSync(patchPath, next);
73
+ return { added: true, text: next };
74
+ }
75
+
76
+ /**
77
+ * Run `dshloader setup <profile>`.
78
+ * @param {string} profileName
79
+ * @returns {{ profileDir: string, dependencyAdded: boolean, patchAdded: boolean }}
80
+ */
81
+ export function setupProfile(profileName) {
82
+ const dir = profileDir(profileName);
83
+ if (!existsSync(dir)) {
84
+ throw new Error(`${LOG_PREFIX} profile directory not found: ${dir}`);
85
+ }
86
+ const pkgPath = join(dir, 'package.json');
87
+ const patchPath = join(dir, 'cordis.patch.yml');
88
+ const dep = injectDependency(pkgPath);
89
+ const patch = injectPatch(patchPath);
90
+ console.log(`${LOG_PREFIX} setup ${profileName}: dependency ${dep.added ? 'added' : 'already present'}, patch ${patch.added ? 'appended' : 'already present'}`);
91
+ console.log(`${LOG_PREFIX} note: insert position is irrelevant — cordis reactive DI resolves aliases regardless of order (design.md §6.2)`);
92
+ return { profileDir: dir, dependencyAdded: dep.added, patchAdded: patch.added };
93
+ }
94
+
95
+ /** Best-effort dump-config validation. Returns { ok, output } (never throws). */
96
+ export function dumpConfig(profileName) {
97
+ const dir = profileDir(profileName);
98
+ if (!existsSync(dir)) {
99
+ return { ok: false, output: `${LOG_PREFIX} profile directory not found: ${dir}` };
100
+ }
101
+ const result = spawnSync('dsh', ['--profile', profileName, '--dump-config'], {
102
+ encoding: 'utf8',
103
+ timeout: 60_000,
104
+ });
105
+ const output = (result.stdout ?? '') + (result.stderr ?? '');
106
+ return { ok: result.status === 0, output };
107
+ }
108
+
109
+ /** Print dshloader status for a profile (AC-OB-03, minimal). */
110
+ export function info(profileName) {
111
+ console.log(`${LOG_PREFIX} version ${LOADER_VERSION}`);
112
+ const dir = profileName ? profileDir(profileName) : undefined;
113
+ const dshVersion = detectDshVersion(dir ? { profileDir: dir } : {});
114
+ console.log(`${LOG_PREFIX} detected dsh version: ${dshVersion ?? '<unknown>'}`);
115
+ if (dshVersion) {
116
+ try {
117
+ const reg = registerHostAdapters(new AdapterRegistry());
118
+ const { factory, mode } = reg.select(dshVersion);
119
+ console.log(`${LOG_PREFIX} selected adapter: ${factory.name} (supports ${factory.supports}, mode ${mode})`);
120
+ } catch (error) {
121
+ console.log(`${LOG_PREFIX} adapter selection: ${error.message}`);
122
+ }
123
+ }
124
+ return { loaderVersion: LOADER_VERSION, dshVersion };
125
+ }
@@ -0,0 +1 @@
1
+ export * from '@deepseek-ai/dsh-agent';
@@ -0,0 +1,2 @@
1
+ // Stable re-export: @dsh-plugin/dsh-loader/agent
2
+ export * from '@deepseek-ai/dsh-agent';
@@ -0,0 +1 @@
1
+ export * from '@deepseek-ai/dsh-llm';
@@ -0,0 +1,2 @@
1
+ // Stable re-export: @dsh-plugin/dsh-loader/llm
2
+ export * from '@deepseek-ai/dsh-llm';
@@ -0,0 +1 @@
1
+ export * from '@deepseek-ai/dsh-client-runtime/client';
@@ -0,0 +1,5 @@
1
+ // Stable re-export: @dsh-plugin/dsh-loader/runtime
2
+ //
3
+ // Maps to @deepseek-ai/dsh-client-runtime/client — the public client
4
+ // entry point of the dsh client runtime.
5
+ export * from '@deepseek-ai/dsh-client-runtime/client';
@@ -0,0 +1 @@
1
+ export * from '@deepseek-ai/dsh-client-schema-form';
@@ -0,0 +1,2 @@
1
+ // Stable re-export: @dsh-plugin/dsh-loader/schema-form
2
+ export * from '@deepseek-ai/dsh-client-schema-form';
@@ -0,0 +1 @@
1
+ export * from '@deepseek-ai/dsh-settings';
@@ -0,0 +1,2 @@
1
+ // Stable re-export: @dsh-plugin/dsh-loader/settings
2
+ export * from '@deepseek-ai/dsh-settings';
@@ -0,0 +1 @@
1
+ export * from '@deepseek-ai/dsh-tools';
@@ -0,0 +1,5 @@
1
+ // Stable re-export: @dsh-plugin/dsh-loader/tools
2
+ //
3
+ // Host-side tools API. Plugins import from this subpath instead of
4
+ // directly from @deepseek-ai/dsh-tools.
5
+ export * from '@deepseek-ai/dsh-tools';
@@ -0,0 +1,4 @@
1
+ // Stable subpath types: @dsh-plugin/dsh-loader/ui-primitives
2
+ // Re-export the real package's types so TS consumers (and this plugin's
3
+ // client bundle) stay typed without importing @deepseek-ai/* directly.
4
+ export * from '@deepseek-ai/dsh-client-ui-primitives';
@@ -0,0 +1,6 @@
1
+ // Stable re-export: @dsh-plugin/dsh-loader/ui-primitives
2
+ //
3
+ // Plugins import UI primitives from this subpath instead of directly
4
+ // from @deepseek-ai/dsh-client-ui-primitives. When dsh renames the
5
+ // package, only this file (and the adapter's packageAliases) change.
6
+ export * from '@deepseek-ai/dsh-client-ui-primitives';
@@ -0,0 +1,6 @@
1
+ // Stable subpath types: @dsh-plugin/dsh-loader/ui-settings
2
+ // Side-effect import triggers the dsh-client-ui-settings/client SlotMap
3
+ // merge (adds 'settings.section') so PropsRuntime<'settings.section'>
4
+ // stays typed. Re-export keeps values/types reachable.
5
+ import '@deepseek-ai/dsh-client-ui-settings/client';
6
+ export * from '@deepseek-ai/dsh-client-ui-settings/client';
@@ -0,0 +1,4 @@
1
+ // Stable re-export: @dsh-plugin/dsh-loader/ui-settings
2
+ // Maps to dsh-client-ui-settings/client — the browser entry that owns the
3
+ // settings.section SlotMap contract (see ui-settings.d.ts).
4
+ export * from '@deepseek-ai/dsh-client-ui-settings/client';
@@ -0,0 +1 @@
1
+ export * from '@deepseek-ai/dsh-client-ui-slots';
@@ -0,0 +1,2 @@
1
+ // Stable re-export: @dsh-plugin/dsh-loader/ui-slots
2
+ export * from '@deepseek-ai/dsh-client-ui-slots';
@@ -0,0 +1 @@
1
+ export * from '@deepseek-ai/dsh-client-web-react';
@@ -0,0 +1,2 @@
1
+ // Stable re-export: @dsh-plugin/dsh-loader/web-react
2
+ export * from '@deepseek-ai/dsh-client-web-react';
package/src/version.js ADDED
@@ -0,0 +1,5 @@
1
+ // dshloader version — keep in sync with package.json.
2
+ export const LOADER_VERSION = '1.0.0';
3
+
4
+ // Prefix used in every console log / error so users can grep dshloader output.
5
+ export const LOG_PREFIX = '[dshloader]';