@huanlin/dsh-plugin-interpreters 0.2.3 → 0.4.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.
@@ -1,63 +1,63 @@
1
- /**
2
- * gateway.ts — host-side HTTP gateway exposing the `interpreters` config to
3
- * the browser through a self-hosted `/interpreters/api` route.
4
- *
5
- * The DSH typertGateway `/api` RPC dispatch was the original channel
6
- * (TypertRemoteService + @Remote), but the host's SRC discovery
7
- * (ctx.reflect.props enumeration) is not claiming plugin-owned service
8
- * endpoints on the current dsh snapshot. The self-hosted HTTP route
9
- * mirrors the better-sidebar pattern: `ctx.webServer.register` claims a
10
- * prefix route, the handler reads/writes the settings seam in-process
11
- * (no wire-layer allowlist gate), and the browser reaches it through
12
- * `fetch('/interpreters/api/<method>')`.
13
- *
14
- * Route shape:
15
- * POST /interpreters/api/get → { ok: true, value: { config: ResolvedConfig } }
16
- * POST /interpreters/api/set body: { patch: Partial<Config> }
17
- * → { ok: true, value: { config: ResolvedConfig } }
18
- * Errors carry { ok: false, error: { code, message } }.
19
- *
20
- * @module dsh-interpreters/gateway
21
- */
22
- import type { Context } from '@deepseek-ai/cordis';
23
- import type { Settings } from '@deepseek-ai/dsh-settings';
24
- import { type Config as ConfigType, type ResolvedConfig } from './config.js';
25
- import { type InterpretersSettingsBridge } from './settings.js';
26
- /** Wire view returned by both `get` and `set`: the fully-resolved config. */
27
- export interface InterpretersConfigView {
28
- config: ResolvedConfig;
29
- }
30
- /** Patch shape the `set` endpoint accepts (every field optional, null = clear). */
31
- export type InterpretersConfigPatch = Partial<ConfigType>;
32
- /**
33
- * Register the `/interpreters/api` HTTP route on the host's web server.
34
- *
35
- * The route reads/writes the `interpreters` settings namespace in-process
36
- * through the bridge + `ctx.settings`. The settings service is optional:
37
- * when absent, `get` degrades to the entry source and `set` returns a
38
- * clear error.
39
- * @param ctx - host context carrying `webServer`.
40
- * @param bridge - the settings bridge the route reads through.
41
- */
42
- export declare function registerHttpGateway(ctx: Context, bridge: InterpretersSettingsBridge): void;
43
- /**
44
- * Handle the `set` method: validate the patch, write the user layer, return
45
- * the new resolved config.
46
- * @param body - the parsed JSON body from the request.
47
- * @param settings - the live settings service (undefined when unavailable).
48
- * @param bridge - the settings bridge for reading the source.
49
- * @returns the new resolved config view.
50
- * @throws when the settings service is unavailable.
51
- */
52
- export declare function handleSet(body: unknown, settings: Settings | undefined, bridge: InterpretersSettingsBridge): Promise<InterpretersConfigView>;
53
- /**
54
- * Extract and validate the patch from the request body.
55
- *
56
- * JSON wire boundary: null = "delete" (filtered), undefined never crosses
57
- * JSON. Unknown keys are dropped (the settings service is non-strict and
58
- * would otherwise store them). Light type guards constrain paths to
59
- * strings and timeout to a finite number.
60
- * @param body - the parsed JSON body.
61
- * @returns the normalized patch (only known, well-typed keys).
62
- */
63
- export declare function extractPatch(body: unknown): Record<string, unknown>;
1
+ /**
2
+ * gateway.ts — host-side HTTP gateway exposing the `interpreters` config to
3
+ * the browser through a self-hosted `/interpreters/api` route.
4
+ *
5
+ * The DSH typertGateway `/api` RPC dispatch was the original channel
6
+ * (TypertRemoteService + @Remote), but the host's SRC discovery
7
+ * (ctx.reflect.props enumeration) is not claiming plugin-owned service
8
+ * endpoints on the current dsh snapshot. The self-hosted HTTP route
9
+ * mirrors the better-sidebar pattern: `ctx.webServer.register` claims a
10
+ * prefix route, the handler reads/writes the settings seam in-process
11
+ * (no wire-layer allowlist gate), and the browser reaches it through
12
+ * `fetch('/interpreters/api/<method>')`.
13
+ *
14
+ * Route shape:
15
+ * POST /interpreters/api/get → { ok: true, value: { config: ResolvedConfig } }
16
+ * POST /interpreters/api/set body: { patch: Partial<Config> }
17
+ * → { ok: true, value: { config: ResolvedConfig } }
18
+ * Errors carry { ok: false, error: { code, message } }.
19
+ *
20
+ * @module dsh-interpreters/gateway
21
+ */
22
+ import type { Context } from '@deepseek-ai/cordis';
23
+ import type { SettingsProvider } from '@deepseek-ai/dsh-settings';
24
+ import { type Config as ConfigType, type ResolvedConfig } from './config.js';
25
+ import { type InterpretersSettingsBridge } from './settings.js';
26
+ /** Wire view returned by both `get` and `set`: the fully-resolved config. */
27
+ export interface InterpretersConfigView {
28
+ config: ResolvedConfig;
29
+ }
30
+ /** Patch shape the `set` endpoint accepts (every field optional, null = clear). */
31
+ export type InterpretersConfigPatch = Partial<ConfigType>;
32
+ /**
33
+ * Register the `/interpreters/api` HTTP route on the host's web server.
34
+ *
35
+ * The route reads/writes the `interpreters` settings namespace in-process
36
+ * through the bridge + `ctx.settings`. The settings service is optional:
37
+ * when absent, `get` degrades to the entry source and `set` returns a
38
+ * clear error.
39
+ * @param ctx - host context carrying `webServer`.
40
+ * @param bridge - the settings bridge the route reads through.
41
+ */
42
+ export declare function registerHttpGateway(ctx: Context, bridge: InterpretersSettingsBridge): void;
43
+ /**
44
+ * Handle the `set` method: validate the patch, write the user layer, return
45
+ * the new resolved config.
46
+ * @param body - the parsed JSON body from the request.
47
+ * @param settings - the live settings service (undefined when unavailable).
48
+ * @param bridge - the settings bridge for reading the source.
49
+ * @returns the new resolved config view.
50
+ * @throws when the settings service is unavailable.
51
+ */
52
+ export declare function handleSet(body: unknown, settings: SettingsProvider | undefined, bridge: InterpretersSettingsBridge): Promise<InterpretersConfigView>;
53
+ /**
54
+ * Extract and validate the patch from the request body.
55
+ *
56
+ * JSON wire boundary: null = "delete" (filtered), undefined never crosses
57
+ * JSON. Unknown keys are dropped (the settings service is non-strict and
58
+ * would otherwise store them). Light type guards constrain paths to
59
+ * strings and timeout to a finite number.
60
+ * @param body - the parsed JSON body.
61
+ * @returns the normalized patch (only known, well-typed keys).
62
+ */
63
+ export declare function extractPatch(body: unknown): Record<string, unknown>;
@@ -1,43 +1,43 @@
1
- /**
2
- * settings.ts — host-side bridge between the `interpreters` settings namespace
3
- * and the plugin's other halves (tool registration + RPC gateway).
4
- *
5
- * The composition `Config` (cordis.patch.yml) is the first-boot seed; once the
6
- * `ctx.settings` service mounts, the user-editable layer takes over and live
7
- * re-registration follows every committed change. Headless assemblies without
8
- * a settings provider fall back to the composition config (no persistence, no
9
- * live reload).
10
- *
11
- * The bridge pattern mirrors `dsh-advisor/src/settings.ts`: a `source()` thunk
12
- * the gateway reads in-process, plus an `onChange()` subscription the host
13
- * entry uses to re-register the tools. This avoids any wire-layer allowlist
14
- * (the DSH settings RPC domain only serves a fixed namespace set to browser
15
- * configuration clients; the gateway bypasses it through `/api`).
16
- *
17
- * @module dsh-interpreters/settings
18
- */
19
- import type { Context } from '@deepseek-ai/cordis';
20
- import { type Config as ConfigType } from './config.js';
21
- /** Settings namespace under which interpreter paths persist. */
22
- export declare const SETTINGS_NAMESPACE: import("@deepseek-ai/dsh-settings").SettingsNamespace;
23
- /** Read face the gateway and tool re-registration consume. */
24
- export interface InterpretersSettingsBridge {
25
- /** The current resolved config (composition seed while settings is absent). */
26
- source(): ConfigType;
27
- /** Observe committed changes to the resolved config. */
28
- onChange(callback: () => void): void;
29
- }
30
- /**
31
- * Install the `interpreters` settings namespace and return the bridge.
32
- *
33
- * The settings service is reached through `ctx.inject(['settings'], ...)` so a
34
- * composition without a settings provider still loads the plugin (entry-source
35
- * fallback, no persistence). Multi-fiber dedupe is handled by catching the
36
- * `"already registered"` rejection — host composition may mount several
37
- * concurrent fibers of this plugin, and only the first registration owns the
38
- * namespace.
39
- * @param ctx - host context.
40
- * @param entry - composition-layer config (cordis.patch.yml seed).
41
- * @returns the bridge the gateway and tool re-registration consume.
42
- */
43
- export declare function installInterpretersSettings(ctx: Context, entry: ConfigType): InterpretersSettingsBridge;
1
+ /**
2
+ * settings.ts — host-side bridge between the `interpreters` settings namespace
3
+ * and the plugin's other halves (tool registration + RPC gateway).
4
+ *
5
+ * The composition `Config` (cordis.patch.yml) is the first-boot seed; once the
6
+ * `ctx.settings` service mounts, the user-editable layer takes over and live
7
+ * re-registration follows every committed change. Headless assemblies without
8
+ * a settings provider fall back to the composition config (no persistence, no
9
+ * live reload).
10
+ *
11
+ * The bridge pattern mirrors `dsh-advisor/src/settings.ts`: a `source()` thunk
12
+ * the gateway reads in-process, plus an `onChange()` subscription the host
13
+ * entry uses to re-register the tools. This avoids any wire-layer allowlist
14
+ * (the DSH settings RPC domain only serves a fixed namespace set to browser
15
+ * configuration clients; the gateway bypasses it through `/api`).
16
+ *
17
+ * @module dsh-interpreters/settings
18
+ */
19
+ import type { Context } from '@deepseek-ai/cordis';
20
+ import { type Config as ConfigType } from './config.js';
21
+ /** Settings namespace under which interpreter paths persist. */
22
+ export declare const SETTINGS_NAMESPACE: "interpreters";
23
+ /** Read face the gateway and tool re-registration consume. */
24
+ export interface InterpretersSettingsBridge {
25
+ /** The current resolved config (composition seed while settings is absent). */
26
+ source(): ConfigType;
27
+ /** Observe committed changes to the resolved config. */
28
+ onChange(callback: () => void): void;
29
+ }
30
+ /**
31
+ * Install the `interpreters` settings namespace and return the bridge.
32
+ *
33
+ * The settings service is reached through `ctx.inject(['settings'], ...)` so a
34
+ * composition without a settings provider still loads the plugin (entry-source
35
+ * fallback, no persistence). Multi-fiber dedupe is handled by catching the
36
+ * `"already registered"` rejection — host composition may mount several
37
+ * concurrent fibers of this plugin, and only the first registration owns the
38
+ * namespace.
39
+ * @param ctx - host context.
40
+ * @param entry - composition-layer config (cordis.patch.yml seed).
41
+ * @returns the bridge the gateway and tool re-registration consume.
42
+ */
43
+ export declare function installInterpretersSettings(ctx: Context, entry: ConfigType): InterpretersSettingsBridge;
@@ -1,83 +1,82 @@
1
- /**
2
- * settings.ts — host-side bridge between the `interpreters` settings namespace
3
- * and the plugin's other halves (tool registration + RPC gateway).
4
- *
5
- * The composition `Config` (cordis.patch.yml) is the first-boot seed; once the
6
- * `ctx.settings` service mounts, the user-editable layer takes over and live
7
- * re-registration follows every committed change. Headless assemblies without
8
- * a settings provider fall back to the composition config (no persistence, no
9
- * live reload).
10
- *
11
- * The bridge pattern mirrors `dsh-advisor/src/settings.ts`: a `source()` thunk
12
- * the gateway reads in-process, plus an `onChange()` subscription the host
13
- * entry uses to re-register the tools. This avoids any wire-layer allowlist
14
- * (the DSH settings RPC domain only serves a fixed namespace set to browser
15
- * configuration clients; the gateway bypasses it through `/api`).
16
- *
17
- * @module dsh-interpreters/settings
18
- */
19
- import { settingsNamespace } from '@deepseek-ai/dsh-settings';
20
- import { Config } from './config.js';
21
- /** Settings namespace under which interpreter paths persist. */
22
- export const SETTINGS_NAMESPACE = settingsNamespace('interpreters');
23
- /**
24
- * Mirror of the dsh-settings internal `isUnloading` guard. The cordis const
25
- * enum for fiber state is erased at compile time, so the literal states are
26
- * matched numerically: 4 = DISPOSED, 5 = UNLOADING.
27
- */
28
- function isUnloading(ctx) {
29
- const state = ctx.fiber?.state;
30
- return state === 4 || state === 5;
31
- }
32
- /**
33
- * Install the `interpreters` settings namespace and return the bridge.
34
- *
35
- * The settings service is reached through `ctx.inject(['settings'], ...)` so a
36
- * composition without a settings provider still loads the plugin (entry-source
37
- * fallback, no persistence). Multi-fiber dedupe is handled by catching the
38
- * `"already registered"` rejection host composition may mount several
39
- * concurrent fibers of this plugin, and only the first registration owns the
40
- * namespace.
41
- * @param ctx - host context.
42
- * @param entry - composition-layer config (cordis.patch.yml seed).
43
- * @returns the bridge the gateway and tool re-registration consume.
44
- */
45
- export function installInterpretersSettings(ctx, entry) {
46
- const listeners = new Set();
47
- let source = () => entry;
48
- const notify = () => {
49
- for (const listener of [...listeners])
50
- listener();
51
- };
52
- ctx.inject(['settings'], (sctx) => {
53
- let scope;
54
- try {
55
- scope = sctx.settings.register(SETTINGS_NAMESPACE, Config, { base: entry });
56
- }
57
- catch (error) {
58
- // Multi-fiber dedupe: the first registration owns the namespace; later
59
- // fibers stay on the entry source and emit no notifications of their own.
60
- if (!(error instanceof Error) || !error.message.includes('already registered'))
61
- throw error;
62
- ctx.logger('dsh-interpreters').debug('settings namespace already registered — entry-source fallback');
63
- return;
64
- }
65
- source = () => scope.get();
66
- sctx.effect(() => () => {
67
- if (isUnloading(ctx))
68
- return;
69
- source = () => entry;
70
- notify();
71
- });
72
- notify();
73
- scope.watch(() => {
74
- if (isUnloading(ctx))
75
- return;
76
- notify();
77
- });
78
- });
79
- return {
80
- source: () => source(),
81
- onChange: (cb) => { listeners.add(cb); },
82
- };
83
- }
1
+ /**
2
+ * settings.ts — host-side bridge between the `interpreters` settings namespace
3
+ * and the plugin's other halves (tool registration + RPC gateway).
4
+ *
5
+ * The composition `Config` (cordis.patch.yml) is the first-boot seed; once the
6
+ * `ctx.settings` service mounts, the user-editable layer takes over and live
7
+ * re-registration follows every committed change. Headless assemblies without
8
+ * a settings provider fall back to the composition config (no persistence, no
9
+ * live reload).
10
+ *
11
+ * The bridge pattern mirrors `dsh-advisor/src/settings.ts`: a `source()` thunk
12
+ * the gateway reads in-process, plus an `onChange()` subscription the host
13
+ * entry uses to re-register the tools. This avoids any wire-layer allowlist
14
+ * (the DSH settings RPC domain only serves a fixed namespace set to browser
15
+ * configuration clients; the gateway bypasses it through `/api`).
16
+ *
17
+ * @module dsh-interpreters/settings
18
+ */
19
+ import { Config } from './config.js';
20
+ /** Settings namespace under which interpreter paths persist. */
21
+ export const SETTINGS_NAMESPACE = 'interpreters';
22
+ /**
23
+ * Mirror of the dsh-settings internal `isUnloading` guard. The cordis const
24
+ * enum for fiber state is erased at compile time, so the literal states are
25
+ * matched numerically: 4 = DISPOSED, 5 = UNLOADING.
26
+ */
27
+ function isUnloading(ctx) {
28
+ const state = ctx.fiber?.state;
29
+ return state === 4 || state === 5;
30
+ }
31
+ /**
32
+ * Install the `interpreters` settings namespace and return the bridge.
33
+ *
34
+ * The settings service is reached through `ctx.inject(['settings'], ...)` so a
35
+ * composition without a settings provider still loads the plugin (entry-source
36
+ * fallback, no persistence). Multi-fiber dedupe is handled by catching the
37
+ * `"already registered"` rejection host composition may mount several
38
+ * concurrent fibers of this plugin, and only the first registration owns the
39
+ * namespace.
40
+ * @param ctx - host context.
41
+ * @param entry - composition-layer config (cordis.patch.yml seed).
42
+ * @returns the bridge the gateway and tool re-registration consume.
43
+ */
44
+ export function installInterpretersSettings(ctx, entry) {
45
+ const listeners = new Set();
46
+ let source = () => entry;
47
+ const notify = () => {
48
+ for (const listener of [...listeners])
49
+ listener();
50
+ };
51
+ ctx.inject(['settings'], (sctx) => {
52
+ let scope;
53
+ try {
54
+ scope = sctx.settings.register(SETTINGS_NAMESPACE, Config, { base: entry });
55
+ }
56
+ catch (error) {
57
+ // Multi-fiber dedupe: the first registration owns the namespace; later
58
+ // fibers stay on the entry source and emit no notifications of their own.
59
+ if (!(error instanceof Error) || !error.message.includes('already registered'))
60
+ throw error;
61
+ ctx.logger('dsh-interpreters').debug('settings namespace already registered — entry-source fallback');
62
+ return;
63
+ }
64
+ source = () => scope.get();
65
+ sctx.effect(() => () => {
66
+ if (isUnloading(ctx))
67
+ return;
68
+ source = () => entry;
69
+ notify();
70
+ });
71
+ notify();
72
+ scope.watch(() => {
73
+ if (isUnloading(ctx))
74
+ return;
75
+ notify();
76
+ });
77
+ });
78
+ return {
79
+ source: () => source(),
80
+ onChange: (cb) => { listeners.add(cb); },
81
+ };
82
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@huanlin/dsh-plugin-interpreters",
3
- "version": "0.2.3",
3
+ "version": "0.4.0",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -33,26 +33,26 @@
33
33
  },
