@cosmicdrift/kumiko-bundled-features 0.155.0 → 0.156.0

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.
Files changed (50) hide show
  1. package/package.json +7 -6
  2. package/src/agent-tools/__tests__/tool-catalog.test.ts +179 -0
  3. package/src/agent-tools/__tests__/tool-dispatch.integration.test.ts +100 -0
  4. package/src/agent-tools/__tests__/tool-dispatch.test.ts +126 -0
  5. package/src/agent-tools/index.ts +9 -0
  6. package/src/agent-tools/tool-catalog.ts +130 -0
  7. package/src/agent-tools/tool-dispatch.ts +82 -0
  8. package/src/agent-tools/types.ts +38 -0
  9. package/src/auth-email-password/web/__tests__/signup-complete-screen.test.tsx +5 -1
  10. package/src/auth-email-password/web/__tests__/signup-screen.test.tsx +5 -1
  11. package/src/auth-email-password/web/auth-gate.tsx +14 -4
  12. package/src/auth-mfa/__tests__/enable-flow.integration.test.ts +89 -0
  13. package/src/auth-mfa/__tests__/totp.test.ts +2 -2
  14. package/src/auth-mfa/__tests__/verify.integration.test.ts +29 -4
  15. package/src/auth-mfa/constants.ts +4 -0
  16. package/src/auth-mfa/handlers/disable.write.ts +2 -1
  17. package/src/auth-mfa/handlers/enable-confirm.write.ts +9 -3
  18. package/src/auth-mfa/handlers/regenerate-recovery.write.ts +4 -3
  19. package/src/auth-mfa/handlers/verify.write.ts +22 -12
  20. package/src/auth-mfa/recovery-codes.ts +2 -2
  21. package/src/auth-mfa/schema/user-mfa.ts +10 -0
  22. package/src/auth-mfa/totp.ts +18 -6
  23. package/src/auth-mfa/verify-factor.ts +30 -2
  24. package/src/auth-mfa/web/i18n.ts +2 -0
  25. package/src/auth-mfa/web/mfa-enable-screen.tsx +7 -29
  26. package/src/auth-mfa/web/mfa-error-keys.ts +2 -0
  27. package/src/auth-mfa/web/qrcode-browser.d.ts +5 -0
  28. package/src/config/__tests__/pii-encrypted.integration.test.ts +158 -0
  29. package/src/config/__tests__/write-helpers.test.ts +23 -0
  30. package/src/config/handlers/set.write.ts +47 -1
  31. package/src/config/resolver.ts +28 -0
  32. package/src/config/write-helpers.ts +22 -0
  33. package/src/custom-fields/__tests__/audit-integration.integration.test.ts +1 -1
  34. package/src/delivery/feature.ts +1 -1
  35. package/src/file-foundation/__tests__/feature.test.ts +1 -1
  36. package/src/files-provider-s3/__tests__/s3-provider.integration.test.ts +3 -1
  37. package/src/inbound-mail-foundation/__tests__/feature.test.ts +1 -1
  38. package/src/inbound-mail-foundation/feature.ts +3 -3
  39. package/src/inbound-provider-imap/__tests__/imap-foundation.integration.test.ts +7 -11
  40. package/src/jobs/feature.ts +1 -1
  41. package/src/legal-pages/feature.ts +1 -1
  42. package/src/mail-foundation/__tests__/feature.test.ts +1 -1
  43. package/src/page-render/branding.ts +4 -3
  44. package/src/personal-access-tokens/feature.ts +1 -1
  45. package/src/sessions/__tests__/sessions.integration.test.ts +42 -0
  46. package/src/sessions/feature.ts +2 -2
  47. package/src/user-data-rights/handlers/download-by-job.query.ts +14 -7
  48. package/src/user-data-rights/handlers/download-by-token.query.ts +14 -7
  49. package/src/user-data-rights/lib/storage-provider-resolver.ts +6 -5
  50. package/src/user-data-rights/lib/tenant-file-provider.ts +0 -62
@@ -25,12 +25,16 @@
25
25
 
26
26
  import { requestContext } from "@cosmicdrift/kumiko-framework/api";
27
27
  import { fetchOne } from "@cosmicdrift/kumiko-framework/bun-db";
28
- import { defineQueryHandler } from "@cosmicdrift/kumiko-framework/engine";
28
+ import {
29
+ defineQueryHandler,
30
+ SYSTEM_USER_ID,
31
+ type TenantId,
32
+ } from "@cosmicdrift/kumiko-framework/engine";
29
33
  import { NotFoundError, UnprocessableError } from "@cosmicdrift/kumiko-framework/errors";
30
34
  import { getTemporal } from "@cosmicdrift/kumiko-framework/time";
