plugin-scanner 2.0.596__py3-none-any.whl → 2.0.597__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,4 @@
1
- import { K as requireReact, L as getDefaultExportFromCjs, j as jsxRuntimeExports, l as HiMiniShieldCheck, M as HiMiniLockClosed, N as HiMiniBellAlert, O as HiMiniAdjustmentsHorizontal, Q as HiMiniCog6Tooth, R as HiMiniCircleStack, r as reactExports, T as TabBar, x as HiMiniChevronRight, U as fetchSettings, V as fetchRuntimeSnapshot, W as revokeApprovalGateCooldown, X as enrollApprovalGateTotp, Y as verifyApprovalGateTotp, Z as disableApprovalGateTotp, _ as updateSettings, $ as clearPolicy, a0 as clearReviewQueue, a1 as clearEvidence, a2 as exportDiagnostics, a3 as repairApprovalCenter, a4 as exportSettings, a5 as importSettings, a6 as resetSettings, a7 as setupDesktopNotifications, b as EmptyState, e as GuardHero, a8 as Tag, a9 as HiMiniMagnifyingGlass, S as SectionLabel, B as Badge, A as ActionButton, d as HiMiniCheckCircle, v as HiMiniExclamationTriangle, aa as approvalGateCooldownLabel, u as useFocusTrap, o as HiMiniXMark } from "../guard-dashboard.js";
1
+ import { K as requireReact, L as getDefaultExportFromCjs, j as jsxRuntimeExports, r as reactExports, u as useFocusTrap, M as HiMiniKey, S as SectionLabel, A as ActionButton, l as HiMiniShieldCheck, N as HiMiniLockClosed, O as HiMiniBellAlert, Q as HiMiniAdjustmentsHorizontal, R as HiMiniCog6Tooth, T as HiMiniCircleStack, U as TabBar, x as HiMiniChevronRight, V as fetchSettings, W as fetchRuntimeSnapshot, X as updateSettings, Y as clearPolicy, Z as clearReviewQueue, _ as revokeApprovalGateCooldown, $ as disableApprovalGateTotp, a0 as enrollApprovalGateTotp, a1 as verifyApprovalGateTotp, a2 as clearEvidence, a3 as exportDiagnostics, a4 as repairApprovalCenter, a5 as exportSettings, a6 as importSettings, a7 as resetSettings, a8 as setupDesktopNotifications, b as EmptyState, e as GuardHero, a9 as Tag, aa as HiMiniMagnifyingGlass, d as HiMiniCheckCircle, v as HiMiniExclamationTriangle, ab as approvalGateCooldownLabel, o as HiMiniXMark } from "../guard-dashboard.js";
2
2
  import { a as resolveProtectionLevelCopy } from "./runtime-overview.js";
3
3
  import { f as filterSettingsBySearch, R as RISK_CONTROL_CONSEQUENCES, s as securityLevelLabel } from "./app-catalog.js";
4
4
  var lib = {};
@@ -1269,6 +1269,282 @@ function TotpEnrollmentQrPanel({ enrollment }) {
1269
1269
  ] })
1270
1270
  ] }) });
1271
1271
  }
