@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 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 PublicAccess({ status, pin, showPin, onRevealPin, onHidePin, onRotatePin }) {
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) => ({ ...prev, [field]: value }));
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 } }, "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 } })),
3445
- (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 }))
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)(FieldInput, { type: "password", autoComplete: "off", value: config.hasGitlabToken ? "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022" : "", placeholder: "GitLab token", onChange: (e) => saveField("gitlabToken", e.target.value), style: { width: 200 } }), config.hasGitlabToken ? (0, import_react.createElement)(Button, { variant: "outline", size: "md", onClick: () => saveField("gitlabToken", "") }, "Clear") : null) }),
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)(FieldInput, { type: "password", autoComplete: "off", value: config.hasWebhookSecret ? "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022" : "", placeholder: "Webhook secret", onChange: (e) => saveField("webhookSecret", e.target.value), style: { width: 200 } }), (0, import_react.createElement)(Button, { variant: "outline", size: "md", onClick: () => saveField("webhookSecret", generateWebhookSecret()) }, "Generate")) }),
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)(FieldInput, { type: "password", autoComplete: "off", value: notifierCfg.telegram?.botToken ?? "", placeholder: "123456:ABC-DEF...", onChange: (e) => saveNotifierCfg({ telegram: { botToken: e.target.value } }), style: { width: 220 } }), notifierCfg.telegram?.botToken ? (0, import_react.createElement)(Button, { variant: "outline", size: "md", onClick: () => saveNotifierCfg({ telegram: { botToken: "" } }) }, "Clear") : null) }),
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;AAisCH,wBAAgB,kBAAkB,CAAC,EAAE,OAAO,EAAE,aAAa,EAAE,EAAE;IAAE,OAAO,EAAE,GAAG,CAAC;IAAC,aAAa,CAAC,EAAE,GAAG,CAAA;CAAE;;;;;;;;;mBAsYmE,GAAG;;gBAoBzK"}
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.1",
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",