@ai-matrx/vault 0.1.54 → 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(requestId || executionId ? "Checking status" : "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
  };
@@ -204,6 +208,27 @@ function PasswordHealthView(props) {
204
208
  }
205
209
  writeReference(request, id);
206
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
+ }
207
232
  if (!id || !owns(controller)) return;
208
233
  const first = await read(id, controller);
209
234
  if (!owns(controller) || terminal(first.execution_status)) return;
@@ -213,7 +238,7 @@ function PasswordHealthView(props) {
213
238
  if (!owns(controller)) return;
214
239
  const detail = event.detail;
215
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) {
216
- setProgress({ checked: detail.checked, skipped: detail.skipped });
241
+ setProgress({ checked: detail.checked, skipped: detail.skipped, port });
217
242
  }
218
243
  const lifecycle = event.kind === "started" || event.kind === "resumed" ? "running" : event.kind === "created" ? "pending" : event.kind;
219
244
  const label = STATUS_LABEL[lifecycle];
@@ -228,14 +253,16 @@ function PasswordHealthView(props) {
228
253
  if (!inventoryReady) {
229
254
  ownedAddress.current = null;
230
255
  setResult(null);
231
- if (!starting.current) active.current?.abort();
256
+ if (!starting.current || !owns(starting.current.controller)) active.current?.abort();
232
257
  return;
233
258
  }
234
- 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;
235
262
  const own = ownedAddress.current;
236
263
  if (own && own.request === requestId && own.execution === executionId && own.items === items && own.retry === retry && active.current && !active.current.signal.aborted) return;
237
264
  const controller = fresh();
238
- setStatus(requestId || executionId ? "Checking status" : "No report");
265
+ setStatus("Checking status");
239
266
  void recover(requestId, executionId, controller).catch((e) => {
240
267
  if (owns(controller)) {
241
268
  setResult(null);
@@ -257,7 +284,7 @@ function PasswordHealthView(props) {
257
284
  const run = async (resume2) => {
258
285
  if (!port) return;
259
286
  const controller = fresh();
260
- starting.current = true;
287
+ starting.current = { controller, request: "", execution: "" };
261
288
  const request = crypto.randomUUID();
262
289
  const options = { check_breaches: checkBreaches };
263
290
  let actualRequest = request;
@@ -286,7 +313,7 @@ function PasswordHealthView(props) {
286
313
  if (data && typeof data === "object" && "type" in data) {
287
314
  if (data.type === "vault_password_health_scan_reference") accept(data);
288
315
  if (data.type === "vault_password_health_progress") {
289
- setProgress(data);
316
+ setProgress({ ...data, port });
290
317
  setStatus("Running");
291
318
  }
292
319
  }
@@ -330,20 +357,22 @@ function PasswordHealthView(props) {
330
357
  setError(problem(e));
331
358
  setStatus((value) => value === "Checking status" ? "Unavailable" : value);
332
359
  } finally {
333
- starting.current = false;
360
+ if (starting.current?.controller === controller) starting.current = null;
334
361
  if (owns(controller)) {
335
362
  setBusy(false);
336
363
  if (startedItems !== latest.current.items) setRetry((v) => v + 1);
337
364
  }
338
365
  }
339
366
  };
340
- const current = inventoryReady && result?.items === items && result.value.availability === "current" ? result.value.snapshot : null;
341
- 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;
342
371
  const outcomes = current?.check_breaches ? current.breach_outcomes ?? [] : [];
343
372
  const compromised = outcomes.filter((outcome) => outcome.status === "compromised").length;
344
373
  const notFound = outcomes.filter((outcome) => outcome.status === "not_found").length;
345
374
  const unknown = outcomes.filter((outcome) => outcome.status === "unknown").length;
346
- const resume = result && ["failed", "cancelled"].includes(result.value.execution_status);
375
+ const resume = ownedResult && ["failed", "cancelled"].includes(ownedResult.value.execution_status);
347
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: [
348
377
  props.header,
349
378
  /* @__PURE__ */ jsxs2("div", { className: "flex items-center justify-between gap-2", children: [
@@ -377,9 +406,9 @@ function PasswordHealthView(props) {
377
406
  /* @__PURE__ */ jsx2(InfoHint, { text: "The breach lookup could not establish a result." })
378
407
  ] }), children: unknown })
379
408
  ] }) : null,
380
- !current && progress ? /* @__PURE__ */ jsxs2(Fragment, { children: [
381
- /* @__PURE__ */ jsx2(VaultRow, { label: "Checked", children: progress.checked }),
382
- /* @__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 })
383
412
  ] }) : null,
384
413
  current ? /* @__PURE__ */ jsxs2(Fragment, { children: [
385
414
  /* @__PURE__ */ jsx2(VaultRow, { label: "Checked", children: current.checked_subjects.length }),
@@ -9150,10 +9179,34 @@ function Blank({ children }) {
9150
9179
 
9151
9180
  // src/health/api.ts
9152
9181
  import { followRuntimeOperationToEndWithApi } from "@ai-matrx/api";
9153
- import { startPasswordHealthScan, getPasswordHealthScan, resumePasswordHealthScan } from "@ai-matrx/api/account";
9182
+ import { startPasswordHealthScan, getPasswordHealthScan, resumePasswordHealthScan, discoverPasswordHealthScans } from "@ai-matrx/api/account";
9154
9183
  import { operationStatus } from "@ai-matrx/api/architecture";
9155
9184
  function createHealthPort(api) {
9156
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
+ },
9157
9210
  start: (requestId, signal, options) => startPasswordHealthScan(api, { body: options.check_breaches ? { check_breaches: true } : {} }, { requestId, signal, totalTimeoutMs: null }),
9158
9211
  resume: (executionId, requestId, signal) => resumePasswordHealthScan(api, { path: { execution_id: executionId } }, { requestId, signal, totalTimeoutMs: null }),
9159
9212
  read: (executionId, signal) => getPasswordHealthScan(api, { path: { execution_id: executionId } }, { signal }),