@ai-matrx/vault 0.1.55 → 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,8 +1,12 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.1.56
4
+
5
+ - No changes yet.
6
+
3
7
  ## 0.1.55
4
8
 
5
- - 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.
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.
6
10
 
7
11
  ## 0.1.54
8
12
 
package/dist/index.cjs CHANGED
@@ -2702,7 +2702,7 @@ function normalizeWebsite(raw) {
2702
2702
  }
2703
2703
  function AddOtpPanel({ port, items, definitions, itemId = null, onClose, onChanged, onAdded, onDone }) {
2704
2704
  const confirmed = useConfirmed();
2705
- 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" }));
2706
2706
  const [input, setInput] = (0, import_react13.useState)("");
2707
2707
  const [image, setImage] = (0, import_react13.useState)(null);
2708
2708
  const [target, setTarget] = (0, import_react13.useState)(itemId && candidates.some((c) => c.id === itemId) ? itemId : NEW);
@@ -2741,7 +2741,7 @@ function AddOtpPanel({ port, items, definitions, itemId = null, onClose, onChang
2741
2741
  }
2742
2742
  const it = item;
2743
2743
  const entry = await confirmed(() => image ? port.authenticator.enrolQrImage(it, image, image.name) : port.authenticator.enrol(it, input));
2744
- 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 } };
2745
2745
  const better = authenticatorName(it.name, { issuer: entry.issuer, account: entry.account });
