@almadar/integrations 2.25.0 → 2.27.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.
@@ -117,6 +117,65 @@ interface ValidationError {
117
117
  */
118
118
  declare function validateParams(integration: string, action: string, params: IntegrationParams): ValidationResult;
119
119
 
120
+ /**
121
+ * Optional per-call context for principal-scoped resolution (W4) and
122
+ * caller-identity enforcement (I-25). The default — absent — means the
123
+ * tenant/app-wide credential set with an anonymous caller; role-gated
124
+ * integrations (credentials) fail closed without it. Additive: every
125
+ * existing call site is untouched.
126
+ */
127
+ interface IntegrationCallContext {
128
+ principal?: string;
129
+ /** The caller's roster role, from the same identity entity-ACL enforces against. */
130
+ role?: string;
131
+ }
132
+ /**
133
+ * Factory for creating and managing integration instances
134
+ */
135
+ declare class IntegrationFactory {
136
+ private instances;
137
+ private configs;
138
+ /**
139
+ * Configure an integration (doesn't instantiate yet). A `principal` scopes
140
+ * the config to that principal; the app-wide config (no principal) is the
141
+ * fallback for every principal.
142
+ */
143
+ configure(name: string, config: Omit<IntegrationConfig, 'name'>, principal?: string): void;
144
+ /**
145
+ * Get or create an integration instance. Principal-scoped lookups fall
146
+ * back to the app-wide config when no per-principal config exists.
147
+ */
148
+ get(name: string, principal?: string): BaseIntegration;
149
+ /**
150
+ * Execute an action on an integration
151
+ */
152
+ execute(integration: string, action: string, params: IntegrationParams, context?: IntegrationCallContext): Promise<IntegrationResult>;
153
+ /**
154
+ * Check if integration is configured
155
+ */
156
+ isConfigured(name: string, principal?: string): boolean;
157
+ /**
158
+ * Register an integration instance directly (used by mock infrastructure)
159
+ */
160
+ registerInstance(name: string, instance: BaseIntegration, principal?: string): void;
161
+ /**
162
+ * Drop the cached instance(s) for a name so the next `get` rebuilds from
163
+ * the current config — how a credential change goes live without restart.
164
+ * Configs are kept; without a name, every instance is dropped.
165
+ */
166
+ invalidate(name?: string): void;
167
+ /**
168
+ * Clear all instances (useful for testing)
169
+ */
170
+ clear(): void;
171
+ /**
172
+ * Clear all instances and configs
173
+ */
174
+ reset(): void;
175
+ }
176
+ declare function getIntegrationFactory(): IntegrationFactory;
177
+ declare function resetIntegrationFactory(): void;
178
+
120
179
  /**
121
180
  * Base class for all integrations
122
181
  */
@@ -125,9 +184,11 @@ declare abstract class BaseIntegration {
125
184
  protected logger: IntegrationLogger;
126
185
  constructor(config: IntegrationConfig);
127
186
  /**
128
- * Execute an action
187
+ * Execute an action. `context` carries the caller's identity when the host
188
+ * supplies one; integrations that gate actions by role read it and fail
189
+ * closed when it is absent.
129
190
  */
130
- abstract execute(action: string, params: IntegrationParams): Promise<IntegrationResult>;
191
+ abstract execute(action: string, params: IntegrationParams, context?: IntegrationCallContext): Promise<IntegrationResult>;
131
192
  /**
132
193
  * Validate action params against registry
133
194
  */
@@ -146,4 +207,4 @@ declare abstract class BaseIntegration {
146
207
  protected executeWithRetry<T>(fn: () => Promise<T>): Promise<T>;
147
208
  }
148
209
 
149
- export { BaseIntegration as B, type IntegrationConfig as I, type ValidationError as V, IntegrationError as a, type IntegrationErrorCode as b, type IntegrationLogger as c, type IntegrationParamValue as d, type IntegrationParams as e, type IntegrationResult as f, type ValidationResult as g, validateParams as v };
210
+ export { BaseIntegration as B, type IntegrationCallContext as I, type ValidationError as V, type IntegrationConfig as a, IntegrationError as b, type IntegrationErrorCode as c, IntegrationFactory as d, type IntegrationLogger as e, type IntegrationParamValue as f, type IntegrationParams as g, type IntegrationResult as h, type ValidationResult as i, getIntegrationFactory as j, resetIntegrationFactory as r, validateParams as v };
package/dist/index.d.ts CHANGED
@@ -1,10 +1,8 @@
1
- import { c as IntegrationLogger, b as IntegrationErrorCode, I as IntegrationConfig, B as BaseIntegration, e as IntegrationParams, f as IntegrationResult } from './BaseIntegration-MA-b4fh8.js';
2
- export { a as IntegrationError, d as IntegrationParamValue, V as ValidationError, g as ValidationResult, v as validateParams } from './BaseIntegration-MA-b4fh8.js';
3
- import { e as CredentialStore } from './store-CW1v7Apc.js';
4
- export { A as ArxivActions, C as CLIActions, a as CREDENTIAL_ENTITY_TYPE, b as CREDENTIAL_MASTER_KEY_ENV, c as CredentialEntry, d as CredentialPersistence, D as DatabaseActions, f as DatabaseDriver, g as DatabaseQueryParamValue, h as DatabaseQueryParams, i as DatabaseQueryResult, j as DatabaseRow, k as DeepAgentActions, l as DockerActions, E as EmailActions, G as GitHubActions, I as IconifyActions, m as IntegrationActionName, n as IntegrationContracts, o as IntegrationName, L as LLMIntegrationActions, M as MLActions, O as OAuthActions, p as OtelActions, Q as QueueActions, R as RedisActions, S as StorageActions, q as StripeActions, T as TwilioActions, W as WebhookActions, r as WikimediaActions, Y as YouTubeActions } from './store-CW1v7Apc.js';
5
- import { LogMeta } from '@almadar/core';
6
- import { a as IntegrationFactory } from './factory-BPVhvv5q.js';
7
- export { I as IntegrationCallContext, g as getIntegrationFactory, r as resetIntegrationFactory } from './factory-BPVhvv5q.js';
1
+ import { e as IntegrationLogger, c as IntegrationErrorCode, a as IntegrationConfig, B as BaseIntegration, d as IntegrationFactory, g as IntegrationParams, h as IntegrationResult, I as IntegrationCallContext } from './BaseIntegration-C_5q54DM.js';
2
+ export { b as IntegrationError, f as IntegrationParamValue, V as ValidationError, i as ValidationResult, j as getIntegrationFactory, r as resetIntegrationFactory, v as validateParams } from './BaseIntegration-C_5q54DM.js';
3
+ import { e as CredentialStore, d as CredentialPersistence, H as HookDeclaration } from './store-tehIm2rt.js';
4
+ export { A as ArxivActions, C as CLIActions, a as CREDENTIAL_ENTITY_TYPE, b as CREDENTIAL_MASTER_KEY_ENV, c as CredentialEntry, D as DatabaseActions, f as DatabaseDriver, g as DatabaseQueryParamValue, h as DatabaseQueryParams, i as DatabaseQueryResult, j as DatabaseRow, k as DeepAgentActions, l as DockerActions, E as EmailActions, G as GitHubActions, I as IconifyActions, m as IntegrationActionName, n as IntegrationContracts, o as IntegrationName, L as LLMIntegrationActions, M as MLActions, O as OAuthActions, p as OtelActions, Q as QueueActions, R as RedisActions, S as StorageActions, q as StripeActions, T as TwilioActions, W as WebhookActions, r as WikimediaActions, Y as YouTubeActions, s as serviceHooks } from './store-tehIm2rt.js';
5
+ import { LogMeta, EntityRow, EventPayload } from '@almadar/core';
8
6
  export { GitHubIntegration } from './integrations/github/index.js';
9
7
 
10
8
  /**
@@ -88,6 +86,30 @@ declare function uninstallCredentialStore(): void;
88
86
  */
89
87
  declare function resolveCredentialRef(ref: string, env?: Record<string, string | undefined>): string | undefined;
90
88
 
89
+ /** Env var overriding where the dev credential file lives. */
90
+ declare const CREDENTIALS_FILE_ENV = "ALMADAR_CREDENTIALS_FILE";
91
+ /**
92
+ * File-backed `CredentialPersistence` for dev store-first mode (I-31): rows
93
+ * survive playground restarts so the encrypt-at-rest path is exercised on
94
+ * every dev run, exactly like a deployed app's Firestore-backed store. Rows
95
+ * arrive already encrypted (ciphertext/iv/authTag) — this adapter never sees
96
+ * plaintext. Writes are atomic (tmp file + rename). Never use in production.
97
+ */
98
+ declare class FileCredentialPersistence implements CredentialPersistence {
99
+ private readonly path;
100
+ private counter;
101
+ constructor(path?: string);
102
+ get filePath(): string;
103
+ private load;
104
+ private save;
105
+ create(entityType: string, data: EntityRow): Promise<{
106
+ id: string;
107
+ }>;
108
+ update(_entityType: string, id: string, data: EntityRow): Promise<void>;
109
+ delete(_entityType: string, id: string): Promise<void>;
110
+ list(_entityType: string): Promise<EntityRow[]>;
111
+ }
112
+
91
113
  /**
92
114
  * Canonical Almadar shapes for the Stripe integration.
93
115
  *
@@ -463,13 +485,61 @@ declare class CalendarIntegration extends BaseIntegration {
463
485
  }
464
486
 
465
487
  /**
466
- * Google Drive integration — service account auth, optionally acting as a
467
- * Workspace user via domain-wide delegation (GOOGLE_DRIVE_SUBJECT). File
468
- * content crosses this boundary as base64 (data URL accepted on upload).
488
+ * All registered inbound-hook providers, derived from the `serviceHooks`
489
+ * declarations (the single source the registry and codegen also derive from).
490
+ * Hosts that serve arbitrary schemas (the playground) mount this full map;
491
+ * generated apps get a per-app subset via the compiler's hooks-providers.ts.
492
+ *
493
+ * `HOOK_PROVIDER_FACTORIES` is the static factory table TypeScript needs
494
+ * (export names cannot be resolved dynamically without casts); the drift
495
+ * guard below fails loudly if a declaration names a factory this table lacks.
496
+ */
497
+
498
+ interface HookProviderInput {
499
+ headers: Record<string, string | undefined>;
500
+ rawBody: string;
501
+ }
502
+ type HookProviderResult = {
503
+ event: string;
504
+ payload: EventPayload;
505
+ } | {
506
+ ack: true;
507
+ } | {
508
+ error: string;
509
+ };
510
+ type RegisteredHookProvider = (input: HookProviderInput) => HookProviderResult;
511
+ /** Every hook declaration across all services — used by the drift-guard test. */
512
+ declare function allHookDeclarations(): HookDeclaration[];
513
+ /**
514
+ * Build the full provider map from every service's hook declarations,
515
+ * resolving each provider's verification secret from `env`. Throws when a
516
+ * declaration names a factory the table above does not carry — declaration
517
+ * and implementation must advance together.
518
+ */
519
+ declare function registeredHookProviders(env?: Record<string, string | undefined>): Record<string, RegisteredHookProvider>;
520
+
521
+ /**
522
+ * Google Drive integration — dual auth:
523
+ * - **Service account** (GOOGLE_DRIVE_SA_KEY, optional GOOGLE_DRIVE_SUBJECT
524
+ * delegation) serves reads (list/get/share).
525
+ * - **User OAuth** (GOOGLE_DRIVE_REFRESH_TOKEN + the app's OAUTH_CLIENT_ID/
526
+ * SECRET, obtained once via `scripts/drive-consent.mjs`) serves
527
+ * WRITES (`uploadFile`/`createFolder`) when present — Google enforces zero
528
+ * SA storage quota on personal accounts, so SA writes into a shared My
529
+ * Drive folder are impossible; the user client also covers reads when no
530
+ * SA key is configured. `GOOGLE_DRIVE_FOLDER_ID` is the default upload
531
+ * parent. File content crosses this boundary as base64 (data URL accepted
532
+ * on upload).
469
533
  */
470
534
  declare class DriveIntegration extends BaseIntegration {
471
- private client;
535
+ private readonly saClient;
536
+ private readonly userClient;
537
+ private readonly defaultFolderId;
472
538
  constructor(config: IntegrationConfig);
539
+ /** Reads prefer the SA client (delegation-aware); user client covers its absence. */
540
+ private readClient;
541
+ /** Writes REQUIRE the user client on personal accounts (SA has no storage quota); SA only as a Workspace fallback. */
542
+ private writeClient;
473
543
  execute(action: string, params: IntegrationParams): Promise<IntegrationResult>;
474
544
  private listFiles;
475
545
  private getFile;
@@ -694,6 +764,36 @@ declare class OtelIntegration extends BaseIntegration {
694
764
  private getAllMetrics;
695
765
  }
696
766
 
767
+ /** Supported OAuth providers */
768
+ type OAuthProvider = 'google' | 'github' | 'auth0';
769
+ /** Server-held per-authorization state (never leaves the process). */
770
+ interface PendingAuthorization {
771
+ provider: OAuthProvider;
772
+ redirectUri: string;
773
+ pkceVerifier: string;
774
+ }
775
+ /**
776
+ * Where pending OIDC authorizations (PKCE verifier, redirect URI) live
777
+ * between `authorize` and `token`. The in-memory default works single-
778
+ * instance; a multi-instance deployment injects a shared-store impl (the
779
+ * FirestoreCredentialPersistence structural pattern) via
780
+ * `installPendingGrantStore` — no sticky routing needed once it does.
781
+ * `take` is single-use: it returns AND removes, so a state token can never
782
+ * be replayed. `sweep` drops expired grants.
783
+ */
784
+ interface PendingGrantStore {
785
+ put(state: string, grant: PendingAuthorization, ttlMs: number): Promise<void>;
786
+ take(state: string): Promise<PendingAuthorization | null>;
787
+ sweep(): Promise<void>;
788
+ }
789
+ declare class InMemoryPendingGrantStore implements PendingGrantStore {
790
+ private readonly grants;
791
+ put(state: string, grant: PendingAuthorization, ttlMs: number): Promise<void>;
792
+ take(state: string): Promise<PendingAuthorization | null>;
793
+ sweep(): Promise<void>;
794
+ }
795
+ /** Install a shared pending-grant store (multi-instance hosts); null resets to in-memory. */
796
+ declare function installPendingGrantStore(store: PendingGrantStore | null): void;
697
797
  /**
698
798
  * OAuth2/OIDC integration.
699
799
  *
@@ -707,9 +807,11 @@ declare class OtelIntegration extends BaseIntegration {
707
807
  * behind `OAUTH_MODE=mock` (or absent client credentials) so existing
708
808
  * tests and offline dev flows are untouched.
709
809
  *
710
- * Pending-authorization state (PKCE verifier, redirect URI) is held
711
- * in-process keyed by `state` multi-instance deployments need sticky
712
- * routing between authorize and token (ledger note).
810
+ * Pending-authorization state (PKCE verifier, redirect URI) lives in a
811
+ * `PendingGrantStore` — in-memory by default (single instance), injectable
812
+ * via `installPendingGrantStore` for multi-instance deployments (a shared
813
+ * store replaces the old sticky-routing requirement). Grants are TTL-bound
814
+ * and single-use.
713
815
  */
714
816
  declare class OAuthIntegration extends BaseIntegration {
715
817
  /** Maps state token -> provider for pending MOCK authorization flows */
@@ -720,8 +822,9 @@ declare class OAuthIntegration extends BaseIntegration {
720
822
  private refreshIndex;
721
823
  /** Maps access token -> mock user session */
722
824
  private sessions;
723
- /** Maps state -> pending OIDC authorization (real backend) */
724
- private pending;
825
+ /** Pending OIDC authorizations (real backend) — injectable, in-memory default. */
826
+ private readonly fallbackPending;
827
+ private pendingStore;
725
828
  /** Maps access token -> ID-token subject, for userinfo subject checks */
726
829
  private subjects;
727
830
  /** Discovered issuer configurations, keyed by issuer URL */
@@ -757,13 +860,18 @@ declare class OAuthIntegration extends BaseIntegration {
757
860
  * plaintext); `set` accepts only env vars DECLARED in `serviceCredentials`
758
861
  * for the named service (plus well-formed connection refs for `database`),
759
862
  * so the page can never become an arbitrary env-injection surface; values
760
- * are never logged.
863
+ * are never logged. `set`/`remove`/`test` additionally require the caller's
864
+ * role (from IntegrationCallContext) to be in the admin list
865
+ * (ALMADAR_CREDENTIAL_ADMIN_ROLES, default admin,owner) and FAIL CLOSED
866
+ * without one — the un-bypassable twin of the organism-side page gate (I-25).
761
867
  */
762
868
  declare class CredentialsIntegration extends BaseIntegration {
763
869
  constructor(config: IntegrationConfig);
764
- execute(action: string, params: IntegrationParams): Promise<IntegrationResult>;
870
+ execute(action: string, params: IntegrationParams, context?: IntegrationCallContext): Promise<IntegrationResult>;
871
+ private adminRoles;
765
872
  private declaredFor;
766
873
  private assertSettable;
874
+ private storeFirst;
767
875
  private list;
768
876
  private set;
769
877
  private remove;
@@ -905,4 +1013,4 @@ declare class ArxivIntegration extends BaseIntegration {
905
1013
  private search;
906
1014
  }
907
1015
 
908
- export { AccountingIntegration, type AlmadarCheckoutSession, type AlmadarCustomer, type AlmadarInvoiceFailure, type AlmadarInvoicePayment, type AlmadarPortalSession, type AlmadarStripeEvent, type AlmadarStripeEventOk, type AlmadarSubscription, type AlmadarSubscriptionStatus, type AlmadarTier, ArxivIntegration, type ArxivResult, BankingIntegration, BaseIntegration, CLIIntegration, CalendarIntegration, type CancelSubscriptionInput, ConsoleLogger, type CreateCheckoutInput, type CreateCustomerInput, type CreatePortalInput, CredentialStore, CredentialsIntegration, DatabaseIntegration, DeepAgentIntegration, DockerIntegration, DriveIntegration, EmailIntegration, EsignIntegration, IconifyIntegration, IntegrationConfig, type IntegrationConstructor, IntegrationErrorCode, IntegrationFactory, IntegrationLogger, IntegrationParams, IntegrationResult, LLMIntegration, MLIntegration, MetaAdsIntegration, OAuthIntegration, OtelIntegration, PushIntegration, QueueIntegration, RedisIntegration, type RetryConfig, type SqlGuardResult, StorageIntegration, StripeIntegration, type StripePriceMap, TwilioIntegration, type UpdateSubscriptionInput, type VerifyAndParseInput, WebhookIntegration, WikimediaIntegration, YouTubeIntegration, assertReadOnlySelect, getActiveFactory, getInstalledCredentialStore, getIntegration, getRegisteredIntegrations, googleCalendarHookProvider, installActiveFactory, installCredentialStore, isKnownIntegration, parseCalendarPushNotification, registerIntegration, resolveCredentialRef, uninstallCredentialStore, verifyAndParseStripeEvent, withRetry };
1016
+ export { AccountingIntegration, type AlmadarCheckoutSession, type AlmadarCustomer, type AlmadarInvoiceFailure, type AlmadarInvoicePayment, type AlmadarPortalSession, type AlmadarStripeEvent, type AlmadarStripeEventOk, type AlmadarSubscription, type AlmadarSubscriptionStatus, type AlmadarTier, ArxivIntegration, type ArxivResult, BankingIntegration, BaseIntegration, CLIIntegration, CREDENTIALS_FILE_ENV, CalendarIntegration, type CancelSubscriptionInput, ConsoleLogger, type CreateCheckoutInput, type CreateCustomerInput, type CreatePortalInput, CredentialPersistence, CredentialStore, CredentialsIntegration, DatabaseIntegration, DeepAgentIntegration, DockerIntegration, DriveIntegration, EmailIntegration, EsignIntegration, FileCredentialPersistence, HookDeclaration, type HookProviderInput, type HookProviderResult, IconifyIntegration, InMemoryPendingGrantStore, IntegrationCallContext, IntegrationConfig, type IntegrationConstructor, IntegrationErrorCode, IntegrationFactory, IntegrationLogger, IntegrationParams, IntegrationResult, LLMIntegration, MLIntegration, MetaAdsIntegration, OAuthIntegration, OtelIntegration, type PendingGrantStore, PushIntegration, QueueIntegration, RedisIntegration, type RegisteredHookProvider, type RetryConfig, type SqlGuardResult, StorageIntegration, StripeIntegration, type StripePriceMap, TwilioIntegration, type UpdateSubscriptionInput, type VerifyAndParseInput, WebhookIntegration, WikimediaIntegration, YouTubeIntegration, allHookDeclarations, assertReadOnlySelect, getActiveFactory, getInstalledCredentialStore, getIntegration, getRegisteredIntegrations, googleCalendarHookProvider, installActiveFactory, installCredentialStore, installPendingGrantStore, isKnownIntegration, parseCalendarPushNotification, registerIntegration, registeredHookProviders, resolveCredentialRef, uninstallCredentialStore, verifyAndParseStripeEvent, withRetry };