@ddtcorex/dsh-maestro-config 0.1.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/lib/index.d.ts ADDED
@@ -0,0 +1,12 @@
1
+ import type { Context } from '@deepseek-ai/cordis';
2
+ import { type MaestroConfigService } from './service.ts';
3
+ export declare const name = "maestro-config";
4
+ export declare const inject: string[];
5
+ declare module '@deepseek-ai/cordis' {
6
+ interface Context {
7
+ maestroConfig: MaestroConfigService;
8
+ }
9
+ }
10
+ /** Publish maestroConfig over the shared store + loopback RPC for clients. */
11
+ export declare function apply(ctx: Context): void;
12
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/host/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAElD,OAAO,EAA8B,KAAK,oBAAoB,EAAE,MAAM,cAAc,CAAA;AAEpF,eAAO,MAAM,IAAI,mBAAmB,CAAA;AACpC,eAAO,MAAM,MAAM,UAAiB,CAAA;AAIpC,OAAO,QAAQ,qBAAqB,CAAC;IACnC,UAAU,OAAO;QACf,aAAa,EAAE,oBAAoB,CAAA;KACpC;CACF;AAmBD,8EAA8E;AAC9E,wBAAgB,KAAK,CAAC,GAAG,EAAE,OAAO,GAAG,IAAI,CAuBxC"}
package/lib/index.js ADDED
@@ -0,0 +1,44 @@
1
+ import { createMaestroConfigService } from "./service.js";
2
+ export const name = 'maestro-config';
3
+ export const inject = ['connection'];
4
+ const RPC_CHANNEL = '/dsh-maestro-config';
5
+ function ok(value) {
6
+ return { ok: true, value };
7
+ }
8
+ function fail(message) {
9
+ return {
10
+ ok: false,
11
+ error: {
12
+ code: 'bad-request',
13
+ message,
14
+ // Synthetic details: app-level validation error shoehorned into DSH's
15
+ // shared RPC error taxonomy (same approach across maestro packages).
16
+ details: { issues: [{ message }] },
17
+ },
18
+ };
19
+ }
20
+ /** Publish maestroConfig over the shared store + loopback RPC for clients. */
21
+ export function apply(ctx) {
22
+ const svc = createMaestroConfigService();
23
+ ctx.provide('maestroConfig', svc);
24
+ ctx.effect(() => ctx.connection.rpc.handle(RPC_CHANNEL, async (endpoint, payload) => {
25
+ const body = (payload ?? {});
26
+ if (endpoint === 'list') {
27
+ return ok({ domains: await svc.listDomains() });
28
+ }
29
+ if (endpoint === 'get') {
30
+ if (typeof body.domain !== 'string')
31
+ return fail('domain (string) is required');
32
+ return ok(await svc.get(body.domain));
33
+ }
34
+ if (endpoint === 'set') {
35
+ if (typeof body.domain !== 'string' || typeof body.patch !== 'object' || body.patch === null) {
36
+ return fail('domain (string) and patch (object) are required');
37
+ }
38
+ await svc.set(body.domain, body.patch);
39
+ return ok(null);
40
+ }
41
+ return fail(`unknown endpoint: ${String(endpoint)}`);
42
+ }, { authority: 'loopback' }));
43
+ }
44
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/host/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,0BAA0B,EAA6B,MAAM,cAAc,CAAA;AAEpF,MAAM,CAAC,MAAM,IAAI,GAAG,gBAAgB,CAAA;AACpC,MAAM,CAAC,MAAM,MAAM,GAAG,CAAC,YAAY,CAAC,CAAA;AAEpC,MAAM,WAAW,GAAG,qBAAqB,CAAA;AAQzC,SAAS,EAAE,CAAI,KAAQ;IACrB,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAA;AAC5B,CAAC;AAED,SAAS,IAAI,CAAC,OAAe;IAC3B,OAAO;QACL,EAAE,EAAE,KAAK;QACT,KAAK,EAAE;YACL,IAAI,EAAE,aAAa;YACnB,OAAO;YACP,sEAAsE;YACtE,qEAAqE;YACrE,OAAO,EAAE,EAAE,MAAM,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAuC;SACxE;KACF,CAAA;AACH,CAAC;AAED,8EAA8E;AAC9E,MAAM,UAAU,KAAK,CAAC,GAAY;IAChC,MAAM,GAAG,GAAG,0BAA0B,EAAE,CAAA;IACxC,GAAG,CAAC,OAAO,CAAC,eAAe,EAAE,GAAG,CAAC,CAAA;IACjC,GAAG,CAAC,MAAM,CAAC,GAAG,EAAE,CACd,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,MAAM,CAAC,WAAW,EAAE,KAAK,EAAE,QAAgB,EAAE,OAAgB,EAA+B,EAAE;QAC/G,MAAM,IAAI,GAAG,CAAC,OAAO,IAAI,EAAE,CAAwC,CAAA;QACnE,IAAI,QAAQ,KAAK,MAAM,EAAE,CAAC;YACxB,OAAO,EAAE,CAAC,EAAE,OAAO,EAAE,MAAM,GAAG,CAAC,WAAW,EAAE,EAAE,CAAC,CAAA;QACjD,CAAC;QACD,IAAI,QAAQ,KAAK,KAAK,EAAE,CAAC;YACvB,IAAI,OAAO,IAAI,CAAC,MAAM,KAAK,QAAQ;gBAAE,OAAO,IAAI,CAAC,6BAA6B,CAAC,CAAA;YAC/E,OAAO,EAAE,CAAC,MAAM,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAA;QACvC,CAAC;QACD,IAAI,QAAQ,KAAK,KAAK,EAAE,CAAC;YACvB,IAAI,OAAO,IAAI,CAAC,MAAM,KAAK,QAAQ,IAAI,OAAO,IAAI,CAAC,KAAK,KAAK,QAAQ,IAAI,IAAI,CAAC,KAAK,KAAK,IAAI,EAAE,CAAC;gBAC7F,OAAO,IAAI,CAAC,iDAAiD,CAAC,CAAA;YAChE,CAAC;YACD,MAAM,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,KAAK,CAAC,CAAA;YACtC,OAAO,EAAE,CAAC,IAAI,CAAC,CAAA;QACjB,CAAC;QACD,OAAO,IAAI,CAAC,qBAAqB,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAA;IACtD,CAAC,EAAE,EAAE,SAAS,EAAE,UAAU,EAAE,CAAC,CAC9B,CAAA;AACH,CAAC"}
@@ -0,0 +1,16 @@
1
+ export interface MaestroConfigService {
2
+ /** Union of schema-registered domains and domains present in the store file. */
3
+ listDomains(): Promise<string[]>;
4
+ get(domain: string): Promise<unknown>;
5
+ /** Deep-merges the patch into the domain (same semantics as the lib). */
6
+ set(domain: string, patch: object): Promise<void>;
7
+ onChange(cb: (domain: string) => void): () => void;
8
+ }
9
+ /**
10
+ * Thin facade over @ddtcorex/dsh-maestro-config-lib. `dshHome` defaults to the
11
+ * lib's resolution (explicit > DSH_HOME env > ~/.dsh); tests inject a tmpdir.
12
+ */
13
+ export declare function createMaestroConfigService(opts?: {
14
+ dshHome?: string;
15
+ }): MaestroConfigService;
16
+ //# sourceMappingURL=service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../src/host/service.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,oBAAoB;IACnC,gFAAgF;IAChF,WAAW,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC,CAAA;IAChC,GAAG,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;IACrC,yEAAyE;IACzE,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACjD,QAAQ,CAAC,EAAE,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,GAAG,MAAM,IAAI,CAAA;CACnD;AAED;;;GAGG;AACH,wBAAgB,0BAA0B,CAAC,IAAI,CAAC,EAAE;IAAE,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,oBAAoB,CAiB5F"}
package/lib/service.js ADDED
@@ -0,0 +1,24 @@
1
+ import * as lib from '@ddtcorex/dsh-maestro-config-lib';
2
+ /**
3
+ * Thin facade over @ddtcorex/dsh-maestro-config-lib. `dshHome` defaults to the
4
+ * lib's resolution (explicit > DSH_HOME env > ~/.dsh); tests inject a tmpdir.
5
+ */
6
+ export function createMaestroConfigService(opts) {
7
+ const libOpts = opts?.dshHome ? { dshHome: opts.dshHome } : undefined;
8
+ return {
9
+ async listDomains() {
10
+ const doc = await lib.load(libOpts);
11
+ return [...new Set([...lib.definedDomains(), ...Object.keys(doc.domains)])];
12
+ },
13
+ async get(domain) {
14
+ return lib.get(domain, libOpts);
15
+ },
16
+ async set(domain, patch) {
17
+ await lib.set(domain, patch, libOpts);
18
+ },
19
+ onChange(cb) {
20
+ return lib.onChange(cb);
21
+ },
22
+ };
23
+ }
24
+ //# sourceMappingURL=service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service.js","sourceRoot":"","sources":["../src/host/service.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,GAAG,MAAM,kCAAkC,CAAA;AAWvD;;;GAGG;AACH,MAAM,UAAU,0BAA0B,CAAC,IAA2B;IACpE,MAAM,OAAO,GAAG,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,SAAS,CAAA;IACrE,OAAO;QACL,KAAK,CAAC,WAAW;YACf,MAAM,GAAG,GAAG,MAAM,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;YACnC,OAAO,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,GAAG,CAAC,cAAc,EAAE,EAAE,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAA;QAC7E,CAAC;QACD,KAAK,CAAC,GAAG,CAAC,MAAM;YACd,OAAO,GAAG,CAAC,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;QACjC,CAAC;QACD,KAAK,CAAC,GAAG,CAAC,MAAM,EAAE,KAAK;YACrB,MAAM,GAAG,CAAC,GAAG,CAAC,MAAM,EAAE,KAAK,EAAE,OAAO,CAAC,CAAA;QACvC,CAAC;QACD,QAAQ,CAAC,EAAE;YACT,OAAO,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAA;QACzB,CAAC;KACF,CAAA;AACH,CAAC"}
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Shared between the Settings card and the host endpoint names. The granular
3
+ * dsh-maestro-review settings-rpc row implements this exact surface on the
4
+ * canonical channel.
5
+ */
6
+ export declare const MAESTRO_RPC_CHANNEL = "/dsh-maestro-review";
7
+ export declare const MAESTRO_ENDPOINTS: Readonly<{
8
+ status: "maestro.status";
9
+ getConfig: "maestro.getConfig";
10
+ saveConfig: "maestro.saveConfig";
11
+ tunnelStart: "maestro.tunnelStart";
12
+ tunnelStop: "maestro.tunnelStop";
13
+ proxyStatus: "maestro.proxyStatus";
14
+ getPin: "maestro.getPin";
15
+ rotatePin: "maestro.rotatePin";
16
+ lanPinStatus: "maestro.lanPin.status";
17
+ lanPinSetEnabled: "maestro.lanPin.setEnabled";
18
+ lanPinRotate: "maestro.lanPin.rotate";
19
+ reviewsList: "maestro.reviews.list";
20
+ modelsList: "maestro.models.list";
21
+ modelsCurrent: "maestro.models.current";
22
+ }>;
23
+ //# sourceMappingURL=api.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../../src/client/api.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,wBAAwB,CAAA;AAExD,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;EAe5B,CAAA"}
@@ -0,0 +1,7 @@
1
+ interface ClientCtx {
2
+ get?(name: string): unknown;
3
+ effect(fn: () => () => void, label?: string): unknown;
4
+ }
5
+ export declare function apply(ctx: ClientCtx): void;
6
+ export {};
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/client/index.tsx"],"names":[],"mappings":"AAuCA,UAAU,SAAS;IACjB,GAAG,CAAC,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAA;IAC3B,MAAM,CAAC,EAAE,EAAE,MAAM,MAAM,IAAI,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAA;CACtD;AAaD,wBAAgB,KAAK,CAAC,GAAG,EAAE,SAAS,GAAG,IAAI,CAuB1C"}
@@ -0,0 +1,9 @@
1
+ export function MaestroSettingsTab({ rpcCall }: {
2
+ rpcCall: any;
3
+ }): import("react").DetailedReactHTMLElement<{
4
+ 'data-maestro-settings-card': string;
5
+ style: {
6
+ maxWidth: number;
7
+ };
8
+ }, HTMLElement>;
9
+ //# sourceMappingURL=maestro-card.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"maestro-card.d.ts","sourceRoot":"","sources":["../../../src/client/maestro-card.jsx"],"names":[],"mappings":"AA6bA;;;;;;;gBAmPC"}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Mark this plugin's row in the DSH settings navigation so a style block can
3
+ * replace the shell's fallback gear with the maestro glyph.
4
+ *
5
+ * DSH 0.1.x projects only `id`, `order`, and `label` from a `settings.section`
6
+ * registration and picks icons from a closed built-in list, so each external
7
+ * plugin identifies its own localized row by visible text after the dialog
8
+ * mounts. The marker owns no shell structure and is removed on disposal.
9
+ */
10
+ export declare const SETTINGS_NAV_MARKER = "data-maestro-settings-nav";
11
+ /** Minimal DOM surface used here; tests inject a stub instead of `document`. */
12
+ /** The two DOM iteration shapes this module touches (querySelector results). */
13
+ type NodeSeq = Iterable<Element> & {
14
+ forEach(fn: (el: Element) => void): unknown;
15
+ };
16
+ interface DomScope {
17
+ querySelectorAll(selector: string): NodeSeq;
18
+ }
19
+ export declare function registerSettingsNavIcon(label: () => string, root?: DomScope): () => void;
20
+ export {};
21
+ //# sourceMappingURL=settings-nav-icon.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"settings-nav-icon.d.ts","sourceRoot":"","sources":["../../../src/client/settings-nav-icon.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,eAAO,MAAM,mBAAmB,8BAA8B,CAAA;AAE9D,gFAAgF;AAChF,gFAAgF;AAChF,KAAK,OAAO,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG;IAAE,OAAO,CAAC,EAAE,EAAE,CAAC,EAAE,EAAE,OAAO,KAAK,IAAI,GAAG,OAAO,CAAA;CAAE,CAAA;AAElF,UAAU,QAAQ;IAChB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAA;CAC5C;AAOD,wBAAgB,uBAAuB,CACrC,KAAK,EAAE,MAAM,MAAM,EACnB,IAAI,CAAC,EAAE,QAAQ,GACd,MAAM,IAAI,CAuCZ"}
@@ -0,0 +1,5 @@
1
+ /** Generate an opaque 256-bit secret suitable for GitLab's webhook token. */
2
+ export declare function generateWebhookSecret(randomValues?: <T extends ArrayBufferView>(array: T) => T): string;
3
+ /** GitLab's Merge Request webhook endpoint for the configured public hostname. */
4
+ export declare function gitlabWebhookUrl(hostname?: string): string;
5
+ //# sourceMappingURL=webhook-secret.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"webhook-secret.d.ts","sourceRoot":"","sources":["../../../src/client/webhook-secret.ts"],"names":[],"mappings":"AAEA,6EAA6E;AAC7E,wBAAgB,qBAAqB,CAAC,YAAY,6CAAsC,UAGvF;AAED,kFAAkF;AAClF,wBAAgB,gBAAgB,CAAC,QAAQ,CAAC,EAAE,MAAM,UAGjD"}
package/package.json ADDED
@@ -0,0 +1,65 @@
1
+ {
2
+ "name": "@ddtcorex/dsh-maestro-config",
3
+ "version": "0.1.0",
4
+ "description": "Maestro Config — shared settings service for the dsh-maestro-* suite over the single namespaced store (~/.dsh/maestro/settings.json)",
5
+ "type": "module",
6
+ "main": "./lib/index.js",
7
+ "types": "./lib/index.d.ts",
8
+ "exports": {
9
+ ".": "./lib/index.js",
10
+ "./client": {
11
+ "types": "./lib/types/client/index.d.ts",
12
+ "default": "./lib/client.js"
13
+ },
14
+ "./package.json": "./package.json"
15
+ },
16
+ "files": [
17
+ "lib",
18
+ "src",
19
+ "cordis.patch.yml",
20
+ "README.md"
21
+ ],
22
+ "dsh": {
23
+ "bundle": {
24
+ "patch": "./cordis.patch.yml"
25
+ },
26
+ "client": {
27
+ "platform": "web",
28
+ "inject": [
29
+ "@deepseek-ai/dsh-client-runtime",
30
+ "@deepseek-ai/dsh-client-ui-slots"
31
+ ]
32
+ }
33
+ },
34
+ "peerDependencies": {
35
+ "@deepseek-ai/cordis": "^4.0.1",
36
+ "@deepseek-ai/dsh-client-connection": "0.1.0-rc.8",
37
+ "@deepseek-ai/dsh-host-apiproxy": "0.1.0-rc.8"
38
+ },
39
+ "dependencies": {
40
+ "@ddtcorex/dsh-maestro-config-lib": "^0.1.0"
41
+ },
42
+ "devDependencies": {
43
+ "@deepseek-ai/cordis": "^4.0.1",
44
+ "@deepseek-ai/dsh-client-connection": "0.1.0-rc.8",
45
+ "@deepseek-ai/dsh-host-apiproxy": "0.1.0-rc.8",
46
+ "@types/node": "^22.0.0",
47
+ "@types/qrcode": "^1.5.6",
48
+ "@types/react": "~18.3.1",
49
+ "@types/react-dom": "~18.3.0",
50
+ "esbuild": "^0.25.12",
51
+ "qrcode": "^1.5.4",
52
+ "typescript": "^5.8.0",
53
+ "vitest": "^2.0.0"
54
+ },
55
+ "devDeps": {
56
+ "esbuild": "^0.25.0",
57
+ "qrcode": "^1.5.4",
58
+ "@types/qrcode": "^1.5.5"
59
+ },
60
+ "scripts": {
61
+ "build": "tsc -p tsconfig.json && tsc -p tsconfig.client.json && node scripts/build-client.mjs",
62
+ "verify": "tsc -p tsconfig.json --noEmit && tsc -p tsconfig.client.json --noEmit",
63
+ "test": "vitest run"
64
+ }
65
+ }
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Shared between the Settings card and the host endpoint names. The granular
3
+ * dsh-maestro-review settings-rpc row implements this exact surface on the
4
+ * canonical channel.
5
+ */
6
+ export const MAESTRO_RPC_CHANNEL = '/dsh-maestro-review'
7
+
8
+ export const MAESTRO_ENDPOINTS = Object.freeze({
9
+ status: 'maestro.status',
10
+ getConfig: 'maestro.getConfig',
11
+ saveConfig: 'maestro.saveConfig',
12
+ tunnelStart: 'maestro.tunnelStart',
13
+ tunnelStop: 'maestro.tunnelStop',
14
+ proxyStatus: 'maestro.proxyStatus',
15
+ getPin: 'maestro.getPin',
16
+ rotatePin: 'maestro.rotatePin',
17
+ lanPinStatus: 'maestro.lanPin.status',
18
+ lanPinSetEnabled: 'maestro.lanPin.setEnabled',
19
+ lanPinRotate: 'maestro.lanPin.rotate',
20
+ reviewsList: 'maestro.reviews.list',
21
+ modelsList: 'maestro.models.list',
22
+ modelsCurrent: 'maestro.models.current',
23
+ })
@@ -0,0 +1,79 @@
1
+ // Verbatim legacy-card port (fields/styles preserved; see maestro-card.tsx).
2
+ import { MaestroSettingsTab } from './maestro-card.jsx'
3
+ import { MAESTRO_RPC_CHANNEL } from './api.js'
4
+ import { registerSettingsNavIcon, SETTINGS_NAV_MARKER } from './settings-nav-icon.js'
5
+
6
+ /**
7
+ * DSH 0.1.x gives external settings sections a generic gear and exposes no
8
+ * icon field in the settings.section contract (mirrors dsh-better-sidebar):
9
+ * the marker only claims this plugin's localized row and this CSS paints the
10
+ * Lucide "audio-lines" glyph as a currentColor mask so it follows native nav
11
+ * hover/active colors at the shell's 16px icon rhythm.
12
+ */
13
+ const SETTINGS_NAV_CSS = `
14
+
15
+ /* maestro: replace the settings-nav fallback gear with the maestro glyph */
16
+ [${SETTINGS_NAV_MARKER}] > svg:first-child {
17
+ display: none;
18
+ }
19
+
20
+ [${SETTINGS_NAV_MARKER}]::before {
21
+ content: '';
22
+ flex: none;
23
+ width: 16px;
24
+ height: 16px;
25
+ background: currentColor;
26
+ -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M2 10v3'/%3E%3Cpath d='M6 4v16'/%3E%3Cpath d='M10 8v8'/%3E%3Cpath d='M14 4v16'/%3E%3Cpath d='M18 6v12'/%3E%3Cpath d='M22 10v3'/%3E%3C/svg%3E") center / contain no-repeat;
27
+ mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M2 10v3'/%3E%3Cpath d='M6 4v16'/%3E%3Cpath d='M10 8v8'/%3E%3Cpath d='M14 4v16'/%3E%3Cpath d='M18 6v12'/%3E%3Cpath d='M22 10v3'/%3E%3C/svg%3E") center / contain no-repeat;
28
+ }
29
+ `
30
+
31
+ type SlotsApi = {
32
+ inject(name: string, factory: () => unknown): void
33
+ register(
34
+ options: Record<string, unknown>,
35
+ render: (props: { rpcCall: RpcCall }) => unknown,
36
+ ): unknown
37
+ }
38
+ type RpcCall = (endpoint: string, payload?: unknown, signal?: AbortSignal) => Promise<unknown>
39
+
40
+ interface ClientCtx {
41
+ get?(name: string): unknown
42
+ effect(fn: () => () => void, label?: string): unknown
43
+ }
44
+
45
+ function installNavIconStyle(): () => void {
46
+ const tag = document.createElement('style')
47
+ tag.dataset.plugin = '@ddtcorex/dsh-maestro-config'
48
+ tag.dataset.pluginCss = 'maestro/settings-nav.css'
49
+ tag.textContent = SETTINGS_NAV_CSS
50
+ document.head.appendChild(tag)
51
+ return () => {
52
+ document.querySelector('style[data-plugin-css="maestro/settings-nav.css"]')?.remove()
53
+ }
54
+ }
55
+
56
+ export function apply(ctx: ClientCtx): void {
57
+ const slots = ctx.get?.('slots') as SlotsApi | undefined
58
+ if (slots === undefined) return
59
+
60
+ // The card speaks the granular dsh-maestro-review settings-rpc surface.
61
+ const rpcCall: RpcCall = (endpoint, payload, signal) => {
62
+ const connection = ctx.get?.('connection') as
63
+ | { rpc: { call(ch: string, ep: string, p?: unknown, s?: AbortSignal): Promise<unknown> } }
64
+ | undefined
65
+ if (!connection?.rpc?.call) return Promise.reject(new Error('RPC not available'))
66
+ return connection.rpc.call(MAESTRO_RPC_CHANNEL, endpoint, payload, signal)
67
+ }
68
+
69
+ // Reversible effects: nav-row marker observer + owned style tag.
70
+ ctx.effect(() => registerSettingsNavIcon(() => 'Maestro'), 'maestro: settings nav icon')
71
+ ctx.effect(installNavIconStyle, 'maestro: settings nav css')
72
+
73
+ slots.inject('settings.section', () =>
74
+ slots.register(
75
+ { name: 'settings.section', id: 'maestro', order: 25, label: () => 'Maestro', inject: () => ({ rpcCall }) },
76
+ MaestroSettingsTab,
77
+ ),
78
+ )
79
+ }