@ai-matrx/vault 0.1.54 → 0.1.56

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/CHANGELOG.md CHANGED
@@ -1,9 +1,16 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.1.56
4
+
5
+ - No changes yet.
6
+
7
+ ## 0.1.55
8
+
9
+ - Password Health can discover a prior scan when neither request nor execution handle is available, and binds recovered results and progress to the originating health port. Consumer action: custom `HealthPort` implementations must provide `discover(cursor, signal)`; `createApiVaultPort` wires it automatically. Discovery returns authorized request/execution references only. External request/execution handle changes retire pending work and recover the new address; matching self-written handles preserve the current run.
10
+
3
11
  ## 0.1.54
4
12
 
5
- UNVERIFIED: this release has no Unreleased entry and the package-input comparison could not be made (the caller supplied no source comparison). Whether package inputs changed is unknown; consumer action is unknown.
6
- Check the package's gitHead on npm against this version's gitHead: `git diff <previous gitHead> <this gitHead> -- apps/shared/vault`.
13
+ - Saved health recovery shows Checking status while its read is pending. A missing execution or failed initial recovery read settles as Unavailable and keeps Retry available. Consumer action: none; the existing request/execution handle round trip is unchanged.
7
14
 
8
15
  ## 0.1.53
9
16
 
@@ -359,8 +366,6 @@ Changed shipped source files (8):
359
366
  ## 0.1.7
360
367
  ### Unreleased
361
368
 
362
- - Saved health recovery shows Checking status while its read is pending. A missing execution or failed initial recovery read settles as Unavailable and keeps Retry available. Consumer action: none; the existing request/execution handle round trip is unchanged.
363
-
364
369
  - Host-held lock: `host.lock` (`VaultLock`: `locked`, `subscribe`, `unlock`, optional `lock` and
365
370
  `unlockLabel`). While locked the vault shows only "Vault locked" with Unlock, and every port call
366
371
  (areas included) is refused at call time (`guardPort`, `VaultLockedError`). "Lock" joins the
package/dist/index.cjs CHANGED
@@ -682,7 +682,7 @@ function PasswordHealthView(props) {
682
682
  const latest = (0, import_react4.useRef)(props);
683
683
  latest.current = props;
684
684
  const [result, setResult] = (0, import_react4.useState)(null);
685
- const [status, setStatus] = (0, import_react4.useState)(requestId || executionId ? "Checking status" : "No report");
685
+ const [status, setStatus] = (0, import_react4.useState)("Checking status");
686
686
  const [error, setError] = (0, import_react4.useState)(null);
687
687
  const [progress, setProgress] = (0, import_react4.useState)(null);
688
688
  const [busy, setBusy] = (0, import_react4.useState)(false);
@@ -692,9 +692,13 @@ function PasswordHealthView(props) {
692
692
  }, [port]);
693
693
  const [retry, setRetry] = (0, import_react4.useState)(0);
694
694
  const active = (0, import_react4.useRef)(null);
695
- const starting = (0, import_react4.useRef)(false);
695
+ const starting = (0, import_react4.useRef)(null);
696
696
  const ownedAddress = (0, import_react4.useRef)(null);
697
697
  const writeReference = (request, execution) => {
698
+ if (starting.current?.controller === active.current) {
699
+ starting.current.request = request;
700
+ starting.current.execution = execution;
701
+ }
698
702
  ownedAddress.current = { request, execution, items: latest.current.items, retry };
699
703
  latest.current.onReference(request, execution);
700
704
  };
@@ -722,7 +726,7 @@ function PasswordHealthView(props) {
722
726
  const value = await port.read(id, controller.signal);
723
727
  if (owns(controller)) {
724
728
  setStatus(STATUS_LABEL[value.execution_status]);
725
- setResult({ value, items: inventory });
729
+ setResult({ value, items: inventory, port });
726
730
  }
727
731
  return value;
728
732
  };
@@ -740,6 +744,27 @@ function PasswordHealthView(props) {
740
744
  }
741
745
  writeReference(request, id);
742
746
  }