1272
+ function resolveSettingsSaveProofKind(input) {
1273
+ if (!input.wasConfigured && input.draftGateEnabled) {
1274
+ return "setup-gate";
1275
+ }
1276
+ if (input.wasConfigured && input.draftGateEnabled && !input.savedGateEnabled) {
1277
+ return "verify-save";
1278
+ }
1279
+ if (input.savedGateEnabled) {
1280
+ return "verify-save";
1281
+ }
1282
+ return null;
1283
+ }
1284
+ function requiresSettingsSaveProof(kind) {
1285
+ return kind !== null;
1286
+ }
1287
+ function resolveSettingsSaveProofModalCopy(input) {
1288
+ if (input.mode === "setup-gate") {
1289
+ return {
1290
+ title: "Set your approval password",
1291
+ detail: "Choose a password Guard will ask for before allow or trust changes stick.",
1292
+ confirmLabel: "Save settings"
1293
+ };
1294
+ }
1295
+ if (input.mode === "change-password") {
1296
+ return {
1297
+ title: "Change approval password",
1298
+ detail: "Enter your current password, then choose a new one.",
1299
+ confirmLabel: "Update password"
1300
+ };
1301
+ }
1302
+ if (input.mode === "maintenance") {
1303
+ if (input.maintenanceAction === "clear-approvals") {
1304
+ return {
1305
+ title: "Clear saved approvals",
1306
+ detail: "Guard needs fresh proof before it removes saved allow decisions.",
1307
+ confirmLabel: "Clear approvals"
1308
+ };
1309
+ }
1310
+ if (input.maintenanceAction === "clear-queue") {
1311
+ return {
1312
+ title: "Clear review queue",
1313
+ detail: "Guard needs fresh proof before it removes pending review items.",
1314
+ confirmLabel: "Clear queue"
1315
+ };
1316
+ }
1317
+ if (input.maintenanceAction === "revoke-cooldown") {
1318
+ return {
1319
+ title: "Revoke cooldown",
1320
+ detail: "Confirm your identity before Guard ends the active cooldown.",
1321
+ confirmLabel: "Revoke cooldown"
1322
+ };
1323
+ }
1324
+ if (input.maintenanceAction === "disable-totp") {
1325
+ return {
1326
+ title: "Disconnect authenticator",
1327
+ detail: "Confirm your approval password and a current app code to remove this second factor.",
1328
+ confirmLabel: "Disconnect"
1329
+ };
1330
+ }
1331
+ return {
1332
+ title: "Confirm your identity",
1333
+ detail: "Guard needs fresh proof before this cleanup can continue.",
1334
+ confirmLabel: "Continue"
1335
+ };
1336
+ }
1337
+ if (input.gateSettingsChanged) {
1338
+ return {
1339
+ title: "Confirm before saving gate changes",
1340
+ detail: "Enter your approval password so Guard can apply the gate updates you chose.",
1341
+ confirmLabel: "Save settings"
1342
+ };
1343
+ }
1344
+ return {
1345
+ title: "Confirm before saving",
1346
+ detail: "Enter your approval password so Guard can save these settings.",
1347
+ confirmLabel: "Save settings"
1348
+ };
1349
+ }
1350
+ function isSettingsSaveProofSubmitDisabled(mode2, credentials, totpRequired) {
1351
+ const current = credentials.currentPassword?.trim() ?? "";
1352
+ const next = credentials.newPassword?.trim() ?? "";
1353
+ const confirm = credentials.confirmPassword?.trim() ?? "";
1354
+ const totp = credentials.totpCode?.trim() ?? "";
1355
+ if (mode2 === "setup-gate") {
1356
+ return next.length === 0 || confirm.length === 0;
1357
+ }
1358
+ if (mode2 === "change-password") {
1359
+ if (current.length === 0 || next.length === 0 || confirm.length === 0) {
1360
+ return true;
1361
+ }
1362
+ return totpRequired && totp.length === 0;
1363
+ }
1364
+ if (current.length === 0) {
1365
+ return true;
1366
+ }
1367
+ return totpRequired && totp.length === 0;
1368
+ }
1369
+ function SettingsSaveProofModal(props) {
1370
+ const dialogRef = reactExports.useRef(null);
1371
+ const passwordRef = reactExports.useRef(null);
1372
+ const [currentPassword, setCurrentPassword] = reactExports.useState("");
1373
+ const [newPassword, setNewPassword] = reactExports.useState("");
1374
+ const [confirmPassword, setConfirmPassword] = reactExports.useState("");
1375
+ const [totpCode, setTotpCode] = reactExports.useState("");
1376
+ useFocusTrap(props.open, dialogRef);
1377
+ reactExports.useEffect(() => {
1378
+ if (!props.open) {
1379
+ setCurrentPassword("");
1380
+ setNewPassword("");
1381
+ setConfirmPassword("");
1382
+ setTotpCode("");
1383
+ return;
1384
+ }
1385
+ const timer = setTimeout(() => {
1386
+ passwordRef.current?.focus();
1387
+ }, 50);
1388
+ return () => clearTimeout(timer);
1389
+ }, [props.open, props.mode]);
1390
+ reactExports.useEffect(() => {
1391
+ if (props.open) {
1392
+ document.documentElement.dataset.guardModalOpen = String(
1393
+ Number(document.documentElement.dataset.guardModalOpen ?? 0) + 1
1394
+ );
1395
+ return () => {
1396
+ const count = Number(document.documentElement.dataset.guardModalOpen ?? 1) - 1;
1397
+ if (count <= 0) {
1398
+ delete document.documentElement.dataset.guardModalOpen;
1399
+ } else {
1400
+ document.documentElement.dataset.guardModalOpen = String(count);
1401
+ }
1402
+ };
1403
+ }
1404
+ return void 0;
1405
+ }, [props.open]);
1406
+ const totpRequired = props.gate?.totp_enabled === true && (props.mode === "verify-save" || props.mode === "change-password" || props.mode === "maintenance");
1407
+ const handleCurrentPasswordChange = reactExports.useCallback((event) => {
1408
+ setCurrentPassword(event.target.value);
1409
+ }, []);
1410
+ const handleNewPasswordChange = reactExports.useCallback((event) => {
1411
+ setNewPassword(event.target.value);
1412
+ }, []);
1413
+ const handleConfirmPasswordChange = reactExports.useCallback((event) => {
1414
+ setConfirmPassword(event.target.value);
1415
+ }, []);
1416
+ const handleTotpChange = reactExports.useCallback((event) => {
1417
+ setTotpCode(event.target.value);
1418
+ }, []);
1419
+ const handleBackdropClick = reactExports.useCallback(
1420
+ (event) => {
1421
+ if (event.target === event.currentTarget && !props.pending) {
1422
+ props.onCancel();
1423
+ }
1424
+ },
1425
+ [props.onCancel, props.pending]
1426
+ );
1427
+ const handleConfirm = reactExports.useCallback(() => {
1428
+ props.onConfirm({
1429
+ ...currentPassword.trim().length > 0 ? { currentPassword } : {},
1430
+ ...newPassword.trim().length > 0 ? { newPassword } : {},
1431
+ ...confirmPassword.trim().length > 0 ? { confirmPassword } : {},
1432
+ ...totpCode.trim().length > 0 ? { totpCode } : {}
1433
+ });
1434
+ }, [confirmPassword, currentPassword, newPassword, props, totpCode]);
1435
+ const credentials = {
1436
+ currentPassword,
1437
+ newPassword,
1438
+ confirmPassword,
1439
+ totpCode
1440
+ };
1441
+ const confirmDisabled = isSettingsSaveProofSubmitDisabled(props.mode, credentials, totpRequired);
1442
+ if (!props.open) {
1443
+ return null;
1444
+ }
1445
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(
1446
+ "div",
1447
+ {
1448
+ className: "fixed inset-0 z-50 flex items-center justify-center bg-black/30 p-4 backdrop-blur-sm",
1449
+ onClick: handleBackdropClick,
1450
+ role: "dialog",
1451
+ "aria-modal": "true",
1452
+ "aria-labelledby": "settings-save-proof-title",
1453
+ children: /* @__PURE__ */ jsxRuntimeExports.jsxs(
1454
+ "div",
1455
+ {
1456
+ ref: dialogRef,
1457
+ className: "w-full max-w-md rounded-2xl border border-slate-200 bg-white p-6 shadow-2xl",
1458
+ children: [
1459
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-3", children: [
1460
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "inline-flex h-10 w-10 items-center justify-center rounded-full bg-brand-blue/10", children: /* @__PURE__ */ jsxRuntimeExports.jsx(HiMiniKey, { className: "h-5 w-5 text-brand-blue", "aria-hidden": "true" }) }),
1461
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
1462
+ /* @__PURE__ */ jsxRuntimeExports.jsx(SectionLabel, { children: "Approval required" }),
1463
+ /* @__PURE__ */ jsxRuntimeExports.jsx("h2", { id: "settings-save-proof-title", className: "text-lg font-semibold tracking-tight text-brand-dark", children: props.title }),
1464
+ /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-sm text-brand-dark/70", children: props.detail })
1465
+ ] })
1466
+ ] }),
1467
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mt-5 space-y-3", children: [
1468
+ props.mode !== "setup-gate" ? /* @__PURE__ */ jsxRuntimeExports.jsxs("label", { className: "block", children: [
1469
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-sm font-semibold text-brand-dark", children: "Approval password" }),
1470
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
1471
+ "input",
1472
+ {
1473
+ ref: passwordRef,
1474
+ type: "password",
1475
+ autoComplete: "current-password",
1476
+ value: currentPassword,
1477
+ onChange: handleCurrentPasswordChange,
1478
+ className: "mt-1 min-h-10 w-full rounded-lg border border-slate-200 bg-white px-3 py-1.5 text-sm text-brand-dark focus:border-brand-blue focus:outline-none focus:ring-2 focus:ring-brand-blue/20"
1479
+ }
1480
+ )
1481
+ ] }) : null,
1482
+ props.mode === "setup-gate" || props.mode === "change-password" ? /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
1483
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("label", { className: "block", children: [
1484
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-sm font-semibold text-brand-dark", children: props.mode === "setup-gate" ? "Password" : "New password" }),
1485
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
1486
+ "input",
1487
+ {
1488
+ ref: props.mode === "setup-gate" ? passwordRef : void 0,
1489
+ type: "password",
1490
+ autoComplete: "new-password",
1491
+ value: newPassword,
1492
+ onChange: handleNewPasswordChange,
1493
+ className: "mt-1 min-h-10 w-full rounded-lg border border-slate-200 bg-white px-3 py-1.5 text-sm text-brand-dark focus:border-brand-blue focus:outline-none focus:ring-2 focus:ring-brand-blue/20"
1494
+ }
1495
+ )
1496
+ ] }),
1497
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("label", { className: "block", children: [
1498
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-sm font-semibold text-brand-dark", children: "Confirm password" }),
1499
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
1500
+ "input",
1501
+ {
1502
+ type: "password",
1503
+ autoComplete: "new-password",
1504
+ value: confirmPassword,
1505
+ onChange: handleConfirmPasswordChange,
1506
+ className: "mt-1 min-h-10 w-full rounded-lg border border-slate-200 bg-white px-3 py-1.5 text-sm text-brand-dark focus:border-brand-blue focus:outline-none focus:ring-2 focus:ring-brand-blue/20"
1507
+ }
1508
+ )
1509
+ ] })
1510
+ ] }) : null,
1511
+ totpRequired ? /* @__PURE__ */ jsxRuntimeExports.jsxs("label", { className: "block", children: [
1512
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-sm font-semibold text-brand-dark", children: "Authenticator code" }),
1513
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
1514
+ "input",
1515
+ {
1516
+ type: "text",
1517
+ inputMode: "numeric",
1518
+ pattern: "[0-9]*",
1519
+ maxLength: 6,
1520
+ value: totpCode,
1521
+ onChange: handleTotpChange,
1522
+ placeholder: "123456",
1523
+ className: "mt-1 min-h-10 w-full rounded-lg border border-slate-200 bg-white px-3 py-1.5 text-sm tracking-[0.28em] text-brand-dark focus:border-brand-blue focus:outline-none focus:ring-2 focus:ring-brand-blue/20"
1524
+ }
1525
+ )
1526
+ ] }) : null
1527
+ ] }),
1528
+ props.error !== null ? /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "mt-4 rounded-lg border border-brand-attention/20 bg-brand-attention/[0.04] px-3 py-2 text-xs text-brand-dark", children: props.error }) : null,
1529
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mt-6 flex flex-col gap-2 sm:flex-row sm:justify-end", children: [
1530
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
1531
+ "button",
1532
+ {
1533
+ type: "button",
1534
+ onClick: props.onCancel,
1535
+ disabled: props.pending,
1536
+ className: "rounded-full border border-slate-200 bg-white px-4 py-2 text-sm font-medium text-brand-dark transition-colors hover:bg-slate-50 disabled:opacity-50",
1537
+ children: "Go back"
1538
+ }
1539
+ ),
1540
+ /* @__PURE__ */ jsxRuntimeExports.jsx(ActionButton, { onClick: handleConfirm, disabled: props.pending || confirmDisabled, children: props.pending ? "Working…" : props.confirmLabel })
1541
+ ] })
1542
+ ]
1543
+ }
1544
+ )
1545
+ }
1546
+ );
1547
+ }
1272
1548
  const localSettingsNavGroups = [
1273
1549
  {
1274
1550
  key: "local",
@@ -1505,44 +1781,17 @@ function buildClearReviewQueuePayload(input) {
1505
1781
  function resolveTotpSetupStep(enrollment) {
1506
1782
  return enrollment !== null ? "scan" : "confirm";
1507
1783
  }
1508
- function shouldShowApprovalPasswordCurrentField(wasConfigured, newPassword, confirmPassword, gateSettingsChanged, gateEnabled) {
1509
- if (!wasConfigured) {
1510
- return false;
1511
- }
1512
- if (newPassword.trim().length > 0 || confirmPassword.trim().length > 0) {
1513
- return true;
1514
- }
1515
- if (gateSettingsChanged || gateEnabled) {
1516
- return true;
1517
- }
1518
- return false;
1519
- }
1520
1784
  function hasApprovalGateSettingsChanged(gateConfig, enabled, cooldownSeconds, strictAllDecisions) {
1521
1785
  if (gateConfig === null) {
1522
1786
  return false;
1523
1787
  }
1524
1788
  return enabled !== gateConfig.enabled || cooldownSeconds !== gateConfig.cooldown_seconds || strictAllDecisions !== gateConfig.strict_all_decisions;
1525
1789
  }
1526
- function shouldShowApprovalPasswordChangeForm(wasConfigured, passwordChangeOpen, newPassword, confirmPassword, gateSettingsChanged, savedGateEnabled, draftGateEnabled) {
1527
- if (!wasConfigured) {
1528
- return true;
1529
- }
1530
- if (gateSettingsChanged && !savedGateEnabled && draftGateEnabled) {
1531
- return true;
1532
- }
1533
- return passwordChangeOpen || newPassword.trim().length > 0 || confirmPassword.trim().length > 0;
1534
- }
1535
- function resolveApprovalPasswordVerifyCopy(input) {
1536
- if (input.gateSettingsChanged) {
1537
- return "Enter your current password before saving settings to apply these gate changes. Authenticator setup uses its own prompt below.";
1790
+ function resolveApprovalPasswordSectionCopy(wasConfigured) {
1791
+ if (wasConfigured) {
1792
+ return "Guard asks for this password before allow or trust changes stick. Save settings to confirm changes, or change the password when needed.";
1538
1793
  }
1539
- if (input.gateEnabled) {
1540
- return "Enter your current password before saving settings. Authenticator setup uses its own prompt below.";
1541
- }
1542
- return "";
1543
- }
1544
- function resolveApprovalPasswordChangeCopy() {
1545
- return "Enter a new password below, then save settings to apply it.";
1794
+ return "Choose a password when you save settings. Guard will ask for it before allow or trust changes stick.";
1546
1795
  }
1547
1796
  function resolveTotpSetupModalTitle(isConfirmStep) {
1548
1797
  if (isConfirmStep) {
@@ -1811,9 +2060,6 @@ function SettingsWorkspace({ onApprovalGateChange }) {
1811
2060
  const saveSuccessTimerRef = reactExports.useRef(null);
1812
2061
  const savedSettingsRef = reactExports.useRef(null);
1813
2062
  const [approvalGateEnabled, setApprovalGateEnabled] = reactExports.useState(false);
1814
- const [approvalGateNewPassword, setApprovalGateNewPassword] = reactExports.useState("");
1815
- const [approvalGateConfirmPassword, setApprovalGateConfirmPassword] = reactExports.useState("");
1816
- const [approvalGateCurrentPassword, setApprovalGateCurrentPassword] = reactExports.useState("");
1817
2063
  const [approvalGateTotpCode, setApprovalGateTotpCode] = reactExports.useState("");
1818
2064
  const [approvalGateTotpDeviceLabel, setApprovalGateTotpDeviceLabel] = reactExports.useState("local-device");
1819
2065
  const [approvalGateStrictAllDecisions, setApprovalGateStrictAllDecisions] = reactExports.useState(false);
@@ -1824,9 +2070,11 @@ function SettingsWorkspace({ onApprovalGateChange }) {
1824
2070
  const [totpActionPassword, setTotpActionPassword] = reactExports.useState("");
1825
2071
  const [totpActionPending, setTotpActionPending] = reactExports.useState(null);
1826
2072
  const [totpActionError, setTotpActionError] = reactExports.useState(null);
1827
- const [revokingCooldown, setRevokingCooldown] = reactExports.useState(false);
1828
- const [revokePassword, setRevokePassword] = reactExports.useState("");
1829
- const [revokeError, setRevokeError] = reactExports.useState(null);
2073
+ const [proofModalOpen, setProofModalOpen] = reactExports.useState(false);
2074
+ const [proofModalMode, setProofModalMode] = reactExports.useState("verify-save");
2075
+ const [proofModalError, setProofModalError] = reactExports.useState(null);
2076
+ const [proofModalPending, setProofModalPending] = reactExports.useState(false);
2077
+ const [pendingProofAction, setPendingProofAction] = reactExports.useState(null);
1830
2078
  reactExports.useEffect(() => {
1831
2079
  let cancelled = false;
1832
2080
  fetchSettings().then((payload) => {
@@ -1981,15 +2229,6 @@ function SettingsWorkspace({ onApprovalGateChange }) {
1981
2229
  );
1982
2230
  setSaveError(null);
1983
2231
  }, [approvalGateCooldown, approvalGateStrictAllDecisions]);
1984
- const handleApprovalGateNewPassword = reactExports.useCallback((event) => {
1985
- setApprovalGateNewPassword(event.target.value);
1986
- }, []);
1987
- const handleApprovalGateConfirmPassword = reactExports.useCallback((event) => {
1988
- setApprovalGateConfirmPassword(event.target.value);
1989
- }, []);
1990
- const handleApprovalGateCurrentPassword = reactExports.useCallback((event) => {
1991
- setApprovalGateCurrentPassword(event.target.value);
1992
- }, []);
1993
2232
  const handleApprovalGateTotpCode = reactExports.useCallback((event) => {
1994
2233
  setApprovalGateTotpCode(event.target.value);
1995
2234
  setTotpActionError(null);
@@ -2039,10 +2278,6 @@ function SettingsWorkspace({ onApprovalGateChange }) {
2039
2278
  );
2040
2279
  setSaveError(null);
2041
2280
  }, [approvalGateEnabled, approvalGateCooldown]);
2042
- const handleRevokePasswordChange = reactExports.useCallback((event) => {
2043
- setRevokePassword(event.target.value);
2044
- setRevokeError(null);
2045
- }, []);
2046
2281
  const applyLoadedSettingsPayload = reactExports.useCallback((normalizedPayload) => {
2047
2282
  setState({ kind: "ready", payload: normalizedPayload });
2048
2283
  setDraft(normalizedPayload.settings);
@@ -2055,22 +2290,153 @@ function SettingsWorkspace({ onApprovalGateChange }) {
2055
2290
  onApprovalGateChange?.(gate);
2056
2291
  }
2057
2292
  }, [onApprovalGateChange]);
2058
- const buildApprovalGateWriteProof = reactExports.useCallback(() => ({
2059
- ...approvalGateCurrentPassword.trim() ? { approval_password: approvalGateCurrentPassword } : {},
2060
- ...approvalGateTotpCode.trim() ? { approval_totp_code: approvalGateTotpCode } : {}
2061
- }), [approvalGateCurrentPassword, approvalGateTotpCode]);
2062
- const handleRevokeCooldown = reactExports.useCallback(async () => {
2063
- if (!revokePassword.trim()) {
2064
- setRevokeError("Enter the approval password to revoke cooldown.");
2293
+ const openProofModal = reactExports.useCallback((mode2, action) => {
2294
+ setProofModalMode(mode2);
2295
+ setPendingProofAction(action);
2296
+ setProofModalError(null);
2297
+ setProofModalOpen(true);
2298
+ }, []);
2299
+ const closeProofModal = reactExports.useCallback(() => {
2300
+ if (proofModalPending) {
2301
+ return;
2302
+ }
2303
+ setProofModalOpen(false);
2304
+ setPendingProofAction(null);
2305
+ setProofModalError(null);
2306
+ }, [proofModalPending]);
2307
+ const executeSave = reactExports.useCallback(async (proof) => {
2308
+ if (draft === null) {
2065
2309
  return;
2066
2310
  }
2067
- setRevokingCooldown(true);
2068
- setRevokeError(null);
2311
+ const fromModal = proof !== void 0;
2312
+ if (!fromModal) {
2313
+ setSaving(true);
2314
+ setSaveError(null);
2315
+ setSaveSuccess(false);
2316
+ }
2069
2317
  try {
2070
- const payload = await revokeApprovalGateCooldown(
2071
- revokePassword,
2072
- approvalGateTotpCode.trim().length > 0 ? approvalGateTotpCode : void 0
2073
- );
2318
+ const approvalGateUpdate = {
2319
+ enabled: approvalGateEnabled,
2320
+ configured: draft.approval_gate?.configured ?? false,
2321
+ cooldown_seconds: approvalGateCooldown,
2322
+ cooldown_active: draft.approval_gate?.cooldown_active ?? false,
2323
+ cooldown_expires_at: draft.approval_gate?.cooldown_expires_at ?? null,
2324
+ locked_until: draft.approval_gate?.locked_until ?? null,
2325
+ fail_closed: draft.approval_gate?.fail_closed ?? false,
2326
+ strict_all_decisions: approvalGateStrictAllDecisions,
2327
+ totp_enabled: draft.approval_gate?.totp_enabled ?? false,
2328
+ totp_pending: draft.approval_gate?.totp_pending ?? false,
2329
+ ...proof?.currentPassword ? { current_password: proof.currentPassword } : {},
2330
+ ...proof?.newPassword ? { new_password: proof.newPassword } : {},
2331
+ ...proof?.confirmPassword ? { confirm_password: proof.confirmPassword } : {},
2332
+ ...proof?.totpCode ? { totp_code: proof.totpCode } : {}
2333
+ };
2334
+ const settingsToSave = {
2335
+ ...draft,
2336
+ risk_actions: draft.security_level === "custom" ? draft.risk_actions : draft.risk_action_overrides,
2337
+ approval_gate: approvalGateUpdate
2338
+ };
2339
+ const payload = await updateSettings(settingsToSave);
2340
+ const normalizedPayload = normalizeSettingsPayload(payload);
2341
+ setState({ kind: "ready", payload: normalizedPayload });
2342
+ setDraft(normalizedPayload.settings);
2343
+ savedSettingsRef.current = normalizedPayload.settings;
2344
+ if (normalizedPayload.settings.approval_gate !== void 0) {
2345
+ const gate = normalizedPayload.settings.approval_gate;
2346
+ setApprovalGateEnabled(gate.enabled);
2347
+ setApprovalGateCooldown(gate.cooldown_seconds);
2348
+ setApprovalGateStrictAllDecisions(gate.strict_all_decisions);
2349
+ onApprovalGateChange?.(gate);
2350
+ }
2351
+ if (!fromModal) {
2352
+ setSaveSuccess(true);
2353
+ if (saveSuccessTimerRef.current !== null) clearTimeout(saveSuccessTimerRef.current);
2354
+ saveSuccessTimerRef.current = setTimeout(() => setSaveSuccess(false), 2e3);
2355
+ } else {
2356
+ setSaveSuccess(true);
2357
+ setSaveError(null);
2358
+ if (saveSuccessTimerRef.current !== null) clearTimeout(saveSuccessTimerRef.current);
2359
+ saveSuccessTimerRef.current = setTimeout(() => setSaveSuccess(false), 2e3);
2360
+ }
2361
+ } catch (error) {
2362
+ if (fromModal) {
2363
+ throw error;
2364
+ }
2365
+ setSaveError(error instanceof Error ? error.message : "Unable to save settings.");
2366
+ } finally {
2367
+ if (!fromModal) {
2368
+ setSaving(false);
2369
+ }
2370
+ }
2371
+ }, [
2372
+ draft,
2373
+ approvalGateEnabled,
2374
+ approvalGateCooldown,
2375
+ approvalGateStrictAllDecisions,
2376
+ onApprovalGateChange
2377
+ ]);
2378
+ const executeMaintenanceWithProof = reactExports.useCallback(async (action, proof) => {
2379
+ const password = proof.currentPassword?.trim() ?? "";
2380
+ const totpCode = proof.totpCode?.trim() ?? "";
2381
+ if (action === "clear-approvals") {
2382
+ setClearingApprovals(true);
2383
+ setActionMessage(null);
2384
+ try {
2385
+ await clearPolicy({
2386
+ all: true,
2387
+ approval_password: password || void 0,
2388
+ approval_totp_code: totpCode || void 0
2389
+ });
2390
+ setActionMessage("Saved approvals cleared. Guard will ask again for future matching actions.");
2391
+ setActionMessageKind("success");
2392
+ } finally {
2393
+ setClearingApprovals(false);
2394
+ }
2395
+ return;
2396
+ }
2397
+ if (action === "clear-queue") {
2398
+ setClearingReviewQueue(true);
2399
+ setActionMessage(null);
2400
+ try {
2401
+ const result = await clearReviewQueue(buildClearReviewQueuePayload({
2402
+ approvalPassword: password,
2403
+ approvalTotpCode: totpCode
2404
+ }));
2405
+ setActionMessage(`Review queue cleared. Removed ${result.cleared} pending ${result.cleared === 1 ? "item" : "items"}.`);
2406
+ setActionMessageKind("success");
2407
+ } finally {
2408
+ setClearingReviewQueue(false);
2409
+ }
2410
+ return;
2411
+ }
2412
+ if (action === "revoke-cooldown") {
2413
+ try {
2414
+ const payload = await revokeApprovalGateCooldown(
2415
+ password,
2416
+ totpCode.length > 0 ? totpCode : void 0
2417
+ );
2418
+ const normalizedPayload = normalizeSettingsPayload(payload);
2419
+ const gate = normalizedPayload.settings.approval_gate;
2420
+ setState({ kind: "ready", payload: normalizedPayload });
2421
+ setDraft(normalizedPayload.settings);
2422
+ savedSettingsRef.current = normalizedPayload.settings;
2423
+ if (gate !== void 0) {
2424
+ setApprovalGateEnabled(gate.enabled);
2425
+ setApprovalGateCooldown(gate.cooldown_seconds);
2426
+ setApprovalGateStrictAllDecisions(gate.strict_all_decisions);
2427
+ onApprovalGateChange?.(gate);
2428
+ }
2429
+ setActionMessage("Cooldown revoked successfully.");
2430
+ setActionMessageKind("success");
2431
+ } catch (error) {
2432
+ throw error;
2433
+ }
2434
+ return;
2435
+ }
2436
+ setTotpActionPending("disable");
2437
+ setTotpActionError(null);
2438
+ try {
2439
+ const payload = await disableApprovalGateTotp(password, totpCode);
2074
2440
  const normalizedPayload = normalizeSettingsPayload(payload);
2075
2441
  const gate = normalizedPayload.settings.approval_gate;
2076
2442
  setState({ kind: "ready", payload: normalizedPayload });
@@ -2082,15 +2448,62 @@ function SettingsWorkspace({ onApprovalGateChange }) {
2082
2448
  setApprovalGateStrictAllDecisions(gate.strict_all_decisions);
2083
2449
  onApprovalGateChange?.(gate);
2084
2450
  }
2085
- setRevokePassword("");
2086
- setActionMessage("Cooldown revoked successfully.");
2451
+ setApprovalGateTotpCode("");
2452
+ setTotpActionPassword("");
2453
+ setTotpEnrollment(null);
2454
+ setTotpSetupOpen(false);
2455
+ setTotpSetupStep("confirm");
2456
+ setActionMessage("Authenticator app disconnected.");
2087
2457
  setActionMessageKind("success");
2458
+ } finally {
2459
+ setTotpActionPending(null);
2460
+ }
2461
+ }, [onApprovalGateChange]);
2462
+ const handleProofModalConfirm = reactExports.useCallback(async (proof) => {
2463
+ if (pendingProofAction === null) {
2464
+ return;
2465
+ }
2466
+ setProofModalPending(true);
2467
+ setProofModalError(null);
2468
+ try {
2469
+ if (pendingProofAction.kind === "save") {
2470
+ await executeSave(proof);
2471
+ } else {
2472
+ await executeMaintenanceWithProof(pendingProofAction.action, proof);
2473
+ }
2474
+ setProofModalOpen(false);
2475
+ setPendingProofAction(null);
2088
2476
  } catch (error) {
2089
- setRevokeError(error instanceof Error ? error.message : "Unable to revoke cooldown.");
2477
+ setProofModalError(error instanceof Error ? error.message : "Unable to continue.");
2090
2478
  } finally {
2091
- setRevokingCooldown(false);
2479
+ setProofModalPending(false);
2092
2480
  }
2093
- }, [revokePassword, approvalGateTotpCode, onApprovalGateChange]);
2481
+ }, [pendingProofAction, executeSave, executeMaintenanceWithProof]);
2482
+ const handleSave = reactExports.useCallback(() => {
2483
+ if (draft === null) {
2484
+ return;
2485
+ }
2486
+ const savedGateConfig = savedSettingsRef.current?.approval_gate ?? null;
2487
+ const proofKind = resolveSettingsSaveProofKind({
2488
+ savedGateEnabled: savedGateConfig?.enabled === true,
2489
+ wasConfigured: savedGateConfig?.configured === true,
2490
+ draftGateEnabled: approvalGateEnabled
2491
+ });
2492
+ if (requiresSettingsSaveProof(proofKind)) {
2493
+ openProofModal(proofKind, { kind: "save" });
2494
+ return;
2495
+ }
2496
+ void executeSave();
2497
+ }, [approvalGateEnabled, draft, executeSave, openProofModal]);
2498
+ const handleOpenPasswordChangeModal = reactExports.useCallback(() => {
2499
+ openProofModal("change-password", { kind: "save" });
2500
+ }, [openProofModal]);
2501
+ const handleRequestRevokeCooldown = reactExports.useCallback(() => {
2502
+ openProofModal("maintenance", { kind: "maintenance", action: "revoke-cooldown" });
2503
+ }, [openProofModal]);
2504
+ const handleRequestDisableTotp = reactExports.useCallback(() => {
2505
+ openProofModal("maintenance", { kind: "maintenance", action: "disable-totp" });
2506
+ }, [openProofModal]);
2094
2507
  const handleStartTotpEnrollment = reactExports.useCallback(async () => {
2095
2508
  if (!totpActionPassword.trim()) {
2096
2509
  setTotpActionError("Enter your approval password to continue.");
@@ -2163,135 +2576,50 @@ function SettingsWorkspace({ onApprovalGateChange }) {
2163
2576
  }
2164
2577
  }, [totpActionPassword, approvalGateTotpCode, onApprovalGateChange]);
2165
2578
  const handleDisableTotp = reactExports.useCallback(async () => {
2166
- if (!totpActionPassword.trim()) {
2167
- setTotpActionError("Enter your approval password to disable the authenticator app.");
2579
+ handleRequestDisableTotp();
2580
+ }, [handleRequestDisableTotp]);
2581
+ const handleClearApprovals = reactExports.useCallback(() => {
2582
+ if (!window.confirm("Clear all saved approvals? Guard will ask again for previously approved actions.")) {
2168
2583
  return;
2169
2584
  }
2170
- if (!approvalGateTotpCode.trim()) {
2171
- setTotpActionError("Enter the six-digit code from your authenticator app.");
2585
+ const savedGateEnabled = savedSettingsRef.current?.approval_gate?.enabled === true;
2586
+ if (savedGateEnabled) {
2587
+ openProofModal("maintenance", { kind: "maintenance", action: "clear-approvals" });
2172
2588
  return;
2173
2589
  }
2174
- setTotpActionPending("disable");
2175
- setTotpActionError(null);
2176
- try {
2177
- const payload = await disableApprovalGateTotp(totpActionPassword, approvalGateTotpCode);
2178
- const normalizedPayload = normalizeSettingsPayload(payload);
2179
- const gate = normalizedPayload.settings.approval_gate;
2180
- setState({ kind: "ready", payload: normalizedPayload });
2181
- setDraft(normalizedPayload.settings);
2182
- savedSettingsRef.current = normalizedPayload.settings;
2183
- if (gate !== void 0) {
2184
- setApprovalGateEnabled(gate.enabled);
2185
- setApprovalGateCooldown(gate.cooldown_seconds);
2186
- setApprovalGateStrictAllDecisions(gate.strict_all_decisions);
2187
- onApprovalGateChange?.(gate);
2188
- }
2189
- setApprovalGateTotpCode("");
2190
- setTotpActionPassword("");
2191
- setTotpEnrollment(null);
2192
- setTotpSetupOpen(false);
2193
- setTotpSetupStep("confirm");
2194
- setActionMessage("Authenticator app disconnected.");
2195
- setActionMessageKind("success");
2196
- } catch (error) {
2197
- setTotpActionError(error instanceof Error ? error.message : "Unable to disable TOTP.");
2198
- } finally {
2199
- setTotpActionPending(null);
2200
- }
2201
- }, [totpActionPassword, approvalGateTotpCode, onApprovalGateChange]);
2202
- const handleSave = reactExports.useCallback(async () => {
2203
- if (draft === null) return;
2204
- setSaving(true);
2205
- setSaveError(null);
2206
- setSaveSuccess(false);
2207
- try {
2208
- const approvalGateUpdate = {
2209
- enabled: approvalGateEnabled,
2210
- configured: draft.approval_gate?.configured ?? false,
2211
- cooldown_seconds: approvalGateCooldown,
2212
- cooldown_active: draft.approval_gate?.cooldown_active ?? false,
2213
- cooldown_expires_at: draft.approval_gate?.cooldown_expires_at ?? null,
2214
- locked_until: draft.approval_gate?.locked_until ?? null,
2215
- fail_closed: draft.approval_gate?.fail_closed ?? false,
2216
- strict_all_decisions: approvalGateStrictAllDecisions,
2217
- totp_enabled: draft.approval_gate?.totp_enabled ?? false,
2218
- totp_pending: draft.approval_gate?.totp_pending ?? false,
2219
- ...approvalGateCurrentPassword ? { current_password: approvalGateCurrentPassword } : {},
2220
- ...approvalGateNewPassword ? { new_password: approvalGateNewPassword } : {},
2221
- ...approvalGateConfirmPassword ? { confirm_password: approvalGateConfirmPassword } : {},
2222
- ...approvalGateTotpCode ? { totp_code: approvalGateTotpCode } : {}
2223
- };
2224
- const settingsToSave = {
2225
- ...draft,
2226
- risk_actions: draft.security_level === "custom" ? draft.risk_actions : draft.risk_action_overrides,
2227
- approval_gate: approvalGateUpdate
2228
- };
2229
- const payload = await updateSettings(settingsToSave);
2230
- const normalizedPayload = normalizeSettingsPayload(payload);
2231
- setState({ kind: "ready", payload: normalizedPayload });
2232
- setDraft(normalizedPayload.settings);
2233
- savedSettingsRef.current = normalizedPayload.settings;
2234
- if (normalizedPayload.settings.approval_gate !== void 0) {
2235
- const gate = normalizedPayload.settings.approval_gate;
2236
- setApprovalGateEnabled(gate.enabled);
2237
- setApprovalGateCooldown(gate.cooldown_seconds);
2238
- setApprovalGateStrictAllDecisions(gate.strict_all_decisions);
2239
- onApprovalGateChange?.(gate);
2240
- }
2241
- setSaveSuccess(true);
2242
- setApprovalGateNewPassword("");
2243
- setApprovalGateCurrentPassword("");
2244
- setApprovalGateConfirmPassword("");
2245
- setApprovalGateTotpCode("");
2246
- if (saveSuccessTimerRef.current !== null) clearTimeout(saveSuccessTimerRef.current);
2247
- saveSuccessTimerRef.current = setTimeout(() => setSaveSuccess(false), 2e3);
2248
- } catch (error) {
2249
- setSaveError(error instanceof Error ? error.message : "Unable to save settings.");
2250
- } finally {
2251
- setSaving(false);
2252
- }
2253
- }, [draft, approvalGateEnabled, approvalGateCooldown, approvalGateStrictAllDecisions, approvalGateCurrentPassword, approvalGateNewPassword, approvalGateConfirmPassword, approvalGateTotpCode, onApprovalGateChange]);
2254
- const handleClearApprovals = reactExports.useCallback(async () => {
2255
- if (!window.confirm("Clear all saved approvals? Guard will ask again for previously approved actions.")) return;
2256
2590
  setClearingApprovals(true);
2257
2591
  setActionMessage(null);
2258
- try {
2259
- await clearPolicy({
2260
- all: true,
2261
- approval_password: approvalGateCurrentPassword || void 0,
2262
- approval_totp_code: approvalGateTotpCode || void 0
2263
- });
2592
+ void clearPolicy({ all: true }).then(() => {
2264
2593
  setActionMessage("Saved approvals cleared. Guard will ask again for future matching actions.");
2265
2594
  setActionMessageKind("success");
2266
- setApprovalGateCurrentPassword("");
2267
- setApprovalGateTotpCode("");
2268
- } catch (error) {
2595
+ }).catch((error) => {
2269
2596
  setActionMessage(error instanceof Error ? error.message : "Unable to clear approvals.");
2270
2597
  setActionMessageKind("error");
2271
- } finally {
2598
+ }).finally(() => {
2272
2599
  setClearingApprovals(false);
2600
+ });
2601
+ }, [openProofModal]);
2602
+ const handleClearReviewQueue = reactExports.useCallback(() => {
2603
+ if (!window.confirm("Clear the pending review queue? Guard will remove waiting items without creating allow or block decisions.")) {
2604
+ return;
2605
+ }
2606
+ const savedGateEnabled = savedSettingsRef.current?.approval_gate?.enabled === true;
2607
+ if (savedGateEnabled) {
2608
+ openProofModal("maintenance", { kind: "maintenance", action: "clear-queue" });
2609
+ return;
2273
2610
  }
2274
- }, [approvalGateCurrentPassword, approvalGateTotpCode]);
2275
- const handleClearReviewQueue = reactExports.useCallback(async () => {
2276
- if (!window.confirm("Clear the pending review queue? Guard will remove waiting items without creating allow or block decisions.")) return;
2277
2611
  setClearingReviewQueue(true);
2278
2612
  setActionMessage(null);
2279
- try {
2280
- const result = await clearReviewQueue(buildClearReviewQueuePayload({
2281
- approvalPassword: approvalGateCurrentPassword,
2282
- approvalTotpCode: approvalGateTotpCode
2283
- }));
2613
+ void clearReviewQueue(buildClearReviewQueuePayload({})).then((result) => {
2284
2614
  setActionMessage(`Review queue cleared. Removed ${result.cleared} pending ${result.cleared === 1 ? "item" : "items"}.`);
2285
2615
  setActionMessageKind("success");
2286
- setApprovalGateCurrentPassword("");
2287
- setApprovalGateTotpCode("");
2288
- } catch (error) {
2616
+ }).catch((error) => {
2289
2617
  setActionMessage(error instanceof Error ? error.message : "Unable to clear review queue.");
2290
2618
  setActionMessageKind("error");
2291
- } finally {
2619
+ }).finally(() => {
2292
2620
  setClearingReviewQueue(false);
2293
- }
2294
- }, [approvalGateCurrentPassword, approvalGateTotpCode]);
2621
+ });
2622
+ }, [openProofModal]);
2295
2623
  const handleClearEvidence = reactExports.useCallback(async () => {
2296
2624
  if (!window.confirm("Clear the evidence log permanently? This cannot be undone.")) return;
2297
2625
  setClearingEvidence(true);
@@ -2608,9 +2936,6 @@ function SettingsWorkspace({ onApprovalGateChange }) {
2608
2936
  enabled: approvalGateEnabled,
2609
2937
  gateConfig: draft.approval_gate ?? null,
2610
2938
  savedGateConfig: savedSettingsRef.current?.approval_gate ?? null,
2611
- newPassword: approvalGateNewPassword,
2612
- confirmPassword: approvalGateConfirmPassword,
2613
- currentPassword: approvalGateCurrentPassword,
2614
2939
  totpCode: approvalGateTotpCode,
2615
2940
  totpDeviceLabel: approvalGateTotpDeviceLabel,
2616
2941
  strictAllDecisions: approvalGateStrictAllDecisions,
@@ -2621,13 +2946,8 @@ function SettingsWorkspace({ onApprovalGateChange }) {
2621
2946
  totpActionPassword,
2622
2947
  totpActionPending,
2623
2948
  totpActionError,
2624
- revokingCooldown,
2625
- revokePassword,
2626
- revokeError,
2627
2949
  onToggle: handleApprovalGateToggle,
2628
- onNewPasswordChange: handleApprovalGateNewPassword,
2629
- onConfirmPasswordChange: handleApprovalGateConfirmPassword,
2630
- onCurrentPasswordChange: handleApprovalGateCurrentPassword,
2950
+ onOpenPasswordChangeModal: handleOpenPasswordChangeModal,
2631
2951
  onTotpCodeChange: handleApprovalGateTotpCode,
2632
2952
  onTotpDeviceLabelChange: handleApprovalGateTotpDeviceLabel,
2633
2953
  onTotpActionPasswordChange: handleTotpActionPasswordChange,
@@ -2638,8 +2958,7 @@ function SettingsWorkspace({ onApprovalGateChange }) {
2638
2958
  onStartTotpEnrollment: handleStartTotpEnrollment,
2639
2959
  onVerifyTotpEnrollment: handleVerifyTotpEnrollment,
2640
2960
  onDisableTotp: handleDisableTotp,
2641
- onRevokePasswordChange: handleRevokePasswordChange,
2642
- onRevokeCooldown: handleRevokeCooldown
2961
+ onRevokeCooldown: handleRequestRevokeCooldown
2643
2962
  }
2644
2963
  )
2645
2964
  ] }),
@@ -2728,45 +3047,6 @@ function SettingsWorkspace({ onApprovalGateChange }) {
2728
3047
  tabIndex: -1
2729
3048
  }
2730
3049
  ),
2731
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "rounded-2xl border border-brand-blue/15 bg-brand-blue/[0.04] p-4", children: [
2732
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-wrap items-start justify-between gap-3", children: [
2733
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
2734
- /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-sm font-semibold text-brand-dark", children: "Proof before cleanup" }),
2735
- /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "mt-1 max-w-2xl text-xs text-slate-500", children: "Enter your password or app code before clearing saved decisions or the review list." })
2736
- ] }),
2737
- draft.approval_gate?.totp_enabled === true ? /* @__PURE__ */ jsxRuntimeExports.jsx(Badge, { tone: "blue", children: "App code required" }) : null
2738
- ] }),
2739
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mt-3 grid gap-3 sm:grid-cols-2", children: [
2740
- /* @__PURE__ */ jsxRuntimeExports.jsxs("label", { className: "block", children: [
2741
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-xs font-semibold uppercase tracking-[0.18em] text-slate-500", children: "Password" }),
2742
- /* @__PURE__ */ jsxRuntimeExports.jsx(
2743
- "input",
2744
- {
2745
- type: "password",
2746
- autoComplete: "current-password",
2747
- value: approvalGateCurrentPassword,
2748
- onChange: handleApprovalGateCurrentPassword,
2749
- className: "mt-1 min-h-11 w-full rounded-xl border border-slate-200 bg-white px-3 text-sm text-brand-dark focus:border-brand-blue focus:outline-none focus:ring-2 focus:ring-brand-blue/20"
2750
- }
2751
- )
2752
- ] }),
2753
- /* @__PURE__ */ jsxRuntimeExports.jsxs("label", { className: "block", children: [
2754
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-xs font-semibold uppercase tracking-[0.18em] text-slate-500", children: "App code" }),
2755
- /* @__PURE__ */ jsxRuntimeExports.jsx(
2756
- "input",
2757
- {
2758
- type: "text",
2759
- inputMode: "numeric",
2760
- pattern: "[0-9]*",
2761
- value: approvalGateTotpCode,
2762
- onChange: handleApprovalGateTotpCode,
2763
- placeholder: "123456",
2764
- className: "mt-1 min-h-11 w-full rounded-xl border border-slate-200 bg-white px-3 text-sm tracking-[0.28em] text-brand-dark focus:border-brand-blue focus:outline-none focus:ring-2 focus:ring-brand-blue/20"
2765
- }
2766
- )
2767
- ] })
2768
- ] })
2769
- ] }),
2770
3050
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "grid gap-4 sm:grid-cols-2", children: [
2771
3051
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-3", children: [
2772
3052
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
@@ -2840,6 +3120,28 @@ function SettingsWorkspace({ onApprovalGateChange }) {
2840
3120
  ] })