34
34
  "client": {
35
35
  "inject": [
36
- "@deepseek-ai/dsh-client-runtime",
37
36
  "@deepseek-ai/dsh-client-locale",
38
37
  "@deepseek-ai/dsh-client-connection",
39
- "@deepseek-ai/dsh-client-ui-settings-plugins",
40
- "@deepseek-ai/dsh-client-ui-slots"
38
+ "@deepseek-ai/dsh-client-ui-renderer",
39
+ "@deepseek-ai/dsh-client-ui-settings-plugins"
41
40
  ],
42
41
  "platform": "web"
43
42
  }
44
43
  },
45
44
  "peerDependencies": {
46
- "@deepseek-ai/cordis": "^4.0.1-rc.1",
47
- "@deepseek-ai/dsh-client-connection": "^0.1.0-rc.8",
48
- "@deepseek-ai/dsh-client-locale": "^0.1.0-rc.8",
49
- "@deepseek-ai/dsh-client-runtime": "^0.1.0-rc.8",
50
- "@deepseek-ai/dsh-client-ui-settings-plugins": "^0.1.0-rc.8",
51
- "@deepseek-ai/dsh-client-ui-primitives": "^0.1.0-rc.8",
52
- "@deepseek-ai/dsh-client-ui-slots": "^0.1.0-rc.8",
53
- "@deepseek-ai/dsh-host-webserver": "^0.1.0-rc.8",
54
- "@deepseek-ai/dsh-settings": "^0.1.0-rc.8",
55
- "@deepseek-ai/dsh-tools": "^0.1.0-rc.8",
45
+ "@deepseek-ai/cordis": "^4.0.1",
46
+ "@deepseek-ai/dsh-client-connection": "^0.1.5-rc.1",
47
+ "@deepseek-ai/dsh-client-locale": "^0.1.5-rc.1",
48
+ "@deepseek-ai/dsh-client-store": "^0.1.5-rc.1",
49
+ "@deepseek-ai/dsh-client-ui-primitives": "^0.1.5-rc.1",
50
+ "@deepseek-ai/dsh-client-ui-renderer": "^0.1.5-rc.1",
51
+ "@deepseek-ai/dsh-client-ui-settings-plugins": "^0.1.5-rc.1",
52
+ "@deepseek-ai/dsh-client-ui-slots": "^0.1.5-rc.1",
53
+ "@deepseek-ai/dsh-host-webserver": "^0.1.5-rc.1",
54
+ "@deepseek-ai/dsh-settings": "^0.1.5-rc.1",
55
+ "@deepseek-ai/dsh-tools": "^0.1.5-rc.1",
56
56
  "react": "^18.2.0",
57
57
  "schemastery": "^3.18.0"
58
58
  },
