@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/CHANGELOG.md +5 -4
- package/dist/index.cjs +69 -16
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +70 -17
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
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(
|
|
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(
|
|
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
|
|
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(
|
|
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 =
|
|
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 =
|
|
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
|
|
341
|
-
const
|
|
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 =
|
|
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 &&
|
|
381
|
-
/* @__PURE__ */ jsx2(VaultRow, { label: "Checked", children:
|
|
382
|
-
/* @__PURE__ */ jsx2(VaultRow, { label: "Skipped", children:
|
|
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 }),
|