2841
3121
  }
2842
3122
  ),
3123
+ proofModalOpen && pendingProofAction !== null ? /* @__PURE__ */ jsxRuntimeExports.jsx(
3124
+ SettingsSaveProofModal,
3125
+ {
3126
+ open: proofModalOpen,
3127
+ mode: proofModalMode,
3128
+ gate: savedSettingsRef.current?.approval_gate ?? null,
3129
+ ...resolveSettingsSaveProofModalCopy({
3130
+ mode: proofModalMode,
3131
+ gateSettingsChanged: hasApprovalGateSettingsChanged(
3132
+ savedSettingsRef.current?.approval_gate ?? null,
3133
+ approvalGateEnabled,
3134
+ approvalGateCooldown,
3135
+ approvalGateStrictAllDecisions
3136
+ ),
3137
+ maintenanceAction: pendingProofAction.kind === "maintenance" ? pendingProofAction.action : void 0
3138
+ }),
3139
+ error: proofModalError,
3140
+ pending: proofModalPending || saving,
3141
+ onCancel: closeProofModal,
3142
+ onConfirm: handleProofModalConfirm
3143
+ }
3144
+ ) : null,
2843
3145
  pendingMode === "observe" && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "guard-fade-in fixed inset-0 z-50 flex items-center justify-center bg-black/30 p-4 backdrop-blur-sm", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "w-full max-w-sm rounded-2xl border border-brand-attention/15 bg-white p-6 shadow-xl", children: [
2844
3146
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-start gap-3", children: [
2845
3147
  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "inline-flex h-10 w-10 shrink-0 items-center justify-center rounded-full bg-brand-attention/10", children: /* @__PURE__ */ jsxRuntimeExports.jsx(HiMiniExclamationTriangle, { className: "h-5 w-5 text-brand-attention", "aria-hidden": "true" }) }),