@@ -66,7 +66,10 @@
66
66
  "@deepseek-ai/dsh-client-locale": {
67
67
  "optional": true
68
68
  },
69
- "@deepseek-ai/dsh-client-runtime": {
69
+ "@deepseek-ai/dsh-client-store": {
70
+ "optional": true
71
+ },
72
+ "@deepseek-ai/dsh-client-ui-renderer": {
70
73
  "optional": true
71
74
  },
72
75
  "@deepseek-ai/dsh-client-ui-settings-plugins": {
@@ -95,20 +98,17 @@
95
98
  }
96
99
  },
97
100
  "devDependencies": {
98
- "@deepseek-ai/cordis": "^4.0.1-rc.1",
99
- "@deepseek-ai/dsh-client-connection": "^0.1.0-rc.8",
100
- "@deepseek-ai/dsh-client-locale": "^0.1.0-rc.8",
101
- "@deepseek-ai/dsh-client-runtime": "^0.1.0-rc.8",
102
- "@deepseek-ai/dsh-client-ui-settings-plugins": "^0.1.0-rc.8",
103
- "@deepseek-ai/dsh-client-ui-primitives": "^0.1.0-rc.8",
104
- "@deepseek-ai/dsh-client-ui-slots": "^0.1.0-rc.8",
105
- "@deepseek-ai/dsh-host-webserver": "^0.1.0-rc.8",
106
- "@deepseek-ai/dsh-settings": "^0.1.0-rc.8",
107
- "@deepseek-ai/dsh-tools": "^0.1.0-rc.8",
101
+ "@deepseek-ai/cordis": "link:D:/Projects/deepseek-harness/dsh/vendor/cordis",
102
+ "@deepseek-ai/dsh-client-store": "link:D:/Projects/deepseek-harness/dsh/packages/client/store",
103
+ "@deepseek-ai/dsh-client-ui-primitives": "link:D:/Projects/deepseek-harness/dsh/packages/client/ui-primitives",
104
+ "@deepseek-ai/dsh-client-ui-slots": "link:D:/Projects/deepseek-harness/dsh/packages/client/ui-slots",
105
+ "@deepseek-ai/dsh-settings": "link:D:/Projects/deepseek-harness/dsh/packages/settings/settings",
106
+ "@deepseek-ai/dsh-tools": "link:D:/Projects/deepseek-harness/dsh/packages/core/tools",
108
107
  "@types/node": "^22.20.0",
109
108
  "@types/react": "~18.3.1",
110
109
  "esbuild": "^0.28.2",
111
110
  "lightningcss": "^1.29.2",
111
+ "schemastery": "^3.18.0",
112
112
  "tsdown": "^0.22.2",
113
113
  "typescript": "^5.9.0",
114
114
  "use-sync-external-store": "^1.2.0",