747
+ if (!id && !request) {
748
+ let cursor;
749
+ const seen = /* @__PURE__ */ new Set();
750
+ do {
751
+ const page = await port.discover(cursor, controller.signal);
752
+ if (!owns(controller)) return;
753
+ const reference = page.references[0];
754
+ if (reference) {
755
+ id = reference.execution_id;
756
+ writeReference(reference.request_id, reference.execution_id);
757
+ break;
758
+ }
759
+ cursor = page.continuation ?? void 0;
760
+ if (cursor && seen.has(cursor)) throw new Error("Report unavailable");
761
+ if (cursor) seen.add(cursor);
762
+ } while (cursor);
763
+ if (!id) {
764
+ setStatus("No report");
765
+ return;
766
+ }
767
+ }
743
768
  if (!id || !owns(controller)) return;
744
769
  const first = await read(id, controller);
745
770
  if (!owns(controller) || terminal(first.execution_status)) return;
@@ -749,7 +774,7 @@ function PasswordHealthView(props) {
749
774
  if (!owns(controller)) return;
750
775
  const detail = event.detail;
751
776
  if (event.kind === "note" && detail?.label === "password_health_progress" && detail.__kind === "password_health_progress" && typeof detail.checked === "number" && Number.isSafeInteger(detail.checked) && detail.checked >= 0 && typeof detail.skipped === "number" && Number.isSafeInteger(detail.skipped) && detail.skipped >= 0) {
752
- setProgress({ checked: detail.checked, skipped: detail.skipped });
777
+ setProgress({ checked: detail.checked, skipped: detail.skipped, port });
753
778
  }
754
779
  const lifecycle = event.kind === "started" || event.kind === "resumed" ? "running" : event.kind === "created" ? "pending" : event.kind;
755
780
  const label = STATUS_LABEL[lifecycle];
@@ -764,14 +789,16 @@ function PasswordHealthView(props) {
764
789
  if (!inventoryReady) {
765
790
  ownedAddress.current = null;
766
791
  setResult(null);
767
- if (!starting.current) active.current?.abort();
792
+ if (!starting.current || !owns(starting.current.controller)) active.current?.abort();
768
793
  return;
769
794
  }
770
- if (!port || starting.current) return;
795
+ if (!port) return;
796
+ const start = starting.current;
797
+ if (start && owns(start.controller) && start.request === (requestId ?? "") && start.execution === (executionId ?? "")) return;
771
798
  const own = ownedAddress.current;
772
799
  if (own && own.request === requestId && own.execution === executionId && own.items === items && own.retry === retry && active.current && !active.current.signal.aborted) return;
773
800
  const controller = fresh();
774
- setStatus(requestId || executionId ? "Checking status" : "No report");
801
+ setStatus("Checking status");
775
802
  void recover(requestId, executionId, controller).catch((e) => {
776
803
  if (owns(controller)) {
777
804
  setResult(null);
@@ -793,7 +820,7 @@ function PasswordHealthView(props) {
793
820
  const run = async (resume2) => {
794
821
  if (!port) return;
795
822
  const controller = fresh();
796
- starting.current = true;
823
+ starting.current = { controller, request: "", execution: "" };
797
824
  const request = crypto.randomUUID();
798
825
  const options = { check_breaches: checkBreaches };
799
826
  let actualRequest = request;
@@ -822,7 +849,7 @@ function PasswordHealthView(props) {
822
849
  if (data && typeof data === "object" && "type" in data) {
823
850
  if (data.type === "vault_password_health_scan_reference") accept(data);
824
851
  if (data.type === "vault_password_health_progress") {
825
- setProgress(data);
852
+ setProgress({ ...data, port });
826
853
  setStatus("Running");
827
854
  }
828
855
  }
@@ -866,20 +893,22 @@ function PasswordHealthView(props) {
866
893
  setError(problem(e));
867
894
  setStatus((value) => value === "Checking status" ? "Unavailable" : value);
868
895
  } finally {
869
- starting.current = false;
896
+ if (starting.current?.controller === controller) starting.current = null;
870
897
  if (owns(controller)) {
871
898
  setBusy(false);
872
899
  if (startedItems !== latest.current.items) setRetry((v) => v + 1);
873
900
  }
874
901
  }
875
902
  };
876
- const current = inventoryReady && result?.items === items && result.value.availability === "current" ? result.value.snapshot : null;
877
- const available = result?.value.availability;
903
+ const ownedResult = port && result?.port === port ? result : null;
904
+ const shownProgress = port && progress?.port === port ? progress : null;
905
+ const current = inventoryReady && ownedResult?.items === items && ownedResult.value.availability === "current" ? ownedResult.value.snapshot : null;
906
+ const available = ownedResult?.value.availability;
878
907
  const outcomes = current?.check_breaches ? current.breach_outcomes ?? [] : [];
879
908
  const compromised = outcomes.filter((outcome) => outcome.status === "compromised").length;
880
909
  const notFound = outcomes.filter((outcome) => outcome.status === "not_found").length;
881
910
  const unknown = outcomes.filter((outcome) => outcome.status === "unknown").length;
882
- const resume = result && ["failed", "cancelled"].includes(result.value.execution_status);
911
+ const resume = ownedResult && ["failed", "cancelled"].includes(ownedResult.value.execution_status);
883
912
  return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex h-full min-h-0 flex-col gap-4 overflow-auto p-4", "aria-label": "Password health", children: [
884
913
  props.header,
885
914
  /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex items-center justify-between gap-2", children: [
@@ -913,9 +942,9 @@ function PasswordHealthView(props) {
913
942
  /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_controls2.InfoHint, { text: "The breach lookup could not establish a result." })
914
943
  ] }), children: unknown })
915
944
  ] }) : null,