31
35
  import { z } from "zod";
32
36
  import { recordDownloadUse, recordInvalidAttempt } from "../audit-download";
33
- import { resolveTenantFileProvider } from "../lib/tenant-file-provider";
37
+ import { makeTenantStorageProviderResolver } from "../lib/storage-provider-resolver";
34
38
  import { exportDownloadTokensTable } from "../schema/download-token";
35
39
  import { EXPORT_JOB_STATUS, exportJobsTable } from "../schema/export-job";
36
40
 
@@ -130,11 +134,14 @@ export const downloadByJobQuery = defineQueryHandler({
130
134
  // Provider bound explicitly to the job's tenant — NOT the ambient
131
135
  // session tenant (cross-tenant-same-user: job.requestedFromTenantId
132
136
  // can differ from query.user.tenantId).
133
- const provider = await resolveTenantFileProvider(
134
- ctx,
135
- jobRow.requestedFromTenantId,
136
- "user-data-rights:query:download-by-job",
137
- );
137
+ const provider = await makeTenantStorageProviderResolver({
138
+ registry: ctx.registry,
139
+ configResolver: ctx.configResolver,
140
+ secrets: ctx.secrets,
141
+ db: ctx.db.raw,
142
+ userId: SYSTEM_USER_ID,
143
+ handlerName: "user-data-rights:query:download-by-job",
144
+ })(jobRow.requestedFromTenantId as TenantId); // @cast-boundary engine-payload: TenantId brand
138
145
  if (!provider.getSignedUrl) {
139
146
  await recordInvalidAttempt({
140
147
  db: ctx.db.raw,
@@ -27,12 +27,16 @@
27
27
  // startet. Dieser query-handler liefert nur das JSON.
28
28
 
29
29
  import { fetchOne } from "@cosmicdrift/kumiko-framework/bun-db";
30
- import { defineQueryHandler } from "@cosmicdrift/kumiko-framework/engine";
30
+ import {
31
+ defineQueryHandler,
32
+ SYSTEM_USER_ID,
33
+ type TenantId,
34
+ } from "@cosmicdrift/kumiko-framework/engine";
31
35
  import { NotFoundError, UnprocessableError } from "@cosmicdrift/kumiko-framework/errors";
32
36
  import { getTemporal } from "@cosmicdrift/kumiko-framework/time";
33
37
  import { z } from "zod";
34
38
  import { recordDownloadUse, recordInvalidAttempt } from "../audit-download";
35
- import { resolveTenantFileProvider } from "../lib/tenant-file-provider";
39
+ import { makeTenantStorageProviderResolver } from "../lib/storage-provider-resolver";
36
40
  import { exportDownloadTokensTable } from "../schema/download-token";
37
41
  import { EXPORT_JOB_STATUS, exportJobsTable } from "../schema/export-job";
38
42
  import { hashDownloadToken } from "../token-helpers";
@@ -184,11 +188,14 @@ export const downloadByTokenQuery = defineQueryHandler({
184
188
  // Step 5: signed-URL via provider, explicitly bound to the job's
185
189
  // tenant — NOT the ambient request tenant (anonymous magic-link path
186
190
  // has none under resolverTrust: "authoritative").
187
- const provider = await resolveTenantFileProvider(
188
- ctx,
189
- jobRow.requestedFromTenantId,
190
- "user-data-rights:query:download-by-token",
191
- );
191
+ const provider = await makeTenantStorageProviderResolver({
192
+ registry: ctx.registry,
193
+ configResolver: ctx.configResolver,
194
+ secrets: ctx.secrets,
195
+ db: ctx.db.raw,
196
+ userId: SYSTEM_USER_ID,
197
+ handlerName: "user-data-rights:query:download-by-token",
198
+ })(jobRow.requestedFromTenantId as TenantId); // @cast-boundary engine-payload: TenantId brand
192
199
  if (!provider.getSignedUrl) {
193
200
  await recordInvalidAttempt({
194
201
  db: ctx.db.raw,
@@ -4,8 +4,9 @@
4
4
  // construction). Extracted from the export cron so both crons + the manual
5
5
  // forget handler share one construction site instead of inlining it three times.
6
6
 
7
- import type { DbConnection } from "@cosmicdrift/kumiko-framework/db";
7
+ import type { DbConnection, TenantDb } from "@cosmicdrift/kumiko-framework/db";
8
8
  import type { ConfigResolver, Registry, TenantId } from "@cosmicdrift/kumiko-framework/engine";
9
+ import { InternalError } from "@cosmicdrift/kumiko-framework/errors";
9
10
  import type { FileStorageProvider } from "@cosmicdrift/kumiko-framework/files";
10
11
  import type { SecretsContext } from "@cosmicdrift/kumiko-framework/secrets";
11
12
  import { createConfigAccessor } from "../../config";
@@ -18,7 +19,7 @@ export interface TenantStorageResolverCtx {
18
19
  // Undefined → the returned resolver throws (callers decide fail-loud vs skip).
19
20
  readonly configResolver: ConfigResolver | undefined;
20
21
  readonly secrets: SecretsContext | undefined;
21
- readonly db: DbConnection;
22
+ readonly db: DbConnection | TenantDb;
22
23
  readonly userId: string;
23
24
  readonly handlerName: string;
24
25
  }
@@ -28,9 +29,9 @@ export function makeTenantStorageProviderResolver(
28
29
  ): (tenantId: TenantId) => Promise<FileStorageProvider> {
29
30
  return async (tenantId) => {
30
31
  if (!ctx.configResolver) {
31
- throw new Error(
32
- `${ctx.handlerName}: ctx.configResolver missing — cannot resolve the file provider for tenant ${tenantId}`,
33
- );
32
+ throw new InternalError({
33
+ message: `[${ctx.handlerName}] ctx.configResolver missing — cannot resolve the file provider for tenant ${tenantId}`,
34
+ });
34
35
  }
35
36
  const config = createConfigAccessor(
36
37
  ctx.registry,
@@ -1,62 +0,0 @@
1
- // Resolves the GDPR-download file-storage provider explicitly for
2
- // `jobRow.requestedFromTenantId` instead of the ambient request tenant.
3
- //
4
- // Both download handlers (by-token, by-job) used to call
5
- // `createFileProviderForTenant(ctx, jobRow.requestedFromTenantId, ...)`,
6
- // which reads the PROVIDER SELECTION from `ctx.config` — bound to the
7
- // caller's own ambient tenant, not the job's tenant:
8
- // - by-token (anonymous magic-link, httpRoute): under
9
- // `resolverTrust: "authoritative"` without `defaultTenantId` there is
10
- // no ambient tenant at all — `ctx.config` throws, 500s the whole
11
- // GDPR download flow.
12
- // - by-job (session-authed): a user can own jobs across tenants
13
- // (cross-tenant-same-user); the ambient session tenant silently
14
- // picks the WRONG tenant's provider config for a job requested from
15
- // a different tenant.
16
- //
17
- // Fix: build a fresh config accessor bound explicitly to the given
18
- // tenantId (same construction as `makeTenantStorageProviderResolver`,
19
- // which the export/forget crons already use), instead of the ambient
20
- // `ctx.config`. Deliberately NOT `ctx._fileProviderResolver` — that
21
- // resolver is boot-built and per-tenant-cached for the process lifetime,
22
- // so an operator switching `file-foundation:config:provider` mid-session
23
- // wouldn't take effect until the cache evicts.
24
- import type { DbConnection, TenantDb } from "@cosmicdrift/kumiko-framework/db";
25
- import type { ConfigResolver, Registry, TenantId } from "@cosmicdrift/kumiko-framework/engine";
26
- import { SYSTEM_USER_ID } from "@cosmicdrift/kumiko-framework/engine";
27
- import type { FileStorageProvider } from "@cosmicdrift/kumiko-framework/files";
28
- import type { SecretsContext } from "@cosmicdrift/kumiko-framework/secrets";
29
- import { createConfigAccessor } from "../../config";
30
- import { createFileProviderForTenant } from "../../file-foundation";
31
-
32
- export interface TenantFileProviderCtx {
33
- readonly registry?: Registry;
34
- readonly configResolver?: ConfigResolver;
35
- readonly secrets?: SecretsContext;
36
- readonly db: { readonly raw: DbConnection | TenantDb };
37
- }
38
-
39
- export async function resolveTenantFileProvider(
40
- ctx: TenantFileProviderCtx,
41
- tenantId: string,
42
- handlerName: string,
43
- ): Promise<FileStorageProvider> {
44
- if (!ctx.registry || !ctx.configResolver) {
45
- throw new Error(
46
- `${handlerName}: ctx.registry/ctx.configResolver missing — cannot resolve the file provider for tenant ${tenantId}`,
47
- );
48
- }
49
- const config = createConfigAccessor(
50
- ctx.registry,
51
- ctx.configResolver,
52
- tenantId as TenantId, // @cast-boundary engine-payload: TenantId brand
53
- SYSTEM_USER_ID,
54
- ctx.db.raw,
55
- ctx.secrets,
56
- );
57
- return createFileProviderForTenant(
58
- { config, registry: ctx.registry, secrets: ctx.secrets, _userId: SYSTEM_USER_ID },
59
- tenantId,
60
- handlerName,
61
- );
62
- }