feat: daily Notion → Bedrock KB sync #5
6 changed files with 635 additions and 13 deletions
48
README.md
48
README.md
|
|
@ -18,9 +18,14 @@ slack-processor Lambda ← calls Bedrock Agent, logs to DynamoDB, posts r
|
|||
│
|
||||
▼
|
||||
Bedrock Agent (Claude Sonnet 4.5)
|
||||
├── Knowledge Base (AOSS + S3) ← SA8000 docs, SOPs, employee handbook
|
||||
├── Knowledge Base (AOSS + S3) ← SA8000 docs, SOPs, employee handbook, Notion pages
|
||||
├── QBO_Lookup action group ← QuickBooks vendor search
|
||||
└── Google_Maps_Lookup action group ← fallback vendor search
|
||||
|
||||
EventBridge (daily 02:00 UTC)
|
||||
│
|
||||
▼
|
||||
notion-sync Lambda ← exports Office Operations Notion pages → S3 → KB ingestion
|
||||
```
|
||||
|
||||
### Vendor Query Priority
|
||||
|
|
@ -49,13 +54,14 @@ Bedrock Agent (Claude Sonnet 4.5)
|
|||
|
||||
## Secrets Manager
|
||||
|
||||
These three secrets must exist before deploying. Create them via the AWS Console or CLI:
|
||||
These secrets must exist before deploying. The Slack, QBO, and Maps secrets must be created manually before first deploy. The Notion secret is created automatically by CDK with a placeholder — update it after deploy.
|
||||
|
||||
| Secret Name | Structure |
|
||||
|---|---|
|
||||
| `seahaven/slack/credentials` | `{ "botToken": "xoxb-...", "signingSecret": "..." }` |
|
||||
| `seahaven/qbo/oauth` | `{ "clientId": "", "clientSecret": "", "refreshToken": "", "realmId": "" }` |
|
||||
| `seahaven/google/maps-api-key` | `{ "apiKey": "" }` |
|
||||
| Secret Name | Created | Structure |
|
||||
|---|---|---|
|
||||
| `seahaven/slack/credentials` | Manual (pre-deploy) | `{ "botToken": "xoxb-...", "signingSecret": "..." }` |
|
||||
| `seahaven/qbo/oauth` | Manual (pre-deploy) | `{ "clientId": "", "clientSecret": "", "refreshToken": "", "realmId": "" }` |
|
||||
| `seahaven/google/maps-api-key` | Manual (pre-deploy) | `{ "apiKey": "" }` |
|
||||
| `seahaven/notion/api-key` | Auto (CDK) | `{ "apiKey": "secret_..." }` |
|
||||
|
||||
## Deployment
|
||||
|
||||
|
|
@ -71,6 +77,7 @@ Stack outputs after deploy:
|
|||
- `KBDocsBucketName` — S3 bucket to upload knowledge base documents
|
||||
- `AgentId` — Bedrock Agent ID
|
||||
- `SlackWebhookUrl` — URL to register in Slack app settings
|
||||
- `NotionSecretName` — Secrets Manager secret to populate with your Notion integration token
|
||||
|
||||
## Post-Deployment Setup
|
||||
|
||||
|
|
@ -93,7 +100,27 @@ aws bedrock-agent start-ingestion-job \
|
|||
|
||||
Or trigger from the Bedrock console: **Knowledge Bases → seahaven-kb → Sync**.
|
||||
|
||||
### 3. Configure Slack app
|
||||
### 3. Configure Notion sync
|
||||
|
||||
1. Create a Notion internal integration at **Settings → Connections → Develop or manage integrations**
|
||||
2. Name it `seahaven-office-ops` and associate it with the **Office Operations** teamspace
|
||||
3. Copy the integration token (`secret_...`)
|
||||
4. In AWS Console: **Secrets Manager → `seahaven/notion/api-key` → Edit** — set `apiKey` to your token
|
||||
5. Run a manual test:
|
||||
|
||||
```bash
|
||||
aws lambda invoke \
|
||||
--function-name seahaven-notion-sync \
|
||||
--region us-east-1 \
|
||||
--log-type Tail \
|
||||
--query 'LogResult' \
|
||||
--output text \
|
||||
/dev/null | base64 -d
|
||||
```
|
||||
|
||||
The sync runs automatically every day at 02:00 UTC via EventBridge.
|
||||
|
||||
### 4. Configure Slack app
|
||||
|
||||
In the [Slack API dashboard](https://api.slack.com/apps):
|
||||
|
||||
|
|
@ -115,11 +142,13 @@ lib/
|
|||
bedrock-agent.ts Bedrock Agent + QBO/Maps action groups
|
||||
slack-handler.ts API Gateway + webhook/processor Lambdas
|
||||
conversation-log.ts DynamoDB table
|
||||
notion-sync.ts EventBridge daily cron + notion-sync Lambda + Secrets Manager
|
||||
lambda/
|
||||
slack-webhook/ Verifies Slack signature, fires processor async
|
||||
slack-processor/ Calls agent, writes DynamoDB, posts Slack reply
|
||||
qbo-lookup/ Bedrock action group — QuickBooks vendor search
|
||||
maps-lookup/ Bedrock action group — Google Maps Places search
|
||||
notion-sync/ Daily Notion → S3 export, triggers KB ingestion
|
||||
scripts/
|
||||
create-aoss-index.ts Manual fallback for AOSS index creation (not needed in normal deploy)
|
||||
```
|
||||
|
|
@ -127,4 +156,5 @@ scripts/
|
|||
## Known Maintenance Items
|
||||
|
||||
- **QBO refresh token** expires after 100 days of inactivity. Rotate via the [Intuit OAuth Playground](https://developer.intuit.com/app/developer/playground) and update the `seahaven/qbo/oauth` secret.
|
||||
- **KB sync** must be triggered manually after uploading new documents. Consider adding a scheduled EventBridge rule for automatic nightly syncs.
|
||||
- **Notion sync** runs daily at 02:00 UTC automatically. To trigger an immediate sync, invoke `seahaven-notion-sync` manually via the Lambda console or CLI.
|
||||
- **KB sync for manual S3 uploads** (non-Notion docs) must still be triggered manually after uploading new documents to the S3 bucket.
|
||||
|
|
|
|||
156
lambda/notion-sync/index.ts
Normal file
156
lambda/notion-sync/index.ts
Normal file
|
|
@ -0,0 +1,156 @@
|
|||
import { Client } from '@notionhq/client';
|
||||
import { NotionToMarkdown } from 'notion-to-md';
|
||||
import {
|
||||
S3Client,
|
||||
PutObjectCommand,
|
||||
ListObjectsV2Command,
|
||||
DeleteObjectsCommand,
|
||||
} from '@aws-sdk/client-s3';
|
||||
import {
|
||||
BedrockAgentClient,
|
||||
StartIngestionJobCommand,
|
||||
} from '@aws-sdk/client-bedrock-agent';
|
||||
import {
|
||||
SecretsManagerClient,
|
||||
GetSecretValueCommand,
|
||||
} from '@aws-sdk/client-secrets-manager';
|
||||
|
||||
const s3 = new S3Client({});
|
||||
const bedrockAgent = new BedrockAgentClient({ region: process.env.REGION! });
|
||||
const secrets = new SecretsManagerClient({});
|
||||
|
||||
const NOTION_PREFIX = 'notion/';
|
||||
|
||||
let cachedApiKey: string | undefined;
|
||||
|
||||
async function getApiKey(): Promise<string> {
|
||||
if (cachedApiKey) return cachedApiKey;
|
||||
const res = await secrets.send(
|
||||
new GetSecretValueCommand({ SecretId: process.env.NOTION_SECRET_ARN! }),
|
||||
);
|
||||
const parsed = JSON.parse(res.SecretString!);
|
||||
cachedApiKey = parsed.apiKey;
|
||||
return cachedApiKey!;
|
||||
}
|
||||
|
||||
function getPageTitle(page: Record<string, any>): string {
|
||||
// Regular pages store the title in properties.title or properties.Name
|
||||
const props = page.properties ?? {};
|
||||
for (const prop of Object.values(props) as any[]) {
|
||||
if (prop?.type === 'title' && Array.isArray(prop.title) && prop.title[0]?.plain_text) {
|
||||
return prop.title[0].plain_text;
|
||||
}
|
||||
}
|
||||
return page.id as string;
|
||||
}
|
||||
|
||||
async function clearOldFiles(bucket: string): Promise<void> {
|
||||
let continuationToken: string | undefined;
|
||||
const toDelete: { Key: string }[] = [];
|
||||
|
||||
do {
|
||||
const res = await s3.send(
|
||||
new ListObjectsV2Command({
|
||||
Bucket: bucket,
|
||||
Prefix: NOTION_PREFIX,
|
||||
ContinuationToken: continuationToken,
|
||||
}),
|
||||
);
|
||||
for (const obj of res.Contents ?? []) {
|
||||
if (obj.Key) toDelete.push({ Key: obj.Key });
|
||||
}
|
||||
continuationToken = res.NextContinuationToken;
|
||||
} while (continuationToken);
|
||||
|
||||
if (toDelete.length === 0) return;
|
||||
|
||||
// S3 DeleteObjects accepts up to 1000 keys per request
|
||||
for (let i = 0; i < toDelete.length; i += 1000) {
|
||||
await s3.send(
|
||||
new DeleteObjectsCommand({
|
||||
Bucket: bucket,
|
||||
Delete: { Objects: toDelete.slice(i, i + 1000) },
|
||||
}),
|
||||
);
|
||||
}
|
||||
console.log(`Deleted ${toDelete.length} stale Notion file(s) from S3`);
|
||||
}
|
||||
|
||||
async function getAllPages(notion: Client): Promise<Record<string, any>[]> {
|
||||
const results: Record<string, any>[] = [];
|
||||
let cursor: string | undefined;
|
||||
|
||||
do {
|
||||
const res = await notion.search({
|
||||
filter: { value: 'page', property: 'object' },
|
||||
page_size: 100,
|
||||
start_cursor: cursor,
|
||||
});
|
||||
results.push(...(res.results as Record<string, any>[]));
|
||||
cursor = res.has_more && res.next_cursor ? res.next_cursor : undefined;
|
||||
} while (cursor);
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
export const handler = async (): Promise<void> => {
|
||||
const apiKey = await getApiKey();
|
||||
const notion = new Client({ auth: apiKey });
|
||||
const n2m = new NotionToMarkdown({ notionClient: notion });
|
||||
|
||||
const bucket = process.env.KB_BUCKET_NAME!;
|
||||
const kbId = process.env.KNOWLEDGE_BASE_ID!;
|
||||
const dsId = process.env.DATA_SOURCE_ID!;
|
||||
|
||||
console.log('Clearing stale Notion files from S3...');
|
||||
await clearOldFiles(bucket);
|
||||
|
||||
console.log('Fetching pages from Notion Office Operations teamspace...');
|
||||
const pages = await getAllPages(notion);
|
||||
console.log(`Found ${pages.length} page(s)`);
|
||||
|
||||
let synced = 0;
|
||||
let failed = 0;
|
||||
|
||||
for (const page of pages) {
|
||||
const pageId = page.id as string;
|
||||
const title = getPageTitle(page);
|
||||
|
||||
try {
|
||||
const mdBlocks = await n2m.pageToMarkdown(pageId);
|
||||
const { parent: mdContent } = n2m.toMarkdownString(mdBlocks);
|
||||
|
||||
await s3.send(
|
||||
new PutObjectCommand({
|
||||
Bucket: bucket,
|
||||
Key: `${NOTION_PREFIX}${pageId}.md`,
|
||||
Body: `# ${title}\n\n${mdContent}`,
|
||||
ContentType: 'text/markdown',
|
||||
Metadata: {
|
||||
'notion-page-id': pageId,
|
||||
'notion-title': title.slice(0, 256), // S3 metadata value limit
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
console.log(`Synced: "${title}" (${pageId})`);
|
||||
synced++;
|
||||
} catch (err) {
|
||||
console.error(`Failed to sync page "${title}" (${pageId}):`, err);
|
||||
failed++;
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`Upload complete. ${synced} synced, ${failed} failed.`);
|
||||
|
||||
console.log('Triggering Bedrock KB ingestion job...');
|
||||
const ingestionRes = await bedrockAgent.send(
|
||||
new StartIngestionJobCommand({
|
||||
knowledgeBaseId: kbId,
|
||||
dataSourceId: dsId,
|
||||
}),
|
||||
);
|
||||
console.log(
|
||||
`Ingestion job started: ${ingestionRes.ingestionJob?.ingestionJobId}`,
|
||||
);
|
||||
};
|
||||
83
lib/constructs/notion-sync.ts
Normal file
83
lib/constructs/notion-sync.ts
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
import * as cdk from 'aws-cdk-lib';
|
||||
import { Construct } from 'constructs';
|
||||
import * as lambda from 'aws-cdk-lib/aws-lambda';
|
||||
import * as lambdaNodejs from 'aws-cdk-lib/aws-lambda-nodejs';
|
||||
import * as s3 from 'aws-cdk-lib/aws-s3';
|
||||
import * as secretsmanager from 'aws-cdk-lib/aws-secretsmanager';
|
||||
import * as events from 'aws-cdk-lib/aws-events';
|
||||
import * as targets from 'aws-cdk-lib/aws-events-targets';
|
||||
import * as iam from 'aws-cdk-lib/aws-iam';
|
||||
import * as path from 'path';
|
||||
|
||||
export interface NotionSyncProps {
|
||||
region: string;
|
||||
kbDocsBucket: s3.Bucket;
|
||||
knowledgeBaseId: string;
|
||||
dataSourceId: string;
|
||||
}
|
||||
|
||||
export class NotionSyncConstruct extends Construct {
|
||||
public readonly syncLambda: lambdaNodejs.NodejsFunction;
|
||||
public readonly notionSecret: secretsmanager.Secret;
|
||||
|
||||
constructor(scope: Construct, id: string, props: NotionSyncProps) {
|
||||
super(scope, id);
|
||||
|
||||
// Notion integration token — placeholder created here, filled in post-deploy.
|
||||
// Update via: AWS Console → Secrets Manager → seahaven/notion/api-key → Retrieve and edit
|
||||
this.notionSecret = new secretsmanager.Secret(this, 'NotionSecret', {
|
||||
secretName: 'seahaven/notion/api-key',
|
||||
description: 'Notion integration token for the Office Operations teamspace KB sync',
|
||||
secretObjectValue: {
|
||||
apiKey: cdk.SecretValue.unsafePlainText('REPLACE_ME_after_deploy'),
|
||||
},
|
||||
});
|
||||
|
||||
// Lambda — fetches Notion pages, uploads markdown to S3, triggers KB ingestion
|
||||
this.syncLambda = new lambdaNodejs.NodejsFunction(this, 'SyncLambda', {
|
||||
functionName: 'seahaven-notion-sync',
|
||||
entry: path.join(__dirname, '../../lambda/notion-sync/index.ts'),
|
||||
runtime: lambda.Runtime.NODEJS_22_X,
|
||||
memorySize: 512,
|
||||
timeout: cdk.Duration.minutes(5),
|
||||
environment: {
|
||||
NOTION_SECRET_ARN: this.notionSecret.secretArn,
|
||||
KB_BUCKET_NAME: props.kbDocsBucket.bucketName,
|
||||
KNOWLEDGE_BASE_ID: props.knowledgeBaseId,
|
||||
DATA_SOURCE_ID: props.dataSourceId,
|
||||
REGION: props.region,
|
||||
},
|
||||
});
|
||||
|
||||
// Allow Lambda to read the Notion secret
|
||||
this.notionSecret.grantRead(this.syncLambda);
|
||||
|
||||
// Allow Lambda to read existing notion/ objects and write new ones
|
||||
props.kbDocsBucket.grantReadWrite(this.syncLambda);
|
||||
|
||||
// Allow Lambda to start a Bedrock KB ingestion job
|
||||
this.syncLambda.addToRolePolicy(
|
||||
new iam.PolicyStatement({
|
||||
actions: ['bedrock:StartIngestionJob'],
|
||||
resources: [
|
||||
`arn:aws:bedrock:${props.region}:*:knowledge-base/${props.knowledgeBaseId}`,
|
||||
],
|
||||
}),
|
||||
);
|
||||
|
||||
// EventBridge rule — fires daily at 02:00 UTC
|
||||
const dailyRule = new events.Rule(this, 'DailySyncRule', {
|
||||
ruleName: 'seahaven-notion-daily-sync',
|
||||
description: 'Daily Notion → KB sync at 02:00 UTC',
|
||||
schedule: events.Schedule.cron({ minute: '0', hour: '2' }),
|
||||
});
|
||||
|
||||
dailyRule.addTarget(new targets.LambdaFunction(this.syncLambda));
|
||||
|
||||
// Surface the secret name so operators know where to put the token
|
||||
new cdk.CfnOutput(scope, 'NotionSecretName', {
|
||||
value: this.notionSecret.secretName,
|
||||
description: 'Set your Notion integration token here after deploy (apiKey field)',
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -4,6 +4,7 @@ import { ConversationLogConstruct } from './constructs/conversation-log';
|
|||
import { KnowledgeBaseConstruct } from './constructs/knowledge-base';
|
||||
import { BedrockAgentConstruct } from './constructs/bedrock-agent';
|
||||
import { SlackHandlerConstruct } from './constructs/slack-handler';
|
||||
import { NotionSyncConstruct } from './constructs/notion-sync';
|
||||
|
||||
export class SeahavenSlackBotStack extends cdk.Stack {
|
||||
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
|
||||
|
|
@ -35,6 +36,14 @@ export class SeahavenSlackBotStack extends cdk.Stack {
|
|||
knowledgeBaseArn: knowledgeBase.knowledgeBase.knowledgeBaseArn,
|
||||
});
|
||||
|
||||
// ── Notion → KB daily sync (EventBridge + Lambda) ────────────────────────
|
||||
new NotionSyncConstruct(this, 'NotionSync', {
|
||||
region: this.region,
|
||||
kbDocsBucket: knowledgeBase.docsBucket,
|
||||
knowledgeBaseId: knowledgeBase.knowledgeBase.knowledgeBaseId,
|
||||
dataSourceId: knowledgeBase.dataSource.dataSourceId,
|
||||
});
|
||||
|
||||
// ── Slack webhook handler (API Gateway + Lambda) ───────────────────────────
|
||||
new SlackHandlerConstruct(this, 'SlackHandler', {
|
||||
accountId: this.account,
|
||||
|
|
|
|||
348
package-lock.json
generated
348
package-lock.json
generated
|
|
@ -9,8 +9,10 @@
|
|||
"version": "0.1.0",
|
||||
"dependencies": {
|
||||
"@cdklabs/generative-ai-cdk-constructs": "^0.1.0",
|
||||
"@notionhq/client": "^2.2.15",
|
||||
"aws-cdk-lib": "^2.180.0",
|
||||
"constructs": "^10.4.2"
|
||||
"constructs": "^10.4.2",
|
||||
"notion-to-md": "^3.1.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/aws-lambda": "^8.10.149",
|
||||
|
|
@ -594,6 +596,19 @@
|
|||
"@jridgewell/sourcemap-codec": "^1.4.10"
|
||||
}
|
||||
},
|
||||
"node_modules/@notionhq/client": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@notionhq/client/-/client-2.3.0.tgz",
|
||||
"integrity": "sha512-l7WqTCpQqC+HibkB9chghONQTYcxNQT0/rOJemBfmuKQRTu2vuV8B3yA395iKaUdDo7HI+0KvQaz9687Xskzkw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/node-fetch": "^2.5.10",
|
||||
"node-fetch": "^2.6.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/@tsconfig/node10": {
|
||||
"version": "1.0.12",
|
||||
"resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.12.tgz",
|
||||
|
|
@ -633,12 +648,21 @@
|
|||
"version": "22.19.17",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.17.tgz",
|
||||
"integrity": "sha512-wGdMcf+vPYM6jikpS/qhg6WiqSV/OhG+jeeHT/KlVqxYfD40iYJf9/AE1uQxVWFvU7MipKRkRv8NSHiCGgPr8Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"undici-types": "~6.21.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/node-fetch": {
|
||||
"version": "2.6.13",
|
||||
"resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.13.tgz",
|
||||
"integrity": "sha512-QGpRVpzSaUs30JBSGPjOg4Uveu384erbHBoT1zeONvyCfwQxIkUshLAOqN/k9EjGviPRmWTTe6aH2qySWKTVSw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/node": "*",
|
||||
"form-data": "^4.0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/acorn": {
|
||||
"version": "8.16.0",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz",
|
||||
|
|
@ -672,6 +696,12 @@
|
|||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/asynckit": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
|
||||
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/aws-cdk": {
|
||||
"version": "2.1118.0",
|
||||
"resolved": "https://registry.npmjs.org/aws-cdk/-/aws-cdk-2.1118.0.tgz",
|
||||
|
|
@ -1078,6 +1108,19 @@
|
|||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/call-bind-apply-helpers": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
|
||||
"integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"es-errors": "^1.3.0",
|
||||
"function-bind": "^1.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/cdk-nag": {
|
||||
"version": "2.37.55",
|
||||
"resolved": "https://registry.npmjs.org/cdk-nag/-/cdk-nag-2.37.55.tgz",
|
||||
|
|
@ -1088,6 +1131,18 @@
|
|||
"constructs": "^10.0.5"
|
||||
}
|
||||
},
|
||||
"node_modules/combined-stream": {
|
||||
"version": "1.0.8",
|
||||
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
|
||||
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"delayed-stream": "~1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/constructs": {
|
||||
"version": "10.6.0",
|
||||
"resolved": "https://registry.npmjs.org/constructs/-/constructs-10.6.0.tgz",
|
||||
|
|
@ -1101,6 +1156,15 @@
|
|||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/delayed-stream": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
|
||||
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/diff": {
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/diff/-/diff-4.0.4.tgz",
|
||||
|
|
@ -1111,6 +1175,65 @@
|
|||
"node": ">=0.3.1"
|
||||
}
|
||||
},
|
||||
"node_modules/dunder-proto": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
|
||||
"integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"call-bind-apply-helpers": "^1.0.1",
|
||||
"es-errors": "^1.3.0",
|
||||
"gopd": "^1.2.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/es-define-property": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
|
||||
"integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/es-errors": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
|
||||
"integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/es-object-atoms": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
|
||||
"integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"es-errors": "^1.3.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/es-set-tostringtag": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
|
||||
"integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"es-errors": "^1.3.0",
|
||||
"get-intrinsic": "^1.2.6",
|
||||
"has-tostringtag": "^1.0.2",
|
||||
"hasown": "^2.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/esbuild": {
|
||||
"version": "0.25.12",
|
||||
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz",
|
||||
|
|
@ -1153,6 +1276,119 @@
|
|||
"@esbuild/win32-x64": "0.25.12"
|
||||
}
|
||||
},
|
||||
"node_modules/form-data": {
|
||||
"version": "4.0.5",
|
||||
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz",
|
||||
"integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"asynckit": "^0.4.0",
|
||||
"combined-stream": "^1.0.8",
|
||||
"es-set-tostringtag": "^2.1.0",
|
||||
"hasown": "^2.0.2",
|
||||
"mime-types": "^2.1.12"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/function-bind": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
|
||||
"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/get-intrinsic": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
|
||||
"integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"call-bind-apply-helpers": "^1.0.2",
|
||||
"es-define-property": "^1.0.1",
|
||||
"es-errors": "^1.3.0",
|
||||
"es-object-atoms": "^1.1.1",
|
||||
"function-bind": "^1.1.2",
|
||||
"get-proto": "^1.0.1",
|
||||
"gopd": "^1.2.0",
|
||||
"has-symbols": "^1.1.0",
|
||||
"hasown": "^2.0.2",
|
||||
"math-intrinsics": "^1.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/get-proto": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
|
||||
"integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"dunder-proto": "^1.0.1",
|
||||
"es-object-atoms": "^1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/gopd": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
|
||||
"integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/has-symbols": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
|
||||
"integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/has-tostringtag": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
|
||||
"integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"has-symbols": "^1.0.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/hasown": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
|
||||
"integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"function-bind": "^1.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/make-error": {
|
||||
"version": "1.3.6",
|
||||
"resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
|
||||
|
|
@ -1160,6 +1396,97 @@
|
|||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/markdown-table": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-2.0.0.tgz",
|
||||
"integrity": "sha512-Ezda85ToJUBhM6WGaG6veasyym+Tbs3cMAw/ZhOPqXiYsr0jgocBV3j3nx+4lk47plLlIqjwuTm/ywVI+zjJ/A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"repeat-string": "^1.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/wooorm"
|
||||
}
|
||||
},
|
||||
"node_modules/math-intrinsics": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
|
||||
"integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/mime-db": {
|
||||
"version": "1.52.0",
|
||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
|
||||
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/mime-types": {
|
||||
"version": "2.1.35",
|
||||
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
|
||||
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"mime-db": "1.52.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/node-fetch": {
|
||||
"version": "2.7.0",
|
||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
|
||||
"integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"whatwg-url": "^5.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "4.x || >=6.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"encoding": "^0.1.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"encoding": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/notion-to-md": {
|
||||
"version": "3.1.9",
|
||||
"resolved": "https://registry.npmjs.org/notion-to-md/-/notion-to-md-3.1.9.tgz",
|
||||
"integrity": "sha512-SsYhhigh+jOv06QOiFynOQATPMl96CspWDIL3Q5klzp4eaZ1dYaPI3ELoly80G1K0jf730u3ItvfwskzPKK41g==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"markdown-table": "^2.0.0",
|
||||
"node-fetch": "2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/repeat-string": {
|
||||
"version": "1.6.1",
|
||||
"resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz",
|
||||
"integrity": "sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/tr46": {
|
||||
"version": "0.0.3",
|
||||
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
|
||||
"integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/ts-node": {
|
||||
"version": "10.9.2",
|
||||
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz",
|
||||
|
|
@ -1222,7 +1549,6 @@
|
|||
"version": "6.21.0",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
|
||||
"integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/v8-compile-cache-lib": {
|
||||
|
|
@ -1232,6 +1558,22 @@
|
|||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/webidl-conversions": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
|
||||
"integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==",
|
||||
"license": "BSD-2-Clause"
|
||||
},
|
||||
"node_modules/whatwg-url": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
|
||||
"integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"tr46": "~0.0.3",
|
||||
"webidl-conversions": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/yn": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
|
||||
|
|
|
|||
|
|
@ -20,7 +20,9 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"@cdklabs/generative-ai-cdk-constructs": "^0.1.0",
|
||||
"@notionhq/client": "^2.2.15",
|
||||
"aws-cdk-lib": "^2.180.0",
|
||||
"constructs": "^10.4.2"
|
||||
"constructs": "^10.4.2",
|
||||
"notion-to-md": "^3.1.1"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Reference in a new issue