916
- !current && progress ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
917
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(VaultRow, { label: "Checked", children: progress.checked }),
918
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(VaultRow, { label: "Skipped", children: progress.skipped })
945
+ !current && shownProgress ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
946
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(VaultRow, { label: "Checked", children: shownProgress.checked }),
947
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(VaultRow, { label: "Skipped", children: shownProgress.skipped })
919
948
  ] }) : null,
920
949
  current ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
921
950
  /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(VaultRow, { label: "Checked", children: current.checked_subjects.length }),
@@ -2673,7 +2702,7 @@ function normalizeWebsite(raw) {
2673
2702
  }
2674
2703
  function AddOtpPanel({ port, items, definitions, itemId = null, onClose, onChanged, onAdded, onDone }) {
2675
2704
  const confirmed = useConfirmed();
2676
- 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" }));
2705
+ const candidates = items.filter((i) => i.authenticatorState === "known" && !i.authenticator && !i.deletion && (i.capabilities.canEdit || i.capabilities.canManage)).sort((a, b) => a.name.localeCompare(b.name, void 0, { sensitivity: "base" }));
2677
2706
  const [input, setInput] = (0, import_react13.useState)("");
2678
2707
  const [image, setImage] = (0, import_react13.useState)(null);
2679
2708
  const [target, setTarget] = (0, import_react13.useState)(itemId && candidates.some((c) => c.id === itemId) ? itemId : NEW);
@@ -2712,7 +2741,7 @@ function AddOtpPanel({ port, items, definitions, itemId = null, onClose, onChang
2712
2741
  }
2713
2742
  const it = item;
2714
2743
  const entry = await confirmed(() => image ? port.authenticator.enrolQrImage(it, image, image.name) : port.authenticator.enrol(it, input));
2715
- let next = { ...it, authenticator: { digits: entry.digits, period: entry.period, enabled: entry.enabled } };
2744
+ let next = { ...it, authenticatorState: "known", authenticator: { digits: entry.digits, period: entry.period, enabled: entry.enabled } };
2716
2745
  const better = authenticatorName(it.name, { issuer: entry.issuer, account: entry.account });
2717
2746
  if (!isNew && better !== it.name) {
2718
2747
  try {
@@ -2850,6 +2879,7 @@ function AuthenticatorView({ port, host, items: listed, definitions, query, onOp
2850
2879
  (0, import_react14.useEffect)(() => {
2851
2880
  if (added.size > 0 && [...added.keys()].every((id) => listed.some((i) => i.id === id && i.authenticator))) setAdded(/* @__PURE__ */ new Map());
2852
2881
  }, [listed, added]);
2882
+ const [incomplete, setIncomplete] = (0, import_react14.useState)(true);
2853
2883
  const [entries, setEntries] = (0, import_react14.useState)(/* @__PURE__ */ new Map());
2854
2884
  const [hidden, setHidden] = (0, import_react14.useState)(/* @__PURE__ */ new Set());
2855
2885
  const [adding, setAdding] = (0, import_react14.useState)(false);
@@ -2859,8 +2889,15 @@ function AuthenticatorView({ port, host, items: listed, definitions, query, onOp
2859
2889
  (0, import_react14.useEffect)(() => {
2860
2890
  let live = true;
2861
2891
  port.authenticator.list().then(
2862
- (list) => live && setEntries(new Map(list.map((e) => [e.itemId, e]))),
2863
- (e) => console.error("One-time password accounts could not be read", e)
2892
+ (list) => {
2893
+ if (live) {
2894
+ setEntries(new Map(list.entries.map((e) => [e.itemId, e])));
2895
+ setIncomplete(list.unavailableScopes.length > 0);
2896
+ }
2897
+ },
2898
+ () => {
2899
+ if (live) setIncomplete(true);
2900
+ }
2864
2901
  );
2865
2902
  return () => {
2866
2903
  live = false;
@@ -2878,7 +2915,14 @@ function AuthenticatorView({ port, host, items: listed, definitions, query, onOp
2878
2915
  ] }),
2879
2916
  total > 0 ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_controls10.Button, { variant: "outline", icon: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(PlusGlyph, {}), onClick: () => setAdding(true), children: "Add" }) : null
2880
2917
  ] }),
2881
- total === 0 ? /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex flex-1 flex-col items-center justify-center gap-3 px-6 pb-12 text-center", children: [
2918
+ total > 0 && incomplete ? /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "px-4 text-sm text-muted-foreground", children: [
2919
+ "Some codes unavailable ",
2920
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_controls10.Button, { variant: "quiet", onClick: onChanged, children: "Retry" })
2921
+ ] }) : null,
2922
+ total === 0 && (incomplete || items.some((i) => i.authenticatorState !== "known")) ? /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "px-4 py-10 text-center text-sm text-muted-foreground", children: [
2923
+ "Codes unavailable ",
2924
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_controls10.Button, { variant: "quiet", onClick: onChanged, children: "Retry" })
2925
+ ] }) : total === 0 ? /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex flex-1 flex-col items-center justify-center gap-3 px-6 pb-12 text-center", children: [
2882
2926
  /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "flex size-12 items-center justify-center rounded-full bg-muted text-muted-foreground", children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(ShieldGlyph, { className: "size-6" }) }),
