@ai-matrx/vault 0.1.12 → 0.1.13

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,6 +1,11 @@
1
1
  # Changelog
2
2
 
3
- ### Unreleased
3
+ ## 0.1.13
4
+
5
+ - **Turn on also turns the item on.** A restore leaves the whole item disabled (`status: "disabled"`), not only its fields, so a turned-on field still answered "not found". `VaultItem.turnedOff` reads it; every **Turn on** turns the item on first (`fields.updateItem(item, { active: true })` → `status: "active"`), and turning the one-time password on also re-enables its codes. An off item says "This item is turned off" and its rows read Turned off. Guard: `src/restored-item.test.tsx`, `src/fields/fields.test.ts`. Consumer action: none.
6
+ - **A dead session no longer throws a burst of unhandled rejections**: `load()` answers every part it started, so only the one "You've been signed out" shows. Guard: `src/load-dead-session.test.ts`. Consumer action: none.
7
+
8
+ ## 0.1.12
4
9
 
5
10
  - **Restored items are repairable in place.** An item back from Trash says "N fields are turned off" with **Turn on** (all), and each off row has its own **Turn on**; the one-time password reads "Turned off" + Turn on instead of "Set up" (which invited replacing a 2FA secret it still has), and editing it says "Paste a new key to replace". Plain words in settings: "One-time password key" (was "Totp seed"), "Always shown / Hidden / Never shown" (was "Anyone / On request / Sealed"), "Turned on" (was "Active"). Guard: `src/restored-item.test.tsx`. Consumer action: none.
6
11
  - **Continue with Google checks who came back.** `createSupabaseIdentity` remembers the person across the provider redirect (`sessionStorage`, `CONFIRM_RETURN_KEY`) and `VaultIdentity.checkReturn()` (run by `IdentityGate` on load) refuses a different account: it is signed out of THIS device through the new `onDifferentPerson(message)` option (default `auth.signOut({ scope: "local" })`) and a "Different account" dialog says so. Guard: `src/auth/identity.test.ts`, `src/auth/identity-gate.test.tsx`. **Consumer action:** web app: pass `onDifferentPerson` running the app's device-scoped sign-out. Extension and desktop: none (they check in place).