@@ -3016,48 +3318,12 @@ const cooldownOptions = [
3016
3318
  ];
3017
3319
  function ApprovalGateCard(props) {
3018
3320
  const wasConfigured = props.savedGateConfig?.configured === true;
3019
- const savedGateEnabled = props.savedGateConfig?.enabled === true;
3020
3321
  const gateSettingsChanged = hasApprovalGateSettingsChanged(
3021
3322
  props.savedGateConfig,
3022
3323
  props.enabled,
3023
3324
  props.cooldownSeconds,
3024
3325
  props.strictAllDecisions
3025
3326
  );
3026
- const changingPassword = props.newPassword.trim().length > 0 || props.confirmPassword.trim().length > 0;
3027
- const [passwordChangeOpen, setPasswordChangeOpen] = reactExports.useState(changingPassword);
3028
- reactExports.useEffect(() => {
3029
- if (changingPassword) {
3030
- setPasswordChangeOpen(true);
3031
- return;
3032
- }
3033
- setPasswordChangeOpen(false);
3034
- }, [changingPassword]);
3035
- const showCurrentPassword = shouldShowApprovalPasswordCurrentField(
3036
- wasConfigured,
3037
- props.newPassword,
3038
- props.confirmPassword,
3039
- gateSettingsChanged,
3040
- savedGateEnabled
3041
- );
3042
- const showChangeForm = shouldShowApprovalPasswordChangeForm(
3043
- wasConfigured,
3044
- passwordChangeOpen,
3045
- props.newPassword,
3046
- props.confirmPassword,
3047
- gateSettingsChanged,
3048
- savedGateEnabled,
3049
- props.enabled
3050
- );
3051
- const approvalPasswordVerifyCopy = resolveApprovalPasswordVerifyCopy({
3052
- gateSettingsChanged,
3053
- gateEnabled: savedGateEnabled
3054
- });
3055
- const handleOpenPasswordChange = reactExports.useCallback(() => {
3056
- setPasswordChangeOpen(true);
3057
- }, []);
3058
- const handleClosePasswordChange = reactExports.useCallback(() => {
3059
- setPasswordChangeOpen(false);
3060
- }, []);
3061
3327
  const showGateDetails = props.enabled || gateSettingsChanged;
3062
3328
  const cooldownActive = props.gateConfig?.cooldown_active === true;
3063
3329
  const cooldownExpiresAt = props.gateConfig?.cooldown_expires_at ?? null;
@@ -3082,100 +3348,16 @@ function ApprovalGateCard(props) {
3082
3348
  showGateDetails ? /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-3", children: [
3083
3349
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "rounded-xl border border-slate-100 bg-white p-4", children: [
3084
3350
  /* @__PURE__ */ jsxRuntimeExports.jsx(SectionLabel, { children: "Approval password" }),
3085
- /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "mt-1 text-xs text-slate-500", children: wasConfigured ? "Guard asks for this password before allow or trust changes stick." : "Choose a password. Guard will ask for it before allow or trust changes stick." }),
3086
- wasConfigured ? /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mt-3 space-y-3", children: [
3087
- showCurrentPassword ? /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
3088
- approvalPasswordVerifyCopy.length > 0 ? /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-slate-500", children: approvalPasswordVerifyCopy }) : null,
3089
- /* @__PURE__ */ jsxRuntimeExports.jsxs("label", { className: "block", children: [
3090
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-xs font-medium text-slate-500", children: "Current password" }),
3091
- /* @__PURE__ */ jsxRuntimeExports.jsx(
3092
- "input",
3093
- {
3094
- type: "password",
3095
- autoComplete: "current-password",
3096
- value: props.currentPassword,
3097
- onChange: props.onCurrentPasswordChange,
3098
- className: "mt-1 min-h-11 w-full rounded-lg border border-slate-200 bg-white px-3 py-1.5 text-sm text-brand-dark focus:border-brand-blue focus:outline-none focus:ring-2 focus:ring-brand-blue/20"
3099
- }
3100
- )
3101
- ] })
3102
- ] }) : null,
3103
- !showChangeForm ? /* @__PURE__ */ jsxRuntimeExports.jsx(
3104
- "button",
3105
- {
3106
- type: "button",
3107
- onClick: handleOpenPasswordChange,
3108
- className: "text-xs font-medium text-brand-blue transition-colors hover:text-brand-blue/80",
3109
- children: "Change password"
3110
- }
3111
- ) : /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-3 rounded-lg border border-slate-100 bg-slate-50/60 p-3", children: [
3112
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-start justify-between gap-3", children: [
3113
- /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-slate-500", children: resolveApprovalPasswordChangeCopy() }),
3114
- !changingPassword ? /* @__PURE__ */ jsxRuntimeExports.jsx(
3115
- "button",
3116
- {
3117
- type: "button",
3118
- onClick: handleClosePasswordChange,
3119
- className: "shrink-0 text-xs font-medium text-slate-500 transition-colors hover:text-brand-dark",
3120
- children: "Cancel"
3121
- }
3122
- ) : null
3123
- ] }),
3124
- /* @__PURE__ */ jsxRuntimeExports.jsxs("label", { className: "block", children: [
3125
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-xs font-medium text-slate-500", children: "New password" }),
3126
- /* @__PURE__ */ jsxRuntimeExports.jsx(
3127
- "input",
3128
- {
3129
- type: "password",
3130
- autoComplete: "new-password",
3131
- value: props.newPassword,
3132
- onChange: props.onNewPasswordChange,
3133
- className: "mt-1 min-h-11 w-full rounded-lg border border-slate-200 bg-white px-3 py-1.5 text-sm text-brand-dark focus:border-brand-blue focus:outline-none focus:ring-2 focus:ring-brand-blue/20"
3134
- }
3135
- )
3136
- ] }),
3137
- /* @__PURE__ */ jsxRuntimeExports.jsxs("label", { className: "block", children: [
3138
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-xs font-medium text-slate-500", children: "Confirm new password" }),
3139
- /* @__PURE__ */ jsxRuntimeExports.jsx(
3140
- "input",
3141
- {
3142
- type: "password",
3143
- autoComplete: "new-password",
3144
- value: props.confirmPassword,
3145
- onChange: props.onConfirmPasswordChange,
3146
- className: "mt-1 min-h-11 w-full rounded-lg border border-slate-200 bg-white px-3 py-1.5 text-sm text-brand-dark focus:border-brand-blue focus:outline-none focus:ring-2 focus:ring-brand-blue/20"
3147
- }
3148
- )
3149
- ] })
3150
- ] })
3151
- ] }) : /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mt-3 space-y-3", children: [
3152
- /* @__PURE__ */ jsxRuntimeExports.jsxs("label", { className: "block", children: [
3153
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-xs font-medium text-slate-500", children: "Password" }),
3154
- /* @__PURE__ */ jsxRuntimeExports.jsx(
3155
- "input",
3156
- {
3157
- type: "password",
3158
- autoComplete: "new-password",
3159
- value: props.newPassword,
3160
- onChange: props.onNewPasswordChange,
3161
- className: "mt-1 min-h-11 w-full rounded-lg border border-slate-200 bg-white px-3 py-1.5 text-sm text-brand-dark focus:border-brand-blue focus:outline-none focus:ring-2 focus:ring-brand-blue/20"
3162
- }
3163
- )
3164
- ] }),
3165
- /* @__PURE__ */ jsxRuntimeExports.jsxs("label", { className: "block", children: [
3166
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-xs font-medium text-slate-500", children: "Confirm password" }),
3167
- /* @__PURE__ */ jsxRuntimeExports.jsx(
3168
- "input",
3169
- {
3170
- type: "password",
3171
- autoComplete: "new-password",
3172
- value: props.confirmPassword,
3173
- onChange: props.onConfirmPasswordChange,
3174
- className: "mt-1 min-h-11 w-full rounded-lg border border-slate-200 bg-white px-3 py-1.5 text-sm text-brand-dark focus:border-brand-blue focus:outline-none focus:ring-2 focus:ring-brand-blue/20"
3175
- }
3176
- )
3177
- ] })
3178
- ] })
3351
+ /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "mt-1 text-xs text-slate-500", children: resolveApprovalPasswordSectionCopy(wasConfigured) }),
3352
+ wasConfigured ? /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "mt-3", children: /* @__PURE__ */ jsxRuntimeExports.jsx(
3353
+ "button",
3354
+ {
3355
+ type: "button",
3356
+ onClick: props.onOpenPasswordChangeModal,
3357
+ className: "text-xs font-medium text-brand-blue transition-colors hover:text-brand-blue/80",
3358
+ children: "Change password"
3359
+ }
3360
+ ) }) : null
3179
3361
  ] }),
