@ai-matrx/vault 0.1.19 → 0.1.21

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/dist/index.js CHANGED
@@ -553,6 +553,8 @@ function ConfirmIdentityDialog({ open, identity, onDone, onCancel }) {
553
553
  import { useEffect as useEffect5, useState as useState5 } from "react";
554
554
  import { Badge, Button as Button2, Field as Field2, Textarea } from "@ai-matrx/design-system/controls";
555
555
  import { cn as cn3 } from "@ai-matrx/design-system";
556
+ import { withReferral } from "@ai-matrx/kit/outbound";
557
+ import { OutboundLink } from "@ai-matrx/kit/outbound-react";
556
558
  import { CopyIcon } from "@ai-matrx/design-system/icons";
557
559
 
558
560
  // src/hooks.ts
@@ -671,7 +673,7 @@ var DAY_FORMAT = { year: "numeric", month: "short", day: "numeric" };
671
673
  import { Fragment as Fragment2, jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
672
674
  var MASK = "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022";
673
675
  function SecretText({ value, multiline }) {
674
- if (multiline || value.length > 200) return /* @__PURE__ */ jsx5("span", { className: "whitespace-pre-wrap break-all font-mono text-sm", children: value });
676
+ if (multiline || value.length > 200 || value.includes("\n")) return /* @__PURE__ */ jsx5("span", { className: "whitespace-pre-wrap break-all font-mono text-sm", children: value });
675
677
  return /* @__PURE__ */ jsx5("span", { className: "break-all font-mono text-sm tracking-wide", children: [...value].map((ch, i) => /* @__PURE__ */ jsx5("span", { className: /[0-9]/.test(ch) ? "text-primary" : /[^A-Za-z0-9]/.test(ch) ? "text-warning" : void 0, children: ch }, i)) });
676
678
  }
677
679
  var quietError = (e, fallback) => wasCancelled(e) ? null : e instanceof Error ? e.message : fallback;
@@ -764,13 +766,13 @@ function PlainRow({ label, value, hint: hint2, openUrl, extra, multiline = false
764
766
  top: multiline,
765
767
  actions: /* @__PURE__ */ jsxs5(Fragment2, { children: [
766
768
  extra,
767
- isUrl && openUrl ? /* @__PURE__ */ jsx5(Button2, { variant: "quiet", icon: /* @__PURE__ */ jsx5(ExternalGlyph, {}), "aria-label": `Open ${value}`, onClick: () => openUrl(value) }) : null,
769
+ isUrl && openUrl ? /* @__PURE__ */ jsx5(Button2, { variant: "quiet", icon: /* @__PURE__ */ jsx5(ExternalGlyph, {}), "aria-label": `Open ${value}`, onClick: () => openUrl(withReferral(value)) }) : null,
768
770
  /* @__PURE__ */ jsx5(Button2, { variant: "quiet", icon: /* @__PURE__ */ jsx5(CopyIcon, {}), "aria-label": `Copy ${typeof label === "string" ? label : "value"}`, onClick: () => void copyValue2(value, typeof label === "string" && label ? label : "Value", { secret: false }) })
769
771
  ] }),
770
772
  children: isUrl && openUrl ? (
771
773
  // A real link (right-click, copy link address, open in a new tab); a click goes through the host's opener.
772
774
  /* @__PURE__ */ jsx5(
773
- "a",
775
+ OutboundLink,
774
776
  {
775
777
  href: value,
776
778
  target: "_blank",
@@ -778,7 +780,7 @@ function PlainRow({ label, value, hint: hint2, openUrl, extra, multiline = false
778
780
  className: "block max-w-full truncate text-primary hover:underline",
779
781
  onClick: (e) => {
780
782
  e.preventDefault();
781
- openUrl(value);
783
+ openUrl(withReferral(value));
782
784
  },
783
785
  children: value.replace(/^https?:\/\//i, "").replace(/\/$/, "")
784
786
  }
@@ -807,18 +809,19 @@ function EditRow({ label, hint: hint2, value, onChange, placeholder, secret = fa
807
809
  }
808
810
  );
809
811
  }
810
- function CustomEditRow({ label, value, onLabel, onValue, secret, onRemove, extra, onPasteValue }) {
812
+ function CustomEditRow({ label, value, onLabel, onValue, secret, onRemove, extra, onPasteValue, multiline = false }) {
811
813
  const [shown, setShown] = useState5(false);
812
814
  return /* @__PURE__ */ jsx5(
813
815
  VaultRow,
814
816
  {
815
817
  label: /* @__PURE__ */ jsx5(Field2, { className: "w-full", value: label, onChange: (e) => onLabel(e.target.value), placeholder: "Name", "aria-label": "Field name" }),
818
+ top: multiline,
816
819
  actions: /* @__PURE__ */ jsxs5(Fragment2, { children: [
817
- secret && value ? /* @__PURE__ */ jsx5(Button2, { variant: "quiet", icon: shown ? /* @__PURE__ */ jsx5(EyeOffGlyph, {}) : /* @__PURE__ */ jsx5(EyeGlyph, {}), "aria-label": shown ? "Hide" : "Show", onClick: () => setShown((x) => !x) }) : null,
820
+ secret && value && !multiline ? /* @__PURE__ */ jsx5(Button2, { variant: "quiet", icon: shown ? /* @__PURE__ */ jsx5(EyeOffGlyph, {}) : /* @__PURE__ */ jsx5(EyeGlyph, {}), "aria-label": shown ? "Hide" : "Show", onClick: () => setShown((x) => !x) }) : null,
818
821
  extra,
819
822
  /* @__PURE__ */ jsx5(Button2, { variant: "quiet", removes: true, icon: /* @__PURE__ */ jsx5(XGlyph, {}), "aria-label": "Remove field", onClick: onRemove })
820
823
  ] }),
821
- children: /* @__PURE__ */ jsx5(
824
+ children: multiline ? /* @__PURE__ */ jsx5(Textarea, { className: "w-full", value, onChange: (e) => onValue(e.target.value), rows: 4, placeholder: secret ? "Encrypted, hidden until shown" : "Value", autoComplete: "off", spellCheck: !secret, "aria-label": label ? `${label} value` : "Field value" }) : /* @__PURE__ */ jsx5(
822
825
  Field2,
823
826
  {
824
827
  className: "w-full",
@@ -937,6 +940,7 @@ function sortItems(items, sort, recents) {
937
940
  if (sort === "updated") return out.sort((a, b) => b.updatedAt.localeCompare(a.updatedAt));
938
941
  return out.sort((a, b) => b.createdAt.localeCompare(a.createdAt));
939
942
  }
943
+ var SECURE_NOTE_KEY = "secure_notes";
940
944
  function draftOf(item) {
941
945
  return {
942
946
  name: item?.name ?? "",
@@ -987,7 +991,9 @@ function rebaseDraft(oldBase, newBase, draft) {
987
991
  secrets: draft.secrets,
988
992
  custom,
989
993
  removed: draft.removed,
990
- authenticator: draft.authenticator
994
+ authenticator: draft.authenticator,
995
+ ...draft.browser ? { browser: draft.browser } : {},
996
+ ...draft.fieldSettings ? { fieldSettings: draft.fieldSettings } : {}
991
997
  };
992
998
  }
993
999
  function panesShown(mode, specialView, showingDetail) {
@@ -1015,7 +1021,8 @@ var FAMILY_LABELS = {
1015
1021
  signing_files: "Signing & files"
1016
1022
  };
1017
1023
  var COMMON_DEFINITION_KEYS = ["website_login", "username_password", "api_key", "env_value", "database_login", "custom"];
1018
- var MULTILINE = /(json|pem|private_key|certificate|cert|bundle|document|recovery_codes)/;
1024
+ var MULTILINE = /(json|pem|private_key|certificate|cert|bundle|document|recovery_codes|(^|_)notes?($|_))/;
1025
+ var isMultilineKey = (key) => MULTILINE.test(key);
1019
1026
  var SECRET = /(password|secret|token|api_key|private|passphrase|credential|seed|key$|_key)/;
1020
1027
  var URLISH = /(url|uri|endpoint|webhook|hook)/;
1021
1028
  function humanize(key) {
@@ -2229,7 +2236,7 @@ function normalizeWebsite(raw) {
2229
2236
  return null;
2230
2237
  }
2231
2238
  }
2232
- function AddOtpPanel({ port, items, definitions, itemId = null, onClose, onChanged, onDone }) {
2239
+ function AddOtpPanel({ port, items, definitions, itemId = null, onClose, onChanged, onAdded, onDone }) {
2233
2240
  const confirmed = useConfirmed();
2234
2241
  const candidates = items.filter((i) => !i.authenticator && !i.deletion && (i.capabilities.canEdit || i.capabilities.canManage)).sort((a, b) => a.name.localeCompare(b.name, void 0, { sensitivity: "base" }));
2235
2242
  const [input, setInput] = useState12("");
@@ -2282,6 +2289,7 @@ function AddOtpPanel({ port, items, definitions, itemId = null, onClose, onChang
2282
2289
  setInput("");
2283
2290
  setImage(null);
2284
2291
  setSaved(next);
2292
+ onAdded?.(next);
2285
2293
  onChanged();
2286
2294
  } catch (e) {
2287
2295
  setError(wasCancelled(e) ? "Not confirmed, so nothing was added." : otpActionError(e, "add it"));
@@ -2390,7 +2398,23 @@ function otpGroups(items, entries, query, hidden = /* @__PURE__ */ new Set()) {
2390
2398
  const rank = (k) => k === "me" ? 0 : k === "shared" ? 2 : 1;
2391
2399
  return [...groups.entries()].sort(([a, x], [b, y]) => rank(a) - rank(b) || x.label.localeCompare(y.label)).map(([key, g]) => ({ key, ...g }));
2392
2400
  }
2393
- function AuthenticatorView({ port, host, items, definitions, query, onOpen, onChanged, header }) {
2401
+ function withAdded(items, added) {
2402
+ if (added.size === 0) return items;
2403
+ const seen = /* @__PURE__ */ new Set();
2404
+ const out = items.map((i) => {
2405
+ seen.add(i.id);
2406
+ const mine = added.get(i.id);
2407
+ return mine && !i.authenticator ? { ...i, authenticator: mine.authenticator, name: mine.name } : i;
2408
+ });
2409
+ for (const [id, a] of added) if (!seen.has(id)) out.push(a);
2410
+ return out;
2411
+ }
2412
+ function AuthenticatorView({ port, host, items: listed, definitions, query, onOpen, onChanged, header }) {
2413
+ const [added, setAdded] = useState13(/* @__PURE__ */ new Map());
2414
+ const items = withAdded(listed, added);
2415
+ useEffect11(() => {
2416
+ if (added.size > 0 && [...added.keys()].every((id) => listed.some((i) => i.id === id && i.authenticator))) setAdded(/* @__PURE__ */ new Map());
2417
+ }, [listed, added]);
2394
2418
  const [entries, setEntries] = useState13(/* @__PURE__ */ new Map());
2395
2419
  const [hidden, setHidden] = useState13(/* @__PURE__ */ new Set());
2396
2420
  const [adding, setAdding] = useState13(false);
@@ -2406,7 +2430,7 @@ function AuthenticatorView({ port, host, items, definitions, query, onOpen, onCh
2406
2430
  return () => {
2407
2431
  live = false;
2408
2432
  };
2409
- }, [port, items]);
2433
+ }, [port, listed, added]);
2410
2434
  const groups = otpGroups(items, entries, query, hidden);
2411
2435
  const total = items.filter((i) => i.authenticator && !i.deletion && !hidden.has(i.id)).length;
2412
2436
  const familyOf2 = (key) => definitions.find((d) => d.key === key)?.family ?? "generic";
@@ -2452,6 +2476,7 @@ function AuthenticatorView({ port, host, items, definitions, query, onOpen, onCh
2452
2476
  definitions,
2453
2477
  onClose: () => setAdding(false),
2454
2478
  onChanged,
2479
+ onAdded: (item) => setAdded((cur) => new Map(cur).set(item.id, item)),
2455
2480
  onDone: (id) => {
2456
2481
  setAdding(false);
2457
2482
  onOpen(id);
@@ -4797,7 +4822,7 @@ import { Button as Button23, SearchField } from "@ai-matrx/design-system/control
4797
4822
  import { CopyIcon as CopyIcon7 } from "@ai-matrx/design-system/icons";
4798
4823
 
4799
4824
  // src/ItemPane.tsx
4800
- import { useEffect as useEffect16, useMemo as useMemo3, useRef as useRef14, useState as useState27 } from "react";
4825
+ import { Fragment as Fragment17, useEffect as useEffect16, useMemo as useMemo3, useRef as useRef14, useState as useState27 } from "react";
4801
4826
  import { DropdownMenu as DropdownMenu3, DropdownMenuContent as DropdownMenuContent3, DropdownMenuItem as DropdownMenuItem3, DropdownMenuSeparator as DropdownMenuSeparator3, DropdownMenuTrigger as DropdownMenuTrigger3 } from "@ai-matrx/design-system";
4802
4827
  import { cn as cn12 } from "@ai-matrx/design-system";
4803
4828
  import { Badge as Badge7, Button as Button22, CONTROLS_CONTAINER_CLASS, OptionCombobox, Switch as Switch6 } from "@ai-matrx/design-system/controls";
@@ -5179,14 +5204,12 @@ import { confirm as confirm7 } from "@ai-matrx/kit/confirm-opener";
5179
5204
  import { Fragment as Fragment15, jsx as jsx22, jsxs as jsxs21 } from "react/jsx-runtime";
5180
5205
  var HANDLINGS = ["visible", "revealable", "sealed"];
5181
5206
  var hint = (text, tone = "muted") => /* @__PURE__ */ jsx22("span", { className: tone === "destructive" ? "text-destructive" : void 0, title: text, children: text });
5182
- var hasSecretSettings = (s) => Boolean(s.envKey?.trim() || s.injectIntoSandbox || s.handling && s.handling !== "revealable");
5183
- function SecretBuilderRow({ field, onChange, onRemove }) {
5184
- const [open, setOpen] = useState22(hasSecretSettings(field));
5185
- const env = field.envKey ?? "";
5207
+ var saysMore = (s, defaultHandling) => Boolean(s.envKey?.trim() || s.injectIntoSandbox || s.handling && s.handling !== defaultHandling);
5208
+ function FieldSettingsRows({ name, value, defaultHandling = "revealable", onChange }) {
5209
+ const env = value.envKey ?? "";
5186
5210
  const envProblem = envKeyProblem(env);
5187
5211
  const envOk = env.trim() !== "" && !envProblem;
5188
- const handling = field.handling ?? "revealable";
5189
- const name = field.label.trim() || "Secret";
5212
+ const handling = value.handling ?? defaultHandling;
5190
5213
  const chooseHandling = async (next) => {
5191
5214
  if (next === handling) return;
5192
5215
  if (next === "sealed") {
@@ -5200,6 +5223,38 @@ function SecretBuilderRow({ field, onChange, onRemove }) {
5200
5223
  }
5201
5224
  onChange({ handling: next });
5202
5225
  };
5226
+ return /* @__PURE__ */ jsxs21(Fragment15, { children: [
5227
+ /* @__PURE__ */ jsx22(VaultRow, { label: "Env key", hint: envProblem ? hint(envProblem, "destructive") : hint("The name sandboxes and workflows use"), children: /* @__PURE__ */ jsx22(
5228
+ Field8,
5229
+ {
5230
+ className: "w-full font-mono",
5231
+ "aria-label": `${name} env key`,
5232
+ "aria-invalid": Boolean(envProblem),
5233
+ placeholder: "e.g. GITHUB_TOKEN",
5234
+ autoComplete: "off",
5235
+ spellCheck: false,
5236
+ value: env,
5237
+ onChange: (e) => onChange({ envKey: e.target.value, ...e.target.value.trim() ? {} : { injectIntoSandbox: false } })
5238
+ }
5239
+ ) }),
5240
+ /* @__PURE__ */ jsx22(VaultRow, { label: "Who can see it", hint: hint(HANDLING_WORDS[handling].hint), children: /* @__PURE__ */ jsx22(
5241
+ SegmentedControl5,
5242
+ {
5243
+ "aria-label": `Who can see ${name}`,
5244
+ value: handling,
5245
+ onValueChange: (v) => void chooseHandling(v),
5246
+ data: HANDLINGS.map((h) => ({ value: h, label: HANDLING_WORDS[h].label, title: HANDLING_WORDS[h].hint }))
5247
+ }
5248
+ ) }),
5249
+ /* @__PURE__ */ jsx22(VaultRow, { label: "Use in sandbox", hint: envOk ? hint("Sandboxes get it as an env variable") : hint("Needs an env key"), children: /* @__PURE__ */ jsx22(Switch5, { "aria-label": `Use ${name} in sandbox`, checked: envOk && value.injectIntoSandbox === true, disabled: !envOk, onCheckedChange: (v) => onChange({ injectIntoSandbox: v }) }) })
5250
+ ] });
5251
+ }
5252
+ var hasSecretSettings = (s) => saysMore(s, "revealable");
5253
+ var hasDeclaredSettings = (s, defaultHandling) => Boolean(s && saysMore(s, defaultHandling));
5254
+ function SecretBuilderRow({ field, onChange, onRemove }) {
5255
+ const [open, setOpen] = useState22(hasSecretSettings(field));
5256
+ const handling = field.handling ?? "revealable";
5257
+ const name = field.label.trim() || "Secret";
5203
5258
  return /* @__PURE__ */ jsxs21(Fragment15, { children: [
5204
5259
  /* @__PURE__ */ jsx22(
5205
5260
  CustomEditRow,
@@ -5207,47 +5262,26 @@ function SecretBuilderRow({ field, onChange, onRemove }) {
5207
5262
  label: field.label,
5208
5263
  value: field.value,
5209
5264
  secret: true,
5265
+ multiline: field.multiline === true,
5210
5266
  onLabel: (v) => onChange({ label: v }),
5211
5267
  onValue: (v) => onChange({ value: v }),
5212
5268
  onRemove,
5213
- onPasteValue: (text) => {
5214
- const a = parseEnvAssignment(text);
5215
- if (!a) return false;
5216
- onChange({ envKey: a.key, value: a.value, ...field.label.trim() ? {} : { label: labelFromEnvKey(a.key) } });
5217
- setOpen(true);
5218
- return true;
5269
+ ...field.multiline ? {} : {
5270
+ onPasteValue: (text) => {
5271
+ const a = parseEnvAssignment(text);
5272
+ if (!a) return false;
5273
+ onChange({ envKey: a.key, value: a.value, ...field.label.trim() ? {} : { label: labelFromEnvKey(a.key) } });
5274
+ setOpen(true);
5275
+ return true;
5276
+ }
5219
5277
  },
5220
5278
  extra: /* @__PURE__ */ jsxs21(Fragment15, { children: [
5221
- handling !== "sealed" ? /* @__PURE__ */ jsx22(GenerateButton, { onUse: (v) => onChange({ value: v }) }) : null,
5279
+ handling !== "sealed" && !field.multiline ? /* @__PURE__ */ jsx22(GenerateButton, { onUse: (v) => onChange({ value: v }) }) : null,
5222
5280
  /* @__PURE__ */ jsx22(Button18, { variant: "quiet", icon: /* @__PURE__ */ jsx22(SlidersGlyph, {}), "aria-label": `${name} settings`, pressed: open, onClick: () => setOpen((o) => !o) })
5223
5281
  ] })
5224
5282
  }
5225
5283
  ),
5226
- open ? /* @__PURE__ */ jsxs21(Fragment15, { children: [
5227
- /* @__PURE__ */ jsx22(VaultRow, { label: "Env key", hint: envProblem ? hint(envProblem, "destructive") : hint("The name sandboxes and workflows use"), children: /* @__PURE__ */ jsx22(
5228
- Field8,
5229
- {
5230
- className: "w-full font-mono",
5231
- "aria-label": `${name} env key`,
5232
- "aria-invalid": Boolean(envProblem),
5233
- placeholder: "e.g. GITHUB_TOKEN",
5234
- autoComplete: "off",
5235
- spellCheck: false,
5236
- value: env,
5237
- onChange: (e) => onChange({ envKey: e.target.value, ...e.target.value.trim() ? {} : { injectIntoSandbox: false } })
5238
- }
5239
- ) }),
5240
- /* @__PURE__ */ jsx22(VaultRow, { label: "Who can see it", hint: hint(HANDLING_WORDS[handling].hint), children: /* @__PURE__ */ jsx22(
5241
- SegmentedControl5,
5242
- {
5243
- "aria-label": `Who can see ${name}`,
5244
- value: handling,
5245
- onValueChange: (v) => void chooseHandling(v),
5246
- data: HANDLINGS.map((h) => ({ value: h, label: HANDLING_WORDS[h].label, title: HANDLING_WORDS[h].hint }))
5247
- }
5248
- ) }),
5249
- /* @__PURE__ */ jsx22(VaultRow, { label: "Use in sandbox", hint: envOk ? hint("Sandboxes get it as an env variable") : hint("Needs an env key"), children: /* @__PURE__ */ jsx22(Switch5, { "aria-label": `Use ${name} in sandbox`, checked: envOk && field.injectIntoSandbox === true, disabled: !envOk, onCheckedChange: (v) => onChange({ injectIntoSandbox: v }) }) })
5250
- ] }) : null
5284
+ open ? /* @__PURE__ */ jsx22(FieldSettingsRows, { name, value: field, onChange }) : null
5251
5285
  ] });
5252
5286
  }
5253
5287
  function withMfaMethod(custom, v) {
@@ -5876,7 +5910,9 @@ function SharingSummary({ item, port, className }) {
5876
5910
 
5877
5911
  // src/ItemPane.tsx
5878
5912
  import { formatAbsoluteDate as formatAbsoluteDate3 } from "@ai-matrx/kit/format";
5879
- import { Fragment as Fragment17, jsx as jsx27, jsxs as jsxs25 } from "react/jsx-runtime";
5913
+ import { Fragment as Fragment18, jsx as jsx27, jsxs as jsxs25 } from "react/jsx-runtime";
5914
+ var ENV_VALUE_KEY = "env_value";
5915
+ var secureNoteDraft = () => ({ key: SECURE_NOTE_KEY, label: "Secure note", value: "", multiline: true, description: "Encrypted notes" });
5880
5916
  var USERNAME_KEYS = /^(username|user_name|email|login|username_or_email|account_email)$/;
5881
5917
  function ItemPane(props) {
5882
5918
  const { port, item, definition, mode, host, organizations, defaultOwner, seed, onBack, onMode, onSaved, onDeleted, onDirty, onCancelNew, onPanel, onChanged, favorite, notice, refreshKey } = props;
@@ -6067,6 +6103,16 @@ function ItemPane(props) {
6067
6103
  const fieldSettings = (f) => canEdit ? /* @__PURE__ */ jsx27(Button22, { variant: "quiet", icon: /* @__PURE__ */ jsx27(SlidersGlyph, {}), "aria-label": `${f.label} settings`, title: `${f.label} settings`, onClick: () => onPanel(`field:${f.id}`) }) : null;
6068
6104
  const turnOnOne = canTurnOn ? (f) => () => turnOn([f]) : () => void 0;
6069
6105
  const declaredKeys = new Set(declared.map((f) => f.key));
6106
+ const hasSecureNote = declaredKeys.has(SECURE_NOTE_KEY) || Boolean(item?.fields.some((f) => f.key === SECURE_NOTE_KEY)) || draft.secrets.some((x) => x.key === SECURE_NOTE_KEY);
6107
+ const [settingsToggled, setSettingsToggled] = useState27(/* @__PURE__ */ new Set());
6108
+ const settingsOpen = (f) => settingsToggled.has(f.key) !== (definition?.key === ENV_VALUE_KEY || hasDeclaredSettings(draft.fieldSettings?.[f.key], f.handling));
6109
+ const toggleSettings = (key) => setSettingsToggled((cur) => {
6110
+ const next = new Set(cur);
6111
+ if (next.has(key)) next.delete(key);
6112
+ else next.add(key);
6113
+ return next;
6114
+ });
6115
+ const patchFieldSettings = (key, p) => setDraft((d) => ({ ...d, fieldSettings: { ...d.fieldSettings, [key]: { ...d.fieldSettings?.[key], ...p } } }));
6070
6116
  const extraFields = (item?.fields ?? []).filter((f) => !declaredKeys.has(f.key) && !draft.removed.includes(f.id) && !isAuthenticatorStorage(f.key));
6071
6117
  const metaValue = (key) => draft.values[key] ?? item?.custom.find((c) => c.key === key)?.value ?? "";
6072
6118
  const extraCustom = draft.custom.filter((c) => !declaredKeys.has(c.key) && c.key !== MFA_METHOD_KEY && !isAuthenticatorStorage(c.key));
@@ -6149,7 +6195,7 @@ function ItemPane(props) {
6149
6195
  /* @__PURE__ */ jsx27(SettingsIcon, { className: "size-4" }),
6150
6196
  "Settings"
6151
6197
  ] }) : null,
6152
- canManage ? /* @__PURE__ */ jsxs25(Fragment17, { children: [
6198
+ canManage ? /* @__PURE__ */ jsxs25(Fragment18, { children: [
6153
6199
  /* @__PURE__ */ jsx27(DropdownMenuSeparator3, {}),
6154
6200
  /* @__PURE__ */ jsxs25(DropdownMenuItem3, { className: "text-destructive", disabled: busy, onSelect: () => void remove(), children: [
6155
6201
  /* @__PURE__ */ jsx27(TrashGlyph, { className: "size-4" }),
@@ -6169,7 +6215,7 @@ function ItemPane(props) {
6169
6215
  /* @__PURE__ */ jsx27("span", { className: "min-w-0 flex-1 truncate text-warning", children: offFields.length === 0 ? "This item is turned off" : offFields.length === 1 ? "1 field is turned off" : `${offFields.length} fields are turned off` }),
6170
6216
  canTurnOn ? /* @__PURE__ */ jsx27(TurnOnButton, { label: "all fields", onTurnOn: () => turnOn(offFields) }) : null
6171
6217
  ] }) : null,
6172
- /* @__PURE__ */ jsx27("div", { className: "min-h-0 flex-1 overflow-y-auto", children: sent ? /* @__PURE__ */ jsx27(AssignResult, { sent, onAnother: createAnother, onDone: onCancelNew }) : /* @__PURE__ */ jsx27("div", { className: "mx-auto flex w-full max-w-3xl flex-col gap-5 px-3 py-4 sm:px-6 sm:py-6", children: editing ? /* @__PURE__ */ jsxs25(Fragment17, { children: [
6218
+ /* @__PURE__ */ jsx27("div", { className: "min-h-0 flex-1 overflow-y-auto", children: sent ? /* @__PURE__ */ jsx27(AssignResult, { sent, onAnother: createAnother, onDone: onCancelNew }) : /* @__PURE__ */ jsx27("div", { className: "mx-auto flex w-full max-w-3xl flex-col gap-5 px-3 py-4 sm:px-6 sm:py-6", children: editing ? /* @__PURE__ */ jsxs25(Fragment18, { children: [
6173
6219
  /* @__PURE__ */ jsxs25(VaultSection, { children: [
6174
6220
  mode === "new" ? /* @__PURE__ */ jsx27(VaultRow, { label: "Save to", children: /* @__PURE__ */ jsx27(
6175
6221
  OptionCombobox,
@@ -6190,7 +6236,7 @@ function ItemPane(props) {
6190
6236
  }
6191
6237
  ) }) : null,
6192
6238
  /* @__PURE__ */ jsx27(EditRow, { label: "Name", value: draft.name, onChange: (v) => patch({ name: v }), placeholder: "Name", autoFocus: mode === "new" }),
6193
- mode === "new" && owner === "person" ? /* @__PURE__ */ jsxs25(Fragment17, { children: [
6239
+ mode === "new" && owner === "person" ? /* @__PURE__ */ jsxs25(Fragment18, { children: [
6194
6240
  /* @__PURE__ */ jsx27(EditRow, { label: "Their email", value: recipient, placeholder: "name@company.com", inputMode: "email", onChange: setRecipient }),
6195
6241
  declared.some((f) => f.storage === "encrypted" && f.kind === "secret") ? /* @__PURE__ */ jsx27(VaultRow, { label: "Password", hint: "You never see it", children: /* @__PURE__ */ jsx27(Switch6, { checked: generatePrivately, onCheckedChange: setGeneratePrivately, "aria-label": "Generate the password privately" }) }) : null
6196
6242
  ] }) : null,
@@ -6199,24 +6245,29 @@ function ItemPane(props) {
6199
6245
  const secretKind = f.storage === "encrypted" && f.kind === "secret";
6200
6246
  const value = f.storage === "metadata" ? metaValue(f.key) : draft.values[f.key] ?? "";
6201
6247
  const hidden = existing && existing.handling !== "visible" && !(f.key in loaded);
6202
- return /* @__PURE__ */ jsx27(
6203
- EditRow,
6204
- {
6205
- label: f.label,
6206
- value,
6207
- placeholder: hidden ? "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022" : f.required ? "Required" : isRecoveryCodesField(f) ? "Backup codes, one per line" : "",
6208
- secret: secretKind,
6209
- multiline: f.kind === "multiline",
6210
- inputMode: f.kind === "url" ? "url" : USERNAME_KEYS.test(f.key) ? "email" : "text",
6211
- onChange: (v) => setValue(f.key, v),
6212
- actions: /* @__PURE__ */ jsxs25(Fragment17, { children: [
6213
- hidden && existing?.handling === "revealable" && item?.capabilities.canReveal ? /* @__PURE__ */ jsx27(Button22, { variant: "quiet", icon: /* @__PURE__ */ jsx27(EyeGlyph, {}), "aria-label": `Show current ${f.label}`, onClick: () => void loadForEdit(f.key) }) : null,
6214
- secretKind ? /* @__PURE__ */ jsx27(GenerateButton, { onUse: (v) => setValue(f.key, v) }) : null,
6215
- existing ? /* @__PURE__ */ jsx27(Button22, { variant: "quiet", icon: /* @__PURE__ */ jsx27(SettingsIcon, {}), "aria-label": `${f.label} settings`, onClick: () => onPanel(`field:${existing.id}`) }) : null
6216
- ] })
6217
- },
6218
- f.key
6219
- );
6248
+ const newSecret = mode === "new" && f.storage === "encrypted" && owner !== "person";
6249
+ const open = newSecret && settingsOpen(f);
6250
+ return /* @__PURE__ */ jsxs25(Fragment17, { children: [
6251
+ /* @__PURE__ */ jsx27(
6252
+ EditRow,
6253
+ {
6254
+ label: f.label,
6255
+ value,
6256
+ placeholder: hidden ? "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022" : f.required ? "Required" : isRecoveryCodesField(f) ? "Backup codes, one per line" : "",
6257
+ secret: secretKind,
6258
+ multiline: f.kind === "multiline" || value.includes("\n"),
6259
+ inputMode: f.kind === "url" ? "url" : USERNAME_KEYS.test(f.key) ? "email" : "text",
6260
+ onChange: (v) => setValue(f.key, v),
6261
+ actions: /* @__PURE__ */ jsxs25(Fragment18, { children: [
6262
+ hidden && existing?.handling === "revealable" && item?.capabilities.canReveal ? /* @__PURE__ */ jsx27(Button22, { variant: "quiet", icon: /* @__PURE__ */ jsx27(EyeGlyph, {}), "aria-label": `Show current ${f.label}`, onClick: () => void loadForEdit(f.key) }) : null,
6263
+ secretKind ? /* @__PURE__ */ jsx27(GenerateButton, { onUse: (v) => setValue(f.key, v) }) : null,
6264
+ existing ? /* @__PURE__ */ jsx27(Button22, { variant: "quiet", icon: /* @__PURE__ */ jsx27(SettingsIcon, {}), "aria-label": `${f.label} settings`, onClick: () => onPanel(`field:${existing.id}`) }) : null,
6265
+ newSecret ? /* @__PURE__ */ jsx27(Button22, { variant: "quiet", icon: /* @__PURE__ */ jsx27(SlidersGlyph, {}), "aria-label": `${f.label} settings`, pressed: open, onClick: () => toggleSettings(f.key) }) : null
6266
+ ] })
6267
+ }
6268
+ ),
6269
+ open ? /* @__PURE__ */ jsx27(FieldSettingsRows, { name: f.label, value: draft.fieldSettings?.[f.key] ?? {}, defaultHandling: f.handling, onChange: (p) => patchFieldSettings(f.key, p) }) : null
6270
+ ] }, f.key);
6220
6271
  }),
6221
6272
  extraFields.map((f) => /* @__PURE__ */ jsx27(
6222
6273
  EditRow,
@@ -6225,11 +6276,11 @@ function ItemPane(props) {
6225
6276
  value: draft.values[f.key] ?? "",
6226
6277
  placeholder: f.handling === "visible" || f.key in loaded ? "" : "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022",
6227
6278
  secret: f.handling !== "visible" && !isRecoveryCodesField(f),
6228
- multiline: isRecoveryCodesField(f),
6279
+ multiline: f.kind === "multiline" || isRecoveryCodesField(f) || (draft.values[f.key] ?? "").includes("\n"),
6229
6280
  onChange: (v) => setValue(f.key, v),
6230
6281
  onRemove: () => patch({ removed: [...draft.removed, f.id] }),
6231
6282
  removeLabel: `Delete ${f.label}`,
6232
- actions: /* @__PURE__ */ jsxs25(Fragment17, { children: [
6283
+ actions: /* @__PURE__ */ jsxs25(Fragment18, { children: [
6233
6284
  f.handling === "revealable" && !(f.key in loaded) && item?.capabilities.canReveal ? /* @__PURE__ */ jsx27(Button22, { variant: "quiet", icon: /* @__PURE__ */ jsx27(EyeGlyph, {}), "aria-label": `Show current ${f.label}`, onClick: () => void loadForEdit(f.key) }) : null,
6234
6285
  /* @__PURE__ */ jsx27(Button22, { variant: "quiet", icon: /* @__PURE__ */ jsx27(SettingsIcon, {}), "aria-label": `${f.label} settings`, onClick: () => onPanel(`field:${f.id}`) })
6235
6286
  ] })
@@ -6294,6 +6345,7 @@ function ItemPane(props) {
6294
6345
  /* @__PURE__ */ jsx27(DropdownMenuTrigger3, { asChild: true, children: /* @__PURE__ */ jsx27(Button22, { variant: "quiet", icon: /* @__PURE__ */ jsx27(PlusGlyph, {}), children: "Add" }) }),
6295
6346
  /* @__PURE__ */ jsxs25(DropdownMenuContent3, { align: "end", children: [
6296
6347
  /* @__PURE__ */ jsx27(DropdownMenuItem3, { onSelect: () => patch({ secrets: [...draft.secrets, { key: "", label: "", value: "" }] }), children: "Secret field" }),
6348
+ !hasSecureNote ? /* @__PURE__ */ jsx27(DropdownMenuItem3, { onSelect: () => patch({ secrets: [...draft.secrets, secureNoteDraft()] }), children: "Secure note" }) : null,
6297
6349
  /* @__PURE__ */ jsx27(DropdownMenuItem3, { onSelect: () => patch({ custom: [...draft.custom, { key: `custom_${draft.custom.length + 1}_${Date.now() % 1e5}`, label: "", value: "" }] }), children: "Text field" }),
6298
6350
  !tagsVisible ? /* @__PURE__ */ jsx27(DropdownMenuItem3, { onSelect: () => setShowTags(true), children: "Tags" }) : null,
6299
6351
  !descriptionVisible ? /* @__PURE__ */ jsx27(DropdownMenuItem3, { onSelect: () => setShowDescription(true), children: "Description" }) : null,
@@ -6326,7 +6378,7 @@ function ItemPane(props) {
6326
6378
  }
6327
6379
  ),
6328
6380
  filesProps ? /* @__PURE__ */ jsx27(FilesSection, { ...filesProps, editing: true }) : mode === "new" && owner !== "person" ? /* @__PURE__ */ jsx27(DraftFilesSection, { files: queuedFiles, onFiles: setQueuedFiles }) : null
6329
- ] }) : item ? /* @__PURE__ */ jsxs25(Fragment17, { children: [
6381
+ ] }) : item ? /* @__PURE__ */ jsxs25(Fragment18, { children: [
6330
6382
  /* @__PURE__ */ jsxs25(VaultSection, { children: [
6331
6383
  declared.map((f) => {
6332
6384
  if (f.storage === "metadata") {
@@ -6647,7 +6699,7 @@ function refusalText(reason) {
6647
6699
  // src/lists/RestorePanel.tsx
6648
6700
  import { useEffect as useEffect18, useState as useState29 } from "react";
6649
6701
  import { Button as Button24 } from "@ai-matrx/design-system/controls";
6650
- import { Fragment as Fragment18, jsx as jsx29, jsxs as jsxs27 } from "react/jsx-runtime";
6702
+ import { Fragment as Fragment19, jsx as jsx29, jsxs as jsxs27 } from "react/jsx-runtime";
6651
6703
  var count = (n, one) => `${n} ${one}${n === 1 ? "" : "s"}`;
6652
6704
  function RestorePanel({ lists, item, restore, onClose }) {
6653
6705
  const [preview, setPreview] = useState29(null);
@@ -6692,7 +6744,7 @@ function RestorePanel({ lists, item, restore, onClose }) {
6692
6744
  title: `Restore "${item.name}"`,
6693
6745
  size: "md",
6694
6746
  onClose,
6695
- footer: /* @__PURE__ */ jsxs27(Fragment18, { children: [
6747
+ footer: /* @__PURE__ */ jsxs27(Fragment19, { children: [
6696
6748
  /* @__PURE__ */ jsx29(Button24, { variant: "quiet", onClick: onClose, children: refused ? "Close" : "Cancel" }),
6697
6749
  refused ? null : /* @__PURE__ */ jsx29(Button24, { variant: "primary", icon: /* @__PURE__ */ jsx29(RestoreGlyph, {}), disabled: busy || loading || lists.preview !== void 0 && preview === null, onClick: () => void run(), children: busy ? "Restoring\u2026" : "Restore" })
6698
6750
  ] }),
@@ -7190,7 +7242,7 @@ function addPasskeyFailure(message2) {
7190
7242
  var isRevoked = (d) => d.revokedAt !== null;
7191
7243
 
7192
7244
  // src/devices/DevicesPanel.tsx
7193
- import { Fragment as Fragment19, jsx as jsx33, jsxs as jsxs31 } from "react/jsx-runtime";
7245
+ import { Fragment as Fragment20, jsx as jsx33, jsxs as jsxs31 } from "react/jsx-runtime";
7194
7246
  var messageOf3 = (e, fallback) => e instanceof Error && e.message ? e.message : fallback;
7195
7247
  function ErrorLine2({ text }) {
7196
7248
  return text ? /* @__PURE__ */ jsx33("p", { role: "alert", className: "px-1 text-sm text-destructive", children: text }) : null;
@@ -7321,7 +7373,7 @@ function ApprovePanel({ port, host, onClose, approvalKey, label, kind = "browser
7321
7373
  };
7322
7374
  const canApprove = Boolean(key && passkeys && here && methods?.passkey && !approvedUntil);
7323
7375
  const title = isMac ? "Approve this Mac" : name ? `Approve ${name}` : "Approve this browser";
7324
- const footer = approvedUntil ? /* @__PURE__ */ jsx33(Button28, { variant: "primary", onClick: onClose, children: "Done" }) : /* @__PURE__ */ jsxs31(Fragment19, { children: [
7376
+ const footer = approvedUntil ? /* @__PURE__ */ jsx33(Button28, { variant: "primary", onClick: onClose, children: "Done" }) : /* @__PURE__ */ jsxs31(Fragment20, { children: [
7325
7377
  /* @__PURE__ */ jsx33(Button28, { variant: "quiet", disabled: busy !== null, onClick: refuse, children: key ? "Refuse" : "Close" }),
7326
7378
  canApprove ? /* @__PURE__ */ jsx33(Button28, { variant: "primary", icon: /* @__PURE__ */ jsx33(FingerprintGlyph, {}), disabled: busy !== null, onClick: () => void approve(), children: busy === "approve" ? "Approving\u2026" : "Approve with passkey" }) : null
7327
7379
  ] });
@@ -7329,7 +7381,7 @@ function ApprovePanel({ port, host, onClose, approvalKey, label, kind = "browser
7329
7381
  "This link names no key. Start again from ",
7330
7382
  where,
7331
7383
  "."
7332
- ] }) }) : /* @__PURE__ */ jsxs31(Fragment19, { children: [
7384
+ ] }) }) : /* @__PURE__ */ jsxs31(Fragment20, { children: [
7333
7385
  /* @__PURE__ */ jsxs31(VaultSection, { title: "What is asking", children: [
7334
7386
  /* @__PURE__ */ jsx33(VaultRow, { label: "Asking", hint: "Approve only if you just asked", children: asker }),
7335
7387
  name ? /* @__PURE__ */ jsx33(VaultRow, { label: "Name", children: /* @__PURE__ */ jsx33("span", { className: "block truncate", children: name }) }) : null,
@@ -7629,7 +7681,7 @@ function useNativeImport(bridge, stillAllowed, onImported) {
7629
7681
  }
7630
7682
 
7631
7683
  // src/native/NativeImportPanel.tsx
7632
- import { Fragment as Fragment20, jsx as jsx34, jsxs as jsxs32 } from "react/jsx-runtime";
7684
+ import { Fragment as Fragment21, jsx as jsx34, jsxs as jsxs32 } from "react/jsx-runtime";
7633
7685
  var TONE = {
7634
7686
  eligible: "success",
7635
7687
  committed: "success",
@@ -7673,16 +7725,16 @@ function Importer({ bridge, readLimits, activeOrganizationId, organizations, onC
7673
7725
  const yes = await confirm13({ title: `Add ${plural2(ready, "passkey")} to ${orgName}?`, description: "Passkeys already there are skipped.", confirmLabel: "Import" });
7674
7726
  if (yes) await flow.confirm();
7675
7727
  };
7676
- const footer = preview ? /* @__PURE__ */ jsxs32(Fragment20, { children: [
7728
+ const footer = preview ? /* @__PURE__ */ jsxs32(Fragment21, { children: [
7677
7729
  /* @__PURE__ */ jsx34(Button29, { variant: "quiet", disabled: busy, onClick: () => void flow.cancel(), children: "Cancel" }),
7678
7730
  /* @__PURE__ */ jsxs32(Button29, { variant: "primary", icon: /* @__PURE__ */ jsx34(ImportGlyph, { className: "size-4" }), disabled: busy || ready === 0, onClick: () => void importNow(), children: [
7679
7731
  "Import ",
7680
7732
  ready
7681
7733
  ] })
7682
- ] }) : status ? active ? /* @__PURE__ */ jsxs32(Fragment20, { children: [
7734
+ ] }) : status ? active ? /* @__PURE__ */ jsxs32(Fragment21, { children: [
7683
7735
  /* @__PURE__ */ jsx34(Button29, { variant: "outline", disabled: busy, onClick: () => void flow.refresh(), children: "Refresh" }),
7684
7736
  /* @__PURE__ */ jsx34(Button29, { variant: "outline", disabled: busy, onClick: () => void flow.cancel(), children: "Cancel import" })
7685
- ] }) : /* @__PURE__ */ jsxs32(Fragment20, { children: [
7737
+ ] }) : /* @__PURE__ */ jsxs32(Fragment21, { children: [
7686
7738
  /* @__PURE__ */ jsx34(Button29, { variant: "quiet", onClick: flow.reset, children: "New file" }),
7687
7739
  /* @__PURE__ */ jsx34(Button29, { variant: "primary", onClick: onClose, children: "Done" })
7688
7740
  ] }) : /* @__PURE__ */ jsx34(Button29, { variant: "quiet", onClick: onClose, children: "Close" });
@@ -7721,7 +7773,7 @@ import { GlobeIcon as GlobeIcon4 } from "@ai-matrx/design-system/icons";
7721
7773
  // src/extension/CaptureCard.tsx
7722
7774
  import { useState as useState36, useSyncExternalStore as useSyncExternalStore3 } from "react";
7723
7775
  import { Button as Button30, SearchField as SearchField3 } from "@ai-matrx/design-system/controls";
7724
- import { Fragment as Fragment21, jsx as jsx35, jsxs as jsxs33 } from "react/jsx-runtime";
7776
+ import { Fragment as Fragment22, jsx as jsx35, jsxs as jsxs33 } from "react/jsx-runtime";
7725
7777
  var SEARCH_FROM = 4;
7726
7778
  function CaptureCard({ capture, onChanged }) {
7727
7779
  const prompt = useSyncExternalStore3(capture.subscribe, capture.get, capture.get);
@@ -7754,7 +7806,7 @@ function CaptureCard({ capture, onChanged }) {
7754
7806
  ] }),
7755
7807
  /* @__PURE__ */ jsx35(Button30, { variant: "quiet", icon: /* @__PURE__ */ jsx35(XGlyph, {}), "aria-label": "Not now", disabled: busy, onClick: () => void decide({ action: "dismiss" }) })
7756
7808
  ] }),
7757
- prompt.unavailable ? /* @__PURE__ */ jsx35("p", { className: "text-xs text-muted-foreground", children: "Save it by hand with Save a login." }) : /* @__PURE__ */ jsxs33(Fragment21, { children: [
7809
+ prompt.unavailable ? /* @__PURE__ */ jsx35("p", { className: "text-xs text-muted-foreground", children: "Save it by hand with Save a login." }) : /* @__PURE__ */ jsxs33(Fragment22, { children: [
7758
7810
  existing.length > 0 ? /* @__PURE__ */ jsxs33("div", { className: "flex flex-col gap-1", children: [
7759
7811
  existing.length >= SEARCH_FROM ? /* @__PURE__ */ jsx35(SearchField3, { value: q, onChange: (e) => setQ(e.target.value), placeholder: "Search logins", "aria-label": "Search logins to update" }) : null,
7760
7812
  /* @__PURE__ */ jsxs33("ul", { className: "flex max-h-36 flex-col gap-px overflow-y-auto", children: [
@@ -7860,7 +7912,7 @@ import { useState as useState38 } from "react";
7860
7912
  import { Popover as Popover2, PopoverContent as PopoverContent2, PopoverTrigger as PopoverTrigger2 } from "@ai-matrx/design-system";
7861
7913
  import { Button as Button32 } from "@ai-matrx/design-system/controls";
7862
7914
  import { CopyIcon as CopyIcon8 } from "@ai-matrx/design-system/icons";
7863
- import { Fragment as Fragment22, jsx as jsx37, jsxs as jsxs35 } from "react/jsx-runtime";
7915
+ import { Fragment as Fragment23, jsx as jsx37, jsxs as jsxs35 } from "react/jsx-runtime";
7864
7916
  var HOLD_MS = 6e4;
7865
7917
  function PageGenerator({ fields }) {
7866
7918
  const limits = useGeneratorLimitsSource();
@@ -7911,12 +7963,12 @@ function PageGenerator({ fields }) {
7911
7963
  }
7912
7964
  };
7913
7965
  const value = held.value;
7914
- return /* @__PURE__ */ jsxs35(Fragment22, { children: [
7966
+ return /* @__PURE__ */ jsxs35(Fragment23, { children: [
7915
7967
  /* @__PURE__ */ jsx37(
7916
7968
  VaultRow,
7917
7969
  {
7918
7970
  label: "New password",
7919
- actions: /* @__PURE__ */ jsxs35(Fragment22, { children: [
7971
+ actions: /* @__PURE__ */ jsxs35(Fragment23, { children: [
7920
7972
  value ? /* @__PURE__ */ jsx37(Button32, { variant: "quiet", icon: /* @__PURE__ */ jsx37(CopyIcon8, {}), "aria-label": "Copy password", onClick: () => void copy(value, "Password") }) : null,
7921
7973
  /* @__PURE__ */ jsxs35(Popover2, { open, onOpenChange: setOpen, children: [
7922
7974
  /* @__PURE__ */ jsx37(PopoverTrigger2, { asChild: true, children: /* @__PURE__ */ jsx37(Button32, { variant: "outline", icon: /* @__PURE__ */ jsx37(KeyGlyph, {}), disabled: busy, children: "Generate" }) }),
@@ -9062,6 +9114,28 @@ function trashItem(r, ctx) {
9062
9114
  createdAt: ""
9063
9115
  };
9064
9116
  }
9117
+ async function withTypes(items, door, ctx) {
9118
+ if (!door.schema || items.length === 0) return items;
9119
+ const keys = /* @__PURE__ */ new Map();
9120
+ const CHUNK = 100;
9121
+ try {
9122
+ for (let i = 0; i < items.length; i += CHUNK) {
9123
+ const ids = items.slice(i, i + CHUNK).map((x) => x.id);
9124
+ const { data, error } = await door.schema("users").from("credential_items").select("id,definition_key").in("id", ids);
9125
+ if (error) throw new Error(error.message || "credential_items read failed");
9126
+ for (const r of Array.isArray(data) ? data : []) {
9127
+ if (typeof r.id === "string" && typeof r.definition_key === "string" && r.definition_key) keys.set(r.id, r.definition_key);
9128
+ }
9129
+ }
9130
+ } catch (e) {
9131
+ console.warn("[vault] Trash item types could not be read; rows say Credential", e);
9132
+ return items;
9133
+ }
9134
+ return items.map((it) => {
9135
+ const key = keys.get(it.id);
9136
+ return key ? { ...it, definitionKey: key, definitionLabel: ctx.definition(key)?.label ?? humanize(key) } : it;
9137
+ });
9138
+ }
9065
9139
  var previewOf = async (door, id) => parseRecoveryPreview(await call(door, "vault_recovery_preview", { p_id: id }));
9066
9140
  function createListsPort(ctx, door) {
9067
9141
  return {
@@ -9094,7 +9168,8 @@ function createListsPort(ctx, door) {
9094
9168
  if (rows.length < PAGE2) break;
9095
9169
  }
9096
9170
  const items = all.map((r) => trashItem(r, ctx)).filter((i) => i !== null);
9097
- return [...new Map(items.map((i) => [i.id, i])).values()];
9171
+ const unique = [...new Map(items.map((i) => [i.id, i])).values()];
9172
+ return withTypes(unique, door, ctx);
9098
9173
  }
9099
9174
  },
9100
9175
  favorites: {
@@ -9117,16 +9192,25 @@ function createListsPort(ctx, door) {
9117
9192
  import * as account6 from "@ai-matrx/api/account";
9118
9193
 
9119
9194
  // src/secret-field-in.ts
9120
- function secretFieldIn(s) {
9195
+ function settingsIn(s) {
9121
9196
  const envKey = s.envKey?.trim() || null;
9122
9197
  return {
9123
- field_key: fieldKeyOf(s.label),
9124
- value: s.value,
9125
- handling: s.handling ?? "revealable",
9126
9198
  ...envKey ? { env_key: envKey } : {},
9127
9199
  ...envKey && s.injectIntoSandbox ? { inject_into_sandbox: true } : {}
9128
9200
  };
9129
9201
  }
9202
+ function secretFieldIn(s) {
9203
+ return {
9204
+ field_key: s.key.trim() || fieldKeyOf(s.label),
9205
+ value: s.value,
9206
+ handling: s.handling ?? "revealable",
9207
+ ...s.description ? { description: s.description } : {},
9208
+ ...settingsIn(s)
9209
+ };
9210
+ }
9211
+ function declaredFieldIn(f, value, settings) {
9212
+ return { field_key: f.key, value, handling: settings?.handling ?? f.handling, ...settingsIn(settings ?? {}) };
9213
+ }
9130
9214
 
9131
9215
  // src/sharing/api.ts
9132
9216
  function grantOf(w) {
@@ -9227,7 +9311,7 @@ function createSharingPort(ctx, options = {}) {
9227
9311
  initiation: "user",
9228
9312
  non_secret_fields: [...meta.map((f) => ({ key: f.key, label: f.label, value: draft.values[f.key] ?? "" })), ...draft.custom.filter((c) => c.label.trim() !== "").map((c) => ({ key: c.key || fieldKeyOf(c.label), label: c.label, value: c.value }))],
9229
9313
  fields: [
9230
- ...encrypted.map((f) => ({ field_key: f.key, value: draft.values[f.key] ?? "", handling: f.handling })),
9314
+ ...encrypted.map((f) => declaredFieldIn(f, draft.values[f.key] ?? "", draft.fieldSettings?.[f.key])),
9231
9315
  ...draft.secrets.filter((s) => s.label.trim() !== "" && s.value !== "").map(secretFieldIn)
9232
9316
  ]
9233
9317
  }
@@ -9356,7 +9440,7 @@ async function readNativeImportLimits(api, organizationId) {
9356
9440
  var recoveryOf = (w) => w.lifecycle?.vault_recovery;
9357
9441
  var isDeleted = (w) => recoveryOf(w)?.state === "deleted";
9358
9442
  var MATCH_MODES2 = ["host", "domain", "exact", "never"];
9359
- var FIELD_WORDS = { totp_seed: "One-time password key", authenticator_last_time_step: "Last code used" };
9443
+ var FIELD_WORDS = { secure_notes: "Secure note", totp_seed: "One-time password key", authenticator_last_time_step: "Last code used" };
9360
9444
  var handlingOf = (v) => v === "visible" || v === "sealed" ? v : "revealable";
9361
9445
  function fieldOf(w, def) {
9362
9446
  const declared = def?.fields.find((f) => f.key === w.field_key);
@@ -9365,7 +9449,8 @@ function fieldOf(w, def) {
9365
9449
  id: w.id,
9366
9450
  key: w.field_key,
9367
9451
  label: declared?.label ?? FIELD_WORDS[w.field_key] ?? humanize(w.field_key),
9368
- kind: declared?.kind ?? (handling === "visible" ? "text" : "secret"),
9452
+ // A field no type declares still edits as several lines when its key says so (an old "Secure note").
9453
+ kind: declared?.kind ?? (isMultilineKey(w.field_key) ? "multiline" : handling === "visible" ? "text" : "secret"),
9369
9454
  handling,
9370
9455
  editable: w.editable ?? true,
9371
9456
  envKey: w.env_key ?? null,
@@ -9520,7 +9605,7 @@ function createApiVaultPort(api, options = {}) {
9520
9605
  initiation: "user",
9521
9606
  non_secret_fields: [...meta.map((f) => ({ key: f.key, label: f.label, value: draft.values[f.key] ?? "" })), ...draft.custom.filter((c) => c.label.trim() !== "").map((c) => ({ key: c.key || fieldKeyOf(c.label), label: c.label, value: c.value }))],
9522
9607
  fields: [
9523
- ...encrypted.map((f) => ({ field_key: f.key, value: draft.values[f.key] ?? "", handling: f.handling })),
9608
+ ...encrypted.map((f) => declaredFieldIn(f, draft.values[f.key] ?? "", draft.fieldSettings?.[f.key])),
9524
9609
  ...draft.secrets.filter((s) => s.label.trim() !== "" && s.value !== "").map(secretFieldIn),
9525
9610
  // Backup codes on a type that does not declare them (a website login): their own encrypted field.
9526
9611
  ...!declared.some((f) => f.key === RECOVERY_CODES_KEY) && (draft.values[RECOVERY_CODES_KEY] ?? "").trim() !== "" ? [{ field_key: RECOVERY_CODES_KEY, value: parseRecoveryCodes(draft.values[RECOVERY_CODES_KEY] ?? "").join("\n"), handling: "revealable", description: "One-time account recovery codes" }] : []