2883
2927
  /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("p", { className: "text-sm font-medium text-foreground", children: "No authenticator codes yet" }),
2884
2928
  /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_controls10.Button, { variant: "primary", icon: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(PlusGlyph, {}), onClick: () => setAdding(true), children: "Add authenticator" })
@@ -6925,7 +6969,10 @@ function ItemPane(props) {
6925
6969
  i
6926
6970
  )),
6927
6971
  /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(AddRow, { label: draft.loginUrls.length === 0 ? "Website" : "", onAdd: () => patch({ loginUrls: [...draft.loginUrls, ""] }), children: draft.loginUrls.length === 0 ? "Add website" : "Add another website" }),
6928
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(TotpEditRow, { value: draft.authenticator, onChange: (v) => patch({ authenticator: v }), hasExisting: Boolean(item?.authenticator || seedField), item, port, onEnrolled: onChanged }),
6972
+ !item || item.authenticatorState === "known" ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(TotpEditRow, { value: draft.authenticator, onChange: (v) => patch({ authenticator: v }), hasExisting: Boolean(item?.authenticator || seedField), item, port, onEnrolled: onChanged }) : /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(VaultRow, { label: "Authenticator", children: [
6973
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("span", { className: "text-muted-foreground", children: item.authenticatorState === "pending" ? "Loading" : "Unavailable" }),
6974
+ item.authenticatorState === "unavailable" ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_controls23.Button, { variant: "quiet", onClick: () => onChanged?.(), children: "Retry" }) : null
6975
+ ] }),
6929
6976
  item && isLogin && !item.authenticator ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(TwoFactorMethodRow, { port, item, onChanged }) : null,
6930
6977
  !item && isLogin && draft.authenticator.trim() === "" ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(DraftTwoFactorRow, { custom: draft.custom, onCustom: (custom) => patch({ custom }) }) : null,
