@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.
- package/dist/{BaseIntegration-MA-b4fh8.d.ts → BaseIntegration-C_5q54DM.d.ts} +64 -3
- package/dist/index.d.ts +127 -19
- package/dist/index.js +505 -198
- package/dist/index.js.map +1 -1
- package/dist/integrations/github/index.d.ts +1 -1
- package/dist/integrations/github/index.js +11 -0
- package/dist/integrations/github/index.js.map +1 -1
- package/dist/mocks/index.d.ts +1 -2
- package/dist/mocks/index.js +12 -1
- package/dist/mocks/index.js.map +1 -1
- package/dist/runtime/index.d.ts +14 -4
- package/dist/runtime/index.js +379 -193
- package/dist/runtime/index.js.map +1 -1
- package/dist/{store-CW1v7Apc.d.ts → store-tehIm2rt.d.ts} +68 -4
- package/package.json +2 -2
- package/dist/factory-BPVhvv5q.d.ts +0 -59
package/dist/index.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { createLogger } from '@almadar/logger';
|
|
2
2
|
import { integratorsRegistry } from '@almadar/core/patterns';
|
|
3
3
|
import { randomBytes, createCipheriv, createDecipheriv, createHmac, createHash } from 'crypto';
|
|
4
|
+
import { readFileSync, mkdirSync, writeFileSync, renameSync, mkdtempSync, rmSync, promises } from 'fs';
|
|
5
|
+
import { join, dirname } from 'path';
|
|
4
6
|
import Stripe from 'stripe';
|
|
5
7
|
import { google } from 'googleapis';
|
|
6
8
|
import twilio from 'twilio';
|
|
@@ -11,8 +13,6 @@ import { Readable } from 'stream';
|
|
|
11
13
|
import { getAvailableProvider, LLMClient, EmbeddingClient } from '@almadar/llm';
|
|
12
14
|
import { z } from 'zod';
|
|
13
15
|
import { execSync, spawn } from 'child_process';
|
|
14
|
-
import { mkdtempSync, writeFileSync, rmSync, promises } from 'fs';
|
|
15
|
-
import { join } from 'path';
|
|
16
16
|
import { tmpdir } from 'os';
|
|
17
17
|
import * as oidc from 'openid-client';
|
|
18
18
|
import { S3Client, PutObjectCommand, GetObjectCommand, ListObjectsV2Command, DeleteObjectCommand } from '@aws-sdk/client-s3';
|
|
@@ -38,6 +38,143 @@ var IntegrationError = class extends Error {
|
|
|
38
38
|
};
|
|
39
39
|
}
|
|
40
40
|
};
|
|
41
|
+
|
|
42
|
+
// src/contracts.ts
|
|
43
|
+
var serviceCredentials = {
|
|
44
|
+
stripe: [
|
|
45
|
+
{ envVar: "STRIPE_SECRET_KEY", required: true, description: "Stripe secret API key" },
|
|
46
|
+
{ envVar: "STRIPE_WEBHOOK_SECRET", required: false, description: "Webhook endpoint signing secret" }
|
|
47
|
+
],
|
|
48
|
+
youtube: [
|
|
49
|
+
{ envVar: "YOUTUBE_API_KEY", required: true, description: "YouTube Data API key" }
|
|
50
|
+
],
|
|
51
|
+
twilio: [
|
|
52
|
+
{ envVar: "TWILIO_ACCOUNT_SID", required: true, description: "Twilio account SID" },
|
|
53
|
+
{ envVar: "TWILIO_AUTH_TOKEN", required: true, description: "Twilio auth token" },
|
|
54
|
+
{ envVar: "TWILIO_PHONE_NUMBER", required: false, description: "Default sender phone number" }
|
|
55
|
+
],
|
|
56
|
+
email: [
|
|
57
|
+
{ envVar: "SENDGRID_API_KEY", required: false, description: "SendGrid API key (use this OR RESEND_API_KEY)" },
|
|
58
|
+
{ envVar: "RESEND_API_KEY", required: false, description: "Resend API key (use this OR SENDGRID_API_KEY)" },
|
|
59
|
+
{ envVar: "FROM_EMAIL", required: false, description: "Default sender email address" }
|
|
60
|
+
],
|
|
61
|
+
webhook: [
|
|
62
|
+
{ envVar: "WEBHOOK_SIGNING_SECRET", required: false, description: "Default HMAC-SHA256 signing secret (per-call secret overrides)" },
|
|
63
|
+
{ envVar: "WEBHOOK_TIMEOUT_MS", required: false, description: "Per-request timeout (ms, default 10000)" }
|
|
64
|
+
],
|
|
65
|
+
push: [
|
|
66
|
+
{ envVar: "VAPID_PUBLIC_KEY", required: true, description: "VAPID public key (web-push generate-vapid-keys)" },
|
|
67
|
+
{ envVar: "VAPID_PRIVATE_KEY", required: true, description: "VAPID private key" },
|
|
68
|
+
{ envVar: "VAPID_SUBJECT", required: true, description: "VAPID subject (mailto: or https: contact URI)" }
|
|
69
|
+
],
|
|
70
|
+
calendar: [
|
|
71
|
+
{ envVar: "GOOGLE_CALENDAR_SA_KEY", required: true, description: "Google service-account key JSON (raw or base64) with calendar scope; store in Secret Manager, bind as env" },
|
|
72
|
+
{ envVar: "GOOGLE_CALENDAR_SUBJECT", required: false, description: "Workspace user to impersonate (domain-wide delegation); empty = act as the service account" },
|
|
73
|
+
{ envVar: "GOOGLE_CALENDAR_ID", required: false, description: "Default calendar id (default: primary)" },
|
|
74
|
+
{ envVar: "GOOGLE_CALENDAR_CHANNEL_TOKEN", required: false, description: "Watch-channel verification token \u2014 required to receive inbound calendar hooks (two-way sync)" }
|
|
75
|
+
],
|
|
76
|
+
drive: [
|
|
77
|
+
{ envVar: "GOOGLE_DRIVE_SA_KEY", required: false, description: "Google service-account key JSON (raw or base64) with drive scope \u2014 serves reads; store in Secret Manager, bind as env" },
|
|
78
|
+
{ envVar: "GOOGLE_DRIVE_SUBJECT", required: false, description: "Workspace user to impersonate (domain-wide delegation); empty = act as the service account" },
|
|
79
|
+
{ envVar: "GOOGLE_DRIVE_REFRESH_TOKEN", required: false, description: "User OAuth refresh token (drive-consent.mjs) \u2014 serves writes; SA uploads are impossible on personal accounts (no SA storage quota)" },
|
|
80
|
+
{ envVar: "GOOGLE_DRIVE_FOLDER_ID", required: false, description: "Default parent folder for uploads/new folders when the call names none" }
|
|
81
|
+
],
|
|
82
|
+
metaAds: [
|
|
83
|
+
{ envVar: "META_ACCESS_TOKEN", required: true, description: "Meta Graph API access token (Marketing API, ads_read)" },
|
|
84
|
+
{ envVar: "META_AD_ACCOUNT_ID", required: false, description: "Default ad account id (act_\u2026); per-call accountId overrides" }
|
|
85
|
+
],
|
|
86
|
+
accounting: [],
|
|
87
|
+
banking: [
|
|
88
|
+
{ envVar: "GOCARDLESS_SECRET_ID", required: true, description: "GoCardless Bank Account Data secret id" },
|
|
89
|
+
{ envVar: "GOCARDLESS_SECRET_KEY", required: true, description: "GoCardless Bank Account Data secret key" }
|
|
90
|
+
],
|
|
91
|
+
esign: [
|
|
92
|
+
{ envVar: "DOCUSIGN_BASE_URL", required: true, description: "DocuSign REST base (e.g. https://demo.docusign.net/restapi/v2.1/accounts/<accountId>)" },
|
|
93
|
+
{ envVar: "DOCUSIGN_ACCESS_TOKEN", required: true, description: "DocuSign OAuth access token (JWT grant rotation is the deployment concern)" }
|
|
94
|
+
],
|
|
95
|
+
llm: [
|
|
96
|
+
{ envVar: "ANTHROPIC_API_KEY", required: false, description: "Anthropic API key (use this OR OPENAI_API_KEY)" },
|
|
97
|
+
{ envVar: "OPENAI_API_KEY", required: false, description: "OpenAI API key (use this OR ANTHROPIC_API_KEY)" }
|
|
98
|
+
],
|
|
99
|
+
"llm-integration": [
|
|
100
|
+
{ envVar: "ANTHROPIC_API_KEY", required: false, description: "Anthropic API key" },
|
|
101
|
+
{ envVar: "OPENAI_API_KEY", required: false, description: "OpenAI API key" }
|
|
102
|
+
],
|
|
103
|
+
ml: [
|
|
104
|
+
{ envVar: "MASAR_URL", required: true, description: "Base URL of the deployed model-serving orbital" },
|
|
105
|
+
{ envVar: "MASAR_ML_TRAIT", required: true, description: "Kebab-case trait name the serving orbital exposes its /events route under" }
|
|
106
|
+
],
|
|
107
|
+
deepagent: [
|
|
108
|
+
{ envVar: "DEEPAGENT_API_URL", required: true, description: "DeepAgent server URL" },
|
|
109
|
+
{ envVar: "DEEPAGENT_API_KEY", required: false, description: "DeepAgent API key" }
|
|
110
|
+
],
|
|
111
|
+
github: [
|
|
112
|
+
{ envVar: "GITHUB_TOKEN", required: true, description: "GitHub personal access token" },
|
|
113
|
+
{ envVar: "GITHUB_OWNER", required: false, description: "Default repository owner" },
|
|
114
|
+
{ envVar: "GITHUB_REPO", required: false, description: "Default repository name" }
|
|
115
|
+
],
|
|
116
|
+
docker: [
|
|
117
|
+
{ envVar: "DOCKER_HOST", required: false, description: "Docker daemon host URL" }
|
|
118
|
+
],
|
|
119
|
+
storage: [
|
|
120
|
+
{ envVar: "STORAGE_ACCESS_KEY_ID", required: true, description: "S3-compatible access key id" },
|
|
121
|
+
{ envVar: "STORAGE_SECRET_ACCESS_KEY", required: true, description: "S3-compatible secret access key" },
|
|
122
|
+
{ envVar: "STORAGE_BUCKET", required: true, description: "Default bucket name" },
|
|
123
|
+
{ envVar: "STORAGE_REGION", required: false, description: "Region (default us-east-1)" },
|
|
124
|
+
{ envVar: "STORAGE_ENDPOINT", required: false, description: "Custom S3-compatible endpoint (R2/MinIO/\u2026); empty = AWS S3" },
|
|
125
|
+
{ envVar: "STORAGE_PUBLIC_URL_BASE", required: false, description: "Base URL for public-acl object links; empty = endpoint-derived" }
|
|
126
|
+
],
|
|
127
|
+
queue: [
|
|
128
|
+
{ envVar: "QUEUE_URL", required: true, description: "Message queue connection URL" }
|
|
129
|
+
],
|
|
130
|
+
redis: [
|
|
131
|
+
{ envVar: "REDIS_URL", required: true, description: "Redis connection URL" }
|
|
132
|
+
],
|
|
133
|
+
oauth: [
|
|
134
|
+
{ envVar: "OAUTH_CLIENT_ID", required: true, description: "OIDC client ID" },
|
|
135
|
+
{ envVar: "OAUTH_CLIENT_SECRET", required: true, description: "OIDC client secret" },
|
|
136
|
+
{ envVar: "OAUTH_REDIRECT_URI", required: false, description: "OIDC redirect URI (default per-call param)" },
|
|
137
|
+
{ envVar: "OIDC_ISSUER_URL", required: false, description: "OIDC issuer URL (default https://accounts.google.com)" }
|
|
138
|
+
],
|
|
139
|
+
credentials: [
|
|
140
|
+
{ envVar: "ALMADAR_CREDENTIAL_MASTER_KEY", required: false, description: "AES-256 master key (64-char hex) enabling the hosted credential store \u2014 hold it alone in the platform secret store" },
|
|
141
|
+
{ envVar: "ALMADAR_CREDENTIAL_MASTER_KEY_PREVIOUS", required: false, description: "Previous master key, present only during a rotation window \u2014 decrypts old rows until `credentials.rotate` re-encrypts them" }
|
|
142
|
+
],
|
|
143
|
+
otel: [
|
|
144
|
+
{ envVar: "OTEL_EXPORTER_OTLP_ENDPOINT", required: true, description: "OpenTelemetry collector endpoint" },
|
|
145
|
+
{ envVar: "OTEL_SERVICE_NAME", required: false, description: "Service name for traces" }
|
|
146
|
+
],
|
|
147
|
+
cli: [],
|
|
148
|
+
// No fixed env vars — connection strings are resolved per-query from the
|
|
149
|
+
// caller-supplied connectionRef, so credentials cannot be declared statically.
|
|
150
|
+
database: [],
|
|
151
|
+
wikimedia: [
|
|
152
|
+
{ envVar: "WIKIMEDIA_USER_AGENT", required: false, description: "Descriptive User-Agent for the Wikipedia API" },
|
|
153
|
+
{ envVar: "WIKIMEDIA_TIMEOUT_MS", required: false, description: "Per-request timeout (ms)" }
|
|
154
|
+
],
|
|
155
|
+
iconify: [
|
|
156
|
+
{ envVar: "ICONIFY_USER_AGENT", required: false, description: "User-Agent for the Iconify API" },
|
|
157
|
+
{ envVar: "ICONIFY_TIMEOUT_MS", required: false, description: "Per-request timeout (ms)" }
|
|
158
|
+
],
|
|
159
|
+
arxiv: [
|
|
160
|
+
{ envVar: "ARXIV_TIMEOUT_MS", required: false, description: "Per-request timeout (ms)" }
|
|
161
|
+
]
|
|
162
|
+
};
|
|
163
|
+
var serviceHooks = {
|
|
164
|
+
calendar: [
|
|
165
|
+
{
|
|
166
|
+
provider: "google-calendar",
|
|
167
|
+
event: "CAL_REMOTE_CHANGED",
|
|
168
|
+
credentialEnv: "GOOGLE_CALENDAR_CHANNEL_TOKEN",
|
|
169
|
+
providerExport: "googleCalendarHookProvider"
|
|
170
|
+
}
|
|
171
|
+
]
|
|
172
|
+
};
|
|
173
|
+
var serviceProbes = {
|
|
174
|
+
calendar: { action: "listEvents", params: { maxResults: 1 } },
|
|
175
|
+
drive: { action: "listFiles", params: { maxResults: 1 } },
|
|
176
|
+
metaAds: { action: "listCampaigns", params: {} }
|
|
177
|
+
};
|
|
41
178
|
var ConsoleLogger = class {
|
|
42
179
|
constructor(_level = "info") {
|
|
43
180
|
this.log = createLogger("almadar:integrations");
|
|
@@ -55,6 +192,7 @@ var ConsoleLogger = class {
|
|
|
55
192
|
this.log.error(message, meta);
|
|
56
193
|
}
|
|
57
194
|
};
|
|
195
|
+
var RESERVED_ENVELOPE_KEYS = /* @__PURE__ */ new Set(["emit", "onSuccess", "onError", "timeout"]);
|
|
58
196
|
function validateParams(integration, action, params) {
|
|
59
197
|
const typedRegistry = integratorsRegistry;
|
|
60
198
|
const registry = typedRegistry.integrators[integration];
|
|
@@ -77,6 +215,16 @@ function validateParams(integration, action, params) {
|
|
|
77
215
|
};
|
|
78
216
|
}
|
|
79
217
|
const errors = [];
|
|
218
|
+
const declaredNames = new Set(actionDef.params.map((p) => p.name));
|
|
219
|
+
for (const key of Object.keys(params)) {
|
|
220
|
+
if (RESERVED_ENVELOPE_KEYS.has(key)) continue;
|
|
221
|
+
if (!declaredNames.has(key)) {
|
|
222
|
+
errors.push({
|
|
223
|
+
param: key,
|
|
224
|
+
message: `Unknown parameter: ${key} (declared: ${[...declaredNames].sort().join(", ")})`
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
}
|
|
80
228
|
for (const paramDef of actionDef.params) {
|
|
81
229
|
if (paramDef.required && !(paramDef.name in params)) {
|
|
82
230
|
errors.push({
|
|
@@ -272,7 +420,7 @@ var IntegrationFactory = class {
|
|
|
272
420
|
*/
|
|
273
421
|
async execute(integration, action, params, context) {
|
|
274
422
|
const instance = this.get(integration, context?.principal);
|
|
275
|
-
return await instance.execute(action, params);
|
|
423
|
+
return await instance.execute(action, params, context);
|
|
276
424
|
}
|
|
277
425
|
/**
|
|
278
426
|
* Check if integration is configured
|
|
@@ -329,24 +477,31 @@ function resetIntegrationFactory() {
|
|
|
329
477
|
}
|
|
330
478
|
var CREDENTIAL_ENTITY_TYPE = "AlmadarIntegrationCredential";
|
|
331
479
|
var CREDENTIAL_MASTER_KEY_ENV = "ALMADAR_CREDENTIAL_MASTER_KEY";
|
|
480
|
+
var CREDENTIAL_PREVIOUS_MASTER_KEY_ENV = "ALMADAR_CREDENTIAL_MASTER_KEY_PREVIOUS";
|
|
332
481
|
var ENCRYPTION_ALGORITHM = "aes-256-gcm";
|
|
333
482
|
function isNonEmptyString(v) {
|
|
334
483
|
return typeof v === "string" && v.length > 0;
|
|
335
484
|
}
|
|
485
|
+
function keyIdOf(masterKeyHex) {
|
|
486
|
+
return createHash("sha256").update(masterKeyHex, "hex").digest("hex").slice(0, 16);
|
|
487
|
+
}
|
|
488
|
+
var MASTER_KEY_SHAPE = /^[0-9a-fA-F]{64}$/;
|
|
336
489
|
var CredentialStore = class {
|
|
337
|
-
constructor(adapter, masterKey) {
|
|
490
|
+
constructor(adapter, masterKey, previousMasterKey) {
|
|
338
491
|
this.byEnvVar = /* @__PURE__ */ new Map();
|
|
339
492
|
this.listeners = /* @__PURE__ */ new Set();
|
|
340
493
|
this.warmed = false;
|
|
341
494
|
this.adapter = adapter;
|
|
342
495
|
const key = masterKey ?? process.env[CREDENTIAL_MASTER_KEY_ENV];
|
|
343
|
-
const valid = typeof key === "string" &&
|
|
496
|
+
const valid = typeof key === "string" && MASTER_KEY_SHAPE.test(key);
|
|
344
497
|
if (!valid && process.env.NODE_ENV === "production") {
|
|
345
498
|
throw new Error(
|
|
346
499
|
`${CREDENTIAL_MASTER_KEY_ENV} must be a 64-character hex string (32 bytes) in production \u2014 generate one with: node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"`
|
|
347
500
|
);
|
|
348
501
|
}
|
|
349
502
|
this.masterKeyHex = valid ? key : void 0;
|
|
503
|
+
const prev = previousMasterKey ?? process.env[CREDENTIAL_PREVIOUS_MASTER_KEY_ENV];
|
|
504
|
+
this.previousKeyHex = typeof prev === "string" && MASTER_KEY_SHAPE.test(prev) ? prev : void 0;
|
|
350
505
|
}
|
|
351
506
|
/** True when a valid master key is present (writes and decryption enabled). */
|
|
352
507
|
get enabled() {
|
|
@@ -368,15 +523,17 @@ var CredentialStore = class {
|
|
|
368
523
|
const cipher = createCipheriv(ENCRYPTION_ALGORITHM, Buffer.from(this.masterKeyHex, "hex"), iv);
|
|
369
524
|
let ciphertext = cipher.update(plaintext, "utf8", "hex");
|
|
370
525
|
ciphertext += cipher.final("hex");
|
|
371
|
-
return {
|
|
526
|
+
return {
|
|
527
|
+
ciphertext,
|
|
528
|
+
iv: iv.toString("hex"),
|
|
529
|
+
authTag: cipher.getAuthTag().toString("hex"),
|
|
530
|
+
keyId: keyIdOf(this.masterKeyHex)
|
|
531
|
+
};
|
|
372
532
|
}
|
|
373
|
-
|
|
374
|
-
if (!this.masterKeyHex) {
|
|
375
|
-
throw new Error(`${CREDENTIAL_MASTER_KEY_ENV} is not configured \u2014 cannot decrypt credentials`);
|
|
376
|
-
}
|
|
533
|
+
decryptWithKey(keyHex, ciphertext, ivHex, authTagHex) {
|
|
377
534
|
const decipher = createDecipheriv(
|
|
378
535
|
ENCRYPTION_ALGORITHM,
|
|
379
|
-
Buffer.from(
|
|
536
|
+
Buffer.from(keyHex, "hex"),
|
|
380
537
|
Buffer.from(ivHex, "hex")
|
|
381
538
|
);
|
|
382
539
|
decipher.setAuthTag(Buffer.from(authTagHex, "hex"));
|
|
@@ -384,6 +541,32 @@ var CredentialStore = class {
|
|
|
384
541
|
plaintext += decipher.final("utf8");
|
|
385
542
|
return plaintext;
|
|
386
543
|
}
|
|
544
|
+
/**
|
|
545
|
+
* Decrypt one row, selecting the key by the row's stamped `keyId` (I-28):
|
|
546
|
+
* current key first, then the rotation-window previous key. Legacy rows
|
|
547
|
+
* (no `keyId`) try current then previous. Returns null when no held key
|
|
548
|
+
* decrypts the row — the row is unreadable, never silently wrong (GCM
|
|
549
|
+
* auth tags make a wrong-key success impossible).
|
|
550
|
+
*/
|
|
551
|
+
decryptRow(ciphertext, ivHex, authTagHex, rowKeyId) {
|
|
552
|
+
if (!this.masterKeyHex) {
|
|
553
|
+
throw new Error(`${CREDENTIAL_MASTER_KEY_ENV} is not configured \u2014 cannot decrypt credentials`);
|
|
554
|
+
}
|
|
555
|
+
const candidates = [];
|
|
556
|
+
if (rowKeyId === void 0 || rowKeyId === keyIdOf(this.masterKeyHex)) {
|
|
557
|
+
candidates.push(this.masterKeyHex);
|
|
558
|
+
}
|
|
559
|
+
if (this.previousKeyHex !== void 0 && (rowKeyId === void 0 || rowKeyId === keyIdOf(this.previousKeyHex))) {
|
|
560
|
+
candidates.push(this.previousKeyHex);
|
|
561
|
+
}
|
|
562
|
+
for (const key of candidates) {
|
|
563
|
+
try {
|
|
564
|
+
return this.decryptWithKey(key, ciphertext, ivHex, authTagHex);
|
|
565
|
+
} catch {
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
return null;
|
|
569
|
+
}
|
|
387
570
|
/**
|
|
388
571
|
* Load and decrypt every stored row into memory. Returns the number of
|
|
389
572
|
* resolvable credentials. Without a master key nothing decrypts (rows are
|
|
@@ -399,7 +582,13 @@ var CredentialStore = class {
|
|
|
399
582
|
if (!isNonEmptyString(id) || !isNonEmptyString(service) || !isNonEmptyString(envVar) || !isNonEmptyString(ciphertext) || !isNonEmptyString(iv) || !isNonEmptyString(authTag)) {
|
|
400
583
|
continue;
|
|
401
584
|
}
|
|
402
|
-
const value = this.
|
|
585
|
+
const value = this.decryptRow(
|
|
586
|
+
ciphertext,
|
|
587
|
+
iv,
|
|
588
|
+
authTag,
|
|
589
|
+
isNonEmptyString(row.keyId) ? row.keyId : void 0
|
|
590
|
+
);
|
|
591
|
+
if (value === null) continue;
|
|
403
592
|
this.byEnvVar.set(envVar, {
|
|
404
593
|
id,
|
|
405
594
|
service,
|
|
@@ -428,10 +617,10 @@ var CredentialStore = class {
|
|
|
428
617
|
/** Upsert one credential (encrypts, persists, re-warms the entry, notifies). */
|
|
429
618
|
async set(service, envVar, value) {
|
|
430
619
|
if (!this.warmed) await this.warm();
|
|
431
|
-
const { ciphertext, iv, authTag } = this.encrypt(value);
|
|
620
|
+
const { ciphertext, iv, authTag, keyId } = this.encrypt(value);
|
|
432
621
|
const updatedAt = Date.now();
|
|
433
622
|
const existing = this.byEnvVar.get(envVar);
|
|
434
|
-
const data = { service, envVar, ciphertext, iv, authTag, updatedAt };
|
|
623
|
+
const data = { service, envVar, ciphertext, iv, authTag, keyId, updatedAt };
|
|
435
624
|
let id;
|
|
436
625
|
if (existing) {
|
|
437
626
|
id = existing.id;
|
|
@@ -445,6 +634,58 @@ var CredentialStore = class {
|
|
|
445
634
|
const { service: s, envVar: e, last4, updatedAt: u } = entry;
|
|
446
635
|
return { service: s, envVar: e, last4, updatedAt: u };
|
|
447
636
|
}
|
|
637
|
+
/**
|
|
638
|
+
* Re-encrypt every readable row under the CURRENT master key (I-28).
|
|
639
|
+
* Rotation window: deploy the new key as `ALMADAR_CREDENTIAL_MASTER_KEY`
|
|
640
|
+
* with the old one as `..._PREVIOUS`, call `rotate()`, then retire the
|
|
641
|
+
* previous key. Rows no held key can read are counted, never dropped.
|
|
642
|
+
*/
|
|
643
|
+
async rotate() {
|
|
644
|
+
if (!this.masterKeyHex) {
|
|
645
|
+
throw new Error(`${CREDENTIAL_MASTER_KEY_ENV} is not configured \u2014 cannot rotate`);
|
|
646
|
+
}
|
|
647
|
+
const currentKeyId = keyIdOf(this.masterKeyHex);
|
|
648
|
+
let rotated = 0;
|
|
649
|
+
let alreadyCurrent = 0;
|
|
650
|
+
let unreadable = 0;
|
|
651
|
+
const rows = await this.adapter.list(CREDENTIAL_ENTITY_TYPE);
|
|
652
|
+
for (const row of rows) {
|
|
653
|
+
const { id, service, envVar, ciphertext, iv, authTag } = row;
|
|
654
|
+
if (!isNonEmptyString(id) || !isNonEmptyString(service) || !isNonEmptyString(envVar) || !isNonEmptyString(ciphertext) || !isNonEmptyString(iv) || !isNonEmptyString(authTag)) {
|
|
655
|
+
unreadable += 1;
|
|
656
|
+
continue;
|
|
657
|
+
}
|
|
658
|
+
const rowKeyId = isNonEmptyString(row.keyId) ? row.keyId : void 0;
|
|
659
|
+
if (rowKeyId === currentKeyId) {
|
|
660
|
+
alreadyCurrent += 1;
|
|
661
|
+
continue;
|
|
662
|
+
}
|
|
663
|
+
const value = this.decryptRow(ciphertext, iv, authTag, rowKeyId);
|
|
664
|
+
if (value === null) {
|
|
665
|
+
unreadable += 1;
|
|
666
|
+
continue;
|
|
667
|
+
}
|
|
668
|
+
if (rowKeyId === void 0 && this.decryptRow(ciphertext, iv, authTag, currentKeyId) !== null) {
|
|
669
|
+
alreadyCurrent += 1;
|
|
670
|
+
await this.adapter.update(CREDENTIAL_ENTITY_TYPE, id, { ...row, keyId: currentKeyId });
|
|
671
|
+
continue;
|
|
672
|
+
}
|
|
673
|
+
const fresh = this.encrypt(value);
|
|
674
|
+
await this.adapter.update(CREDENTIAL_ENTITY_TYPE, id, {
|
|
675
|
+
service,
|
|
676
|
+
envVar,
|
|
677
|
+
ciphertext: fresh.ciphertext,
|
|
678
|
+
iv: fresh.iv,
|
|
679
|
+
authTag: fresh.authTag,
|
|
680
|
+
keyId: fresh.keyId,
|
|
681
|
+
updatedAt: Date.now()
|
|
682
|
+
});
|
|
683
|
+
rotated += 1;
|
|
684
|
+
}
|
|
685
|
+
await this.warm();
|
|
686
|
+
this.notify();
|
|
687
|
+
return { rotated, alreadyCurrent, unreadable };
|
|
688
|
+
}
|
|
448
689
|
/** Delete one credential row; resolution falls back to env afterwards. */
|
|
449
690
|
async remove(envVar) {
|
|
450
691
|
if (!this.warmed) await this.warm();
|
|
@@ -478,6 +719,58 @@ function uninstallCredentialStore() {
|
|
|
478
719
|
function resolveCredentialRef(ref, env = process.env) {
|
|
479
720
|
return installedStore?.resolve(ref) ?? env[ref];
|
|
480
721
|
}
|
|
722
|
+
var CREDENTIALS_FILE_ENV = "ALMADAR_CREDENTIALS_FILE";
|
|
723
|
+
var DEFAULT_RELATIVE_PATH = join(".almadar", "dev-credentials.json");
|
|
724
|
+
var FileCredentialPersistence = class {
|
|
725
|
+
constructor(path) {
|
|
726
|
+
this.counter = 0;
|
|
727
|
+
this.path = path ?? process.env[CREDENTIALS_FILE_ENV] ?? join(process.cwd(), DEFAULT_RELATIVE_PATH);
|
|
728
|
+
}
|
|
729
|
+
get filePath() {
|
|
730
|
+
return this.path;
|
|
731
|
+
}
|
|
732
|
+
load() {
|
|
733
|
+
try {
|
|
734
|
+
const raw = readFileSync(this.path, "utf-8");
|
|
735
|
+
const rows = JSON.parse(raw);
|
|
736
|
+
const map = /* @__PURE__ */ new Map();
|
|
737
|
+
for (const row of rows) {
|
|
738
|
+
if (typeof row.id === "string") map.set(row.id, row);
|
|
739
|
+
}
|
|
740
|
+
return map;
|
|
741
|
+
} catch {
|
|
742
|
+
return /* @__PURE__ */ new Map();
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
save(rows) {
|
|
746
|
+
mkdirSync(dirname(this.path), { recursive: true });
|
|
747
|
+
const tmp = `${this.path}.tmp`;
|
|
748
|
+
writeFileSync(tmp, JSON.stringify([...rows.values()], null, 2), "utf-8");
|
|
749
|
+
renameSync(tmp, this.path);
|
|
750
|
+
}
|
|
751
|
+
async create(entityType, data) {
|
|
752
|
+
const rows = this.load();
|
|
753
|
+
const id = `${entityType}-${Date.now()}-${++this.counter}`;
|
|
754
|
+
rows.set(id, { ...data, id });
|
|
755
|
+
this.save(rows);
|
|
756
|
+
return { id };
|
|
757
|
+
}
|
|
758
|
+
async update(_entityType, id, data) {
|
|
759
|
+
const rows = this.load();
|
|
760
|
+
const existing = rows.get(id);
|
|
761
|
+
if (existing) {
|
|
762
|
+
rows.set(id, { ...existing, ...data, id });
|
|
763
|
+
this.save(rows);
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
async delete(_entityType, id) {
|
|
767
|
+
const rows = this.load();
|
|
768
|
+
if (rows.delete(id)) this.save(rows);
|
|
769
|
+
}
|
|
770
|
+
async list(_entityType) {
|
|
771
|
+
return [...this.load().values()];
|
|
772
|
+
}
|
|
773
|
+
};
|
|
481
774
|
var STRIPE_API_VERSION = "2025-02-24.acacia";
|
|
482
775
|
function priceToTier(priceId, prices) {
|
|
483
776
|
if (priceId === prices.solo) return "solo";
|
|
@@ -1043,10 +1336,10 @@ var TwilioIntegration = class extends BaseIntegration {
|
|
|
1043
1336
|
}
|
|
1044
1337
|
}
|
|
1045
1338
|
async sendSMS(params) {
|
|
1046
|
-
const { to, body } = params;
|
|
1339
|
+
const { to, body, from } = params;
|
|
1047
1340
|
this.logger.debug("Sending SMS", { to: String(to ?? "") });
|
|
1048
1341
|
const message = await this.client.messages.create({
|
|
1049
|
-
from: this.phoneNumber,
|
|
1342
|
+
from: from || this.phoneNumber,
|
|
1050
1343
|
to,
|
|
1051
1344
|
body
|
|
1052
1345
|
});
|
|
@@ -1125,31 +1418,35 @@ var EmailIntegration = class extends BaseIntegration {
|
|
|
1125
1418
|
}
|
|
1126
1419
|
}
|
|
1127
1420
|
async send(params) {
|
|
1128
|
-
const { to, subject, body, from } = params;
|
|
1421
|
+
const { to, subject, body, from, htmlBody, replyTo, templateId } = params;
|
|
1129
1422
|
this.logger.debug("Sending email", { to: String(to), subject: String(subject), provider: this.provider });
|
|
1423
|
+
const message = {
|
|
1424
|
+
to,
|
|
1425
|
+
subject,
|
|
1426
|
+
body,
|
|
1427
|
+
from: from || this.fromEmail,
|
|
1428
|
+
htmlBody: htmlBody || void 0,
|
|
1429
|
+
replyTo: replyTo || void 0,
|
|
1430
|
+
templateId: templateId || void 0
|
|
1431
|
+
};
|
|
1130
1432
|
if (this.provider === "sendgrid") {
|
|
1131
|
-
return await this.sendViaSendGrid(
|
|
1132
|
-
to,
|
|
1133
|
-
subject,
|
|
1134
|
-
body,
|
|
1135
|
-
from || this.fromEmail
|
|
1136
|
-
);
|
|
1433
|
+
return await this.sendViaSendGrid(message);
|
|
1137
1434
|
} else if (this.provider === "resend") {
|
|
1138
|
-
return await this.sendViaResend(
|
|
1139
|
-
to,
|
|
1140
|
-
subject,
|
|
1141
|
-
body,
|
|
1142
|
-
from || this.fromEmail
|
|
1143
|
-
);
|
|
1435
|
+
return await this.sendViaResend(message);
|
|
1144
1436
|
}
|
|
1145
1437
|
throw new Error(`Unknown email provider: ${this.provider}`);
|
|
1146
1438
|
}
|
|
1147
|
-
async sendViaSendGrid(
|
|
1439
|
+
async sendViaSendGrid(message) {
|
|
1148
1440
|
const msg = {
|
|
1149
|
-
to,
|
|
1150
|
-
from,
|
|
1151
|
-
subject,
|
|
1152
|
-
|
|
1441
|
+
to: message.to,
|
|
1442
|
+
from: message.from,
|
|
1443
|
+
subject: message.subject,
|
|
1444
|
+
// htmlBody present → it carries the HTML and body becomes the
|
|
1445
|
+
// plain-text alternative; absent → body renders as HTML (legacy).
|
|
1446
|
+
html: message.htmlBody ?? message.body,
|
|
1447
|
+
...message.htmlBody ? { text: message.body } : {},
|
|
1448
|
+
...message.replyTo ? { replyTo: message.replyTo } : {},
|
|
1449
|
+
...message.templateId ? { templateId: message.templateId } : {}
|
|
1153
1450
|
};
|
|
1154
1451
|
const response = await sgMail.send(msg);
|
|
1155
1452
|
return {
|
|
@@ -1157,15 +1454,20 @@ var EmailIntegration = class extends BaseIntegration {
|
|
|
1157
1454
|
status: "sent"
|
|
1158
1455
|
};
|
|
1159
1456
|
}
|
|
1160
|
-
async sendViaResend(
|
|
1457
|
+
async sendViaResend(message) {
|
|
1161
1458
|
if (!this.resendClient) {
|
|
1162
1459
|
throw new Error("Resend client not initialized");
|
|
1163
1460
|
}
|
|
1461
|
+
if (message.templateId) {
|
|
1462
|
+
throw new Error("templateId is not supported by the resend provider \u2014 use sendgrid or drop templateId");
|
|
1463
|
+
}
|
|
1164
1464
|
const response = await this.resendClient.emails.send({
|
|
1165
|
-
from,
|
|
1166
|
-
to,
|
|
1167
|
-
subject,
|
|
1168
|
-
html: body
|
|
1465
|
+
from: message.from,
|
|
1466
|
+
to: message.to,
|
|
1467
|
+
subject: message.subject,
|
|
1468
|
+
html: message.htmlBody ?? message.body,
|
|
1469
|
+
...message.htmlBody ? { text: message.body } : {},
|
|
1470
|
+
...message.replyTo ? { replyTo: message.replyTo } : {}
|
|
1169
1471
|
});
|
|
1170
1472
|
return {
|
|
1171
1473
|
id: response.data?.id,
|
|
@@ -1595,6 +1897,27 @@ var CalendarIntegration = class extends BaseIntegration {
|
|
|
1595
1897
|
}
|
|
1596
1898
|
};
|
|
1597
1899
|
registerIntegration("calendar", CalendarIntegration);
|
|
1900
|
+
|
|
1901
|
+
// src/hooks.ts
|
|
1902
|
+
var HOOK_PROVIDER_FACTORIES = {
|
|
1903
|
+
googleCalendarHookProvider
|
|
1904
|
+
};
|
|
1905
|
+
function allHookDeclarations() {
|
|
1906
|
+
return Object.values(serviceHooks).flat();
|
|
1907
|
+
}
|
|
1908
|
+
function registeredHookProviders(env = process.env) {
|
|
1909
|
+
const providers = {};
|
|
1910
|
+
for (const decl of allHookDeclarations()) {
|
|
1911
|
+
const factory = HOOK_PROVIDER_FACTORIES[decl.providerExport];
|
|
1912
|
+
if (!factory) {
|
|
1913
|
+
throw new Error(
|
|
1914
|
+
`serviceHooks declares provider export '${decl.providerExport}' but HOOK_PROVIDER_FACTORIES has no entry for it`
|
|
1915
|
+
);
|
|
1916
|
+
}
|
|
1917
|
+
providers[decl.provider] = factory(env[decl.credentialEnv]);
|
|
1918
|
+
}
|
|
1919
|
+
return providers;
|
|
1920
|
+
}
|
|
1598
1921
|
function decodeContent(content) {
|
|
1599
1922
|
const dataUrlMatch = /^data:([^;,]+)?(;base64)?,(.*)$/s.exec(content);
|
|
1600
1923
|
if (dataUrlMatch) {
|
|
@@ -1608,20 +1931,53 @@ var DriveIntegration = class extends BaseIntegration {
|
|
|
1608
1931
|
constructor(config) {
|
|
1609
1932
|
super(config);
|
|
1610
1933
|
const rawKey = config.env.GOOGLE_DRIVE_SA_KEY;
|
|
1611
|
-
if (
|
|
1612
|
-
|
|
1934
|
+
if (rawKey) {
|
|
1935
|
+
const keyJson = rawKey.trim().startsWith("{") ? rawKey : Buffer.from(rawKey, "base64").toString("utf8");
|
|
1936
|
+
const key = JSON.parse(keyJson);
|
|
1937
|
+
const subject = config.env.GOOGLE_DRIVE_SUBJECT || void 0;
|
|
1938
|
+
const auth = new google.auth.JWT({
|
|
1939
|
+
email: key.client_email,
|
|
1940
|
+
key: key.private_key,
|
|
1941
|
+
scopes: ["https://www.googleapis.com/auth/drive"],
|
|
1942
|
+
subject
|
|
1943
|
+
});
|
|
1944
|
+
this.saClient = google.drive({ version: "v3", auth });
|
|
1945
|
+
} else {
|
|
1946
|
+
this.saClient = null;
|
|
1947
|
+
}
|
|
1948
|
+
const refreshToken = config.env.GOOGLE_DRIVE_REFRESH_TOKEN;
|
|
1949
|
+
const clientId = config.env.OAUTH_CLIENT_ID;
|
|
1950
|
+
const clientSecret = config.env.OAUTH_CLIENT_SECRET;
|
|
1951
|
+
if (refreshToken && clientId && clientSecret) {
|
|
1952
|
+
const oauth2 = new google.auth.OAuth2(clientId, clientSecret);
|
|
1953
|
+
oauth2.setCredentials({ refresh_token: refreshToken });
|
|
1954
|
+
this.userClient = google.drive({ version: "v3", auth: oauth2 });
|
|
1955
|
+
} else {
|
|
1956
|
+
this.userClient = null;
|
|
1613
1957
|
}
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1958
|
+
if (!this.saClient && !this.userClient) {
|
|
1959
|
+
throw new Error(
|
|
1960
|
+
"Drive not configured \u2014 set GOOGLE_DRIVE_SA_KEY (reads) and/or GOOGLE_DRIVE_REFRESH_TOKEN + OAUTH_CLIENT_ID/SECRET (writes)"
|
|
1961
|
+
);
|
|
1962
|
+
}
|
|
1963
|
+
this.defaultFolderId = config.env.GOOGLE_DRIVE_FOLDER_ID || void 0;
|
|
1964
|
+
this.logger.info("Drive integration initialized", {
|
|
1965
|
+
serviceAccount: this.saClient !== null,
|
|
1966
|
+
userToken: this.userClient !== null,
|
|
1967
|
+
delegated: Boolean(config.env.GOOGLE_DRIVE_SUBJECT)
|
|
1622
1968
|
});
|
|
1623
|
-
|
|
1624
|
-
|
|
1969
|
+
}
|
|
1970
|
+
/** Reads prefer the SA client (delegation-aware); user client covers its absence. */
|
|
1971
|
+
readClient() {
|
|
1972
|
+
const client = this.saClient ?? this.userClient;
|
|
1973
|
+
if (!client) throw new Error("Drive not configured");
|
|
1974
|
+
return client;
|
|
1975
|
+
}
|
|
1976
|
+
/** Writes REQUIRE the user client on personal accounts (SA has no storage quota); SA only as a Workspace fallback. */
|
|
1977
|
+
writeClient() {
|
|
1978
|
+
const client = this.userClient ?? this.saClient;
|
|
1979
|
+
if (!client) throw new Error("Drive not configured");
|
|
1980
|
+
return client;
|
|
1625
1981
|
}
|
|
1626
1982
|
async execute(action, params) {
|
|
1627
1983
|
const validation = this.validateParams(action, params);
|
|
@@ -1673,7 +2029,7 @@ var DriveIntegration = class extends BaseIntegration {
|
|
|
1673
2029
|
const clauses = ["trashed = false"];
|
|
1674
2030
|
if (folderId) clauses.push(`'${String(folderId).replace(/'/g, "\\'")}' in parents`);
|
|
1675
2031
|
if (query) clauses.push(String(query));
|
|
1676
|
-
const response = await this.
|
|
2032
|
+
const response = await this.readClient().files.list({
|
|
1677
2033
|
q: clauses.join(" and "),
|
|
1678
2034
|
pageSize: maxResults || 100,
|
|
1679
2035
|
fields: "files(id, name, mimeType, size, modifiedTime, webViewLink)"
|
|
@@ -1691,11 +2047,11 @@ var DriveIntegration = class extends BaseIntegration {
|
|
|
1691
2047
|
}
|
|
1692
2048
|
async getFile(params) {
|
|
1693
2049
|
const fileId = params.fileId;
|
|
1694
|
-
const meta = await this.
|
|
2050
|
+
const meta = await this.readClient().files.get({
|
|
1695
2051
|
fileId,
|
|
1696
2052
|
fields: "id, name, mimeType, size"
|
|
1697
2053
|
});
|
|
1698
|
-
const content = await this.
|
|
2054
|
+
const content = await this.readClient().files.get(
|
|
1699
2055
|
{ fileId, alt: "media" },
|
|
1700
2056
|
{ responseType: "arraybuffer" }
|
|
1701
2057
|
);
|
|
@@ -1711,10 +2067,11 @@ var DriveIntegration = class extends BaseIntegration {
|
|
|
1711
2067
|
async uploadFile(params) {
|
|
1712
2068
|
const { name, content, mimeType, folderId } = params;
|
|
1713
2069
|
const { bytes, contentType } = decodeContent(content);
|
|
1714
|
-
const
|
|
2070
|
+
const parent = folderId || this.defaultFolderId;
|
|
2071
|
+
const response = await this.writeClient().files.create({
|
|
1715
2072
|
requestBody: {
|
|
1716
2073
|
name,
|
|
1717
|
-
parents:
|
|
2074
|
+
parents: parent ? [parent] : void 0
|
|
1718
2075
|
},
|
|
1719
2076
|
media: {
|
|
1720
2077
|
mimeType: mimeType || contentType || "application/octet-stream",
|
|
@@ -1730,11 +2087,12 @@ var DriveIntegration = class extends BaseIntegration {
|
|
|
1730
2087
|
}
|
|
1731
2088
|
async createFolder(params) {
|
|
1732
2089
|
const { name, parentId } = params;
|
|
1733
|
-
const
|
|
2090
|
+
const parent = parentId || this.defaultFolderId;
|
|
2091
|
+
const response = await this.writeClient().files.create({
|
|
1734
2092
|
requestBody: {
|
|
1735
2093
|
name,
|
|
1736
2094
|
mimeType: "application/vnd.google-apps.folder",
|
|
1737
|
-
parents:
|
|
2095
|
+
parents: parent ? [parent] : void 0
|
|
1738
2096
|
},
|
|
1739
2097
|
fields: "id, name"
|
|
1740
2098
|
});
|
|
@@ -1742,7 +2100,7 @@ var DriveIntegration = class extends BaseIntegration {
|
|
|
1742
2100
|
}
|
|
1743
2101
|
async shareFile(params) {
|
|
1744
2102
|
const { fileId, email, role } = params;
|
|
1745
|
-
const response = await this.
|
|
2103
|
+
const response = await this.readClient().permissions.create({
|
|
1746
2104
|
fileId,
|
|
1747
2105
|
requestBody: {
|
|
1748
2106
|
type: "user",
|
|
@@ -3756,6 +4114,31 @@ var PROVIDER_AUTH_URLS = {
|
|
|
3756
4114
|
var PROVIDER_ISSUERS = {
|
|
3757
4115
|
google: "https://accounts.google.com"
|
|
3758
4116
|
};
|
|
4117
|
+
var PENDING_GRANT_TTL_MS = 10 * 60 * 1e3;
|
|
4118
|
+
var InMemoryPendingGrantStore = class {
|
|
4119
|
+
constructor() {
|
|
4120
|
+
this.grants = /* @__PURE__ */ new Map();
|
|
4121
|
+
}
|
|
4122
|
+
async put(state, grant, ttlMs) {
|
|
4123
|
+
this.grants.set(state, { grant, expiresAt: Date.now() + ttlMs });
|
|
4124
|
+
}
|
|
4125
|
+
async take(state) {
|
|
4126
|
+
const entry = this.grants.get(state);
|
|
4127
|
+
if (!entry) return null;
|
|
4128
|
+
this.grants.delete(state);
|
|
4129
|
+
return entry.expiresAt >= Date.now() ? entry.grant : null;
|
|
4130
|
+
}
|
|
4131
|
+
async sweep() {
|
|
4132
|
+
const now = Date.now();
|
|
4133
|
+
for (const [state, entry] of this.grants) {
|
|
4134
|
+
if (entry.expiresAt < now) this.grants.delete(state);
|
|
4135
|
+
}
|
|
4136
|
+
}
|
|
4137
|
+
};
|
|
4138
|
+
var installedPendingGrantStore = null;
|
|
4139
|
+
function installPendingGrantStore(store) {
|
|
4140
|
+
installedPendingGrantStore = store;
|
|
4141
|
+
}
|
|
3759
4142
|
var OAuthIntegration = class extends BaseIntegration {
|
|
3760
4143
|
constructor(config) {
|
|
3761
4144
|
super(config);
|
|
@@ -3767,8 +4150,8 @@ var OAuthIntegration = class extends BaseIntegration {
|
|
|
3767
4150
|
this.refreshIndex = /* @__PURE__ */ new Map();
|
|
3768
4151
|
/** Maps access token -> mock user session */
|
|
3769
4152
|
this.sessions = /* @__PURE__ */ new Map();
|
|
3770
|
-
/**
|
|
3771
|
-
this.
|
|
4153
|
+
/** Pending OIDC authorizations (real backend) — injectable, in-memory default. */
|
|
4154
|
+
this.fallbackPending = new InMemoryPendingGrantStore();
|
|
3772
4155
|
/** Maps access token -> ID-token subject, for userinfo subject checks */
|
|
3773
4156
|
this.subjects = /* @__PURE__ */ new Map();
|
|
3774
4157
|
/** Discovered issuer configurations, keyed by issuer URL */
|
|
@@ -3778,6 +4161,9 @@ var OAuthIntegration = class extends BaseIntegration {
|
|
|
3778
4161
|
this.real ? "OAuth integration initialized (OIDC backend via openid-client)" : "OAuth integration initialized (mock backend)"
|
|
3779
4162
|
);
|
|
3780
4163
|
}
|
|
4164
|
+
pendingStore() {
|
|
4165
|
+
return installedPendingGrantStore ?? this.fallbackPending;
|
|
4166
|
+
}
|
|
3781
4167
|
async execute(action, params) {
|
|
3782
4168
|
const validation = this.validateParams(action, params);
|
|
3783
4169
|
if (!validation.valid) {
|
|
@@ -3869,17 +4255,17 @@ var OAuthIntegration = class extends BaseIntegration {
|
|
|
3869
4255
|
parameters.prompt = "consent";
|
|
3870
4256
|
}
|
|
3871
4257
|
const authUrl = oidc.buildAuthorizationUrl(configuration, parameters);
|
|
3872
|
-
this.
|
|
4258
|
+
await this.pendingStore().put(state, { provider, redirectUri, pkceVerifier }, PENDING_GRANT_TTL_MS);
|
|
4259
|
+
void this.pendingStore().sweep();
|
|
3873
4260
|
return { authUrl: authUrl.toString(), state };
|
|
3874
4261
|
}
|
|
3875
4262
|
async oidcToken(params) {
|
|
3876
4263
|
const code = params.code;
|
|
3877
4264
|
const state = params.state;
|
|
3878
|
-
const pendingAuth = this.
|
|
4265
|
+
const pendingAuth = await this.pendingStore().take(state);
|
|
3879
4266
|
if (!pendingAuth) {
|
|
3880
4267
|
throw new Error(`Invalid or expired state token: ${state}`);
|
|
3881
4268
|
}
|
|
3882
|
-
this.pending.delete(state);
|
|
3883
4269
|
const configuration = await this.configurationFor(pendingAuth.provider);
|
|
3884
4270
|
const callbackUrl = new URL(pendingAuth.redirectUri);
|
|
3885
4271
|
callbackUrl.searchParams.set("code", code);
|
|
@@ -4066,131 +4452,10 @@ var OAuthIntegration = class extends BaseIntegration {
|
|
|
4066
4452
|
};
|
|
4067
4453
|
registerIntegration("oauth", OAuthIntegration);
|
|
4068
4454
|
|
|
4069
|
-
// src/contracts.ts
|
|
4070
|
-
var serviceCredentials = {
|
|
4071
|
-
stripe: [
|
|
4072
|
-
{ envVar: "STRIPE_SECRET_KEY", required: true, description: "Stripe secret API key" },
|
|
4073
|
-
{ envVar: "STRIPE_WEBHOOK_SECRET", required: false, description: "Webhook endpoint signing secret" }
|
|
4074
|
-
],
|
|
4075
|
-
youtube: [
|
|
4076
|
-
{ envVar: "YOUTUBE_API_KEY", required: true, description: "YouTube Data API key" }
|
|
4077
|
-
],
|
|
4078
|
-
twilio: [
|
|
4079
|
-
{ envVar: "TWILIO_ACCOUNT_SID", required: true, description: "Twilio account SID" },
|
|
4080
|
-
{ envVar: "TWILIO_AUTH_TOKEN", required: true, description: "Twilio auth token" },
|
|
4081
|
-
{ envVar: "TWILIO_PHONE_NUMBER", required: false, description: "Default sender phone number" }
|
|
4082
|
-
],
|
|
4083
|
-
email: [
|
|
4084
|
-
{ envVar: "SENDGRID_API_KEY", required: false, description: "SendGrid API key (use this OR RESEND_API_KEY)" },
|
|
4085
|
-
{ envVar: "RESEND_API_KEY", required: false, description: "Resend API key (use this OR SENDGRID_API_KEY)" },
|
|
4086
|
-
{ envVar: "FROM_EMAIL", required: false, description: "Default sender email address" }
|
|
4087
|
-
],
|
|
4088
|
-
webhook: [
|
|
4089
|
-
{ envVar: "WEBHOOK_SIGNING_SECRET", required: false, description: "Default HMAC-SHA256 signing secret (per-call secret overrides)" },
|
|
4090
|
-
{ envVar: "WEBHOOK_TIMEOUT_MS", required: false, description: "Per-request timeout (ms, default 10000)" }
|
|
4091
|
-
],
|
|
4092
|
-
push: [
|
|
4093
|
-
{ envVar: "VAPID_PUBLIC_KEY", required: true, description: "VAPID public key (web-push generate-vapid-keys)" },
|
|
4094
|
-
{ envVar: "VAPID_PRIVATE_KEY", required: true, description: "VAPID private key" },
|
|
4095
|
-
{ envVar: "VAPID_SUBJECT", required: true, description: "VAPID subject (mailto: or https: contact URI)" }
|
|
4096
|
-
],
|
|
4097
|
-
calendar: [
|
|
4098
|
-
{ envVar: "GOOGLE_CALENDAR_SA_KEY", required: true, description: "Google service-account key JSON (raw or base64) with calendar scope; store in Secret Manager, bind as env" },
|
|
4099
|
-
{ envVar: "GOOGLE_CALENDAR_SUBJECT", required: false, description: "Workspace user to impersonate (domain-wide delegation); empty = act as the service account" },
|
|
4100
|
-
{ envVar: "GOOGLE_CALENDAR_ID", required: false, description: "Default calendar id (default: primary)" }
|
|
4101
|
-
],
|
|
4102
|
-
drive: [
|
|
4103
|
-
{ envVar: "GOOGLE_DRIVE_SA_KEY", required: true, description: "Google service-account key JSON (raw or base64) with drive scope; store in Secret Manager, bind as env" },
|
|
4104
|
-
{ envVar: "GOOGLE_DRIVE_SUBJECT", required: false, description: "Workspace user to impersonate (domain-wide delegation); empty = act as the service account" }
|
|
4105
|
-
],
|
|
4106
|
-
metaAds: [
|
|
4107
|
-
{ envVar: "META_ACCESS_TOKEN", required: true, description: "Meta Graph API access token (Marketing API, ads_read)" },
|
|
4108
|
-
{ envVar: "META_AD_ACCOUNT_ID", required: false, description: "Default ad account id (act_\u2026); per-call accountId overrides" }
|
|
4109
|
-
],
|
|
4110
|
-
accounting: [],
|
|
4111
|
-
banking: [
|
|
4112
|
-
{ envVar: "GOCARDLESS_SECRET_ID", required: true, description: "GoCardless Bank Account Data secret id" },
|
|
4113
|
-
{ envVar: "GOCARDLESS_SECRET_KEY", required: true, description: "GoCardless Bank Account Data secret key" }
|
|
4114
|
-
],
|
|
4115
|
-
esign: [
|
|
4116
|
-
{ envVar: "DOCUSIGN_BASE_URL", required: true, description: "DocuSign REST base (e.g. https://demo.docusign.net/restapi/v2.1/accounts/<accountId>)" },
|
|
4117
|
-
{ envVar: "DOCUSIGN_ACCESS_TOKEN", required: true, description: "DocuSign OAuth access token (JWT grant rotation is the deployment concern)" }
|
|
4118
|
-
],
|
|
4119
|
-
llm: [
|
|
4120
|
-
{ envVar: "ANTHROPIC_API_KEY", required: false, description: "Anthropic API key (use this OR OPENAI_API_KEY)" },
|
|
4121
|
-
{ envVar: "OPENAI_API_KEY", required: false, description: "OpenAI API key (use this OR ANTHROPIC_API_KEY)" }
|
|
4122
|
-
],
|
|
4123
|
-
"llm-integration": [
|
|
4124
|
-
{ envVar: "ANTHROPIC_API_KEY", required: false, description: "Anthropic API key" },
|
|
4125
|
-
{ envVar: "OPENAI_API_KEY", required: false, description: "OpenAI API key" }
|
|
4126
|
-
],
|
|
4127
|
-
ml: [
|
|
4128
|
-
{ envVar: "MASAR_URL", required: true, description: "Base URL of the deployed model-serving orbital" },
|
|
4129
|
-
{ envVar: "MASAR_ML_TRAIT", required: true, description: "Kebab-case trait name the serving orbital exposes its /events route under" }
|
|
4130
|
-
],
|
|
4131
|
-
deepagent: [
|
|
4132
|
-
{ envVar: "DEEPAGENT_API_URL", required: true, description: "DeepAgent server URL" },
|
|
4133
|
-
{ envVar: "DEEPAGENT_API_KEY", required: false, description: "DeepAgent API key" }
|
|
4134
|
-
],
|
|
4135
|
-
github: [
|
|
4136
|
-
{ envVar: "GITHUB_TOKEN", required: true, description: "GitHub personal access token" },
|
|
4137
|
-
{ envVar: "GITHUB_OWNER", required: false, description: "Default repository owner" },
|
|
4138
|
-
{ envVar: "GITHUB_REPO", required: false, description: "Default repository name" }
|
|
4139
|
-
],
|
|
4140
|
-
docker: [
|
|
4141
|
-
{ envVar: "DOCKER_HOST", required: false, description: "Docker daemon host URL" }
|
|
4142
|
-
],
|
|
4143
|
-
storage: [
|
|
4144
|
-
{ envVar: "STORAGE_ACCESS_KEY_ID", required: true, description: "S3-compatible access key id" },
|
|
4145
|
-
{ envVar: "STORAGE_SECRET_ACCESS_KEY", required: true, description: "S3-compatible secret access key" },
|
|
4146
|
-
{ envVar: "STORAGE_BUCKET", required: true, description: "Default bucket name" },
|
|
4147
|
-
{ envVar: "STORAGE_REGION", required: false, description: "Region (default us-east-1)" },
|
|
4148
|
-
{ envVar: "STORAGE_ENDPOINT", required: false, description: "Custom S3-compatible endpoint (R2/MinIO/\u2026); empty = AWS S3" },
|
|
4149
|
-
{ envVar: "STORAGE_PUBLIC_URL_BASE", required: false, description: "Base URL for public-acl object links; empty = endpoint-derived" }
|
|
4150
|
-
],
|
|
4151
|
-
queue: [
|
|
4152
|
-
{ envVar: "QUEUE_URL", required: true, description: "Message queue connection URL" }
|
|
4153
|
-
],
|
|
4154
|
-
redis: [
|
|
4155
|
-
{ envVar: "REDIS_URL", required: true, description: "Redis connection URL" }
|
|
4156
|
-
],
|
|
4157
|
-
oauth: [
|
|
4158
|
-
{ envVar: "OAUTH_CLIENT_ID", required: true, description: "OIDC client ID" },
|
|
4159
|
-
{ envVar: "OAUTH_CLIENT_SECRET", required: true, description: "OIDC client secret" },
|
|
4160
|
-
{ envVar: "OAUTH_REDIRECT_URI", required: false, description: "OIDC redirect URI (default per-call param)" },
|
|
4161
|
-
{ envVar: "OIDC_ISSUER_URL", required: false, description: "OIDC issuer URL (default https://accounts.google.com)" }
|
|
4162
|
-
],
|
|
4163
|
-
credentials: [
|
|
4164
|
-
{ envVar: "ALMADAR_CREDENTIAL_MASTER_KEY", required: false, description: "AES-256 master key (64-char hex) enabling the hosted credential store \u2014 hold it alone in the platform secret store" }
|
|
4165
|
-
],
|
|
4166
|
-
otel: [
|
|
4167
|
-
{ envVar: "OTEL_EXPORTER_OTLP_ENDPOINT", required: true, description: "OpenTelemetry collector endpoint" },
|
|
4168
|
-
{ envVar: "OTEL_SERVICE_NAME", required: false, description: "Service name for traces" }
|
|
4169
|
-
],
|
|
4170
|
-
cli: [],
|
|
4171
|
-
// No fixed env vars — connection strings are resolved per-query from the
|
|
4172
|
-
// caller-supplied connectionRef, so credentials cannot be declared statically.
|
|
4173
|
-
database: [],
|
|
4174
|
-
wikimedia: [
|
|
4175
|
-
{ envVar: "WIKIMEDIA_USER_AGENT", required: false, description: "Descriptive User-Agent for the Wikipedia API" },
|
|
4176
|
-
{ envVar: "WIKIMEDIA_TIMEOUT_MS", required: false, description: "Per-request timeout (ms)" }
|
|
4177
|
-
],
|
|
4178
|
-
iconify: [
|
|
4179
|
-
{ envVar: "ICONIFY_USER_AGENT", required: false, description: "User-Agent for the Iconify API" },
|
|
4180
|
-
{ envVar: "ICONIFY_TIMEOUT_MS", required: false, description: "Per-request timeout (ms)" }
|
|
4181
|
-
],
|
|
4182
|
-
arxiv: [
|
|
4183
|
-
{ envVar: "ARXIV_TIMEOUT_MS", required: false, description: "Per-request timeout (ms)" }
|
|
4184
|
-
]
|
|
4185
|
-
};
|
|
4186
|
-
var serviceProbes = {
|
|
4187
|
-
calendar: { action: "listEvents", params: { maxResults: 1 } },
|
|
4188
|
-
drive: { action: "listFiles", params: { maxResults: 1 } },
|
|
4189
|
-
metaAds: { action: "listCampaigns", params: {} }
|
|
4190
|
-
};
|
|
4191
|
-
|
|
4192
4455
|
// src/integrations/credentials/index.ts
|
|
4193
4456
|
var ENV_VAR_NAME = /^[A-Z][A-Z0-9_]*$/;
|
|
4457
|
+
var ROLE_GATED_ACTIONS = /* @__PURE__ */ new Set(["set", "remove", "test", "rotate"]);
|
|
4458
|
+
var DEFAULT_ADMIN_ROLES = ["admin", "owner"];
|
|
4194
4459
|
function isProbeService(service) {
|
|
4195
4460
|
return service in serviceProbes;
|
|
4196
4461
|
}
|
|
@@ -4199,7 +4464,20 @@ var CredentialsIntegration = class extends BaseIntegration {
|
|
|
4199
4464
|
super(config);
|
|
4200
4465
|
this.logger.info("Credentials integration initialized (tenant credential store surface)");
|
|
4201
4466
|
}
|
|
4202
|
-
async execute(action, params) {
|
|
4467
|
+
async execute(action, params, context) {
|
|
4468
|
+
if (ROLE_GATED_ACTIONS.has(action)) {
|
|
4469
|
+
const allowed = this.adminRoles();
|
|
4470
|
+
if (!context?.role || !allowed.includes(context.role)) {
|
|
4471
|
+
return {
|
|
4472
|
+
success: false,
|
|
4473
|
+
error: new IntegrationError(
|
|
4474
|
+
`forbidden: "${action}" requires one of roles: ${allowed.join(", ")}`,
|
|
4475
|
+
"AUTH_ERROR"
|
|
4476
|
+
),
|
|
4477
|
+
metadata: this.createMetadata(action, 0)
|
|
4478
|
+
};
|
|
4479
|
+
}
|
|
4480
|
+
}
|
|
4203
4481
|
const validation = this.validateParams(action, params);
|
|
4204
4482
|
if (!validation.valid) {
|
|
4205
4483
|
return {
|
|
@@ -4227,8 +4505,16 @@ var CredentialsIntegration = class extends BaseIntegration {
|
|
|
4227
4505
|
data = await this.remove(params.service, params.envVar);
|
|
4228
4506
|
break;
|
|
4229
4507
|
case "test":
|
|
4230
|
-
data = await this.test(params.service);
|
|
4508
|
+
data = await this.test(params.service, context);
|
|
4509
|
+
break;
|
|
4510
|
+
case "rotate": {
|
|
4511
|
+
const store = getInstalledCredentialStore();
|
|
4512
|
+
if (!store) {
|
|
4513
|
+
throw new Error("No credential store is installed on this host");
|
|
4514
|
+
}
|
|
4515
|
+
data = await store.rotate();
|
|
4231
4516
|
break;
|
|
4517
|
+
}
|
|
4232
4518
|
default:
|
|
4233
4519
|
throw new Error(`Unknown action: ${action}`);
|
|
4234
4520
|
}
|
|
@@ -4241,6 +4527,11 @@ var CredentialsIntegration = class extends BaseIntegration {
|
|
|
4241
4527
|
return this.handleError(action, error);
|
|
4242
4528
|
}
|
|
4243
4529
|
}
|
|
4530
|
+
adminRoles() {
|
|
4531
|
+
const raw = this.config.env["ALMADAR_CREDENTIAL_ADMIN_ROLES"] || process.env["ALMADAR_CREDENTIAL_ADMIN_ROLES"] || "";
|
|
4532
|
+
const roles = raw.split(",").map((r) => r.trim()).filter((r) => r.length > 0);
|
|
4533
|
+
return roles.length > 0 ? roles : DEFAULT_ADMIN_ROLES;
|
|
4534
|
+
}
|
|
4244
4535
|
declaredFor(service) {
|
|
4245
4536
|
return serviceCredentials[service] ?? [];
|
|
4246
4537
|
}
|
|
@@ -4260,6 +4551,10 @@ var CredentialsIntegration = class extends BaseIntegration {
|
|
|
4260
4551
|
throw new Error(`"${envVar}" is not a declared credential of "${service}" (declared: ${valid})`);
|
|
4261
4552
|
}
|
|
4262
4553
|
}
|
|
4554
|
+
storeFirst() {
|
|
4555
|
+
const flag = this.config.env["ALMADAR_CREDENTIALS_SOURCE"] || process.env["ALMADAR_CREDENTIALS_SOURCE"];
|
|
4556
|
+
return flag === "store";
|
|
4557
|
+
}
|
|
4263
4558
|
list(serviceFilter) {
|
|
4264
4559
|
const store = getInstalledCredentialStore();
|
|
4265
4560
|
const stored = new Map((store?.entries() ?? []).map((e) => [`${e.service}\0${e.envVar}`, e]));
|
|
@@ -4269,7 +4564,7 @@ var CredentialsIntegration = class extends BaseIntegration {
|
|
|
4269
4564
|
for (const { envVar, required, description } of declared) {
|
|
4270
4565
|
const fromStore = stored.get(`${service}\0${envVar}`);
|
|
4271
4566
|
stored.delete(`${service}\0${envVar}`);
|
|
4272
|
-
const fromEnv = process.env[envVar];
|
|
4567
|
+
const fromEnv = this.storeFirst() ? void 0 : process.env[envVar];
|
|
4273
4568
|
const source = fromStore ? "store" : fromEnv ? "env" : "none";
|
|
4274
4569
|
entries.push({
|
|
4275
4570
|
service,
|
|
@@ -4313,7 +4608,7 @@ var CredentialsIntegration = class extends BaseIntegration {
|
|
|
4313
4608
|
this.assertSettable(service, envVar);
|
|
4314
4609
|
return { removed: await store.remove(envVar) };
|
|
4315
4610
|
}
|
|
4316
|
-
async test(service) {
|
|
4611
|
+
async test(service, context) {
|
|
4317
4612
|
const declared = this.declaredFor(service);
|
|
4318
4613
|
const missing = declared.filter((c) => c.required && !resolveCredentialRef(c.envVar)).map((c) => c.envVar);
|
|
4319
4614
|
const configured = missing.length === 0;
|
|
@@ -4328,7 +4623,7 @@ var CredentialsIntegration = class extends BaseIntegration {
|
|
|
4328
4623
|
if (!factory.isConfigured(service)) {
|
|
4329
4624
|
return { service, configured, missing, probed: false, ok: false, message: "Credentials present but the service is not configured on this host \u2014 restart or re-save a credential" };
|
|
4330
4625
|
}
|
|
4331
|
-
const result = await factory.execute(service, probe.action, probe.params);
|
|
4626
|
+
const result = await factory.execute(service, probe.action, probe.params, context);
|
|
4332
4627
|
if (!result.success) {
|
|
4333
4628
|
return { service, configured, missing, probed: true, ok: false, message: result.error?.message ?? `Probe ${probe.action} failed` };
|
|
4334
4629
|
}
|
|
@@ -4558,10 +4853,16 @@ var StorageIntegration = class extends BaseIntegration {
|
|
|
4558
4853
|
const bucket = this.bucketOf(params);
|
|
4559
4854
|
const prefix = params.prefix ?? "";
|
|
4560
4855
|
const maxKeys = params.maxKeys ?? 1e3;
|
|
4856
|
+
const continuationToken = params.continuationToken;
|
|
4561
4857
|
this.logger.debug("Storage LIST", { bucket, prefix, maxKeys });
|
|
4562
4858
|
if (this.s3) {
|
|
4563
4859
|
const response = await this.s3.send(
|
|
4564
|
-
new ListObjectsV2Command({
|
|
4860
|
+
new ListObjectsV2Command({
|
|
4861
|
+
Bucket: bucket,
|
|
4862
|
+
Prefix: prefix || void 0,
|
|
4863
|
+
MaxKeys: maxKeys,
|
|
4864
|
+
ContinuationToken: continuationToken
|
|
4865
|
+
})
|
|
4565
4866
|
);
|
|
4566
4867
|
return {
|
|
4567
4868
|
keys: (response.Contents ?? []).map((entry) => ({
|
|
@@ -4569,9 +4870,8 @@ var StorageIntegration = class extends BaseIntegration {
|
|
|
4569
4870
|
size: entry.Size ?? 0,
|
|
4570
4871
|
lastModified: entry.LastModified?.getTime() ?? 0
|
|
4571
4872
|
})),
|
|
4572
|
-
|
|
4573
|
-
|
|
4574
|
-
truncated: Boolean(response.IsTruncated)
|
|
4873
|
+
truncated: Boolean(response.IsTruncated),
|
|
4874
|
+
...response.IsTruncated && response.NextContinuationToken !== void 0 ? { nextToken: response.NextContinuationToken } : {}
|
|
4575
4875
|
};
|
|
4576
4876
|
}
|
|
4577
4877
|
const bucketPrefix = `${bucket}/`;
|
|
@@ -4586,7 +4886,14 @@ var StorageIntegration = class extends BaseIntegration {
|
|
|
4586
4886
|
});
|
|
4587
4887
|
}
|
|
4588
4888
|
results.sort((a, b) => a.key.localeCompare(b.key));
|
|
4589
|
-
|
|
4889
|
+
const offset = continuationToken !== void 0 ? Number.parseInt(continuationToken, 10) || 0 : 0;
|
|
4890
|
+
const page = results.slice(offset, offset + maxKeys);
|
|
4891
|
+
const truncated = offset + maxKeys < results.length;
|
|
4892
|
+
return {
|
|
4893
|
+
keys: page,
|
|
4894
|
+
truncated,
|
|
4895
|
+
...truncated ? { nextToken: String(offset + maxKeys) } : {}
|
|
4896
|
+
};
|
|
4590
4897
|
}
|
|
4591
4898
|
async deleteObject(params) {
|
|
4592
4899
|
const bucket = this.bucketOf(params);
|
|
@@ -5379,6 +5686,6 @@ var ArxivIntegration = class extends BaseIntegration {
|
|
|
5379
5686
|
};
|
|
5380
5687
|
registerIntegration("arxiv", ArxivIntegration);
|
|
5381
5688
|
|
|
5382
|
-
export { AccountingIntegration, ArxivIntegration, BankingIntegration, BaseIntegration, CLIIntegration, CREDENTIAL_ENTITY_TYPE, CREDENTIAL_MASTER_KEY_ENV, CalendarIntegration, ConsoleLogger, CredentialStore, CredentialsIntegration, DatabaseIntegration, DeepAgentIntegration, DockerIntegration, DriveIntegration, EmailIntegration, EsignIntegration, GitHubIntegration, IconifyIntegration, IntegrationError, IntegrationFactory, LLMIntegration, MLIntegration, MetaAdsIntegration, OAuthIntegration, OtelIntegration, PushIntegration, QueueIntegration, RedisIntegration, StorageIntegration, StripeIntegration, TwilioIntegration, WebhookIntegration, WikimediaIntegration, YouTubeIntegration, assertReadOnlySelect, getActiveFactory, getInstalledCredentialStore, getIntegration, getIntegrationFactory, getRegisteredIntegrations, googleCalendarHookProvider, installActiveFactory, installCredentialStore, isKnownIntegration, parseCalendarPushNotification, registerIntegration, resetIntegrationFactory, resolveCredentialRef, uninstallCredentialStore, validateParams, verifyAndParseStripeEvent, withRetry };
|
|
5689
|
+
export { AccountingIntegration, ArxivIntegration, BankingIntegration, BaseIntegration, CLIIntegration, CREDENTIALS_FILE_ENV, CREDENTIAL_ENTITY_TYPE, CREDENTIAL_MASTER_KEY_ENV, CalendarIntegration, ConsoleLogger, CredentialStore, CredentialsIntegration, DatabaseIntegration, DeepAgentIntegration, DockerIntegration, DriveIntegration, EmailIntegration, EsignIntegration, FileCredentialPersistence, GitHubIntegration, IconifyIntegration, InMemoryPendingGrantStore, IntegrationError, IntegrationFactory, LLMIntegration, MLIntegration, MetaAdsIntegration, OAuthIntegration, OtelIntegration, PushIntegration, QueueIntegration, RedisIntegration, StorageIntegration, StripeIntegration, TwilioIntegration, WebhookIntegration, WikimediaIntegration, YouTubeIntegration, allHookDeclarations, assertReadOnlySelect, getActiveFactory, getInstalledCredentialStore, getIntegration, getIntegrationFactory, getRegisteredIntegrations, googleCalendarHookProvider, installActiveFactory, installCredentialStore, installPendingGrantStore, isKnownIntegration, parseCalendarPushNotification, registerIntegration, registeredHookProviders, resetIntegrationFactory, resolveCredentialRef, serviceHooks, uninstallCredentialStore, validateParams, verifyAndParseStripeEvent, withRetry };
|
|
5383
5690
|
//# sourceMappingURL=index.js.map
|
|
5384
5691
|
//# sourceMappingURL=index.js.map
|