3180
3362
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "rounded-xl border border-slate-100 bg-white p-4", children: [
3181
3363
  /* @__PURE__ */ jsxRuntimeExports.jsx(SectionLabel, { children: "Extra checks" }),
@@ -3215,7 +3397,7 @@ function ApprovalGateCard(props) {
3215
3397
  !totpEnabled && !totpPending && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-wrap items-center justify-between gap-3", children: [
3216
3398
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "max-w-xl space-y-1", children: [
3217
3399
  /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-sm font-medium text-brand-dark", children: "Add a second factor for high-risk approvals." }),
3218
- /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-slate-500", children: "Setup walks you through password confirmation, then a QR scan. It does not use the password fields above." })
3400
+ /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-slate-500", children: "Setup opens a guided flow for password confirmation, then QR scan." })
3219
3401
  ] }),
3220
3402
  /* @__PURE__ */ jsxRuntimeExports.jsx(
3221
3403
  ActionButton,
@@ -3242,37 +3424,8 @@ function ApprovalGateCard(props) {
3242
3424
  }
3243
3425
  )
3244
3426
  ] }),
3245
- totpEnabled && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-3", children: [
3246
- /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-slate-500", children: "Confirm your approval password and a current app code to disconnect the authenticator." }),
3247
- /* @__PURE__ */ jsxRuntimeExports.jsxs("label", { className: "block", children: [
3248
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-xs font-medium text-slate-500", children: "Approval password" }),
3249
- /* @__PURE__ */ jsxRuntimeExports.jsx(
3250
- "input",
3251
- {
3252
- type: "password",
3253
- autoComplete: "current-password",
3254
- value: props.totpActionPassword,
3255
- onChange: props.onTotpActionPasswordChange,
3256
- className: "mt-1 min-h-11 w-full rounded-lg border border-slate-200 bg-white px-3 py-1.5 text-sm text-brand-dark focus:border-brand-blue focus:outline-none focus:ring-2 focus:ring-brand-blue/20"
3257
- }
3258
- )
3259
- ] }),
3260
- /* @__PURE__ */ jsxRuntimeExports.jsxs("label", { className: "block", children: [
3261
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-xs font-medium text-slate-500", children: "Authenticator code" }),
3262
- /* @__PURE__ */ jsxRuntimeExports.jsx(
3263
- "input",
3264
- {
3265
- type: "text",
3266
- inputMode: "numeric",
3267
- pattern: "[0-9]*",
3268
- maxLength: 6,
3269
- value: props.totpCode,
3270
- onChange: props.onTotpCodeChange,
3271
- placeholder: "123456",
3272
- className: "mt-1 min-h-11 w-full rounded-lg border border-slate-200 bg-white px-3 py-1.5 text-sm tracking-[0.28em] text-brand-dark focus:border-brand-blue focus:outline-none focus:ring-2 focus:ring-brand-blue/20"
3273
- }
3274
- )
3275
- ] }),
3427
+ totpEnabled && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-wrap items-center justify-between gap-3", children: [
3428
+ /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "max-w-xl text-xs text-slate-500", children: "Disconnecting removes the app code requirement from future high-risk approvals." }),
3276
3429
  /* @__PURE__ */ jsxRuntimeExports.jsx(
3277
3430
  ActionButton,
3278
3431
  {
@@ -3310,37 +3463,7 @@ function ApprovalGateCard(props) {
3310
3463
  "Cooldown active until ",
3311
3464
  cooldownLabel
3312
3465
  ] }),
