@ai-matrx/vault 0.1.53 → 0.1.55

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.d.cts CHANGED
@@ -3,6 +3,7 @@ import { Look } from '@ai-matrx/design-system';
3
3
  import { ApiStream, NdjsonEvent, JsonOrStream, FollowRuntimeOperationToEndOptions, FollowRuntimeOperationToEndResult, Api } from '@ai-matrx/api';
4
4
  import * as account from '@ai-matrx/api/account';
5
5
  import { VaultPasswordHealthScanRequest, VaultPasswordHealthScanReferenceData, PasswordHealthDurableResult } from '@ai-matrx/api/account';
6
+ import { VaultPasswordHealthDiscoveryPageData } from '@ai-matrx/api/stream';
6
7
  import { OperationStatusResult } from '@ai-matrx/api/architecture';
7
8
  import { VaultIdentity } from './identity.cjs';
8
9
  export { CONFIRM_RETURN_KEY, Confirmed, IdentityAccount, IdentityGate, IdentityMethod, ReturnStore, SupabaseAuthLike, SupabaseIdentityOptions, createSupabaseIdentity, needsRecentSignIn, useConfirmed, wasCancelled } from './identity.cjs';
@@ -222,6 +223,7 @@ type HealthScanOptions = VaultPasswordHealthScanRequest;
222
223
  type HealthResult = PasswordHealthDurableResult;
223
224
  type HealthReference = VaultPasswordHealthScanReferenceData;