@@ -14,11 +19,6 @@
14
19
  - **Confirm in place.** `VaultIdentity.withProvider` may resolve `"confirmed"` when the host confirms without leaving the page (the desktop's Touch ID); "Confirm it's you" then closes and runs the refused call again — one confirmation, the value appears. Before, the dialog stayed open and the person had to cancel and press reveal again. Labels for `touch_id` ("Touch ID") and `browser`. Guard: `src/auth/identity-gate.test.tsx` (fails on the old gate). **Consumer action:** Matrx Desktop: return `"confirmed"` from `withProvider` once the confirmation succeeded in place. Web app and extension: none.
15
20
  - **`VaultApp look="web"|"desktop"`.** The same screens and features, two skins: `"web"` (default) is the website's and the extension's look, unchanged; `"desktop"` draws the workspace in the desktop standard through `@ai-matrx/design-system`'s `LookScope` — the system font and Apple text styles, AppKit-size controls, System Settings/Passwords grouped boxes (no border, hairlines between rows, bold section titles), the source list's open row in the accent, and every panel as a modal macOS sheet, light and dark. The playground's Desktop host uses it. Guard: `src/look.test.tsx`. **Consumer action:** Matrx Desktop: pass `look="desktop"` to `<VaultApp>` (Account › Vault). Web app and extension: none.
16
21
 
17
- ## 0.1.12
18
-
19
- UNVERIFIED: this release has no Unreleased entry and the source comparison could not be made (the caller supplied no source comparison). Whether source changed is unknown; consumer action is unknown.
20
- Check the package's gitHead on npm against this version's gitHead: `git diff <previous gitHead> <this gitHead> -- apps/shared/vault/src`.
21
-
22
22
  ## 0.1.11
23
23
 
24
24
  SOURCE CHANGED WITH NO RELEASE NOTE. This release shipped source changes that nobody described in an Unreleased entry,
package/dist/index.cjs CHANGED
@@ -749,7 +749,7 @@ function SecretRow({ port, item, field, extra, onTurnOn }) {
749
749
  const [error, setError] = (0, import_react5.useState)(null);
750
750
  const sealed = field.handling === "sealed";
751
751
  const visible = field.handling === "visible";
752
- const off = field.active === false;
752
+ const off = field.active === false || item.turnedOff === true;
753
753
  const canSee = !sealed && !off && (visible ? item.capabilities.canUse : item.capabilities.canReveal);
754
754
  (0, import_react5.useEffect)(() => {
755
755
  if (!visible || !canSee) return;
@@ -5832,13 +5832,19 @@ function ItemPane(props) {
5832
5832
  }
5833
5833
  }
5834
5834
  const offFields = item ? item.fields.filter((f) => !f.active) : [];
5835
+ const itemOff = item?.turnedOff === true;
5835
5836
  const seedField = item?.fields.find((f) => f.key === OTP_SEED_KEY) ?? null;
5836
5837
  const canTurnOn = Boolean(item && canEdit);
5837
5838
  async function turnOn(fields) {
5838
- if (!item || fields.length === 0) return;
5839
+ if (!item) return;
5839
5840
  setError(null);
5840
5841
  try {
5842
+ if (item.turnedOff) await confirmed(() => port.fields.updateItem(item, { active: true }));
5841
5843
  for (const f of fields) await confirmed(() => port.fields.updateField(item, f.id, { active: true }));
5844
+ if (fields.some((f) => f.key === OTP_SEED_KEY)) {
5845
+ const codes = await port.authenticator.get(item);
5846
+ if (codes && !codes.enabled) await confirmed(() => port.authenticator.setEnabled(item, true));
5847
+ }
5842
5848
  onChanged();
5843
5849
  } catch (e) {
5844
5850
  if (!wasCancelled(e)) setError(e instanceof Error ? e.message : "It could not be turned on.");
@@ -5941,8 +5947,8 @@ function ItemPane(props) {
5941
5947
  header,
5942
5948
  error ? /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("div", { role: "alert", className: "border-b border-border bg-muted px-5 py-2 text-sm text-destructive", children: error }) : null,
5943
5949
  notice && !editing ? /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("div", { role: "status", className: "border-b border-border bg-muted px-5 py-2 text-sm text-warning", children: notice }) : null,
5944
- item && !editing && offFields.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("div", { role: "status", className: "flex items-center gap-3 border-b border-border bg-muted px-5 py-2 text-sm", children: [
5945
- /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("span", { className: "min-w-0 flex-1 truncate text-warning", children: offFields.length === 1 ? "1 field is turned off" : `${offFields.length} fields are turned off` }),
5950
+ item && !editing && (itemOff || offFields.length > 0) ? /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("div", { role: "status", className: "flex items-center gap-3 border-b border-border bg-muted px-5 py-2 text-sm", children: [
5951
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("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` }),
5946
5952
  canTurnOn ? /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(TurnOnButton, { label: "all fields", onTurnOn: () => turnOn(offFields) }) : null
5947
5953
  ] }) : null,
5948
5954
  /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("div", { className: "min-h-0 flex-1 overflow-y-auto", children: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("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__ */ (0, import_jsx_runtime26.jsxs)(import_jsx_runtime26.Fragment, { children: [
@@ -8522,6 +8528,7 @@ function createFieldsPort(ctx) {
8522
8528
  if (patch.uriMatchMode !== void 0) body.uri_match_mode = patch.uriMatchMode;
8523
8529
  if (patch.browserFillEnabled !== void 0) body.browser_fill_enabled = patch.browserFillEnabled;
8524
8530
  if (patch.loginUrls !== void 0) body.login_urls = patch.loginUrls;
8531
+ if (patch.active !== void 0) body.status = patch.active ? "active" : "disabled";
8525
8532
  await account3.updateItem(api, { path: { item_id: item.id }, body }, ctx.optsFor(item));
8526
8533
  return reread(item);
8527
8534
  },
@@ -8934,6 +8941,7 @@ function itemOf(w, owner, defs, auth) {
8934
8941
  uriMatchMode: MATCH_MODES2.find((m) => m === w.uri_match_mode) ?? "host",
8935
8942
  browserFillEnabled: w.browser_fill_enabled ?? true,
8936
8943
  accessMode: w.access_mode ?? null,
8944
+ turnedOff: w.status === "disabled",
8937
8945
  ownerUserId: w.user_id ?? null,
8938
8946
  deletion: recovery?.state === "deleted" ? { deletionId: typeof recovery.deletion_id === "string" ? recovery.deletion_id : null, deletedAt: typeof recovery.deleted_at === "string" ? recovery.deleted_at : null } : null,
8939
8947
  updatedAt: w.updated_at,
@@ -8993,9 +9001,12 @@ function createApiVaultPort(api, options = {}) {
8993
9001
  const authP = retrying(() => apps2.listAuthenticators(api));
8994
9002
  const userP = retrying(() => account9.listItems(api, { query: { principal_type: "user" } }));
8995
9003
  const sharedP = retrying(() => account9.sharedWithMe(api));
9004
+ const quiet = (p) => void p.catch(() => void 0);
9005
+ [catalogP, authP, userP, sharedP].forEach(quiet);
8996
9006
  const org = options.organizations ? null : await retrying(() => platform.listMyOrganizations(api));
8997
9007
  orgs = [...new Map((options.organizations ?? (org?.organizations ?? []).filter((o) => !o.archived_at).map((o) => ({ id: o.id, name: o.name }))).map((o) => [o.id, o])).values()];
8998
9008
  const orgListsP = orgs.map((o) => retrying(() => account9.listItems(api, { query: { principal_type: "organization", organization_id: o.id } }, { organizationId: o.id })));
9009
+ orgListsP.forEach(quiet);
8999
9010
  const [catalog, mine] = await Promise.all([catalogP, userP]);
9000
9011
  const [authSettled, sharedSettled, ...orgSettled] = await Promise.allSettled([authP, sharedP, ...orgListsP]);
9001
9012
  const unavailable = [];