2746
2746
  if (!isNew && better !== it.name) {
2747
2747
  try {
@@ -2879,6 +2879,7 @@ function AuthenticatorView({ port, host, items: listed, definitions, query, onOp
2879
2879
  (0, import_react14.useEffect)(() => {
2880
2880
  if (added.size > 0 && [...added.keys()].every((id) => listed.some((i) => i.id === id && i.authenticator))) setAdded(/* @__PURE__ */ new Map());
2881
2881
  }, [listed, added]);
2882
+ const [incomplete, setIncomplete] = (0, import_react14.useState)(true);
2882
2883
  const [entries, setEntries] = (0, import_react14.useState)(/* @__PURE__ */ new Map());
2883
2884
  const [hidden, setHidden] = (0, import_react14.useState)(/* @__PURE__ */ new Set());
2884
2885
  const [adding, setAdding] = (0, import_react14.useState)(false);
@@ -2888,8 +2889,15 @@ function AuthenticatorView({ port, host, items: listed, definitions, query, onOp
2888
2889
  (0, import_react14.useEffect)(() => {
2889
2890
  let live = true;
2890
2891
  port.authenticator.list().then(
2891
- (list) => live && setEntries(new Map(list.map((e) => [e.itemId, e]))),
2892
- (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
+ }
2893
2901
  );
2894
2902
  return () => {
2895
2903
  live = false;
@@ -2907,7 +2915,14 @@ function AuthenticatorView({ port, host, items: listed, definitions, query, onOp
2907
2915
  ] }),
2908
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
2909
2917
  ] }),
2910
- 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: [
2911
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" }) }),
2912
2927
  /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("p", { className: "text-sm font-medium text-foreground", children: "No authenticator codes yet" }),
2913
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" })
@@ -6954,7 +6969,10 @@ function ItemPane(props) {
6954
6969
  i
6955
6970
  )),
6956
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" }),
6957
- /* @__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
+ ] }),
6958
6976
  item && isLogin && !item.authenticator ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(TwoFactorMethodRow, { port, item, onChanged }) : null,
6959
6977
  !item && isLogin && draft.authenticator.trim() === "" ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(DraftTwoFactorRow, { custom: draft.custom, onCustom: (custom) => patch({ custom }) }) : null,
6960
6978
  recoveryEditing ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
@@ -7033,7 +7051,10 @@ function ItemPane(props) {
7033
7051
  }),
7034
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)),
7035
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" }),
7036
- 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 ? (
7037
7058
  // The secret is still there, only off: turning it on brings the codes back. Never "Set up",
7038
7059
  // which would invite replacing a working two-factor secret.
7039
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" }) })
@@ -9862,7 +9883,7 @@ function createAuthenticatorPort(ctx) {
9862
9883
  return {
9863
9884
  async list() {
9864
9885
  const r = await apps.listAuthenticators(api);
9865
- 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 })) };
9866
9887
  },
9867
9888
  async get(item) {
9868
9889
  try {
@@ -10076,6 +10097,7 @@ function trashItem(r, ctx) {
10076
10097
  fields: [],
10077
10098
  custom: [],
10078
10099
  authenticator: null,
10100
+ authenticatorState: "known",
10079
10101
  capabilities: { canUse: false, canEdit: false, canReveal: false, canManage: true },
10080
10102
  attachments: [],
10081
10103
  uriMatchMode: "host",
@@ -10432,7 +10454,8 @@ function fieldOf(w, def) {
10432
10454
  active: w.is_active ?? true
10433
10455
  };
10434
10456
  }
10435
- 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) {
10436
10459
  const def = defs.get(w.definition_key ?? "");
10437
10460
  const a = auth.get(w.id);
10438
10461
  const cap = w.capabilities ?? {};
@@ -10450,6 +10473,7 @@ function itemOf(w, owner, defs, auth) {
10450
10473
  fields: (w.fields ?? []).filter((f) => f.execution_purpose !== "passkey_private").map((f) => fieldOf(f, def)),
10451
10474
  custom: (w.non_secret_fields ?? []).map((c) => ({ key: c["key"] ?? "", label: c["label"] ?? humanize(c["key"] ?? ""), value: c["value"] ?? "" })),
10452
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",
10453
10477
  capabilities: { canUse: cap.can_use ?? false, canEdit: cap.can_edit ?? false, canReveal: cap.can_reveal ?? false, canManage: cap.can_manage ?? false },
10454
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 })),
10455
10479
  uriMatchMode: MATCH_MODES2.find((m) => m === w.uri_match_mode) ?? "host",
@@ -10486,6 +10510,9 @@ function createApiVaultPort(hostApi, options = {}) {
10486
10510
  };
10487
10511
  let defs = /* @__PURE__ */ new Map();
10488
10512
  let auth = /* @__PURE__ */ new Map();
10513
+ let authState = "pending";
10514
+ let authUnavailable = [];
10515
+ let inventoryOwners = /* @__PURE__ */ new Map();
10489
10516
  let orgs = [];
10490
10517
  const orgOf = (id, userId, mine) => {
10491
10518
  if (id) return { kind: "organization", label: orgs.find((o) => o.id === id)?.name ?? "Organization", organizationId: id };
@@ -10499,7 +10526,7 @@ function createApiVaultPort(hostApi, options = {}) {
10499
10526
  };
10500
10527
  async function refetch(id, owner) {
10501
10528
  const w = await account9.getItem(api, { path: { item_id: id } });
10502
- return itemOf(w, owner, defs, auth);
10529
+ return itemOf(w, owner, defs, auth, authState, authUnavailable, inventoryOwners.get(id) ?? owner);
10503
10530
  }
10504
10531
  const visibleCache = /* @__PURE__ */ new Map();
10505
10532
  const ctx = {
@@ -10526,6 +10553,10 @@ function createApiVaultPort(hostApi, options = {}) {
10526
10553
  fields: createFieldsPort(ctx),
10527
10554
  lists: createListsPort(ctx, options.userState),
10528
10555
  async load(onProgress) {
10556
+ authState = "pending";
10557
+ auth = /* @__PURE__ */ new Map();
10558
+ authUnavailable = [];
10559
+ inventoryOwners = /* @__PURE__ */ new Map();
10529
10560
  const catalogP = retrying(() => apps2.getAppCatalogKind(api, { path: { app: "matrx", kind: "credential_definition" } }));
10530
10561
  const authP = retrying(() => apps2.listAuthenticators(api));
10531
10562
  const userP = retrying(() => account9.listItems(api, { query: { principal_type: "user" } }));
@@ -10548,9 +10579,10 @@ function createApiVaultPort(hostApi, options = {}) {
10548
10579
  const meId = raw.meId ?? mineItems.find((w) => w.user_id)?.user_id ?? null;
10549
10580
  const seen = /* @__PURE__ */ new Map();
10550
10581
  const deleted = /* @__PURE__ */ new Map();
10551
- const take = (w, owner) => {
10582
+ const take = (w, owner, inventoryOwner = owner) => {
10552
10583
  if (seen.has(w.id) || deleted.has(w.id) || isHiddenItem(w)) return;
10553
- const item = itemOf(w, owner, defs, auth);
10584
+ inventoryOwners.set(w.id, inventoryOwner);
10585
+ const item = itemOf(w, owner, defs, auth, authState, authUnavailable, inventoryOwner);
10554
10586
  (isDeleted(w) ? deleted : seen).set(w.id, item);
10555
10587
  };
10556
10588
  for (const w of mineItems) take(w, orgOf(w.organization_id, null, true));
@@ -10558,7 +10590,7 @@ function createApiVaultPort(hostApi, options = {}) {
10558
10590
  const via = orgs[i];
10559
10591
  for (const w of l?.items ?? []) take(w, ownerInOrgList(w, via, meId));
10560
10592
  });
10561
- 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 });
10562
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"]];
10563
10595
  return {
10564
10596
  unavailable: [...unavailable],
@@ -10617,9 +10649,14 @@ function createApiVaultPort(hostApi, options = {}) {
10617
10649
  })();
10618
10650
  quiet(orgListsReady);
10619
10651
  const authDone = authP.then((a) => {
10620
- 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");
10621
10657
  progress();
10622
10658
  }, () => {
10659
+ authState = "unavailable";
10623
10660
  unavailable.push("One-time codes");
10624
10661
  });
10625
10662
  const sharedDone = sharedP.then((sh) => {