@ddtcorex/dsh-maestro-config 0.3.1 → 0.5.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/cordis.patch.yml +4 -0
- package/lib/client.js +192 -9
- package/lib/types/client/MaestroSettings.d.ts.map +1 -1
- package/lib/types/client/pin-ttl.d.ts +23 -0
- package/lib/types/client/pin-ttl.d.ts.map +1 -0
- package/package.json +1 -1
- package/src/client/MaestroSettings.tsx +189 -32
- package/src/client/pin-ttl.ts +31 -0
- package/lib/types/client/Settings.d.ts +0 -11
- package/lib/types/client/Settings.d.ts.map +0 -1
- package/lib/types/client/maestro-card.d.ts +0 -10
- package/lib/types/client/maestro-card.d.ts.map +0 -1
- package/src/client/maestro-card.jsx +0 -945
package/cordis.patch.yml
CHANGED
|
@@ -3,4 +3,8 @@
|
|
|
3
3
|
- insert:
|
|
4
4
|
- id: maestro-config
|
|
5
5
|
name: '@ddtcorex/dsh-maestro-config'
|
|
6
|
+
# rpc.handle registers a webServer route inside an effect fiber which
|
|
7
|
+
# carries only the row's inject (not the module's); without webServer
|
|
8
|
+
# the row fails on harnesses that provide webServer after rows apply (DSH 0.1.5-rc.1+).
|
|
9
|
+
inject: ['connection', 'webServer']
|
|
6
10
|
config: {}
|
package/lib/client.js
CHANGED
|
@@ -2156,6 +2156,22 @@ function gitlabWebhookUrl(hostname) {
|
|
|
2156
2156
|
return `https://${authority}/hooks/gitlab-mr`;
|
|
2157
2157
|
}
|
|
2158
2158
|
|
|
2159
|
+
// src/client/pin-ttl.ts
|
|
2160
|
+
var PIN_TTL_PRESETS = [
|
|
2161
|
+
{ hours: 0, label: "Session only" },
|
|
2162
|
+
{ hours: 1, label: "1 hour" },
|
|
2163
|
+
{ hours: 8, label: "8 hours" },
|
|
2164
|
+
{ hours: 24, label: "1 day (default)" },
|
|
2165
|
+
{ hours: 168, label: "7 days" },
|
|
2166
|
+
{ hours: 720, label: "30 days" }
|
|
2167
|
+
];
|
|
2168
|
+
var DEFAULT_PIN_TTL_HOURS = 24;
|
|
2169
|
+
var MAX_PIN_TTL_HOURS = 8760;
|
|
2170
|
+
function presetForTtlHours(hours) {
|
|
2171
|
+
const value = hours === void 0 || !Number.isFinite(hours) ? DEFAULT_PIN_TTL_HOURS : hours;
|
|
2172
|
+
return PIN_TTL_PRESETS.some((preset) => preset.hours === value) ? value : null;
|
|
2173
|
+
}
|
|
2174
|
+
|
|
2159
2175
|
// src/client/MaestroSettings.tsx
|
|
2160
2176
|
var t = {
|
|
2161
2177
|
bgLayer1: "var(--dsw-alias-bg-layer-1)",
|
|
@@ -2906,12 +2922,96 @@ function ProjectMappingsEditor({ mappings, onChange, catalog, globalReviewModel
|
|
|
2906
2922
|
label: null
|
|
2907
2923
|
})
|
|
2908
2924
|
)
|
|
2925
|
+
),
|
|
2926
|
+
// Row for per-project trigger overrides — tri-state: inherit the global toggle or force on/off.
|
|
2927
|
+
(0, import_react.createElement)(
|
|
2928
|
+
"div",
|
|
2929
|
+
{ "data-maestro-project-triggers-row": "", style: { display: "flex", gap: 12, flexWrap: "wrap", alignItems: "flex-start" } },
|
|
2930
|
+
(0, import_react.createElement)(
|
|
2931
|
+
"label",
|
|
2932
|
+
{ style: { ...fieldLabelStyle, flex: "1 1 160px", minWidth: 0 } },
|
|
2933
|
+
"Re-review on push",
|
|
2934
|
+
(0, import_react.createElement)(
|
|
2935
|
+
"select",
|
|
2936
|
+
{
|
|
2937
|
+
value: row.rereviewOnPush === void 0 ? "inherit" : row.rereviewOnPush ? "on" : "off",
|
|
2938
|
+
onChange: (e) => updateRow(i, "rereviewOnPush", e.target.value === "inherit" ? void 0 : e.target.value === "on"),
|
|
2939
|
+
"aria-label": `Re-review on push ${i + 1}`,
|
|
2940
|
+
style: {
|
|
2941
|
+
height: 36,
|
|
2942
|
+
width: "100%",
|
|
2943
|
+
padding: "0 14px",
|
|
2944
|
+
border: "none",
|
|
2945
|
+
borderRadius: 18,
|
|
2946
|
+
background: "var(--dsw-alias-bg-module-platform, #F5F6F7)",
|
|
2947
|
+
color: t.labelPrimary,
|
|
2948
|
+
font: "inherit",
|
|
2949
|
+
fontSize: 13
|
|
2950
|
+
}
|
|
2951
|
+
},
|
|
2952
|
+
(0, import_react.createElement)("option", { value: "inherit" }, "Inherit (global)"),
|
|
2953
|
+
(0, import_react.createElement)("option", { value: "on" }, "On"),
|
|
2954
|
+
(0, import_react.createElement)("option", { value: "off" }, "Off")
|
|
2955
|
+
)
|
|
2956
|
+
),
|
|
2957
|
+
(0, import_react.createElement)(
|
|
2958
|
+
"label",
|
|
2959
|
+
{ style: { ...fieldLabelStyle, flex: "1 1 160px", minWidth: 0 } },
|
|
2960
|
+
"Review on assign",
|
|
2961
|
+
(0, import_react.createElement)(
|
|
2962
|
+
"select",
|
|
2963
|
+
{
|
|
2964
|
+
value: row.reviewOnAssign === void 0 ? "inherit" : row.reviewOnAssign ? "on" : "off",
|
|
2965
|
+
onChange: (e) => updateRow(i, "reviewOnAssign", e.target.value === "inherit" ? void 0 : e.target.value === "on"),
|
|
2966
|
+
"aria-label": `Review on assign ${i + 1}`,
|
|
2967
|
+
style: {
|
|
2968
|
+
height: 36,
|
|
2969
|
+
width: "100%",
|
|
2970
|
+
padding: "0 14px",
|
|
2971
|
+
border: "none",
|
|
2972
|
+
borderRadius: 18,
|
|
2973
|
+
background: "var(--dsw-alias-bg-module-platform, #F5F6F7)",
|
|
2974
|
+
color: t.labelPrimary,
|
|
2975
|
+
font: "inherit",
|
|
2976
|
+
fontSize: 13
|
|
2977
|
+
}
|
|
2978
|
+
},
|
|
2979
|
+
(0, import_react.createElement)("option", { value: "inherit" }, "Inherit (global)"),
|
|
2980
|
+
(0, import_react.createElement)("option", { value: "on" }, "On"),
|
|
2981
|
+
(0, import_react.createElement)("option", { value: "off" }, "Off")
|
|
2982
|
+
)
|
|
2983
|
+
)
|
|
2909
2984
|
)
|
|
2910
2985
|
)
|
|
2911
2986
|
)
|
|
2912
2987
|
)
|
|
2913
2988
|
);
|
|
2914
2989
|
}
|
|
2990
|
+
function SecretField({ placeholder, hasSaved, onSave, width }) {
|
|
2991
|
+
const [draft, setDraft] = (0, import_react.useState)("");
|
|
2992
|
+
(0, import_react.useEffect)(() => {
|
|
2993
|
+
if (!hasSaved) setDraft("");
|
|
2994
|
+
}, [hasSaved]);
|
|
2995
|
+
const commit = () => {
|
|
2996
|
+
if (draft !== "") {
|
|
2997
|
+
onSave(draft);
|
|
2998
|
+
setDraft("");
|
|
2999
|
+
}
|
|
3000
|
+
};
|
|
3001
|
+
return (0, import_react.createElement)(FieldInput, {
|
|
3002
|
+
placeholder: hasSaved === true ? "saved \u2014 type new value to replace" : placeholder,
|
|
3003
|
+
type: "password",
|
|
3004
|
+
autoComplete: "off",
|
|
3005
|
+
value: draft,
|
|
3006
|
+
onChange: (e) => setDraft(e.target.value),
|
|
3007
|
+
onBlur: commit,
|
|
3008
|
+
onKeyDown: (e) => {
|
|
3009
|
+
if (e.key === "Enter") e.target.blur();
|
|
3010
|
+
},
|
|
3011
|
+
"aria-label": placeholder,
|
|
3012
|
+
style: { width: width ?? 200 }
|
|
3013
|
+
});
|
|
3014
|
+
}
|
|
2915
3015
|
function LanAccess({ proxyStatus, lanPin }) {
|
|
2916
3016
|
const urls = proxyStatus?.lanUrls ?? [];
|
|
2917
3017
|
const [selected, setSelected] = (0, import_react.useState)(0);
|
|
@@ -2919,6 +3019,14 @@ function LanAccess({ proxyStatus, lanPin }) {
|
|
|
2919
3019
|
if (!proxyStatus?.running) {
|
|
2920
3020
|
return (0, import_react.createElement)("p", { style: { color: t.stateError, fontSize: 12, margin: "8px 0 0" } }, proxyStatus?.errorMessage ?? "Proxy not running");
|
|
2921
3021
|
}
|
|
3022
|
+
if (urls.length === 0) {
|
|
3023
|
+
return (0, import_react.createElement)(
|
|
3024
|
+
"div",
|
|
3025
|
+
null,
|
|
3026
|
+
(0, import_react.createElement)("p", { style: captionStyle }, "No LAN listener is configured \u2014 set a LAN port under Tunnel to expose one."),
|
|
3027
|
+
lanPin !== null ? (0, import_react.createElement)(LanPinRow, { lanPin }) : null
|
|
3028
|
+
);
|
|
3029
|
+
}
|
|
2922
3030
|
return (0, import_react.createElement)(
|
|
2923
3031
|
"div",
|
|
2924
3032
|
null,
|
|
@@ -2959,7 +3067,56 @@ function LanPinRow({ lanPin }) {
|
|
|
2959
3067
|
) : null
|
|
2960
3068
|
);
|
|
2961
3069
|
}
|
|
2962
|
-
function
|
|
3070
|
+
function PinSessionTtl({ hours, onSave }) {
|
|
3071
|
+
const selected = presetForTtlHours(hours);
|
|
3072
|
+
const [custom, setCustom] = (0, import_react.useState)(false);
|
|
3073
|
+
const [draft, setDraft] = (0, import_react.useState)("");
|
|
3074
|
+
const customActive = custom || selected === null;
|
|
3075
|
+
const commit = () => {
|
|
3076
|
+
const parsed = Number(draft);
|
|
3077
|
+
if (!Number.isInteger(parsed) || parsed < 0 || parsed > MAX_PIN_TTL_HOURS) return;
|
|
3078
|
+
onSave(parsed);
|
|
3079
|
+
};
|
|
3080
|
+
return (0, import_react.createElement)(
|
|
3081
|
+
"div",
|
|
3082
|
+
{ "data-maestro-pin-ttl": "", style: { display: "flex", flexDirection: "column", gap: 6, alignItems: "flex-end" } },
|
|
3083
|
+
(0, import_react.createElement)(
|
|
3084
|
+
"select",
|
|
3085
|
+
{
|
|
3086
|
+
"data-maestro-pin-ttl-select": "",
|
|
3087
|
+
value: customActive ? "custom" : String(selected),
|
|
3088
|
+
onChange: (e) => {
|
|
3089
|
+
const next = e.target.value;
|
|
3090
|
+
if (next === "custom") {
|
|
3091
|
+
setCustom(true);
|
|
3092
|
+
setDraft(String(hours ?? 24));
|
|
3093
|
+
return;
|
|
3094
|
+
}
|
|
3095
|
+
setCustom(false);
|
|
3096
|
+
onSave(Number(next));
|
|
3097
|
+
},
|
|
3098
|
+
style: { height: 36, padding: "0 12px", border: `1px solid ${t.borderL2}`, borderRadius: 18, background: "var(--dsw-alias-bg-module-platform, #F5F6F7)", color: t.labelPrimary, font: "inherit", fontSize: 13 }
|
|
3099
|
+
},
|
|
3100
|
+
...PIN_TTL_PRESETS.map((preset) => (0, import_react.createElement)("option", { key: preset.hours, value: String(preset.hours) }, preset.label)),
|
|
3101
|
+
(0, import_react.createElement)("option", { key: "custom", value: "custom" }, "Custom\u2026")
|
|
3102
|
+
),
|
|
3103
|
+
customActive ? (0, import_react.createElement)(FieldInput, {
|
|
3104
|
+
"data-maestro-pin-ttl-custom": "",
|
|
3105
|
+
inputMode: "numeric",
|
|
3106
|
+
placeholder: "hours",
|
|
3107
|
+
value: draft,
|
|
3108
|
+
min: 1,
|
|
3109
|
+
max: MAX_PIN_TTL_HOURS,
|
|
3110
|
+
onChange: (e) => setDraft(e.target.value),
|
|
3111
|
+
onBlur: commit,
|
|
3112
|
+
onKeyDown: (e) => {
|
|
3113
|
+
if (e.key === "Enter") commit();
|
|
3114
|
+
},
|
|
3115
|
+
style: { width: 120 }
|
|
3116
|
+
}) : null
|
|
3117
|
+
);
|
|
3118
|
+
}
|
|
3119
|
+
function PublicAccess({ status, pin, showPin, onRevealPin, onHidePin, onRotatePin, pinTtlHours, onSavePinTtl }) {
|
|
2963
3120
|
return (0, import_react.createElement)(
|
|
2964
3121
|
"div",
|
|
2965
3122
|
null,
|
|
@@ -2981,7 +3138,15 @@ function PublicAccess({ status, pin, showPin, onRevealPin, onHidePin, onRotatePi
|
|
|
2981
3138
|
showPin ? (0, import_react.createElement)(Button, { variant: "outline", size: "sm", onClick: onHidePin }, "Hide") : (0, import_react.createElement)(Button, { variant: "outline", size: "sm", onClick: onRevealPin }, "Show"),
|
|
2982
3139
|
(0, import_react.createElement)(Button, { variant: "outline", size: "sm", onClick: onRotatePin }, "Rotate")
|
|
2983
3140
|
),
|
|
2984
|
-
(0, import_react.createElement)("p", { style: captionStyle }, "Stays the same across tunnel and DSH restarts; use Rotate when you need a new PIN.")
|
|
3141
|
+
(0, import_react.createElement)("p", { style: captionStyle }, "Stays the same across tunnel and DSH restarts; use Rotate when you need a new PIN."),
|
|
3142
|
+
(0, import_react.createElement)(SettingRow, {
|
|
3143
|
+
title: "PIN session duration",
|
|
3144
|
+
// The caption lives in the description column on purpose: a long caption
|
|
3145
|
+
// inside the control column claims its intrinsic width (min-width: auto)
|
|
3146
|
+
// and squeezes the title/description to a few characters per line.
|
|
3147
|
+
description: "How long a browser stays signed in after entering the PIN. Applies to the next login; covers the public tunnel and LAN access.",
|
|
3148
|
+
control: (0, import_react.createElement)(PinSessionTtl, { hours: pinTtlHours, onSave: onSavePinTtl })
|
|
3149
|
+
})
|
|
2985
3150
|
);
|
|
2986
3151
|
}
|
|
2987
3152
|
function NamedTunnelSetupNote() {
|
|
@@ -3409,11 +3574,28 @@ function MaestroSettingsTab({ rpcCall, configRpcCall }) {
|
|
|
3409
3574
|
setBusy(false);
|
|
3410
3575
|
}
|
|
3411
3576
|
};
|
|
3577
|
+
const SECRET_SAVE_FLAGS = { gitlabToken: "hasGitlabToken", webhookSecret: "hasWebhookSecret" };
|
|
3412
3578
|
const saveField = async (field, value) => {
|
|
3413
3579
|
setError(null);
|
|
3414
|
-
setConfig((prev) =>
|
|
3580
|
+
setConfig((prev) => {
|
|
3581
|
+
const next = { ...prev, [field]: value };
|
|
3582
|
+
const flag = SECRET_SAVE_FLAGS[field];
|
|
3583
|
+
if (flag) {
|
|
3584
|
+
delete next[field];
|
|
3585
|
+
next[flag] = value !== "";
|
|
3586
|
+
}
|
|
3587
|
+
return next;
|
|
3588
|
+
});
|
|
3415
3589
|
try {
|
|
3416
|
-
await call(MAESTRO_ENDPOINTS.saveConfig, { [field]: value });
|
|
3590
|
+
const saved = await call(MAESTRO_ENDPOINTS.saveConfig, { [field]: value });
|
|
3591
|
+
if (saved && typeof saved === "object") {
|
|
3592
|
+
setConfig((prev) => {
|
|
3593
|
+
const next = { ...prev, ...saved };
|
|
3594
|
+
const flag = SECRET_SAVE_FLAGS[field];
|
|
3595
|
+
if (flag) delete next[field];
|
|
3596
|
+
return next;
|
|
3597
|
+
});
|
|
3598
|
+
}
|
|
3417
3599
|
} catch (err) {
|
|
3418
3600
|
setError(err.message);
|
|
3419
3601
|
}
|
|
@@ -3441,16 +3623,16 @@ function MaestroSettingsTab({ rpcCall, configRpcCall }) {
|
|
|
3441
3623
|
(0, import_react.createElement)(SettingRow, { title: "Hostname", description: "Public hostname for the tunnel.", control: (0, import_react.createElement)(FieldInput, { placeholder: "dsh.example.com", value: config.tunnelHostname ?? "", onChange: (e) => saveField("tunnelHostname", e.target.value), style: { width: 260 } }) })
|
|
3442
3624
|
) : null,
|
|
3443
3625
|
(0, import_react.createElement)("div", { style: { display: "flex", gap: 8, flexWrap: "wrap", padding: "12px 0", borderBottom: `1px solid ${t.borderL2}` } }, status?.running ? (0, import_react.createElement)(Button, { variant: "outline", size: "md", disabled: busy, onClick: stopTunnel }, "Stop tunnel") : (0, import_react.createElement)(Button, { variant: "primary", size: "md", disabled: busy, onClick: startTunnel }, "Start tunnel")),
|
|
3444
|
-
(0, import_react.createElement)("div", { style: { ...cardInsetStyle, marginTop: "12px" } }, (0, import_react.createElement)("div", { style: { fontSize: 13, fontWeight: 600, color: t.labelPrimary } }, "
|
|
3445
|
-
(0, import_react.createElement)("div", { style: { ...cardInsetStyle, marginTop: "12px" } }, (0, import_react.createElement)("div", { style: { fontSize: 13, fontWeight: 600, color: t.labelPrimary } }, "
|
|
3626
|
+
(0, import_react.createElement)("div", { style: { ...cardInsetStyle, marginTop: "12px" } }, (0, import_react.createElement)("div", { style: { fontSize: 13, fontWeight: 600, color: t.labelPrimary } }, "Public access"), (0, import_react.createElement)(PublicAccess, { status, pin, showPin, onRevealPin: revealPin, onHidePin: () => setShowPin(false), onRotatePin: rotatePin, pinTtlHours: config.pinSessionTtlHours, onSavePinTtl: (value) => saveField("pinSessionTtlHours", value) })),
|
|
3627
|
+
(0, import_react.createElement)("div", { style: { ...cardInsetStyle, marginTop: "12px" } }, (0, import_react.createElement)("div", { style: { fontSize: 13, fontWeight: 600, color: t.labelPrimary } }, "Remote access \u2014 LAN"), (0, import_react.createElement)(LanAccess, { proxyStatus, lanPin: lanPinEnabled === null ? null : { enabled: lanPinEnabled, pin: lanPin, show: showLanPin, onShow: revealLanPin, onHide: () => setShowLanPin(false), onRotate: rotateLanPin, onToggle: toggleLanPin } }))
|
|
3446
3628
|
),
|
|
3447
3629
|
gitlab: (0, import_react.createElement)(
|
|
3448
3630
|
"div",
|
|
3449
3631
|
{ style: { display: "flex", flexDirection: "column" } },
|
|
3450
3632
|
(0, import_react.createElement)(SettingRow, { title: "GitLab base URL", description: "e.g. https://gitlab.example.com", control: (0, import_react.createElement)(FieldInput, { placeholder: "https://gitlab.example.com", value: config.gitlabBaseUrl ?? "", onChange: (e) => saveField("gitlabBaseUrl", e.target.value), style: { width: 260 } }) }),
|
|
3451
|
-
(0, import_react.createElement)(SettingRow, { title: "GitLab token", description: "Personal access token with api scope.", control: (0, import_react.createElement)("div", { style: { display: "flex", gap: 8, alignItems: "center" } }, (0, import_react.createElement)(
|
|
3633
|
+
(0, import_react.createElement)(SettingRow, { title: "GitLab token", description: "Personal access token with api scope.", control: (0, import_react.createElement)("div", { style: { display: "flex", gap: 8, alignItems: "center" } }, (0, import_react.createElement)(SecretField, { placeholder: "GitLab token", hasSaved: config.hasGitlabToken === true, width: 200, onSave: (v) => saveField("gitlabToken", v) }), config.hasGitlabToken ? (0, import_react.createElement)(Button, { variant: "outline", size: "md", onClick: () => saveField("gitlabToken", "") }, "Clear") : null) }),
|
|
3452
3634
|
(0, import_react.createElement)(SettingRow, { title: "Bot username", description: "Username of the bot that posts reviews.", control: (0, import_react.createElement)(FieldInput, { placeholder: "maestro-bot", value: config.botUsername ?? "", onChange: (e) => saveField("botUsername", e.target.value), style: { width: 220 } }) }),
|
|
3453
|
-
(0, import_react.createElement)(SettingRow, { title: "Webhook secret", description: "Secret token for GitLab webhooks.", control: (0, import_react.createElement)("div", { style: { display: "flex", gap: 8, alignItems: "center" } }, (0, import_react.createElement)(
|
|
3635
|
+
(0, import_react.createElement)(SettingRow, { title: "Webhook secret", description: "Secret token for GitLab webhooks.", control: (0, import_react.createElement)("div", { style: { display: "flex", gap: 8, alignItems: "center" } }, (0, import_react.createElement)(SecretField, { placeholder: "Webhook secret", hasSaved: config.hasWebhookSecret === true, width: 200, onSave: (v) => saveField("webhookSecret", v) }), (0, import_react.createElement)(Button, { variant: "outline", size: "md", onClick: () => saveField("webhookSecret", generateWebhookSecret()) }, "Generate")) }),
|
|
3454
3636
|
(0, import_react.createElement)(
|
|
3455
3637
|
"div",
|
|
3456
3638
|
{ style: { padding: "16px 0", display: "flex", flexDirection: "column", gap: 6 } },
|
|
@@ -3462,6 +3644,7 @@ function MaestroSettingsTab({ rpcCall, configRpcCall }) {
|
|
|
3462
3644
|
"div",
|
|
3463
3645
|
{ style: { display: "flex", flexDirection: "column" } },
|
|
3464
3646
|
(0, import_react.createElement)(ToggleRow, { title: "Re-review on push", description: "When new commits are pushed, trigger an automatic re-review.", checked: config.autoRereviewOnPush === true, onChange: (v) => saveField("autoRereviewOnPush", v) }),
|
|
3647
|
+
(0, import_react.createElement)(ToggleRow, { title: "Review on assign", description: "When the bot is assigned as reviewer, trigger an automatic review.", checked: config.autoReviewOnAssign !== false, onChange: (v) => saveField("autoReviewOnAssign", v) }),
|
|
3465
3648
|
(0, import_react.createElement)(SettingRow, { title: "Global review model", description: "Model for automated reviews. Empty = DSH default.", control: (0, import_react.createElement)(ReviewModelSelector, { value: config.reviewModel ?? null, catalog, fallbackValue: catalog?.current ?? null, fallbackLabel: "Use DSH default", onChange: (v) => saveField("reviewModel", v), label: null }) }),
|
|
3466
3649
|
(0, import_react.createElement)(ProjectMappingsEditor, { mappings: config.projectMappings ?? [], onChange: (mappings) => saveField("projectMappings", mappings), catalog, globalReviewModel: config.reviewModel ?? null })
|
|
3467
3650
|
),
|
|
@@ -3519,7 +3702,7 @@ function MaestroSettingsTab({ rpcCall, configRpcCall }) {
|
|
|
3519
3702
|
"div",
|
|
3520
3703
|
{ style: { display: "flex", flexDirection: "column" } },
|
|
3521
3704
|
(0, import_react.createElement)("div", { style: { padding: "12px 0", borderBottom: `1px solid ${t.borderL2}` } }, (0, import_react.createElement)("p", { style: captionStyle }, "Telegram bot settings for notifications: startup PIN, review digests, PIN rotation. Leave blank to disable.")),
|
|
3522
|
-
(0, import_react.createElement)(SettingRow, { title: "Bot token", description: "Telegram bot token from @BotFather.", control: (0, import_react.createElement)("div", { style: { display: "flex", gap: 8, alignItems: "center" } }, (0, import_react.createElement)(
|
|
3705
|
+
(0, import_react.createElement)(SettingRow, { title: "Bot token", description: "Telegram bot token from @BotFather.", control: (0, import_react.createElement)("div", { style: { display: "flex", gap: 8, alignItems: "center" } }, (0, import_react.createElement)(SecretField, { placeholder: "123456:ABC-DEF...", hasSaved: (notifierCfg.telegram?.botToken ?? "") !== "", width: 220, onSave: (v) => saveNotifierCfg({ telegram: { botToken: v } }) }), notifierCfg.telegram?.botToken ? (0, import_react.createElement)(Button, { variant: "outline", size: "md", onClick: () => saveNotifierCfg({ telegram: { botToken: "" } }) }, "Clear") : null) }),
|
|
3523
3706
|
(0, import_react.createElement)(SettingRow, { title: "Chat ID", description: "Target chat, e.g. -1001234567890.", control: (0, import_react.createElement)(FieldInput, { value: notifierCfg.telegram?.chatId ?? "", placeholder: "-1001234567890", onChange: (e) => saveNotifierCfg({ telegram: { chatId: e.target.value } }), style: { width: 220 } }) }),
|
|
3524
3707
|
(0, import_react.createElement)(ToggleRow, { title: "Review notifications", description: "Also notify about finished reviews.", checked: notifierCfg.policy?.reviewNotifications === true, onChange: (v) => saveNotifierCfg({ policy: { reviewNotifications: v } }) })
|
|
3525
3708
|
)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MaestroSettings.d.ts","sourceRoot":"","sources":["../../../src/client/MaestroSettings.tsx"],"names":[],"mappings":"AACA;;;;;;;;GAQG;
|
|
1
|
+
{"version":3,"file":"MaestroSettings.d.ts","sourceRoot":"","sources":["../../../src/client/MaestroSettings.tsx"],"names":[],"mappings":"AACA;;;;;;;;GAQG;AAw0CH,wBAAgB,kBAAkB,CAAC,EAAE,OAAO,EAAE,aAAa,EAAE,EAAE;IAAE,OAAO,EAAE,GAAG,CAAC;IAAC,aAAa,CAAC,EAAE,GAAG,CAAA;CAAE;;;;;;;;;mBA4ZmE,GAAG;;gBAoBzK"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PIN login-cookie lifetimes offered by the Settings card.
|
|
3
|
+
* `0` = session cookie (expires when the browser closes); an absent setting
|
|
4
|
+
* means the dsh-maestro-remote default of 24 hours.
|
|
5
|
+
*/
|
|
6
|
+
export declare const PIN_TTL_PRESETS: ReadonlyArray<{
|
|
7
|
+
hours: number;
|
|
8
|
+
label: string;
|
|
9
|
+
}>;
|
|
10
|
+
/** Mirrors DEFAULT_PIN_SESSION_TTL_HOURS in dsh-maestro-remote. */
|
|
11
|
+
export declare const DEFAULT_PIN_TTL_HOURS = 24;
|
|
12
|
+
/**
|
|
13
|
+
* Mirrors MAX_PIN_SESSION_TTL_HOURS in dsh-maestro-remote. This bound only
|
|
14
|
+
* hints the custom input — the host resolver and the settings RPC enforce it.
|
|
15
|
+
*/
|
|
16
|
+
export declare const MAX_PIN_TTL_HOURS = 8760;
|
|
17
|
+
/**
|
|
18
|
+
* The preset hours to show as selected, or `null` when the stored value is not
|
|
19
|
+
* a preset (the caller then shows the "Custom…" option). An unset or unusable
|
|
20
|
+
* value falls back to the product default.
|
|
21
|
+
*/
|
|
22
|
+
export declare function presetForTtlHours(hours: number | undefined): number | null;
|
|
23
|
+
//# sourceMappingURL=pin-ttl.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pin-ttl.d.ts","sourceRoot":"","sources":["../../../src/client/pin-ttl.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,eAAO,MAAM,eAAe,EAAE,aAAa,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAO3E,CAAA;AAED,mEAAmE;AACnE,eAAO,MAAM,qBAAqB,KAAK,CAAA;AACvC;;;GAGG;AACH,eAAO,MAAM,iBAAiB,OAAO,CAAA;AAErC;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI,CAG1E"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ddtcorex/dsh-maestro-config",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Maestro Config — shared settings service for the dsh-maestro-* suite over the single namespaced store (~/.dsh/maestro/settings.json)",
|
|
6
6
|
"type": "module",
|