3313
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mt-3 space-y-3", children: [
3314
- /* @__PURE__ */ jsxRuntimeExports.jsxs("label", { className: "block", children: [
3315
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-xs font-medium text-slate-500", children: "Password to revoke" }),
3316
- /* @__PURE__ */ jsxRuntimeExports.jsx(
3317
- "input",
3318
- {
3319
- type: "password",
3320
- autoComplete: "current-password",
3321
- value: props.revokePassword,
3322
- onChange: props.onRevokePasswordChange,
3323
- className: "mt-1 min-h-9 w-full rounded-lg border border-slate-200 bg-white px-3 py-1.5 text-sm text-brand-dark focus:border-brand-blue focus:outline-none focus:ring-2 focus:ring-brand-blue/20"
3324
- }
3325
- )
3326
- ] }),
3327
- totpEnabled && /* @__PURE__ */ jsxRuntimeExports.jsxs("label", { className: "block", children: [
3328
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-xs font-medium text-slate-500", children: "Authenticator code to revoke" }),
3329
- /* @__PURE__ */ jsxRuntimeExports.jsx(
3330
- "input",
3331
- {
3332
- type: "text",
3333
- inputMode: "numeric",
3334
- pattern: "[0-9]*",
3335
- value: props.totpCode,
3336
- onChange: props.onTotpCodeChange,
3337
- className: "mt-1 min-h-9 w-full rounded-lg border border-slate-200 bg-white px-3 py-1.5 text-sm text-brand-dark focus:border-brand-blue focus:outline-none focus:ring-2 focus:ring-brand-blue/20"
3338
- }
3339
- )
3340
- ] }),
3341
- props.revokeError !== null && /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-brand-purple", children: props.revokeError }),
3342
- /* @__PURE__ */ jsxRuntimeExports.jsx(ActionButton, { onClick: props.onRevokeCooldown, disabled: props.revokingCooldown, variant: "outline", children: props.revokingCooldown ? "Revoking…" : "Revoke cooldown" })
3343
- ] })
3466
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "mt-3", children: /* @__PURE__ */ jsxRuntimeExports.jsx(ActionButton, { onClick: props.onRevokeCooldown, variant: "outline", children: "Revoke cooldown" }) })
3344
3467
  ] })
3345
3468
  ] }) : null
3346
3469
  ] });
@@ -3501,14 +3624,11 @@ export {
3501
3624
  hasApprovalGateSettingsChanged,
3502
3625
  hasUnsavedChanges,
3503
3626
  isFineTuningEditable,
3504
- resolveApprovalPasswordChangeCopy,
3505
- resolveApprovalPasswordVerifyCopy,
3627
+ resolveApprovalPasswordSectionCopy,
3506
3628
  resolveFineTuningSectionDescription,
3507
3629
  resolveSecurityLevelCardDescription,
3508
3630
  resolveSecurityLevelDescription,
3509
3631
  resolveTotpSetupModalDescription,
3510
3632
  resolveTotpSetupModalTitle,
3511
- resolveTotpSetupStep,
3512
- shouldShowApprovalPasswordChangeForm,
3513
- shouldShowApprovalPasswordCurrentField
3633
+ resolveTotpSetupStep
3514
3634
  };