6931
6978
  recoveryEditing ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
@@ -7004,7 +7051,10 @@ function ItemPane(props) {
7004
7051
  }),
7005
7052
  extraFields.map((f) => isRecoveryCodesField(f) ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(RecoveryCodesRow, { item, field: f, port, onChanged }, f.id) : /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(SecretRow, { port, item, field: f, onTurnOn: turnOnOne(f), extra: fieldSettings(f) }, f.id)),
7006
7053
  item.loginUrls.length > 0 ? item.loginUrls.map((u, i) => /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(PlainRow, { label: i === 0 ? "Website" : `Website ${i + 1}`, value: u, openUrl: host.openUrl }, `${u}-${i}`)) : /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(AddRow, { label: "Website", onAdd: canEdit ? () => onMode("edit") : null, children: "Add website" }),
7007
- item.authenticator ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(TotpRow, { port, item, onChanged }) : seedField && !seedField.active ? (
7054
+ item.authenticator ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(TotpRow, { port, item, onChanged }) : item.authenticatorState !== "known" ? /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(VaultRow, { label: "Authenticator", children: [
7055
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("span", { className: "text-muted-foreground", children: item.authenticatorState === "pending" ? "Loading" : "Unavailable" }),
7056
+ item.authenticatorState === "unavailable" ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_controls23.Button, { variant: "quiet", onClick: () => onChanged?.(), children: "Retry" }) : null
7057
+ ] }) : seedField && !seedField.active ? (
7008
7058
  // The secret is still there, only off: turning it on brings the codes back. Never "Set up",
7009
7059
  // which would invite replacing a working two-factor secret.
7010
7060
  /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(VaultRow, { label: "Authenticator", actions: canTurnOn ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(TurnOnButton, { label: "Authenticator", onTurnOn: () => turnOn([seedField]) }) : null, children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("span", { className: "text-muted-foreground", children: "Turned off" }) })
@@ -9687,6 +9737,30 @@ var import_account = require("@ai-matrx/api/account");
9687
9737
  var import_architecture = require("@ai-matrx/api/architecture");
9688
9738
  function createHealthPort(api) {
9689
9739
  return {
9740
+ discover: async (cursor, signal) => {
9741
+ const stream = await (0, import_account.discoverPasswordHealthScans)(api, cursor ? { query: { cursor } } : void 0, { signal, totalTimeoutMs: null });
9742
+ let page = null;
9743
+ let ended = false;
9744
+ try {
9745
+ for await (const event of stream) {
9746
+ if (signal.aborted || ended || event.event === "error") throw new Error("Report unavailable");
9747
+ const data = event.data;
9748
+ if (event.event === "completion" && data && typeof data === "object" && "status" in data && data.status !== "success") throw new Error("Report unavailable");
9749
+ if (event.event === "end") {
9750
+ if (!data || typeof data !== "object" || !("reason" in data) || data.reason !== "complete") throw new Error("Report unavailable");
9751
+ ended = true;
9752
+ }
9753
+ if (event.event !== "data") continue;
9754
+ if (!data || typeof data !== "object" || !("type" in data) || data.type !== "vault_password_health_discovery_page") continue;
9755
+ if (page || !("__kind" in data) || data.__kind !== "password_health_discovery_page" || !("references" in data) || !Array.isArray(data.references) || data.references.some((ref) => !ref || typeof ref !== "object" || ref.__kind !== "password_health_discovery_reference" || typeof ref.request_id !== "string" || !ref.request_id || typeof ref.execution_id !== "string" || !ref.execution_id) || !("continuation" in data) || data.continuation !== null && (typeof data.continuation !== "string" || !data.continuation)) throw new Error("Report unavailable");
9756
+ page = data;
9757
+ }
9758
+ if (signal.aborted || !ended || !page) throw new Error("Report unavailable");
9759
+ return page;
9760
+ } finally {
9761
+ stream.cancel();
9762
+ }
9763
+ },
9690
9764
  start: (requestId, signal, options) => (0, import_account.startPasswordHealthScan)(api, { body: options.check_breaches ? { check_breaches: true } : {} }, { requestId, signal, totalTimeoutMs: null }),
9691
9765
  resume: (executionId, requestId, signal) => (0, import_account.resumePasswordHealthScan)(api, { path: { execution_id: executionId } }, { requestId, signal, totalTimeoutMs: null }),
9692
9766
  read: (executionId, signal) => (0, import_account.getPasswordHealthScan)(api, { path: { execution_id: executionId } }, { signal }),
@@ -9809,7 +9883,7 @@ function createAuthenticatorPort(ctx) {
9809
9883
  return {
9810
9884
  async list() {
9811
9885
  const r = await apps.listAuthenticators(api);
9812
- return (r.entries ?? []).map(entryOf);
9886
+ return { entries: (r.entries ?? []).map(entryOf), unavailableScopes: (r.unavailable_scopes ?? []).map((s) => ({ type: s.type, organizationId: s.organization_id ?? null })) };
9813
9887
  },
9814
9888
  async get(item) {
9815
9889
  try {
@@ -10023,6 +10097,7 @@ function trashItem(r, ctx) {
10023
10097
  fields: [],
10024
10098
  custom: [],
10025
10099
  authenticator: null,
10100
+ authenticatorState: "known",
10026
10101
  capabilities: { canUse: false, canEdit: false, canReveal: false, canManage: true },
10027
10102
  attachments: [],
10028
10103
  uriMatchMode: "host",
@@ -10379,7 +10454,8 @@ function fieldOf(w, def) {
10379
10454
  active: w.is_active ?? true
10380
10455
  };
10381
10456
  }
10382
- function itemOf(w, owner, defs, auth) {
10457
+ var scopeUnavailable = (owner, scopes) => scopes.some((s) => s.type === (owner.kind === "me" ? "user" : owner.kind === "organization" ? "organization" : "shared") && (s.type !== "organization" || !s.organization_id || s.organization_id === owner.organizationId));
10458
+ function itemOf(w, owner, defs, auth, state = "known", unavailable = [], inventoryOwner = owner) {
10383
10459
  const def = defs.get(w.definition_key ?? "");
10384
10460
  const a = auth.get(w.id);
10385
10461
  const cap = w.capabilities ?? {};
@@ -10397,6 +10473,7 @@ function itemOf(w, owner, defs, auth) {
10397
10473
  fields: (w.fields ?? []).filter((f) => f.execution_purpose !== "passkey_private").map((f) => fieldOf(f, def)),
10398
10474
  custom: (w.non_secret_fields ?? []).map((c) => ({ key: c["key"] ?? "", label: c["label"] ?? humanize(c["key"] ?? ""), value: c["value"] ?? "" })),
10399
10475
  authenticator: a ? { digits: a.digits, period: a.period, enabled: a.enabled } : null,
10476
+ authenticatorState: a ? "known" : state === "pending" ? "pending" : state === "unavailable" || scopeUnavailable(inventoryOwner, unavailable) ? "unavailable" : "known",
10400
10477
  capabilities: { canUse: cap.can_use ?? false, canEdit: cap.can_edit ?? false, canReveal: cap.can_reveal ?? false, canManage: cap.can_manage ?? false },
10401
10478
  attachments: (w.attachments ?? []).map((a2) => ({ id: a2.id, label: a2.label, description: a2.description ?? "", fileName: a2.file_name, mediaType: a2.media_type ?? "application/octet-stream", sizeBytes: a2.size_bytes, handling: handlingOf(a2.handling), createdAt: a2.created_at, updatedAt: a2.updated_at })),
10402
10479
  uriMatchMode: MATCH_MODES2.find((m) => m === w.uri_match_mode) ?? "host",
@@ -10433,6 +10510,9 @@ function createApiVaultPort(hostApi, options = {}) {
10433
10510
  };
10434
10511
  let defs = /* @__PURE__ */ new Map();
10435
10512
  let auth = /* @__PURE__ */ new Map();
10513
+ let authState = "pending";
10514
+ let authUnavailable = [];
10515
+ let inventoryOwners = /* @__PURE__ */ new Map();
10436
10516
  let orgs = [];
10437
10517
  const orgOf = (id, userId, mine) => {
10438
10518
  if (id) return { kind: "organization", label: orgs.find((o) => o.id === id)?.name ?? "Organization", organizationId: id };
@@ -10446,7 +10526,7 @@ function createApiVaultPort(hostApi, options = {}) {
10446
10526
  };
10447
10527
  async function refetch(id, owner) {
10448
10528
  const w = await account9.getItem(api, { path: { item_id: id } });
10449
- return itemOf(w, owner, defs, auth);
10529
+ return itemOf(w, owner, defs, auth, authState, authUnavailable, inventoryOwners.get(id) ?? owner);
10450
10530
  }
10451
10531
  const visibleCache = /* @__PURE__ */ new Map();
10452
10532
  const ctx = {
@@ -10473,6 +10553,10 @@ function createApiVaultPort(hostApi, options = {}) {
10473
10553
  fields: createFieldsPort(ctx),
10474
10554
  lists: createListsPort(ctx, options.userState),
10475
10555
  async load(onProgress) {
10556
+ authState = "pending";
10557
+ auth = /* @__PURE__ */ new Map();
10558
+ authUnavailable = [];
10559
+ inventoryOwners = /* @__PURE__ */ new Map();
10476
10560
  const catalogP = retrying(() => apps2.getAppCatalogKind(api, { path: { app: "matrx", kind: "credential_definition" } }));
10477
10561
  const authP = retrying(() => apps2.listAuthenticators(api));
10478
10562
  const userP = retrying(() => account9.listItems(api, { query: { principal_type: "user" } }));
@@ -10495,9 +10579,10 @@ function createApiVaultPort(hostApi, options = {}) {
10495
10579
  const meId = raw.meId ?? mineItems.find((w) => w.user_id)?.user_id ?? null;
10496
10580
  const seen = /* @__PURE__ */ new Map();
10497
10581
  const deleted = /* @__PURE__ */ new Map();
10498
- const take = (w, owner) => {
10582
+ const take = (w, owner, inventoryOwner = owner) => {
10499
10583
  if (seen.has(w.id) || deleted.has(w.id) || isHiddenItem(w)) return;
10500
- const item = itemOf(w, owner, defs, auth);
10584
+ inventoryOwners.set(w.id, inventoryOwner);
10585
+ const item = itemOf(w, owner, defs, auth, authState, authUnavailable, inventoryOwner);
10501
10586
  (isDeleted(w) ? deleted : seen).set(w.id, item);
10502
10587
  };
10503
10588
  for (const w of mineItems) take(w, orgOf(w.organization_id, null, true));
@@ -10505,7 +10590,7 @@ function createApiVaultPort(hostApi, options = {}) {
10505
10590
  const via = orgs[i];
10506
10591
  for (const w of l?.items ?? []) take(w, ownerInOrgList(w, via, meId));
10507
10592
  });
10508
- for (const w of raw.shared?.items ?? []) take(w, w.organization_id ? orgOf(w.organization_id, null, false) : { kind: "shared", label: "Shared with me", organizationId: null });
10593
+ for (const w of raw.shared?.items ?? []) take(w, w.organization_id ? orgOf(w.organization_id, null, false) : { kind: "shared", label: "Shared with me", organizationId: null }, { kind: "shared", label: "Shared with me", organizationId: null });
10509
10594
  const pending = done ? [] : [...raw.orgsKnown ? orgs.filter((_, i) => raw.orgLists[i] === void 0).map((o) => o.name) : ["your organizations"], ...raw.shared ? [] : ["Shared with me"]];
10510
10595
  return {
10511
10596
  unavailable: [...unavailable],
@@ -10564,9 +10649,14 @@ function createApiVaultPort(hostApi, options = {}) {
10564
10649
  })();
10565
10650
  quiet(orgListsReady);
10566
10651
  const authDone = authP.then((a) => {
10567
- auth = new Map((a.entries ?? []).map((e) => [e.credential_item_id, e]));
10652
+ authUnavailable = a.unavailable_scopes ?? [];
10653
+ const fresh = new Map((a.entries ?? []).map((e) => [e.credential_item_id, e]));
10654
+ auth = fresh;
10655
+ authState = "known";
10656
+ if (authUnavailable.length) unavailable.push("One-time codes");
10568
10657
  progress();
10569
10658
  }, () => {
10659
+ authState = "unavailable";
10570
10660
  unavailable.push("One-time codes");
10571
10661
  });
10572
10662
  const sharedDone = sharedP.then((sh) => {