@ddtcorex/dsh-maestro-review 0.10.0 → 0.11.1
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/README.md +10 -8
- package/cordis.patch.yml +4 -4
- package/lib/client.js +493 -0
- package/lib/config-store.d.ts +1 -1
- package/lib/config-store.d.ts.map +1 -1
- package/lib/config-store.js +43 -3
- package/lib/config-store.js.map +1 -1
- package/lib/govard-lint-detection.d.ts +20 -0
- package/lib/govard-lint-detection.d.ts.map +1 -0
- package/lib/govard-lint-detection.js +48 -0
- package/lib/govard-lint-detection.js.map +1 -0
- package/lib/orchestrator.d.ts +45 -9
- package/lib/orchestrator.d.ts.map +1 -1
- package/lib/orchestrator.js +79 -22
- package/lib/orchestrator.js.map +1 -1
- package/lib/search-tool.d.ts.map +1 -1
- package/lib/search-tool.js.map +1 -1
- package/lib/settings-rpc.d.ts +13 -11
- package/lib/settings-rpc.d.ts.map +1 -1
- package/lib/settings-rpc.js +45 -142
- package/lib/settings-rpc.js.map +1 -1
- package/lib/types/client/BrandMark.d.ts +22 -0
- package/lib/types/client/BrandMark.d.ts.map +1 -0
- package/lib/types/client/index.d.ts +12 -0
- package/lib/types/client/index.d.ts.map +1 -0
- package/lib/types/client/maestro-mark.d.ts +20 -0
- package/lib/types/client/maestro-mark.d.ts.map +1 -0
- package/lib/types/client/review/ReviewSettings.d.ts +15 -0
- package/lib/types/client/review/ReviewSettings.d.ts.map +1 -0
- package/lib/types/client/review/styles.d.ts +11 -0
- package/lib/types/client/review/styles.d.ts.map +1 -0
- package/lib/types/client/settings-nav-icon.d.ts +20 -0
- package/lib/types/client/settings-nav-icon.d.ts.map +1 -0
- package/lib/vendor/store.d.ts +60 -0
- package/lib/vendor/store.d.ts.map +1 -0
- package/lib/vendor/store.js +500 -0
- package/lib/vendor/store.js.map +1 -0
- package/lib/webhook-secret.d.ts +31 -0
- package/lib/webhook-secret.d.ts.map +1 -0
- package/lib/webhook-secret.js +38 -0
- package/lib/webhook-secret.js.map +1 -0
- package/package.json +24 -6
- package/presets/maestro-auditor.patch.yml +7 -4
- package/presets/maestro-coder/agent.cordis.yml +3 -11
- package/presets/maestro-coder/preset.yml +1 -1
- package/profiles/reviewer-ci/cordis.patch.yml +5 -4
- package/profiles/reviewer-ci/package.json +3 -3
- package/profiles/reviewer-ci/pnpm-lock.yaml +3524 -3239
- package/skills/dsh-native-tools/SKILL.md +5 -5
- package/src/client/BrandMark.tsx +42 -0
- package/src/client/index.tsx +109 -0
- package/src/client/maestro-mark.ts +21 -0
- package/src/client/review/ReviewSettings.tsx +236 -0
- package/src/client/review/styles.ts +117 -0
- package/src/client/settings-nav-icon.ts +68 -0
- package/src/host/config-store.ts +42 -3
- package/src/host/govard-lint-detection.ts +47 -0
- package/src/host/orchestrator.ts +95 -23
- package/src/host/search-tool.ts +3 -4
- package/src/host/settings-rpc.ts +48 -145
- package/src/host/vendor/store.ts +531 -0
- package/src/host/webhook-secret.ts +43 -0
- package/lib/govard-audit-lint-tool.d.ts +0 -95
- package/lib/govard-audit-lint-tool.d.ts.map +0 -1
- package/lib/govard-audit-lint-tool.js +0 -286
- package/lib/govard-audit-lint-tool.js.map +0 -1
- package/lib/govard-tool.d.ts +0 -11
- package/lib/govard-tool.d.ts.map +0 -1
- package/lib/govard-tool.js +0 -95
- package/lib/govard-tool.js.map +0 -1
- package/lib/workspace-tool.d.ts +0 -10
- package/lib/workspace-tool.d.ts.map +0 -1
- package/lib/workspace-tool.js +0 -92
- package/lib/workspace-tool.js.map +0 -1
- package/src/host/govard-audit-lint-tool.ts +0 -286
- package/src/host/govard-tool.ts +0 -114
- package/src/host/workspace-tool.ts +0 -108
package/README.md
CHANGED
|
@@ -6,8 +6,9 @@ child agents on fresh worktrees, webhook intake for GitLab events, and a setting
|
|
|
6
6
|
client UI injected into the DSH Web slots.
|
|
7
7
|
|
|
8
8
|
Part of the Maestro Harness suite (`dsh-maestro-*`). Cordis patch rows:
|
|
9
|
-
`maestro-review-
|
|
10
|
-
`maestro-review-settings-rpc` (settings UI)
|
|
9
|
+
`maestro-review-host` (package root, bundled skills), `maestro-review-webhook` (intake),
|
|
10
|
+
`maestro-review-orchestrator` (pipeline), `maestro-review-settings-rpc` (settings UI),
|
|
11
|
+
all served by this npm package.
|
|
11
12
|
|
|
12
13
|
## What it provides
|
|
13
14
|
|
|
@@ -16,9 +17,9 @@ Part of the Maestro Harness suite (`dsh-maestro-*`). Cordis patch rows:
|
|
|
16
17
|
- **Orchestrator** — reviewer + auditor agent runs against a disposable worktree of the MR,
|
|
17
18
|
model selection overridable globally (`reviewModel`) and per project mapping.
|
|
18
19
|
- **Webhook intake** — receives GitLab MR events, enqueues reviews (dedup + retention).
|
|
19
|
-
- **Review findings/history/signals
|
|
20
|
+
- **Review findings/history/signals**: findings written through the `report_review_findings`
|
|
20
21
|
tool by tool-only review subagents (never free text); history + signals tracked per MR.
|
|
21
|
-
- **
|
|
22
|
+
- **Review tools** (workspace search, Magento/Hyva scans, query-log stats, GitLab diff tools) mounted into review runs.
|
|
22
23
|
- **Client half** — settings section rendered into DSH Web slots; notification copy stays
|
|
23
24
|
here, delivery goes through the optional `maestroNotifier` service.
|
|
24
25
|
|
|
@@ -38,8 +39,10 @@ Templates live in `templates/`, the image in `docker/`.
|
|
|
38
39
|
## Settings
|
|
39
40
|
|
|
40
41
|
Reads its configuration through the **shared namespaced settings store**
|
|
41
|
-
(`~/.dsh/maestro/settings.json
|
|
42
|
-
|
|
42
|
+
(`~/.dsh/dsh-maestro-config/settings.json`) via the flat adapter in
|
|
43
|
+
`src/host/config-store.ts`, over the embedded store copy `src/host/vendor/store.ts`
|
|
44
|
+
(generated from `dsh-maestro-core`, no runtime dependency); machine runtime state stays in a
|
|
45
|
+
package-local sidecar.
|
|
43
46
|
|
|
44
47
|
## Install
|
|
45
48
|
|
|
@@ -53,8 +56,7 @@ dsh plugin --profile web add @ddtcorex/dsh-maestro-review
|
|
|
53
56
|
pnpm install
|
|
54
57
|
pnpm verify # tsc --noEmit
|
|
55
58
|
pnpm test # vitest run
|
|
56
|
-
pnpm build # tsc -> lib/
|
|
57
|
-
pnpm run build:client # browser bundle -> lib/client.js (required after client changes)
|
|
59
|
+
pnpm build # tsc -> lib/, plus the browser bundle lib/client.js
|
|
58
60
|
```
|
|
59
61
|
|
|
60
62
|
See AGENTS.md for conventions (host/client split, secrets handling, live-validation rules).
|
package/cordis.patch.yml
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
# dsh-maestro-review bundle patch
|
|
2
|
-
#
|
|
3
|
-
# channel: /dsh-maestro-review
|
|
1
|
+
# dsh-maestro-review bundle patch: 4 rows, maestro-review-host, maestro-review-webhook,
|
|
2
|
+
# maestro-review-orchestrator, maestro-review-settings-rpc
|
|
3
|
+
# RPC channel: /dsh-maestro-review (see src/host/settings-rpc.ts)
|
|
4
4
|
- insert:
|
|
5
5
|
# Mounts the package root (lib/index.js -> apply()). Required: `dsh plugin add`
|
|
6
6
|
# applies ONLY the rows in this file, so without it the skill-provider
|
|
@@ -32,4 +32,4 @@
|
|
|
32
32
|
# rpc.handle registers a webServer route inside an effect fiber which carries only the row's inject (not the module's);
|
|
33
33
|
# without webServer the row fails on harnesses that provide webServer after rows apply
|
|
34
34
|
# (DSH 0.1.5-rc.1+).
|
|
35
|
-
inject: ['connection', '
|
|
35
|
+
inject: ['connection', 'webServer']
|
package/lib/client.js
ADDED
|
@@ -0,0 +1,493 @@
|
|
|
1
|
+
window.__ModuleLoader__.load({
|
|
2
|
+
id: '@ddtcorex/dsh-maestro-review',
|
|
3
|
+
factory: (require) => {
|
|
4
|
+
var module = { exports: {} };
|
|
5
|
+
var exports = module.exports;
|
|
6
|
+
var React = require("react");
|
|
7
|
+
"use strict";
|
|
8
|
+
var __create = Object.create;
|
|
9
|
+
var __defProp = Object.defineProperty;
|
|
10
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
11
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
12
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
13
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
14
|
+
var __export = (target, all) => {
|
|
15
|
+
for (var name in all)
|
|
16
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
17
|
+
};
|
|
18
|
+
var __copyProps = (to, from, except, desc) => {
|
|
19
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
20
|
+
for (let key of __getOwnPropNames(from))
|
|
21
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
22
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
23
|
+
}
|
|
24
|
+
return to;
|
|
25
|
+
};
|
|
26
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
27
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
28
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
29
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
30
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
31
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
32
|
+
mod
|
|
33
|
+
));
|
|
34
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
35
|
+
|
|
36
|
+
// src/client/index.tsx
|
|
37
|
+
var index_exports = {};
|
|
38
|
+
__export(index_exports, {
|
|
39
|
+
REVIEW_CHANNEL: () => REVIEW_CHANNEL,
|
|
40
|
+
apply: () => apply,
|
|
41
|
+
default: () => index_default,
|
|
42
|
+
inject: () => inject,
|
|
43
|
+
makeRpcCall: () => makeRpcCall
|
|
44
|
+
});
|
|
45
|
+
module.exports = __toCommonJS(index_exports);
|
|
46
|
+
var React2 = __toESM(require("react"), 1);
|
|
47
|
+
|
|
48
|
+
// src/client/review/ReviewSettings.tsx
|
|
49
|
+
var React = __toESM(require("react"), 1);
|
|
50
|
+
|
|
51
|
+
// src/client/BrandMark.tsx
|
|
52
|
+
var import_react = require("react");
|
|
53
|
+
|
|
54
|
+
// src/client/maestro-mark.ts
|
|
55
|
+
var MAESTRO_MARK_PATH = "M2 11 L5 4 L8 9 L11 4 L14 11";
|
|
56
|
+
var MAESTRO_MARK_VIEWBOX = "0 0 16 16";
|
|
57
|
+
var MAESTRO_MARK_STROKE_WIDTH = 1.6;
|
|
58
|
+
var MAESTRO_BRAND_TILE = "#0A84FF";
|
|
59
|
+
|
|
60
|
+
// src/client/BrandMark.tsx
|
|
61
|
+
function MaestroMark(props) {
|
|
62
|
+
const s = props.size ?? 16;
|
|
63
|
+
return (0, import_react.createElement)(
|
|
64
|
+
"svg",
|
|
65
|
+
{ width: s, height: s, viewBox: MAESTRO_MARK_VIEWBOX, fill: "none", "aria-hidden": "true" },
|
|
66
|
+
(0, import_react.createElement)("path", { d: MAESTRO_MARK_PATH, stroke: "currentColor", strokeWidth: MAESTRO_MARK_STROKE_WIDTH, strokeLinecap: "round", strokeLinejoin: "round" })
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
function BrandBadge(props) {
|
|
70
|
+
const outer = props.outer ?? 28;
|
|
71
|
+
const size = props.size ?? 16;
|
|
72
|
+
const radius = props.radius ?? 8;
|
|
73
|
+
return (0, import_react.createElement)("span", {
|
|
74
|
+
"data-maestro-logo": "",
|
|
75
|
+
style: {
|
|
76
|
+
width: outer,
|
|
77
|
+
height: outer,
|
|
78
|
+
borderRadius: radius,
|
|
79
|
+
display: "inline-flex",
|
|
80
|
+
alignItems: "center",
|
|
81
|
+
justifyContent: "center",
|
|
82
|
+
background: `var(--dsw-alias-brand-primary, ${MAESTRO_BRAND_TILE})`,
|
|
83
|
+
backgroundColor: MAESTRO_BRAND_TILE,
|
|
84
|
+
color: "#fff",
|
|
85
|
+
flex: "none",
|
|
86
|
+
border: "1px solid rgba(0,0,0,0.08)",
|
|
87
|
+
boxShadow: "0 0 0 1px var(--dsw-alias-border-l1)",
|
|
88
|
+
boxSizing: "border-box",
|
|
89
|
+
...props.style
|
|
90
|
+
}
|
|
91
|
+
}, (0, import_react.createElement)(MaestroMark, { size }));
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// src/client/review/ReviewSettings.tsx
|
|
95
|
+
var FIELD_LABELS = {
|
|
96
|
+
gitlabBaseUrl: "GitLab base URL",
|
|
97
|
+
botUsername: "Bot username",
|
|
98
|
+
webhookPort: "Webhook port",
|
|
99
|
+
autoRereviewOnPush: "Re-review on push",
|
|
100
|
+
autoReviewOnAssign: "Review on assign",
|
|
101
|
+
agentTimeoutMs: "Agent timeout (ms)",
|
|
102
|
+
reviewSessionRetentionDays: "Session retention (days)"
|
|
103
|
+
};
|
|
104
|
+
function Field(props) {
|
|
105
|
+
return React.createElement(
|
|
106
|
+
"div",
|
|
107
|
+
{ "data-review-row": "" },
|
|
108
|
+
React.createElement(
|
|
109
|
+
"div",
|
|
110
|
+
{ "data-review-row-text": "" },
|
|
111
|
+
React.createElement("label", { "data-review-label": "", htmlFor: props.id }, props.label),
|
|
112
|
+
props.hint ? React.createElement("p", { "data-review-hint": "" }, props.hint) : null
|
|
113
|
+
),
|
|
114
|
+
React.createElement("div", { "data-review-control": "" }, props.children)
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
function SecretField(props) {
|
|
118
|
+
return Field({
|
|
119
|
+
id: props.id,
|
|
120
|
+
label: props.label,
|
|
121
|
+
hint: props.present ? "A value is stored. Leave blank to keep it." : "Not set.",
|
|
122
|
+
children: React.createElement(
|
|
123
|
+
"div",
|
|
124
|
+
{ "data-review-secret-group": "" },
|
|
125
|
+
React.createElement("input", {
|
|
126
|
+
id: props.id,
|
|
127
|
+
type: "password",
|
|
128
|
+
value: props.value,
|
|
129
|
+
placeholder: props.present ? "stored \u2014 type to replace" : "not set",
|
|
130
|
+
disabled: props.disabled,
|
|
131
|
+
autoComplete: "new-password",
|
|
132
|
+
"data-review-secret": "",
|
|
133
|
+
onChange: (e) => props.onChange(e.target.value)
|
|
134
|
+
}),
|
|
135
|
+
// The save belongs to this field: it stays disabled until the field
|
|
136
|
+
// holds something, and floating it below the row left it orphaned.
|
|
137
|
+
React.createElement("button", {
|
|
138
|
+
type: "button",
|
|
139
|
+
disabled: props.disabled || props.value === "",
|
|
140
|
+
"data-review-save-secret": props.name,
|
|
141
|
+
onClick: props.onSave
|
|
142
|
+
}, "Save")
|
|
143
|
+
)
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
function ReviewSettings(props) {
|
|
147
|
+
const { rpcCall } = props;
|
|
148
|
+
const [cfg, setCfg] = React.useState({});
|
|
149
|
+
const [gitlabToken, setGitlabToken] = React.useState("");
|
|
150
|
+
const [webhookSecret, setWebhookSecret] = React.useState("");
|
|
151
|
+
const [busy, setBusy] = React.useState(false);
|
|
152
|
+
const [notice, setNotice] = React.useState(null);
|
|
153
|
+
const fail = React.useCallback((e) => {
|
|
154
|
+
setNotice({ tone: "bad", text: e instanceof Error ? e.message : String(e) });
|
|
155
|
+
}, []);
|
|
156
|
+
const refresh = React.useCallback(async () => {
|
|
157
|
+
setBusy(true);
|
|
158
|
+
try {
|
|
159
|
+
setCfg(await rpcCall("maestro.getConfig") ?? {});
|
|
160
|
+
} catch (e) {
|
|
161
|
+
fail(e);
|
|
162
|
+
} finally {
|
|
163
|
+
setBusy(false);
|
|
164
|
+
}
|
|
165
|
+
}, [rpcCall, fail]);
|
|
166
|
+
React.useEffect(() => {
|
|
167
|
+
void refresh();
|
|
168
|
+
}, [refresh]);
|
|
169
|
+
const save = React.useCallback(
|
|
170
|
+
async (patch) => {
|
|
171
|
+
setBusy(true);
|
|
172
|
+
try {
|
|
173
|
+
await rpcCall("maestro.saveConfig", patch);
|
|
174
|
+
if ("gitlabToken" in patch) setGitlabToken("");
|
|
175
|
+
if ("webhookSecret" in patch) setWebhookSecret("");
|
|
176
|
+
setNotice({ tone: "ok", text: "Saved." });
|
|
177
|
+
await refresh();
|
|
178
|
+
} catch (e) {
|
|
179
|
+
fail(e);
|
|
180
|
+
} finally {
|
|
181
|
+
setBusy(false);
|
|
182
|
+
}
|
|
183
|
+
},
|
|
184
|
+
[rpcCall, refresh, fail]
|
|
185
|
+
);
|
|
186
|
+
const text = (key, placeholder = "") => Field({
|
|
187
|
+
id: `review-${String(key)}`,
|
|
188
|
+
label: FIELD_LABELS[key] ?? String(key),
|
|
189
|
+
children: React.createElement("input", {
|
|
190
|
+
id: `review-${String(key)}`,
|
|
191
|
+
type: "text",
|
|
192
|
+
value: cfg[key] ?? "",
|
|
193
|
+
placeholder,
|
|
194
|
+
disabled: busy,
|
|
195
|
+
"data-review-input": String(key),
|
|
196
|
+
onChange: (e) => setCfg((c) => ({ ...c, [key]: e.target.value })),
|
|
197
|
+
onBlur: (e) => void save({ [key]: e.target.value })
|
|
198
|
+
})
|
|
199
|
+
});
|
|
200
|
+
const toggle = (key) => Field({
|
|
201
|
+
id: `review-${String(key)}`,
|
|
202
|
+
label: FIELD_LABELS[key] ?? String(key),
|
|
203
|
+
children: React.createElement("input", {
|
|
204
|
+
id: `review-${String(key)}`,
|
|
205
|
+
type: "checkbox",
|
|
206
|
+
checked: cfg[key] === true,
|
|
207
|
+
disabled: busy,
|
|
208
|
+
"data-review-toggle": String(key),
|
|
209
|
+
onChange: (e) => void save({ [key]: e.target.checked })
|
|
210
|
+
})
|
|
211
|
+
});
|
|
212
|
+
return React.createElement(
|
|
213
|
+
"div",
|
|
214
|
+
{ "data-review-root": "" },
|
|
215
|
+
// House header: badge, title, one-line status. The notice lives here so it
|
|
216
|
+
// reports without pushing the rows down.
|
|
217
|
+
React.createElement(
|
|
218
|
+
"div",
|
|
219
|
+
{ "data-review-header": "" },
|
|
220
|
+
React.createElement(BrandBadge, { style: { alignSelf: "flex-start", marginTop: 2 } }),
|
|
221
|
+
React.createElement(
|
|
222
|
+
"div",
|
|
223
|
+
{ "data-review-heading": "" },
|
|
224
|
+
React.createElement("h2", { "data-review-title": "" }, "GitLab and Review"),
|
|
225
|
+
React.createElement(
|
|
226
|
+
"div",
|
|
227
|
+
{ "data-review-status": "" },
|
|
228
|
+
notice ? React.createElement("p", { "data-review-notice": "", "data-tone": notice.tone, role: "status" }, notice.text) : React.createElement("span", null, "GitLab credentials and review triggers.")
|
|
229
|
+
)
|
|
230
|
+
)
|
|
231
|
+
),
|
|
232
|
+
React.createElement(
|
|
233
|
+
"div",
|
|
234
|
+
{ "data-review-actions": "" },
|
|
235
|
+
React.createElement("button", { type: "button", disabled: busy, onClick: () => void refresh() }, "Refresh")
|
|
236
|
+
),
|
|
237
|
+
text("gitlabBaseUrl", "https://gitlab.example.com"),
|
|
238
|
+
text("botUsername", "maestro-bot"),
|
|
239
|
+
SecretField({
|
|
240
|
+
id: "review-gitlab-token",
|
|
241
|
+
name: "gitlabToken",
|
|
242
|
+
label: "GitLab token",
|
|
243
|
+
present: cfg.hasGitlabToken === true,
|
|
244
|
+
value: gitlabToken,
|
|
245
|
+
disabled: busy,
|
|
246
|
+
onChange: setGitlabToken,
|
|
247
|
+
onSave: () => void save({ gitlabToken })
|
|
248
|
+
}),
|
|
249
|
+
SecretField({
|
|
250
|
+
id: "review-webhook-secret",
|
|
251
|
+
name: "webhookSecret",
|
|
252
|
+
label: "Webhook secret",
|
|
253
|
+
present: cfg.hasWebhookSecret === true,
|
|
254
|
+
value: webhookSecret,
|
|
255
|
+
disabled: busy,
|
|
256
|
+
onChange: setWebhookSecret,
|
|
257
|
+
onSave: () => void save({ webhookSecret })
|
|
258
|
+
}),
|
|
259
|
+
text("webhookPort", "3081"),
|
|
260
|
+
toggle("autoRereviewOnPush"),
|
|
261
|
+
toggle("autoReviewOnAssign"),
|
|
262
|
+
text("agentTimeoutMs", "600000"),
|
|
263
|
+
text("reviewSessionRetentionDays", "30")
|
|
264
|
+
);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// src/client/review/styles.ts
|
|
268
|
+
var REVIEW_CSS = `
|
|
269
|
+
[data-review-root], [data-review-root] * { box-sizing: border-box; }
|
|
270
|
+
[data-review-root] {
|
|
271
|
+
display: flex; flex-direction: column; gap: 12px;
|
|
272
|
+
color: var(--dsw-alias-label-primary);
|
|
273
|
+
font-size: 13px; line-height: 1.5;
|
|
274
|
+
min-width: 0; width: 100%; max-width: 640px;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/* House header: badge + title + one-line status. */
|
|
278
|
+
[data-review-header] { display: flex; gap: 10px; align-items: flex-start; padding: 2px 2px 8px; }
|
|
279
|
+
[data-review-heading] { display: flex; flex-direction: column; min-width: 0; }
|
|
280
|
+
[data-review-title] { margin: 0; font-size: 15px; font-weight: 600; line-height: 22px; }
|
|
281
|
+
[data-review-status] { font-size: 12px; line-height: 16px; color: var(--dsw-alias-label-secondary); overflow-wrap: anywhere; }
|
|
282
|
+
[data-review-status] [data-review-notice] { margin: 0; }
|
|
283
|
+
[data-review-status] [data-review-notice][data-tone="ok"] { color: var(--dsw-alias-state-success-primary); }
|
|
284
|
+
[data-review-status] [data-review-notice][data-tone="bad"] { color: var(--dsw-alias-state-error-primary); }
|
|
285
|
+
|
|
286
|
+
[data-review-actions] { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
|
|
287
|
+
[data-review-actions] button {
|
|
288
|
+
min-height: 32px; padding: 0 12px; border-radius: 8px;
|
|
289
|
+
border: 1px solid var(--dsw-alias-border-l2); background: var(--dsw-alias-bg-layer-1);
|
|
290
|
+
color: inherit; font: inherit; cursor: pointer;
|
|
291
|
+
}
|
|
292
|
+
[data-review-actions] button:disabled { opacity: 0.55; cursor: default; }
|
|
293
|
+
[data-review-actions] button:focus-visible { outline: 2px solid var(--dsw-alias-border-l2); outline-offset: 2px; }
|
|
294
|
+
|
|
295
|
+
/* House row: label and hint left, control held right, hairline between. */
|
|
296
|
+
[data-review-row] {
|
|
297
|
+
display: flex; align-items: center; gap: 8px;
|
|
298
|
+
padding: 16px 0; border-bottom: 1px solid var(--dsw-alias-border-l2); min-width: 0;
|
|
299
|
+
}
|
|
300
|
+
[data-review-row]:last-of-type { border-bottom: none; }
|
|
301
|
+
[data-review-row-text] { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 4px; padding-right: 48px; }
|
|
302
|
+
[data-review-control] { flex: none; display: flex; align-items: center; justify-content: flex-end; gap: 8px; min-height: 36px; }
|
|
303
|
+
/* Declared once. These two rules previously existed twice \u2014 the 12px/600 pair
|
|
304
|
+
sat AFTER the house values and won on source order at equal specificity, so
|
|
305
|
+
the whole tab rendered at the old weight and the old hint size. */
|
|
306
|
+
[data-review-label] { font-size: 14px; font-weight: 400; line-height: 22px; color: var(--dsw-alias-label-primary); }
|
|
307
|
+
[data-review-hint] { margin: 0; font-size: 12px; line-height: 18px; color: var(--dsw-alias-label-tertiary); }
|
|
308
|
+
|
|
309
|
+
/* A secret whose value is committed by a button: input and save travel
|
|
310
|
+
together on ONE row. The button used to sit on a row of its own below the
|
|
311
|
+
field \u2014 the group was a column and the button was align-self: flex-end \u2014
|
|
312
|
+
which orphaned it from the control it saves. It stays disabled until the
|
|
313
|
+
field holds something, so proximity to that field is the point. */
|
|
314
|
+
[data-review-secret-group] { display: flex; flex-direction: row; align-items: center; gap: 8px; min-width: 220px; }
|
|
315
|
+
[data-review-secret-group] input { flex: 1 1 auto; min-width: 0; }
|
|
316
|
+
[data-review-secret-group] button {
|
|
317
|
+
flex: none; align-self: center; min-height: 44px; padding: 0 12px; border-radius: 8px;
|
|
318
|
+
border: 1px solid var(--dsw-alias-border-l2); background: var(--dsw-alias-bg-layer-1);
|
|
319
|
+
color: inherit; font: inherit; cursor: pointer;
|
|
320
|
+
}
|
|
321
|
+
[data-review-secret-group] button:disabled { opacity: 0.55; cursor: default; }
|
|
322
|
+
[data-review-secret-group] button:focus-visible { outline: 2px solid var(--dsw-alias-border-l2); outline-offset: 2px; }
|
|
323
|
+
|
|
324
|
+
/* Every row control gets the same box \u2014 the shared settings field box, copied
|
|
325
|
+
from the host's own form primitive (ui-primitives ConfigField) so this tab
|
|
326
|
+
follows the shell instead of carrying a geometry of its own. Only
|
|
327
|
+
min-height: 44px is Maestro's: it is the touch target AGENTS.md requires,
|
|
328
|
+
which the host's line-box sizing does not give.
|
|
329
|
+
The password type belongs in this list: without it the two secret fields
|
|
330
|
+
rendered at the UA default \u2014 21px tall, no radius \u2014 beside their neighbours
|
|
331
|
+
in the same column. */
|
|
332
|
+
[data-review-control] input[type="text"], [data-review-control] input[type="password"], [data-review-control] select {
|
|
333
|
+
min-height: 44px; padding: 6px 12px; border: 0.5px solid var(--dsw-alias-border-l4);
|
|
334
|
+
border-radius: var(--dsw-radius-md); background: var(--dsw-alias-bg-layer-3);
|
|
335
|
+
color: var(--dsw-alias-label-primary); font: inherit;
|
|
336
|
+
}
|
|
337
|
+
[data-review-control] input:focus-visible, [data-review-control] select:focus-visible {
|
|
338
|
+
outline: 2px solid var(--dsw-alias-border-l2); outline-offset: 2px;
|
|
339
|
+
}
|
|
340
|
+
/* The checkbox is the one control the harness draws at its own size; without
|
|
341
|
+
this it rendered at the UA's 13px. Same values as the reference row. */
|
|
342
|
+
[data-review-control] input[type="checkbox"] {
|
|
343
|
+
width: 16px; height: 16px; margin: 0; flex: none;
|
|
344
|
+
accent-color: var(--dsw-alias-brand-primary, #0A84FF);
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
[data-review-pin], [data-review-lan] { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
|
|
348
|
+
[data-review-pin] code, [data-review-lan] code {
|
|
349
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
|
|
350
|
+
padding: 2px 6px; border-radius: 6px; background: var(--dsw-alias-bg-layer-2);
|
|
351
|
+
}
|
|
352
|
+
[data-review-pin] button, [data-review-lan] button {
|
|
353
|
+
min-height: 32px; padding: 0 10px; border-radius: 8px;
|
|
354
|
+
border: 1px solid var(--dsw-alias-border-l2); background: var(--dsw-alias-bg-layer-1);
|
|
355
|
+
color: inherit; font: inherit; cursor: pointer;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
@media (max-width: 640px) {
|
|
359
|
+
/* Below the measure the row stacks: a right-held control has no room left,
|
|
360
|
+
and a wrapped one reads as a third column. */
|
|
361
|
+
[data-review-row] { flex-direction: column; align-items: stretch; gap: 8px; }
|
|
362
|
+
[data-review-row-text] { padding-right: 0; }
|
|
363
|
+
[data-review-control] { justify-content: flex-start; }
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
@media (max-width: 480px) {
|
|
367
|
+
[data-review-actions] button { flex: 1 1 auto; min-height: 40px; }
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
/* iOS 16px field floor: the magnifier fires below this on a focused field. */
|
|
371
|
+
@media (max-width: 1023px) and (pointer: coarse) {
|
|
372
|
+
html[data-mobile-nav-ios] [data-review-root] input,
|
|
373
|
+
html[data-mobile-nav-ios] [data-review-root] select { font-size: 16px !important; }
|
|
374
|
+
}
|
|
375
|
+
`;
|
|
376
|
+
|
|
377
|
+
// src/client/settings-nav-icon.ts
|
|
378
|
+
var SETTINGS_NAV_MARKER = "data-maestro-review-settings-nav";
|
|
379
|
+
function registerSettingsNavIcon(label, root) {
|
|
380
|
+
if (typeof document === "undefined" && root === void 0) {
|
|
381
|
+
return () => {
|
|
382
|
+
};
|
|
383
|
+
}
|
|
384
|
+
const scope = root ?? document;
|
|
385
|
+
let disposed = false;
|
|
386
|
+
const sync = () => {
|
|
387
|
+
if (disposed) return;
|
|
388
|
+
const currentLabel = label().trim();
|
|
389
|
+
const buttons = scope.querySelectorAll('[role="dialog"] nav button');
|
|
390
|
+
for (const button of Array.from(buttons)) {
|
|
391
|
+
const el = button;
|
|
392
|
+
const matches = currentLabel.length > 0 && button.textContent != null && button.textContent.trim() === currentLabel;
|
|
393
|
+
if (matches) el.setAttribute(SETTINGS_NAV_MARKER, "");
|
|
394
|
+
else if (el.hasAttribute(SETTINGS_NAV_MARKER)) el.removeAttribute(SETTINGS_NAV_MARKER);
|
|
395
|
+
}
|
|
396
|
+
};
|
|
397
|
+
sync();
|
|
398
|
+
let observer = null;
|
|
399
|
+
if (typeof MutationObserver !== "undefined") {
|
|
400
|
+
observer = new MutationObserver(sync);
|
|
401
|
+
observer.observe(document.body, { childList: true, subtree: true, characterData: true });
|
|
402
|
+
}
|
|
403
|
+
return () => {
|
|
404
|
+
disposed = true;
|
|
405
|
+
if (observer !== null) observer.disconnect();
|
|
406
|
+
for (const element of Array.from(scope.querySelectorAll(`[${SETTINGS_NAV_MARKER}]`))) {
|
|
407
|
+
element.removeAttribute(SETTINGS_NAV_MARKER);
|
|
408
|
+
}
|
|
409
|
+
};
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
// src/client/index.tsx
|
|
413
|
+
var REVIEW_CHANNEL = "/dsh-maestro-review";
|
|
414
|
+
function unwrap(res) {
|
|
415
|
+
return res && typeof res === "object" && "ok" in res ? res.ok ? res.value : null : res;
|
|
416
|
+
}
|
|
417
|
+
function makeRpcCall(ctx) {
|
|
418
|
+
return async (endpoint, payload) => {
|
|
419
|
+
const conn = ctx.get?.("connection");
|
|
420
|
+
if (conn?.rpc?.call === void 0) throw new Error("RPC not available");
|
|
421
|
+
const res = await conn.rpc.call(REVIEW_CHANNEL, endpoint, payload ?? {});
|
|
422
|
+
const value = unwrap(res);
|
|
423
|
+
if (value === null) {
|
|
424
|
+
throw new Error(String(res?.error?.message ?? res?.error ?? endpoint));
|
|
425
|
+
}
|
|
426
|
+
return value;
|
|
427
|
+
};
|
|
428
|
+
}
|
|
429
|
+
var REVIEW_NAV_CSS = `
|
|
430
|
+
[${SETTINGS_NAV_MARKER}] > svg:first-child,
|
|
431
|
+
[${SETTINGS_NAV_MARKER}] > svg.zWKi1a_navIcon {
|
|
432
|
+
display: none !important;
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
[${SETTINGS_NAV_MARKER}]::before {
|
|
436
|
+
content: '';
|
|
437
|
+
flex: none;
|
|
438
|
+
width: 16px;
|
|
439
|
+
height: 16px;
|
|
440
|
+
display: inline-block;
|
|
441
|
+
background: currentColor;
|
|
442
|
+
-webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 16 16' fill='none' stroke='black' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M2 11 L5 4 L8 9 L11 4 L14 11'/%3E%3C/svg%3E") center / contain no-repeat;
|
|
443
|
+
mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 16 16' fill='none' stroke='black' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M2 11 L5 4 L8 9 L11 4 L14 11'/%3E%3C/svg%3E") center / contain no-repeat;
|
|
444
|
+
}
|
|
445
|
+
`;
|
|
446
|
+
function installStyleTag(css, pluginCss) {
|
|
447
|
+
if (typeof document === "undefined") return () => {
|
|
448
|
+
};
|
|
449
|
+
const tag = document.createElement("style");
|
|
450
|
+
tag.dataset.plugin = "@ddtcorex/dsh-maestro-review";
|
|
451
|
+
tag.dataset.pluginCss = pluginCss;
|
|
452
|
+
tag.textContent = css;
|
|
453
|
+
document.head.appendChild(tag);
|
|
454
|
+
return () => {
|
|
455
|
+
document.querySelector(`style[data-plugin-css="${pluginCss}"]`)?.remove();
|
|
456
|
+
};
|
|
457
|
+
}
|
|
458
|
+
var inject = ["slots", "connection"];
|
|
459
|
+
function apply(ctx) {
|
|
460
|
+
const slots = ctx.get?.("slots");
|
|
461
|
+
if (!slots?.inject || !slots?.register) return;
|
|
462
|
+
const rpcCall = makeRpcCall(ctx);
|
|
463
|
+
ctx.effect(() => registerSettingsNavIcon(() => "Maestro Review"), "maestro-review: settings nav icon");
|
|
464
|
+
ctx.effect(() => installStyleTag(REVIEW_NAV_CSS, "maestro-review/settings-nav.css"), "maestro-review: settings nav css");
|
|
465
|
+
ctx.effect(() => installStyleTag(REVIEW_CSS, "maestro-review/settings.css"), "maestro-review: settings css");
|
|
466
|
+
ctx.effect(() => {
|
|
467
|
+
const dispose = slots.inject(
|
|
468
|
+
"settings.section",
|
|
469
|
+
() => slots.register(
|
|
470
|
+
{
|
|
471
|
+
name: "settings.section",
|
|
472
|
+
id: "maestro-review",
|
|
473
|
+
order: 32,
|
|
474
|
+
label: () => "Maestro Review",
|
|
475
|
+
inject: () => ({ rpcCall })
|
|
476
|
+
},
|
|
477
|
+
(props) => React2.createElement(ReviewSettings, props)
|
|
478
|
+
)
|
|
479
|
+
);
|
|
480
|
+
return () => {
|
|
481
|
+
try {
|
|
482
|
+
;
|
|
483
|
+
dispose?.();
|
|
484
|
+
} catch {
|
|
485
|
+
}
|
|
486
|
+
};
|
|
487
|
+
}, "maestro-review: settings");
|
|
488
|
+
}
|
|
489
|
+
var index_default = { inject, apply };
|
|
490
|
+
|
|
491
|
+
return module.exports;
|
|
492
|
+
}
|
|
493
|
+
});
|
package/lib/config-store.d.ts
CHANGED
|
@@ -55,7 +55,7 @@ export interface MaestroUserConfig {
|
|
|
55
55
|
}
|
|
56
56
|
/**
|
|
57
57
|
* Settings live in the SHARED namespaced store (`~/.dsh/dsh-maestro-config/settings.json`,
|
|
58
|
-
* owned by
|
|
58
|
+
* owned by dsh-maestro-core and embedded here at `src/host/vendor/store.ts`); this store is a thin adapter that
|
|
59
59
|
* keeps the package's flat `MaestroUserConfig` API while delegating persistence.
|
|
60
60
|
* Machine runtime state (RUNTIME_KEYS) never enters settings — it stays in this
|
|
61
61
|
* package's own sidecar so a settings edit can never silently flip tunnel state.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config-store.d.ts","sourceRoot":"","sources":["../src/host/config-store.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAA;AAO1D,MAAM,WAAW,oBAAoB;IACnC,QAAQ,EAAE,MAAM,CAAA;IAChB,KAAK,EAAE,MAAM,CAAA;IACb,eAAe,CAAC,EAAE,MAAM,CAAA;CACzB;AAED,MAAM,WAAW,cAAc;IAC7B,WAAW,EAAE,MAAM,CAAA;IACnB,aAAa,EAAE,MAAM,CAAA;IACrB,aAAa,CAAC,EAAE,kBAAkB,CAAA;IAClC,WAAW,CAAC,EAAE,oBAAoB,CAAA;IAClC,+EAA+E;IAC/E,cAAc,CAAC,EAAE,OAAO,CAAA;IACxB,+EAA+E;IAC/E,cAAc,CAAC,EAAE,OAAO,CAAA;CACzB;AAED,MAAM,WAAW,iBAAiB;IAChC,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,eAAe,CAAC,EAAE,KAAK,CAAC,cAAc,CAAC,CAAA;IACvC,wGAAwG;IACxG,WAAW,CAAC,EAAE,oBAAoB,CAAA;IAClC,mFAAmF;IACnF,kBAAkB,CAAC,EAAE,OAAO,CAAA;IAC5B,gGAAgG;IAChG,kBAAkB,CAAC,EAAE,OAAO,CAAA;IAC5B,+CAA+C;IAC/C,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,uFAAuF;IACvF,0BAA0B,CAAC,EAAE,MAAM,CAAA;IACnC,UAAU,CAAC,EAAE,OAAO,GAAG,OAAO,CAAA;IAC9B,WAAW,CAAC,EAAE,SAAS,GAAG,SAAS,CAAA;IACnC,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,qBAAqB,CAAC,EAAE,MAAM,CAAA;IAC9B,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,iBAAiB,CAAC,EAAE,OAAO,CAAA;IAC3B,2EAA2E;IAC3E,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB;;;;OAIG;IACH,kBAAkB,CAAC,EAAE,MAAM,CAAA;IAC3B,8DAA8D;IAC9D,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,mEAAmE;IACnE,2BAA2B,CAAC,EAAE,OAAO,CAAA;CACtC;
|
|
1
|
+
{"version":3,"file":"config-store.d.ts","sourceRoot":"","sources":["../src/host/config-store.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAA;AAO1D,MAAM,WAAW,oBAAoB;IACnC,QAAQ,EAAE,MAAM,CAAA;IAChB,KAAK,EAAE,MAAM,CAAA;IACb,eAAe,CAAC,EAAE,MAAM,CAAA;CACzB;AAED,MAAM,WAAW,cAAc;IAC7B,WAAW,EAAE,MAAM,CAAA;IACnB,aAAa,EAAE,MAAM,CAAA;IACrB,aAAa,CAAC,EAAE,kBAAkB,CAAA;IAClC,WAAW,CAAC,EAAE,oBAAoB,CAAA;IAClC,+EAA+E;IAC/E,cAAc,CAAC,EAAE,OAAO,CAAA;IACxB,+EAA+E;IAC/E,cAAc,CAAC,EAAE,OAAO,CAAA;CACzB;AAED,MAAM,WAAW,iBAAiB;IAChC,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,eAAe,CAAC,EAAE,KAAK,CAAC,cAAc,CAAC,CAAA;IACvC,wGAAwG;IACxG,WAAW,CAAC,EAAE,oBAAoB,CAAA;IAClC,mFAAmF;IACnF,kBAAkB,CAAC,EAAE,OAAO,CAAA;IAC5B,gGAAgG;IAChG,kBAAkB,CAAC,EAAE,OAAO,CAAA;IAC5B,+CAA+C;IAC/C,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,uFAAuF;IACvF,0BAA0B,CAAC,EAAE,MAAM,CAAA;IACnC,UAAU,CAAC,EAAE,OAAO,GAAG,OAAO,CAAA;IAC9B,WAAW,CAAC,EAAE,SAAS,GAAG,SAAS,CAAA;IACnC,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,qBAAqB,CAAC,EAAE,MAAM,CAAA;IAC9B,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,iBAAiB,CAAC,EAAE,OAAO,CAAA;IAC3B,2EAA2E;IAC3E,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB;;;;OAIG;IACH,kBAAkB,CAAC,EAAE,MAAM,CAAA;IAC3B,8DAA8D;IAC9D,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,mEAAmE;IACnE,2BAA2B,CAAC,EAAE,OAAO,CAAA;CACtC;AAuBD;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,CAExD;AAyBD,wBAAsB,cAAc,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC,CA4BjF;AAED,wBAAsB,cAAc,CAAC,KAAK,EAAE,iBAAiB,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAc3G"}
|
package/lib/config-store.js
CHANGED
|
@@ -1,13 +1,29 @@
|
|
|
1
1
|
import { chmod, mkdir, readFile, writeFile } from 'node:fs/promises';
|
|
2
2
|
import { homedir } from 'node:os';
|
|
3
3
|
import { dirname, join } from 'node:path';
|
|
4
|
-
import { RUNTIME_KEYS, readFlat, writeLegacyPatch, } from '
|
|
4
|
+
import { RUNTIME_KEYS, readFlat, writeLegacyPatch, } from './vendor/store.js';
|
|
5
5
|
function resolveDshHome(dshHome) {
|
|
6
6
|
return dshHome ?? process.env.DSH_HOME ?? join(homedir(), '.dsh');
|
|
7
7
|
}
|
|
8
|
+
/**
|
|
9
|
+
* The keys review reads and may persist: its own gitlab + review settings, plus
|
|
10
|
+
* the one machine-runtime flag that belongs to its own sidecar.
|
|
11
|
+
*
|
|
12
|
+
* Deliberately the READ counterpart of `SAVABLE_KEYS` in `settings-rpc.ts`, plus
|
|
13
|
+
* `lastTunnelRunning` — which review reads from its sidecar and never writes to
|
|
14
|
+
* the shared store. Keep both in step: a key here that is not savable is a read
|
|
15
|
+
* review needs but cannot change, and a savable key missing here is one the UI
|
|
16
|
+
* could write but never read back.
|
|
17
|
+
*/
|
|
18
|
+
const REVIEW_OWNED_KEYS = [
|
|
19
|
+
'gitlabBaseUrl', 'gitlabToken', 'botUsername', 'webhookSecret', 'webhookPort',
|
|
20
|
+
'projectMappings', 'autoRereviewOnPush', 'autoReviewOnAssign',
|
|
21
|
+
'reviewModel', 'agentTimeoutMs', 'reviewSessionRetentionDays',
|
|
22
|
+
'lastTunnelRunning',
|
|
23
|
+
];
|
|
8
24
|
/**
|
|
9
25
|
* Settings live in the SHARED namespaced store (`~/.dsh/dsh-maestro-config/settings.json`,
|
|
10
|
-
* owned by
|
|
26
|
+
* owned by dsh-maestro-core and embedded here at `src/host/vendor/store.ts`); this store is a thin adapter that
|
|
11
27
|
* keeps the package's flat `MaestroUserConfig` API while delegating persistence.
|
|
12
28
|
* Machine runtime state (RUNTIME_KEYS) never enters settings — it stays in this
|
|
13
29
|
* package's own sidecar so a settings edit can never silently flip tunnel state.
|
|
@@ -38,7 +54,31 @@ export async function loadUserConfig(dshHome) {
|
|
|
38
54
|
readFlat({ dshHome }),
|
|
39
55
|
readRuntimeState(dshHome),
|
|
40
56
|
]);
|
|
41
|
-
|
|
57
|
+
// Scope the read to the keys review OWNS. `readFlat` returns the WHOLE store
|
|
58
|
+
// view — every `DOMAIN_KEY_MAP` key across every domain — so returning it
|
|
59
|
+
// unfiltered made review's own settings RPC (`/dsh-maestro-review`,
|
|
60
|
+
// `maestro.getConfig`) answer with remote's tunnel identity: `tunnelHostname`,
|
|
61
|
+
// `tunnelId`, `tunnelCredentialsFile` (the local path of the machine's
|
|
62
|
+
// Cloudflare credentials file), `lanPort`, `lanPinEnabled`, `proxyPort`,
|
|
63
|
+
// `pinSessionTtlHours` — plus the notifier's telegram token and chat id.
|
|
64
|
+
//
|
|
65
|
+
// The leak was one-directional and therefore invisible: `SAVABLE_KEYS` already
|
|
66
|
+
// rejected every one of those keys on a WRITE (with `OWNER_OF` naming the
|
|
67
|
+
// owning plugin), and nothing in review reads them — but the read side handed
|
|
68
|
+
// them to the client anyway.
|
|
69
|
+
//
|
|
70
|
+
// An ALLOWLIST, not a denylist, so a key added to the shared store later
|
|
71
|
+
// cannot leak by default. `tests/config-scope.spec.ts` pins the set.
|
|
72
|
+
const scoped = {};
|
|
73
|
+
for (const key of REVIEW_OWNED_KEYS) {
|
|
74
|
+
const value = flat[key];
|
|
75
|
+
if (value !== undefined)
|
|
76
|
+
scoped[key] = value;
|
|
77
|
+
const local = runtime[key];
|
|
78
|
+
if (local !== undefined)
|
|
79
|
+
scoped[key] = local;
|
|
80
|
+
}
|
|
81
|
+
return scoped;
|
|
42
82
|
}
|
|
43
83
|
export async function saveUserConfig(patch, dshHome) {
|
|
44
84
|
const settingsPatch = {};
|
package/lib/config-store.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config-store.js","sourceRoot":"","sources":["../src/host/config-store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAA;AACpE,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAA;AACjC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAEzC,OAAO,EACL,YAAY,EACZ,QAAQ,EACR,gBAAgB,GACjB,MAAM,
|
|
1
|
+
{"version":3,"file":"config-store.js","sourceRoot":"","sources":["../src/host/config-store.ts"],"names":[],"mappings":"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"}
|