224
225
  interface HealthPort {
226
+ discover(cursor: string | undefined, signal: AbortSignal): Promise<VaultPasswordHealthDiscoveryPageData>;
225
227
  start(requestId: string, signal: AbortSignal, options: HealthScanOptions): Promise<ApiStream<NdjsonEvent>>;
226
228
  resume(executionId: string, requestId: string, signal: AbortSignal): Promise<JsonOrStream<HealthReference, NdjsonEvent>>;
227
229
  read(executionId: string, signal: AbortSignal): Promise<HealthResult>;
package/dist/index.d.ts CHANGED
@@ -3,6 +3,7 @@ import { Look } from '@ai-matrx/design-system';
3
3
  import { ApiStream, NdjsonEvent, JsonOrStream, FollowRuntimeOperationToEndOptions, FollowRuntimeOperationToEndResult, Api } from '@ai-matrx/api';
4
4
  import * as account from '@ai-matrx/api/account';
5
5
  import { VaultPasswordHealthScanRequest, VaultPasswordHealthScanReferenceData, PasswordHealthDurableResult } from '@ai-matrx/api/account';
6
+ import { VaultPasswordHealthDiscoveryPageData } from '@ai-matrx/api/stream';
6
7
  import { OperationStatusResult } from '@ai-matrx/api/architecture';
7
8
  import { VaultIdentity } from './identity.js';
8
9
  export { CONFIRM_RETURN_KEY, Confirmed, IdentityAccount, IdentityGate, IdentityMethod, ReturnStore, SupabaseAuthLike, SupabaseIdentityOptions, createSupabaseIdentity, needsRecentSignIn, useConfirmed, wasCancelled } from './identity.js';
@@ -222,6 +223,7 @@ type HealthScanOptions = VaultPasswordHealthScanRequest;
222
223
  type HealthResult = PasswordHealthDurableResult;
223
224
  type HealthReference = VaultPasswordHealthScanReferenceData;
224
225
  interface HealthPort {
226
+ discover(cursor: string | undefined, signal: AbortSignal): Promise<VaultPasswordHealthDiscoveryPageData>;
225
227
  start(requestId: string, signal: AbortSignal, options: HealthScanOptions): Promise<ApiStream<NdjsonEvent>>;
226
228
  resume(executionId: string, requestId: string, signal: AbortSignal): Promise<JsonOrStream<HealthReference, NdjsonEvent>>;
227
229
  read(executionId: string, signal: AbortSignal): Promise<HealthResult>;
package/dist/index.js CHANGED
@@ -146,7 +146,7 @@ function PasswordHealthView(props) {
146
146
  const latest = useRef(props);
147
147
  latest.current = props;
148
148
  const [result, setResult] = useState2(null);
149
- const [status, setStatus] = useState2("No report");
149
+ const [status, setStatus] = useState2("Checking status");
150
150
  const [error, setError] = useState2(null);
151
151
  const [progress, setProgress] = useState2(null);
152
152
  const [busy, setBusy] = useState2(false);
@@ -156,9 +156,13 @@ function PasswordHealthView(props) {
156
156
  }, [port]);
157
157
  const [retry, setRetry] = useState2(0);
158
158
  const active = useRef(null);
159
- const starting = useRef(false);
159
+ const starting = useRef(null);
160
160
  const ownedAddress = useRef(null);
161
161
  const writeReference = (request, execution) => {
162
+ if (starting.current?.controller === active.current) {
163
+ starting.current.request = request;
164
+ starting.current.execution = execution;
165
+ }
162
166
  ownedAddress.current = { request, execution, items: latest.current.items, retry };
163
167
  latest.current.onReference(request, execution);
164
168
  };
@@ -186,7 +190,7 @@ function PasswordHealthView(props) {
186
190
  const value = await port.read(id, controller.signal);
187
191
  if (owns(controller)) {
188
192
  setStatus(STATUS_LABEL[value.execution_status]);
189
- setResult({ value, items: inventory });
193
+ setResult({ value, items: inventory, port });
190
194
  }
191
195
  return value;
192
196
  };
@@ -198,11 +202,33 @@ function PasswordHealthView(props) {
198
202
  if (!owns(controller)) return;
199
203
  id = known.operations[0]?.execution_id;
200
204
  if (!id) {
205
+ setStatus("Unavailable");
201
206
  setError("Status unavailable");
202
207
  return;
203
208
  }
204
209
  writeReference(request, id);
205
210
  }
211
+ if (!id && !request) {
212
+ let cursor;
213
+ const seen = /* @__PURE__ */ new Set();
214
+ do {
215
+ const page = await port.discover(cursor, controller.signal);
216
+ if (!owns(controller)) return;
217
+ const reference = page.references[0];
218
+ if (reference) {
219
+ id = reference.execution_id;
220
+ writeReference(reference.request_id, reference.execution_id);
221
+ break;
222
+ }
223
+ cursor = page.continuation ?? void 0;
224
+ if (cursor && seen.has(cursor)) throw new Error("Report unavailable");
225
+ if (cursor) seen.add(cursor);
226
+ } while (cursor);
227
+ if (!id) {
228
+ setStatus("No report");
229
+ return;
230
+ }
231
+ }
206
232
  if (!id || !owns(controller)) return;
207
233
  const first = await read(id, controller);
208
234
  if (!owns(controller) || terminal(first.execution_status)) return;
@@ -212,7 +238,7 @@ function PasswordHealthView(props) {
212
238
  if (!owns(controller)) return;
213
239
  const detail = event.detail;
214
240
  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) {
215
- setProgress({ checked: detail.checked, skipped: detail.skipped });
241
+ setProgress({ checked: detail.checked, skipped: detail.skipped, port });
216
242
  }
217
243
  const lifecycle = event.kind === "started" || event.kind === "resumed" ? "running" : event.kind === "created" ? "pending" : event.kind;
218
244
  const label = STATUS_LABEL[lifecycle];
@@ -227,17 +253,21 @@ function PasswordHealthView(props) {
227
253
  if (!inventoryReady) {
228
254
  ownedAddress.current = null;
229
255
  setResult(null);
230
- if (!starting.current) active.current?.abort();
256
+ if (!starting.current || !owns(starting.current.controller)) active.current?.abort();
231
257
  return;
232
258
  }
233
- if (!port || starting.current) return;
259
+ if (!port) return;
260
+ const start = starting.current;
261
+ if (start && owns(start.controller) && start.request === (requestId ?? "") && start.execution === (executionId ?? "")) return;
234
262
  const own = ownedAddress.current;
235
263
  if (own && own.request === requestId && own.execution === executionId && own.items === items && own.retry === retry && active.current && !active.current.signal.aborted) return;
236
264
  const controller = fresh();
265
+ setStatus("Checking status");
237
266
  void recover(requestId, executionId, controller).catch((e) => {
238
267
  if (owns(controller)) {
239
268
  setResult(null);
240
269
  setError(problem(e));
270
+ setStatus((value) => value === "Checking status" ? "Unavailable" : value);
241
271
  }
242
272
  }).finally(() => {
243
273
  if (owns(controller)) setBusy(false);
@@ -254,7 +284,7 @@ function PasswordHealthView(props) {
254
284
  const run = async (resume2) => {
255
285
  if (!port) return;
256
286
  const controller = fresh();
257
- starting.current = true;
287
+ starting.current = { controller, request: "", execution: "" };
258
288
  const request = crypto.randomUUID();
259
289
  const options = { check_breaches: checkBreaches };
260
290
  let actualRequest = request;
@@ -283,7 +313,7 @@ function PasswordHealthView(props) {
283
313
  if (data && typeof data === "object" && "type" in data) {
284
314
  if (data.type === "vault_password_health_scan_reference") accept(data);
285
315
  if (data.type === "vault_password_health_progress") {
286
- setProgress(data);
316
+ setProgress({ ...data, port });
287
317
  setStatus("Running");
288
318
  }
289
319
  }
@@ -325,21 +355,24 @@ function PasswordHealthView(props) {
325
355
  }
326
356
  setResult(null);
327
357
  setError(problem(e));
358
+ setStatus((value) => value === "Checking status" ? "Unavailable" : value);
328
359
  } finally {
329
- starting.current = false;
360
+ if (starting.current?.controller === controller) starting.current = null;
330
361
  if (owns(controller)) {
331
362
  setBusy(false);
332
363
  if (startedItems !== latest.current.items) setRetry((v) => v + 1);
333
364
  }
334
365
  }
335
366
  };
336
- const current = inventoryReady && result?.items === items && result.value.availability === "current" ? result.value.snapshot : null;
337
- const available = result?.value.availability;
367
+ const ownedResult = port && result?.port === port ? result : null;
368
+ const shownProgress = port && progress?.port === port ? progress : null;
369
+ const current = inventoryReady && ownedResult?.items === items && ownedResult.value.availability === "current" ? ownedResult.value.snapshot : null;
370
+ const available = ownedResult?.value.availability;
338
371
  const outcomes = current?.check_breaches ? current.breach_outcomes ?? [] : [];
339
372
  const compromised = outcomes.filter((outcome) => outcome.status === "compromised").length;
340
373
  const notFound = outcomes.filter((outcome) => outcome.status === "not_found").length;
341
374
  const unknown = outcomes.filter((outcome) => outcome.status === "unknown").length;
342
- const resume = result && ["failed", "cancelled"].includes(result.value.execution_status);
375
+ const resume = ownedResult && ["failed", "cancelled"].includes(ownedResult.value.execution_status);
343
376
  return /* @__PURE__ */ jsxs2("div", { className: "flex h-full min-h-0 flex-col gap-4 overflow-auto p-4", "aria-label": "Password health", children: [
344
377
  props.header,
345
378
  /* @__PURE__ */ jsxs2("div", { className: "flex items-center justify-between gap-2", children: [
@@ -373,9 +406,9 @@ function PasswordHealthView(props) {
373
406
  /* @__PURE__ */ jsx2(InfoHint, { text: "The breach lookup could not establish a result." })
374
407
  ] }), children: unknown })
375
408
  ] }) : null,
376
- !current && progress ? /* @__PURE__ */ jsxs2(Fragment, { children: [
377
- /* @__PURE__ */ jsx2(VaultRow, { label: "Checked", children: progress.checked }),
378
- /* @__PURE__ */ jsx2(VaultRow, { label: "Skipped", children: progress.skipped })
409
+ !current && shownProgress ? /* @__PURE__ */ jsxs2(Fragment, { children: [
410
+ /* @__PURE__ */ jsx2(VaultRow, { label: "Checked", children: shownProgress.checked }),
411
+ /* @__PURE__ */ jsx2(VaultRow, { label: "Skipped", children: shownProgress.skipped })
379
412
  ] }) : null,
380
413
  current ? /* @__PURE__ */ jsxs2(Fragment, { children: [
381
414
  /* @__PURE__ */ jsx2(VaultRow, { label: "Checked", children: current.checked_subjects.length }),
@@ -9146,10 +9179,34 @@ function Blank({ children }) {
9146
9179
 
9147
9180
  // src/health/api.ts
9148
9181
  import { followRuntimeOperationToEndWithApi } from "@ai-matrx/api";
9149
- import { startPasswordHealthScan, getPasswordHealthScan, resumePasswordHealthScan } from "@ai-matrx/api/account";
9182
+ import { startPasswordHealthScan, getPasswordHealthScan, resumePasswordHealthScan, discoverPasswordHealthScans } from "@ai-matrx/api/account";
9150
9183
  import { operationStatus } from "@ai-matrx/api/architecture";
9151
9184
  function createHealthPort(api) {
9152
9185
  return {
9186
+ discover: async (cursor, signal) => {
9187
+ const stream = await discoverPasswordHealthScans(api, cursor ? { query: { cursor } } : void 0, { signal, totalTimeoutMs: null });
9188
+ let page = null;
9189
+ let ended = false;
9190
+ try {
9191
+ for await (const event of stream) {
9192
+ if (signal.aborted || ended || event.event === "error") throw new Error("Report unavailable");
9193
+ const data = event.data;
9194
+ if (event.event === "completion" && data && typeof data === "object" && "status" in data && data.status !== "success") throw new Error("Report unavailable");
9195
+ if (event.event === "end") {
9196
+ if (!data || typeof data !== "object" || !("reason" in data) || data.reason !== "complete") throw new Error("Report unavailable");
9197
+ ended = true;
9198
+ }
9199
+ if (event.event !== "data") continue;
9200
+ if (!data || typeof data !== "object" || !("type" in data) || data.type !== "vault_password_health_discovery_page") continue;
9201
+ 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");
9202
+ page = data;
9203
+ }
9204
+ if (signal.aborted || !ended || !page) throw new Error("Report unavailable");
9205
+ return page;
9206
+ } finally {
9207
+ stream.cancel();
9208
+ }
9209
+ },
9153
9210
  start: (requestId, signal, options) => startPasswordHealthScan(api, { body: options.check_breaches ? { check_breaches: true } : {} }, { requestId, signal, totalTimeoutMs: null }),
9154
9211
  resume: (executionId, requestId, signal) => resumePasswordHealthScan(api, { path: { execution_id: executionId } }, { requestId, signal, totalTimeoutMs: null }),
9155
9212
  read: (executionId, signal) => getPasswordHealthScan(api, { path: { execution_id: executionId } }, { signal }),