@hasna/connectors 0.4.0 → 0.4.2
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/bin/index.js +189 -2
- package/bin/mcp.js +176 -1
- package/bin/serve.js +175 -0
- package/connectors/connect-airtable/.env.example +11 -0
- package/connectors/connect-airtable/CLAUDE.md +121 -0
- package/connectors/connect-airtable/README.md +193 -0
- package/connectors/connect-airtable/package.json +50 -0
- package/connectors/connect-airtable/src/api/client.ts +139 -0
- package/connectors/connect-airtable/src/api/index.ts +405 -0
- package/connectors/connect-airtable/src/cli/index.ts +637 -0
- package/connectors/connect-airtable/src/index.ts +20 -0
- package/connectors/connect-airtable/src/types/index.ts +349 -0
- package/connectors/connect-airtable/src/utils/config.ts +197 -0
- package/connectors/connect-airtable/tsconfig.json +16 -0
- package/connectors/connect-amplitude/.env.example +11 -0
- package/connectors/connect-amplitude/CLAUDE.md +121 -0
- package/connectors/connect-amplitude/README.md +193 -0
- package/connectors/connect-amplitude/package.json +51 -0
- package/connectors/connect-amplitude/src/api/client.ts +144 -0
- package/connectors/connect-amplitude/src/api/index.ts +215 -0
- package/connectors/connect-amplitude/src/cli/index.ts +532 -0
- package/connectors/connect-amplitude/src/index.ts +22 -0
- package/connectors/connect-amplitude/src/types/index.ts +329 -0
- package/connectors/connect-amplitude/src/utils/config.ts +208 -0
- package/connectors/connect-amplitude/tsconfig.json +16 -0
- package/connectors/connect-calendly/.env.example +11 -0
- package/connectors/connect-calendly/CLAUDE.md +272 -0
- package/connectors/connect-calendly/README.md +193 -0
- package/connectors/connect-calendly/package.json +51 -0
- package/connectors/connect-calendly/scripts/release.ts +179 -0
- package/connectors/connect-calendly/src/api/client.ts +213 -0
- package/connectors/connect-calendly/src/api/example.ts +48 -0
- package/connectors/connect-calendly/src/api/index.ts +51 -0
- package/connectors/connect-calendly/src/cli/index.ts +254 -0
- package/connectors/connect-calendly/src/index.ts +103 -0
- package/connectors/connect-calendly/src/types/index.ts +237 -0
- package/connectors/connect-calendly/src/utils/auth.ts +274 -0
- package/connectors/connect-calendly/src/utils/bulk.ts +212 -0
- package/connectors/connect-calendly/src/utils/config.ts +326 -0
- package/connectors/connect-calendly/src/utils/output.ts +175 -0
- package/connectors/connect-calendly/src/utils/settings.ts +114 -0
- package/connectors/connect-calendly/src/utils/storage.ts +198 -0
- package/connectors/connect-calendly/tsconfig.json +16 -0
- package/connectors/connect-convertkit/.env.example +11 -0
- package/connectors/connect-convertkit/CLAUDE.md +272 -0
- package/connectors/connect-convertkit/README.md +193 -0
- package/connectors/connect-convertkit/package.json +51 -0
- package/connectors/connect-convertkit/scripts/release.ts +179 -0
- package/connectors/connect-convertkit/src/api/client.ts +213 -0
- package/connectors/connect-convertkit/src/api/example.ts +48 -0
- package/connectors/connect-convertkit/src/api/index.ts +51 -0
- package/connectors/connect-convertkit/src/cli/index.ts +254 -0
- package/connectors/connect-convertkit/src/index.ts +103 -0
- package/connectors/connect-convertkit/src/types/index.ts +237 -0
- package/connectors/connect-convertkit/src/utils/auth.ts +274 -0
- package/connectors/connect-convertkit/src/utils/bulk.ts +212 -0
- package/connectors/connect-convertkit/src/utils/config.ts +326 -0
- package/connectors/connect-convertkit/src/utils/output.ts +175 -0
- package/connectors/connect-convertkit/src/utils/settings.ts +114 -0
- package/connectors/connect-convertkit/src/utils/storage.ts +198 -0
- package/connectors/connect-convertkit/tsconfig.json +16 -0
- package/connectors/connect-crisp/.env.example +11 -0
- package/connectors/connect-crisp/CLAUDE.md +272 -0
- package/connectors/connect-crisp/README.md +193 -0
- package/connectors/connect-crisp/package.json +51 -0
- package/connectors/connect-crisp/scripts/release.ts +179 -0
- package/connectors/connect-crisp/src/api/client.ts +213 -0
- package/connectors/connect-crisp/src/api/example.ts +48 -0
- package/connectors/connect-crisp/src/api/index.ts +51 -0
- package/connectors/connect-crisp/src/cli/index.ts +254 -0
- package/connectors/connect-crisp/src/index.ts +103 -0
- package/connectors/connect-crisp/src/types/index.ts +237 -0
- package/connectors/connect-crisp/src/utils/auth.ts +274 -0
- package/connectors/connect-crisp/src/utils/bulk.ts +212 -0
- package/connectors/connect-crisp/src/utils/config.ts +326 -0
- package/connectors/connect-crisp/src/utils/output.ts +175 -0
- package/connectors/connect-crisp/src/utils/settings.ts +114 -0
- package/connectors/connect-crisp/src/utils/storage.ts +198 -0
- package/connectors/connect-crisp/tsconfig.json +16 -0
- package/connectors/connect-docusign/.env.example +11 -0
- package/connectors/connect-docusign/CLAUDE.md +128 -0
- package/connectors/connect-docusign/README.md +193 -0
- package/connectors/connect-docusign/package.json +52 -0
- package/connectors/connect-docusign/src/api/client.ts +133 -0
- package/connectors/connect-docusign/src/api/index.ts +178 -0
- package/connectors/connect-docusign/src/cli/index.ts +381 -0
- package/connectors/connect-docusign/src/index.ts +23 -0
- package/connectors/connect-docusign/src/types/index.ts +208 -0
- package/connectors/connect-docusign/src/utils/config.ts +125 -0
- package/connectors/connect-docusign/src/utils/output.ts +119 -0
- package/connectors/connect-docusign/tsconfig.json +16 -0
- package/connectors/connect-drift/.env.example +11 -0
- package/connectors/connect-drift/CLAUDE.md +272 -0
- package/connectors/connect-drift/README.md +193 -0
- package/connectors/connect-drift/package.json +51 -0
- package/connectors/connect-drift/scripts/release.ts +179 -0
- package/connectors/connect-drift/src/api/client.ts +213 -0
- package/connectors/connect-drift/src/api/example.ts +48 -0
- package/connectors/connect-drift/src/api/index.ts +51 -0
- package/connectors/connect-drift/src/cli/index.ts +254 -0
- package/connectors/connect-drift/src/index.ts +103 -0
- package/connectors/connect-drift/src/types/index.ts +237 -0
- package/connectors/connect-drift/src/utils/auth.ts +274 -0
- package/connectors/connect-drift/src/utils/bulk.ts +212 -0
- package/connectors/connect-drift/src/utils/config.ts +326 -0
- package/connectors/connect-drift/src/utils/output.ts +175 -0
- package/connectors/connect-drift/src/utils/settings.ts +114 -0
- package/connectors/connect-drift/src/utils/storage.ts +198 -0
- package/connectors/connect-drift/tsconfig.json +16 -0
- package/connectors/connect-freshdesk/.env.example +11 -0
- package/connectors/connect-freshdesk/CLAUDE.md +272 -0
- package/connectors/connect-freshdesk/README.md +193 -0
- package/connectors/connect-freshdesk/package.json +51 -0
- package/connectors/connect-freshdesk/scripts/release.ts +179 -0
- package/connectors/connect-freshdesk/src/api/client.ts +213 -0
- package/connectors/connect-freshdesk/src/api/example.ts +48 -0
- package/connectors/connect-freshdesk/src/api/index.ts +51 -0
- package/connectors/connect-freshdesk/src/cli/index.ts +254 -0
- package/connectors/connect-freshdesk/src/index.ts +103 -0
- package/connectors/connect-freshdesk/src/types/index.ts +237 -0
- package/connectors/connect-freshdesk/src/utils/auth.ts +274 -0
- package/connectors/connect-freshdesk/src/utils/bulk.ts +212 -0
- package/connectors/connect-freshdesk/src/utils/config.ts +326 -0
- package/connectors/connect-freshdesk/src/utils/output.ts +175 -0
- package/connectors/connect-freshdesk/src/utils/settings.ts +114 -0
- package/connectors/connect-freshdesk/src/utils/storage.ts +198 -0
- package/connectors/connect-freshdesk/tsconfig.json +16 -0
- package/connectors/connect-gumroad/.env.example +11 -0
- package/connectors/connect-gumroad/CLAUDE.md +272 -0
- package/connectors/connect-gumroad/README.md +193 -0
- package/connectors/connect-gumroad/package.json +51 -0
- package/connectors/connect-gumroad/scripts/release.ts +179 -0
- package/connectors/connect-gumroad/src/api/client.ts +213 -0
- package/connectors/connect-gumroad/src/api/example.ts +48 -0
- package/connectors/connect-gumroad/src/api/index.ts +51 -0
- package/connectors/connect-gumroad/src/cli/index.ts +254 -0
- package/connectors/connect-gumroad/src/index.ts +103 -0
- package/connectors/connect-gumroad/src/types/index.ts +237 -0
- package/connectors/connect-gumroad/src/utils/auth.ts +274 -0
- package/connectors/connect-gumroad/src/utils/bulk.ts +212 -0
- package/connectors/connect-gumroad/src/utils/config.ts +326 -0
- package/connectors/connect-gumroad/src/utils/output.ts +175 -0
- package/connectors/connect-gumroad/src/utils/settings.ts +114 -0
- package/connectors/connect-gumroad/src/utils/storage.ts +198 -0
- package/connectors/connect-gumroad/tsconfig.json +16 -0
- package/connectors/connect-hubspot/.env.example +11 -0
- package/connectors/connect-hubspot/CLAUDE.md +128 -0
- package/connectors/connect-hubspot/README.md +193 -0
- package/connectors/connect-hubspot/package.json +52 -0
- package/connectors/connect-hubspot/src/api/client.ts +136 -0
- package/connectors/connect-hubspot/src/api/index.ts +379 -0
- package/connectors/connect-hubspot/src/cli/index.ts +589 -0
- package/connectors/connect-hubspot/src/index.ts +21 -0
- package/connectors/connect-hubspot/src/types/index.ts +285 -0
- package/connectors/connect-hubspot/src/utils/config.ts +205 -0
- package/connectors/connect-hubspot/src/utils/output.ts +119 -0
- package/connectors/connect-hubspot/tsconfig.json +16 -0
- package/connectors/connect-intercom/.env.example +11 -0
- package/connectors/connect-intercom/CLAUDE.md +131 -0
- package/connectors/connect-intercom/README.md +193 -0
- package/connectors/connect-intercom/package.json +52 -0
- package/connectors/connect-intercom/src/api/client.ts +132 -0
- package/connectors/connect-intercom/src/api/index.ts +366 -0
- package/connectors/connect-intercom/src/cli/index.ts +831 -0
- package/connectors/connect-intercom/src/index.ts +21 -0
- package/connectors/connect-intercom/src/types/index.ts +650 -0
- package/connectors/connect-intercom/src/utils/config.ts +157 -0
- package/connectors/connect-intercom/src/utils/output.ts +119 -0
- package/connectors/connect-intercom/tsconfig.json +16 -0
- package/connectors/connect-lemonsqueezy/.env.example +11 -0
- package/connectors/connect-lemonsqueezy/CLAUDE.md +128 -0
- package/connectors/connect-lemonsqueezy/README.md +193 -0
- package/connectors/connect-lemonsqueezy/package.json +52 -0
- package/connectors/connect-lemonsqueezy/src/api/client.ts +133 -0
- package/connectors/connect-lemonsqueezy/src/api/index.ts +502 -0
- package/connectors/connect-lemonsqueezy/src/cli/index.ts +723 -0
- package/connectors/connect-lemonsqueezy/src/index.ts +21 -0
- package/connectors/connect-lemonsqueezy/src/types/index.ts +353 -0
- package/connectors/connect-lemonsqueezy/src/utils/config.ts +205 -0
- package/connectors/connect-lemonsqueezy/src/utils/output.ts +119 -0
- package/connectors/connect-lemonsqueezy/tsconfig.json +16 -0
- package/connectors/connect-linkedin/.env.example +11 -0
- package/connectors/connect-linkedin/CLAUDE.md +124 -0
- package/connectors/connect-linkedin/README.md +193 -0
- package/connectors/connect-linkedin/package.json +52 -0
- package/connectors/connect-linkedin/src/api/client.ts +132 -0
- package/connectors/connect-linkedin/src/api/index.ts +313 -0
- package/connectors/connect-linkedin/src/cli/index.ts +548 -0
- package/connectors/connect-linkedin/src/index.ts +21 -0
- package/connectors/connect-linkedin/src/types/index.ts +472 -0
- package/connectors/connect-linkedin/src/utils/config.ts +157 -0
- package/connectors/connect-linkedin/src/utils/output.ts +119 -0
- package/connectors/connect-linkedin/tsconfig.json +16 -0
- package/connectors/connect-mailchimp/.env.example +11 -0
- package/connectors/connect-mailchimp/CLAUDE.md +127 -0
- package/connectors/connect-mailchimp/README.md +193 -0
- package/connectors/connect-mailchimp/package.json +52 -0
- package/connectors/connect-mailchimp/src/api/client.ts +162 -0
- package/connectors/connect-mailchimp/src/api/index.ts +580 -0
- package/connectors/connect-mailchimp/src/cli/index.ts +822 -0
- package/connectors/connect-mailchimp/src/index.ts +23 -0
- package/connectors/connect-mailchimp/src/types/index.ts +585 -0
- package/connectors/connect-mailchimp/src/utils/config.ts +208 -0
- package/connectors/connect-mailchimp/src/utils/output.ts +119 -0
- package/connectors/connect-mailchimp/tsconfig.json +16 -0
- package/connectors/connect-mongodb/.env.example +11 -0
- package/connectors/connect-mongodb/CLAUDE.md +272 -0
- package/connectors/connect-mongodb/README.md +193 -0
- package/connectors/connect-mongodb/package.json +51 -0
- package/connectors/connect-mongodb/scripts/release.ts +179 -0
- package/connectors/connect-mongodb/src/api/client.ts +213 -0
- package/connectors/connect-mongodb/src/api/example.ts +48 -0
- package/connectors/connect-mongodb/src/api/index.ts +51 -0
- package/connectors/connect-mongodb/src/cli/index.ts +254 -0
- package/connectors/connect-mongodb/src/index.ts +103 -0
- package/connectors/connect-mongodb/src/types/index.ts +237 -0
- package/connectors/connect-mongodb/src/utils/auth.ts +274 -0
- package/connectors/connect-mongodb/src/utils/bulk.ts +212 -0
- package/connectors/connect-mongodb/src/utils/config.ts +326 -0
- package/connectors/connect-mongodb/src/utils/output.ts +175 -0
- package/connectors/connect-mongodb/src/utils/settings.ts +114 -0
- package/connectors/connect-mongodb/src/utils/storage.ts +198 -0
- package/connectors/connect-mongodb/tsconfig.json +16 -0
- package/connectors/connect-netlify/.env.example +11 -0
- package/connectors/connect-netlify/CLAUDE.md +170 -0
- package/connectors/connect-netlify/README.md +193 -0
- package/connectors/connect-netlify/package.json +53 -0
- package/connectors/connect-netlify/src/api/client.ts +132 -0
- package/connectors/connect-netlify/src/api/index.ts +533 -0
- package/connectors/connect-netlify/src/cli/index.ts +985 -0
- package/connectors/connect-netlify/src/index.ts +22 -0
- package/connectors/connect-netlify/src/types/index.ts +423 -0
- package/connectors/connect-netlify/src/utils/config.ts +171 -0
- package/connectors/connect-netlify/src/utils/output.ts +119 -0
- package/connectors/connect-netlify/tsconfig.json +16 -0
- package/connectors/connect-paypal/.env.example +11 -0
- package/connectors/connect-paypal/CLAUDE.md +128 -0
- package/connectors/connect-paypal/README.md +193 -0
- package/connectors/connect-paypal/package.json +51 -0
- package/connectors/connect-paypal/src/api/client.ts +182 -0
- package/connectors/connect-paypal/src/api/index.ts +235 -0
- package/connectors/connect-paypal/src/cli/index.ts +559 -0
- package/connectors/connect-paypal/src/index.ts +23 -0
- package/connectors/connect-paypal/src/types/index.ts +377 -0
- package/connectors/connect-paypal/src/utils/config.ts +125 -0
- package/connectors/connect-paypal/src/utils/output.ts +119 -0
- package/connectors/connect-paypal/tsconfig.json +16 -0
- package/connectors/connect-pinterest/.env.example +11 -0
- package/connectors/connect-pinterest/CLAUDE.md +115 -0
- package/connectors/connect-pinterest/README.md +193 -0
- package/connectors/connect-pinterest/package.json +52 -0
- package/connectors/connect-pinterest/src/api/client.ts +125 -0
- package/connectors/connect-pinterest/src/api/index.ts +203 -0
- package/connectors/connect-pinterest/src/cli/index.ts +653 -0
- package/connectors/connect-pinterest/src/index.ts +21 -0
- package/connectors/connect-pinterest/src/types/index.ts +268 -0
- package/connectors/connect-pinterest/src/utils/config.ts +157 -0
- package/connectors/connect-pinterest/src/utils/output.ts +119 -0
- package/connectors/connect-pinterest/tsconfig.json +16 -0
- package/connectors/connect-posthog/.env.example +11 -0
- package/connectors/connect-posthog/CLAUDE.md +272 -0
- package/connectors/connect-posthog/README.md +193 -0
- package/connectors/connect-posthog/package.json +51 -0
- package/connectors/connect-posthog/scripts/release.ts +179 -0
- package/connectors/connect-posthog/src/api/client.ts +213 -0
- package/connectors/connect-posthog/src/api/example.ts +48 -0
- package/connectors/connect-posthog/src/api/index.ts +51 -0
- package/connectors/connect-posthog/src/cli/index.ts +254 -0
- package/connectors/connect-posthog/src/index.ts +103 -0
- package/connectors/connect-posthog/src/types/index.ts +237 -0
- package/connectors/connect-posthog/src/utils/auth.ts +274 -0
- package/connectors/connect-posthog/src/utils/bulk.ts +212 -0
- package/connectors/connect-posthog/src/utils/config.ts +326 -0
- package/connectors/connect-posthog/src/utils/output.ts +175 -0
- package/connectors/connect-posthog/src/utils/settings.ts +114 -0
- package/connectors/connect-posthog/src/utils/storage.ts +198 -0
- package/connectors/connect-posthog/tsconfig.json +16 -0
- package/connectors/connect-salesforce/.env.example +11 -0
- package/connectors/connect-salesforce/CLAUDE.md +128 -0
- package/connectors/connect-salesforce/README.md +193 -0
- package/connectors/connect-salesforce/package.json +53 -0
- package/connectors/connect-salesforce/src/api/client.ts +150 -0
- package/connectors/connect-salesforce/src/api/index.ts +367 -0
- package/connectors/connect-salesforce/src/cli/index.ts +594 -0
- package/connectors/connect-salesforce/src/index.ts +21 -0
- package/connectors/connect-salesforce/src/types/index.ts +292 -0
- package/connectors/connect-salesforce/src/utils/config.ts +208 -0
- package/connectors/connect-salesforce/src/utils/output.ts +119 -0
- package/connectors/connect-salesforce/tsconfig.json +16 -0
- package/connectors/connect-segment/.env.example +11 -0
- package/connectors/connect-segment/CLAUDE.md +272 -0
- package/connectors/connect-segment/README.md +193 -0
- package/connectors/connect-segment/package.json +51 -0
- package/connectors/connect-segment/scripts/release.ts +179 -0
- package/connectors/connect-segment/src/api/client.ts +213 -0
- package/connectors/connect-segment/src/api/example.ts +48 -0
- package/connectors/connect-segment/src/api/index.ts +51 -0
- package/connectors/connect-segment/src/cli/index.ts +254 -0
- package/connectors/connect-segment/src/index.ts +103 -0
- package/connectors/connect-segment/src/types/index.ts +237 -0
- package/connectors/connect-segment/src/utils/auth.ts +274 -0
- package/connectors/connect-segment/src/utils/bulk.ts +212 -0
- package/connectors/connect-segment/src/utils/config.ts +326 -0
- package/connectors/connect-segment/src/utils/output.ts +175 -0
- package/connectors/connect-segment/src/utils/settings.ts +114 -0
- package/connectors/connect-segment/src/utils/storage.ts +198 -0
- package/connectors/connect-segment/tsconfig.json +16 -0
- package/connectors/connect-sendgrid/.env.example +11 -0
- package/connectors/connect-sendgrid/CLAUDE.md +125 -0
- package/connectors/connect-sendgrid/README.md +193 -0
- package/connectors/connect-sendgrid/package.json +53 -0
- package/connectors/connect-sendgrid/src/api/client.ts +137 -0
- package/connectors/connect-sendgrid/src/api/index.ts +588 -0
- package/connectors/connect-sendgrid/src/cli/index.ts +764 -0
- package/connectors/connect-sendgrid/src/index.ts +21 -0
- package/connectors/connect-sendgrid/src/types/index.ts +403 -0
- package/connectors/connect-sendgrid/src/utils/config.ts +197 -0
- package/connectors/connect-sendgrid/src/utils/output.ts +119 -0
- package/connectors/connect-sendgrid/tsconfig.json +16 -0
- package/connectors/connect-supabase/.env.example +11 -0
- package/connectors/connect-supabase/CLAUDE.md +132 -0
- package/connectors/connect-supabase/README.md +193 -0
- package/connectors/connect-supabase/package.json +52 -0
- package/connectors/connect-supabase/src/api/client.ts +231 -0
- package/connectors/connect-supabase/src/api/index.ts +439 -0
- package/connectors/connect-supabase/src/cli/index.ts +691 -0
- package/connectors/connect-supabase/src/index.ts +24 -0
- package/connectors/connect-supabase/src/types/index.ts +215 -0
- package/connectors/connect-supabase/src/utils/config.ts +219 -0
- package/connectors/connect-supabase/tsconfig.json +16 -0
- package/connectors/connect-vercel/.env.example +11 -0
- package/connectors/connect-vercel/CLAUDE.md +142 -0
- package/connectors/connect-vercel/README.md +193 -0
- package/connectors/connect-vercel/package.json +52 -0
- package/connectors/connect-vercel/src/api/client.ts +139 -0
- package/connectors/connect-vercel/src/api/index.ts +384 -0
- package/connectors/connect-vercel/src/cli/index.ts +740 -0
- package/connectors/connect-vercel/src/index.ts +24 -0
- package/connectors/connect-vercel/src/types/index.ts +350 -0
- package/connectors/connect-vercel/src/utils/config.ts +182 -0
- package/connectors/connect-vercel/src/utils/output.ts +119 -0
- package/connectors/connect-vercel/tsconfig.json +16 -0
- package/connectors/connect-zendesk/.env.example +10 -0
- package/connectors/connect-zendesk/.github/workflows/deploy.yml +51 -0
- package/connectors/connect-zendesk/CLAUDE.md +78 -0
- package/connectors/connect-zendesk/Makefile +129 -0
- package/connectors/connect-zendesk/README.md +295 -0
- package/connectors/connect-zendesk/SCAFFOLD.md +178 -0
- package/connectors/connect-zendesk/nginx-connector.conf +218 -0
- package/connectors/connect-zendesk/nginx.conf +61 -0
- package/connectors/connect-zendesk/package.json +71 -0
- package/connectors/connect-zendesk/scripts/init.sh +62 -0
- package/connectors/connect-zendesk/scripts/publish.ts +210 -0
- package/connectors/connect-zendesk/server/index.js +142 -0
- package/connectors/connect-zendesk/src/api/automations.ts +95 -0
- package/connectors/connect-zendesk/src/api/brands.ts +80 -0
- package/connectors/connect-zendesk/src/api/bulk.ts +838 -0
- package/connectors/connect-zendesk/src/api/client.test.ts +315 -0
- package/connectors/connect-zendesk/src/api/client.ts +173 -0
- package/connectors/connect-zendesk/src/api/example.ts +59 -0
- package/connectors/connect-zendesk/src/api/groups.ts +60 -0
- package/connectors/connect-zendesk/src/api/index.test.ts +111 -0
- package/connectors/connect-zendesk/src/api/index.ts +131 -0
- package/connectors/connect-zendesk/src/api/macros.ts +124 -0
- package/connectors/connect-zendesk/src/api/organizations.ts +85 -0
- package/connectors/connect-zendesk/src/api/slas.ts +80 -0
- package/connectors/connect-zendesk/src/api/ticket-fields.ts +98 -0
- package/connectors/connect-zendesk/src/api/tickets.test.ts +215 -0
- package/connectors/connect-zendesk/src/api/tickets.ts +103 -0
- package/connectors/connect-zendesk/src/api/triggers.test.ts +204 -0
- package/connectors/connect-zendesk/src/api/triggers.ts +125 -0
- package/connectors/connect-zendesk/src/api/users.test.ts +180 -0
- package/connectors/connect-zendesk/src/api/users.ts +108 -0
- package/connectors/connect-zendesk/src/api/views.test.ts +223 -0
- package/connectors/connect-zendesk/src/api/views.ts +145 -0
- package/connectors/connect-zendesk/src/api/webhooks.test.ts +208 -0
- package/connectors/connect-zendesk/src/api/webhooks.ts +118 -0
- package/connectors/connect-zendesk/src/cli/index.ts +1478 -0
- package/connectors/connect-zendesk/src/index.ts +36 -0
- package/connectors/connect-zendesk/src/server/index.ts +204 -0
- package/connectors/connect-zendesk/src/types/index.ts +910 -0
- package/connectors/connect-zendesk/src/utils/config.test.ts +193 -0
- package/connectors/connect-zendesk/src/utils/config.ts +526 -0
- package/connectors/connect-zendesk/src/utils/export.ts +338 -0
- package/connectors/connect-zendesk/src/utils/logger.ts +105 -0
- package/connectors/connect-zendesk/src/utils/output.test.ts +137 -0
- package/connectors/connect-zendesk/src/utils/output.ts +119 -0
- package/connectors/connect-zendesk/tsconfig.json +31 -0
- package/dist/index.js +175 -0
- package/package.json +1 -1
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
// Scaffold Connector Types
|
|
2
|
+
// Replace CONNECTOR with your API name (e.g., Perplexity, Twitter, etc.)
|
|
3
|
+
|
|
4
|
+
// ============================================
|
|
5
|
+
// Configuration
|
|
6
|
+
// ============================================
|
|
7
|
+
|
|
8
|
+
export interface ConnectorConfig {
|
|
9
|
+
apiKey?: string; // For API key authentication
|
|
10
|
+
token?: string; // Alias for apiKey (some APIs use 'token')
|
|
11
|
+
apiSecret?: string; // Optional - some APIs need only a key
|
|
12
|
+
accessToken?: string; // For OAuth2 authentication
|
|
13
|
+
baseUrl?: string; // Override default base URL
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// ============================================
|
|
17
|
+
// OAuth2 Types
|
|
18
|
+
// ============================================
|
|
19
|
+
|
|
20
|
+
export interface OAuth2Config {
|
|
21
|
+
clientId: string;
|
|
22
|
+
clientSecret: string;
|
|
23
|
+
redirectUri?: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface OAuth2Tokens {
|
|
27
|
+
accessToken: string;
|
|
28
|
+
refreshToken?: string;
|
|
29
|
+
expiresAt: number;
|
|
30
|
+
tokenType?: string;
|
|
31
|
+
scope?: string;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// ============================================
|
|
35
|
+
// Common Types
|
|
36
|
+
// ============================================
|
|
37
|
+
|
|
38
|
+
export type OutputFormat = 'json' | 'pretty';
|
|
39
|
+
|
|
40
|
+
export interface PaginatedResponse<T> {
|
|
41
|
+
data: T[];
|
|
42
|
+
nextToken?: string;
|
|
43
|
+
hasMore: boolean;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// ============================================
|
|
47
|
+
// Example Resource Types
|
|
48
|
+
// ============================================
|
|
49
|
+
|
|
50
|
+
// Replace with your API's resource types
|
|
51
|
+
export interface ExampleResource {
|
|
52
|
+
id: string;
|
|
53
|
+
name: string;
|
|
54
|
+
createdAt: string;
|
|
55
|
+
updatedAt: string;
|
|
56
|
+
// Add more fields as needed
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export interface ExampleListResponse {
|
|
60
|
+
items: ExampleResource[];
|
|
61
|
+
nextPageToken?: string;
|
|
62
|
+
total?: number;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export interface ExampleCreateParams {
|
|
66
|
+
name: string;
|
|
67
|
+
// Add more params as needed
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// ============================================
|
|
71
|
+
// API Error Types
|
|
72
|
+
// ============================================
|
|
73
|
+
|
|
74
|
+
export interface ApiErrorDetail {
|
|
75
|
+
code: string;
|
|
76
|
+
message: string;
|
|
77
|
+
field?: string;
|
|
78
|
+
resource?: string;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export class ConnectorApiError extends Error {
|
|
82
|
+
public readonly statusCode: number;
|
|
83
|
+
public readonly errors?: ApiErrorDetail[];
|
|
84
|
+
public readonly documentationUrl?: string;
|
|
85
|
+
public readonly requestId?: string;
|
|
86
|
+
|
|
87
|
+
constructor(
|
|
88
|
+
message: string,
|
|
89
|
+
statusCode: number,
|
|
90
|
+
options?: {
|
|
91
|
+
errors?: ApiErrorDetail[];
|
|
92
|
+
documentationUrl?: string;
|
|
93
|
+
requestId?: string;
|
|
94
|
+
}
|
|
95
|
+
) {
|
|
96
|
+
super(message);
|
|
97
|
+
this.name = 'ConnectorApiError';
|
|
98
|
+
this.statusCode = statusCode;
|
|
99
|
+
this.errors = options?.errors;
|
|
100
|
+
this.documentationUrl = options?.documentationUrl;
|
|
101
|
+
this.requestId = options?.requestId;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Check if this is a rate limit error
|
|
106
|
+
*/
|
|
107
|
+
isRateLimited(): boolean {
|
|
108
|
+
return this.statusCode === 429;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Check if this is a server error
|
|
113
|
+
*/
|
|
114
|
+
isServerError(): boolean {
|
|
115
|
+
return this.statusCode >= 500 && this.statusCode < 600;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Check if this is a client error
|
|
120
|
+
*/
|
|
121
|
+
isClientError(): boolean {
|
|
122
|
+
return this.statusCode >= 400 && this.statusCode < 500;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Check if this is an authentication error
|
|
127
|
+
*/
|
|
128
|
+
isAuthError(): boolean {
|
|
129
|
+
return this.statusCode === 401 || this.statusCode === 403;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Check if this is a not found error
|
|
134
|
+
*/
|
|
135
|
+
isNotFound(): boolean {
|
|
136
|
+
return this.statusCode === 404;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Get a user-friendly error message
|
|
141
|
+
*/
|
|
142
|
+
getUserMessage(): string {
|
|
143
|
+
switch (this.statusCode) {
|
|
144
|
+
case 400:
|
|
145
|
+
return 'Bad request. Please check your input.';
|
|
146
|
+
case 401:
|
|
147
|
+
return 'Authentication failed. Please check your API key or login again.';
|
|
148
|
+
case 403:
|
|
149
|
+
return 'Access denied. You do not have permission to perform this action.';
|
|
150
|
+
case 404:
|
|
151
|
+
return 'Resource not found.';
|
|
152
|
+
case 429:
|
|
153
|
+
return 'Rate limit exceeded. Please wait and try again.';
|
|
154
|
+
case 500:
|
|
155
|
+
return 'Server error. Please try again later.';
|
|
156
|
+
case 502:
|
|
157
|
+
case 503:
|
|
158
|
+
case 504:
|
|
159
|
+
return 'Service temporarily unavailable. Please try again later.';
|
|
160
|
+
default:
|
|
161
|
+
return this.message;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Convert to JSON for logging/debugging
|
|
167
|
+
*/
|
|
168
|
+
toJSON(): Record<string, unknown> {
|
|
169
|
+
return {
|
|
170
|
+
name: this.name,
|
|
171
|
+
message: this.message,
|
|
172
|
+
statusCode: this.statusCode,
|
|
173
|
+
errors: this.errors,
|
|
174
|
+
documentationUrl: this.documentationUrl,
|
|
175
|
+
requestId: this.requestId,
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Parse an API error response into a ConnectorApiError
|
|
182
|
+
* Handles various common API error response formats
|
|
183
|
+
*/
|
|
184
|
+
export function parseApiError(
|
|
185
|
+
response: unknown,
|
|
186
|
+
statusCode: number
|
|
187
|
+
): ConnectorApiError {
|
|
188
|
+
// Handle string responses
|
|
189
|
+
if (typeof response === 'string') {
|
|
190
|
+
return new ConnectorApiError(response, statusCode);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// Handle null/undefined
|
|
194
|
+
if (!response || typeof response !== 'object') {
|
|
195
|
+
return new ConnectorApiError(`HTTP ${statusCode} Error`, statusCode);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
const data = response as Record<string, unknown>;
|
|
199
|
+
|
|
200
|
+
// Extract message from various common formats
|
|
201
|
+
const message =
|
|
202
|
+
(data.message as string) ||
|
|
203
|
+
(data.error as string) ||
|
|
204
|
+
((data.error as Record<string, unknown>)?.message as string) ||
|
|
205
|
+
(data.error_description as string) ||
|
|
206
|
+
(data.detail as string) ||
|
|
207
|
+
`HTTP ${statusCode} Error`;
|
|
208
|
+
|
|
209
|
+
// Extract errors array if present
|
|
210
|
+
let errors: ApiErrorDetail[] | undefined;
|
|
211
|
+
if (Array.isArray(data.errors)) {
|
|
212
|
+
errors = data.errors.map((e: Record<string, unknown>) => ({
|
|
213
|
+
code: String(e.code || e.error || 'unknown'),
|
|
214
|
+
message: String(e.message || e.description || 'Unknown error'),
|
|
215
|
+
field: e.field as string,
|
|
216
|
+
resource: e.resource as string,
|
|
217
|
+
}));
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// Extract documentation URL
|
|
221
|
+
const documentationUrl =
|
|
222
|
+
(data.documentation_url as string) ||
|
|
223
|
+
(data.docs_url as string) ||
|
|
224
|
+
(data.help_url as string);
|
|
225
|
+
|
|
226
|
+
// Extract request ID
|
|
227
|
+
const requestId =
|
|
228
|
+
(data.request_id as string) ||
|
|
229
|
+
(data.requestId as string) ||
|
|
230
|
+
(data.trace_id as string);
|
|
231
|
+
|
|
232
|
+
return new ConnectorApiError(message, statusCode, {
|
|
233
|
+
errors,
|
|
234
|
+
documentationUrl,
|
|
235
|
+
requestId,
|
|
236
|
+
});
|
|
237
|
+
}
|
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
import { createServer } from 'http';
|
|
2
|
+
import type { OAuth2Tokens, OAuth2Config } from '../types';
|
|
3
|
+
import { saveOAuthTokens, loadOAuthTokens, getOAuthConfig } from './config';
|
|
4
|
+
|
|
5
|
+
// ============================================
|
|
6
|
+
// OAuth2 Authentication Utility
|
|
7
|
+
// ============================================
|
|
8
|
+
|
|
9
|
+
// TODO: Replace with your OAuth provider's endpoints
|
|
10
|
+
const DEFAULT_AUTH_URL = 'https://accounts.example.com/oauth2/authorize';
|
|
11
|
+
const DEFAULT_TOKEN_URL = 'https://accounts.example.com/oauth2/token';
|
|
12
|
+
|
|
13
|
+
// TODO: Define your OAuth scopes
|
|
14
|
+
const DEFAULT_SCOPES = ['read', 'write'].join(' ');
|
|
15
|
+
|
|
16
|
+
const REDIRECT_PORT = 8089;
|
|
17
|
+
const REDIRECT_URI = `http://localhost:${REDIRECT_PORT}/callback`;
|
|
18
|
+
|
|
19
|
+
export interface AuthResult {
|
|
20
|
+
success: boolean;
|
|
21
|
+
tokens?: OAuth2Tokens;
|
|
22
|
+
error?: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface AuthUrlOptions {
|
|
26
|
+
authUrl?: string;
|
|
27
|
+
scopes?: string;
|
|
28
|
+
state?: string;
|
|
29
|
+
extraParams?: Record<string, string>;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Generate the OAuth2 authorization URL
|
|
34
|
+
*/
|
|
35
|
+
export function getAuthUrl(options: AuthUrlOptions = {}): string {
|
|
36
|
+
const config = getOAuthConfig();
|
|
37
|
+
if (!config?.clientId) {
|
|
38
|
+
throw new Error('OAuth client ID not configured. Run "config set-credentials" first.');
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const authUrl = options.authUrl || DEFAULT_AUTH_URL;
|
|
42
|
+
const scopes = options.scopes || DEFAULT_SCOPES;
|
|
43
|
+
|
|
44
|
+
const params = new URLSearchParams({
|
|
45
|
+
client_id: config.clientId,
|
|
46
|
+
redirect_uri: REDIRECT_URI,
|
|
47
|
+
response_type: 'code',
|
|
48
|
+
scope: scopes,
|
|
49
|
+
access_type: 'offline',
|
|
50
|
+
prompt: 'consent', // Force consent to get refresh token
|
|
51
|
+
...(options.state && { state: options.state }),
|
|
52
|
+
...options.extraParams,
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
return `${authUrl}?${params.toString()}`;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Exchange authorization code for tokens
|
|
60
|
+
*/
|
|
61
|
+
export async function exchangeCodeForTokens(
|
|
62
|
+
code: string,
|
|
63
|
+
tokenUrl: string = DEFAULT_TOKEN_URL
|
|
64
|
+
): Promise<OAuth2Tokens> {
|
|
65
|
+
const config = getOAuthConfig();
|
|
66
|
+
|
|
67
|
+
if (!config?.clientId || !config?.clientSecret) {
|
|
68
|
+
throw new Error('OAuth credentials not configured');
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const response = await fetch(tokenUrl, {
|
|
72
|
+
method: 'POST',
|
|
73
|
+
headers: {
|
|
74
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
75
|
+
},
|
|
76
|
+
body: new URLSearchParams({
|
|
77
|
+
code,
|
|
78
|
+
client_id: config.clientId,
|
|
79
|
+
client_secret: config.clientSecret,
|
|
80
|
+
redirect_uri: REDIRECT_URI,
|
|
81
|
+
grant_type: 'authorization_code',
|
|
82
|
+
}),
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
if (!response.ok) {
|
|
86
|
+
const error = await response.json().catch(() => ({ error: response.statusText }));
|
|
87
|
+
throw new Error(`Token exchange failed: ${error.error_description || error.error}`);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const data = await response.json();
|
|
91
|
+
|
|
92
|
+
const tokens: OAuth2Tokens = {
|
|
93
|
+
accessToken: data.access_token,
|
|
94
|
+
refreshToken: data.refresh_token,
|
|
95
|
+
expiresAt: Date.now() + data.expires_in * 1000,
|
|
96
|
+
tokenType: data.token_type,
|
|
97
|
+
scope: data.scope,
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
return tokens;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Refresh the access token using the refresh token
|
|
105
|
+
*/
|
|
106
|
+
export async function refreshAccessToken(
|
|
107
|
+
tokenUrl: string = DEFAULT_TOKEN_URL
|
|
108
|
+
): Promise<OAuth2Tokens> {
|
|
109
|
+
const config = getOAuthConfig();
|
|
110
|
+
const currentTokens = loadOAuthTokens();
|
|
111
|
+
|
|
112
|
+
if (!config?.clientId || !config?.clientSecret) {
|
|
113
|
+
throw new Error('OAuth credentials not configured');
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
if (!currentTokens?.refreshToken) {
|
|
117
|
+
throw new Error('No refresh token available. Please login again.');
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const response = await fetch(tokenUrl, {
|
|
121
|
+
method: 'POST',
|
|
122
|
+
headers: {
|
|
123
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
124
|
+
},
|
|
125
|
+
body: new URLSearchParams({
|
|
126
|
+
client_id: config.clientId,
|
|
127
|
+
client_secret: config.clientSecret,
|
|
128
|
+
refresh_token: currentTokens.refreshToken,
|
|
129
|
+
grant_type: 'refresh_token',
|
|
130
|
+
}),
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
if (!response.ok) {
|
|
134
|
+
const error = await response.json().catch(() => ({ error: response.statusText }));
|
|
135
|
+
throw new Error(`Token refresh failed: ${error.error_description || error.error}`);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const data = await response.json();
|
|
139
|
+
|
|
140
|
+
const tokens: OAuth2Tokens = {
|
|
141
|
+
accessToken: data.access_token,
|
|
142
|
+
refreshToken: data.refresh_token || currentTokens.refreshToken, // Keep original if not returned
|
|
143
|
+
expiresAt: Date.now() + data.expires_in * 1000,
|
|
144
|
+
tokenType: data.token_type,
|
|
145
|
+
scope: data.scope || currentTokens.scope,
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
saveOAuthTokens(tokens);
|
|
149
|
+
return tokens;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Start a local HTTP server to receive the OAuth callback
|
|
154
|
+
*/
|
|
155
|
+
export function startCallbackServer(): Promise<AuthResult> {
|
|
156
|
+
return new Promise((resolve) => {
|
|
157
|
+
const server = createServer(async (req, res) => {
|
|
158
|
+
const url = new URL(req.url || '', `http://localhost:${REDIRECT_PORT}`);
|
|
159
|
+
|
|
160
|
+
if (url.pathname === '/callback') {
|
|
161
|
+
const code = url.searchParams.get('code');
|
|
162
|
+
const error = url.searchParams.get('error');
|
|
163
|
+
|
|
164
|
+
if (error) {
|
|
165
|
+
res.writeHead(200, { 'Content-Type': 'text/html' });
|
|
166
|
+
res.end(`
|
|
167
|
+
<html>
|
|
168
|
+
<body style="font-family: sans-serif; display: flex; justify-content: center; align-items: center; height: 100vh; margin: 0;">
|
|
169
|
+
<div style="text-align: center;">
|
|
170
|
+
<h1 style="color: #dc3545;">Authentication Failed</h1>
|
|
171
|
+
<p>Error: ${error}</p>
|
|
172
|
+
<p>You can close this window.</p>
|
|
173
|
+
</div>
|
|
174
|
+
</body>
|
|
175
|
+
</html>
|
|
176
|
+
`);
|
|
177
|
+
server.close();
|
|
178
|
+
resolve({ success: false, error });
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
if (code) {
|
|
183
|
+
try {
|
|
184
|
+
const tokens = await exchangeCodeForTokens(code);
|
|
185
|
+
res.writeHead(200, { 'Content-Type': 'text/html' });
|
|
186
|
+
res.end(`
|
|
187
|
+
<html>
|
|
188
|
+
<body style="font-family: sans-serif; display: flex; justify-content: center; align-items: center; height: 100vh; margin: 0;">
|
|
189
|
+
<div style="text-align: center;">
|
|
190
|
+
<h1 style="color: #28a745;">Authentication Successful!</h1>
|
|
191
|
+
<p>You can close this window and return to the terminal.</p>
|
|
192
|
+
</div>
|
|
193
|
+
</body>
|
|
194
|
+
</html>
|
|
195
|
+
`);
|
|
196
|
+
server.close();
|
|
197
|
+
resolve({ success: true, tokens });
|
|
198
|
+
} catch (err) {
|
|
199
|
+
res.writeHead(200, { 'Content-Type': 'text/html' });
|
|
200
|
+
res.end(`
|
|
201
|
+
<html>
|
|
202
|
+
<body style="font-family: sans-serif; display: flex; justify-content: center; align-items: center; height: 100vh; margin: 0;">
|
|
203
|
+
<div style="text-align: center;">
|
|
204
|
+
<h1 style="color: #dc3545;">Authentication Failed</h1>
|
|
205
|
+
<p>Error: ${String(err)}</p>
|
|
206
|
+
<p>You can close this window.</p>
|
|
207
|
+
</div>
|
|
208
|
+
</body>
|
|
209
|
+
</html>
|
|
210
|
+
`);
|
|
211
|
+
server.close();
|
|
212
|
+
resolve({ success: false, error: String(err) });
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
server.listen(REDIRECT_PORT, () => {
|
|
219
|
+
// Server is ready
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
// Timeout after 5 minutes
|
|
223
|
+
setTimeout(() => {
|
|
224
|
+
server.close();
|
|
225
|
+
resolve({ success: false, error: 'Authentication timed out' });
|
|
226
|
+
}, 5 * 60 * 1000);
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Get a valid access token, refreshing if necessary
|
|
232
|
+
* @param tokenUrl - Token endpoint URL for refresh
|
|
233
|
+
* @param bufferMs - Refresh buffer in ms (default: 5 minutes)
|
|
234
|
+
*/
|
|
235
|
+
export async function getValidAccessToken(
|
|
236
|
+
tokenUrl: string = DEFAULT_TOKEN_URL,
|
|
237
|
+
bufferMs: number = 5 * 60 * 1000
|
|
238
|
+
): Promise<string> {
|
|
239
|
+
const tokens = loadOAuthTokens();
|
|
240
|
+
|
|
241
|
+
if (!tokens) {
|
|
242
|
+
throw new Error('Not authenticated. Run "auth login" first.');
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// Check if token is expired or will expire within buffer time
|
|
246
|
+
if (Date.now() >= tokens.expiresAt - bufferMs) {
|
|
247
|
+
const newTokens = await refreshAccessToken(tokenUrl);
|
|
248
|
+
return newTokens.accessToken;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
return tokens.accessToken;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Check if the user is authenticated
|
|
256
|
+
*/
|
|
257
|
+
export function isAuthenticated(): boolean {
|
|
258
|
+
const tokens = loadOAuthTokens();
|
|
259
|
+
return !!tokens?.accessToken;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Get the redirect URI for OAuth configuration
|
|
264
|
+
*/
|
|
265
|
+
export function getRedirectUri(): string {
|
|
266
|
+
return REDIRECT_URI;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* Get the redirect port for OAuth callback
|
|
271
|
+
*/
|
|
272
|
+
export function getRedirectPort(): number {
|
|
273
|
+
return REDIRECT_PORT;
|
|
274
|
+
}
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
// ============================================
|
|
2
|
+
// Bulk Operations Utility
|
|
3
|
+
// ============================================
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Options for bulk operations
|
|
7
|
+
*/
|
|
8
|
+
export interface BulkOperationOptions<T> {
|
|
9
|
+
/** Items to process */
|
|
10
|
+
items: T[];
|
|
11
|
+
/** Maximum concurrent operations (default: 10) */
|
|
12
|
+
concurrency?: number;
|
|
13
|
+
/** Dry run - preview without making changes */
|
|
14
|
+
dryRun?: boolean;
|
|
15
|
+
/** Progress callback */
|
|
16
|
+
onProgress?: (current: number, total: number, item: T) => void;
|
|
17
|
+
/** Error callback (return true to continue, false to stop) */
|
|
18
|
+
onError?: (error: Error, item: T) => boolean | void;
|
|
19
|
+
/** Delay between batches in ms (useful for rate limiting) */
|
|
20
|
+
batchDelay?: number;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Result of a bulk operation
|
|
25
|
+
*/
|
|
26
|
+
export interface BulkOperationResult<T, R = void> {
|
|
27
|
+
/** Total items processed */
|
|
28
|
+
total: number;
|
|
29
|
+
/** Successfully processed count */
|
|
30
|
+
success: number;
|
|
31
|
+
/** Failed count */
|
|
32
|
+
failed: number;
|
|
33
|
+
/** Skipped count (dry run) */
|
|
34
|
+
skipped: number;
|
|
35
|
+
/** List of errors */
|
|
36
|
+
errors: Array<{ item: T; error: string }>;
|
|
37
|
+
/** Successfully processed items with results */
|
|
38
|
+
results: Array<{ item: T; result: R }>;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Split an array into chunks of a given size
|
|
43
|
+
*/
|
|
44
|
+
export function chunkArray<T>(array: T[], size: number): T[][] {
|
|
45
|
+
const chunks: T[][] = [];
|
|
46
|
+
for (let i = 0; i < array.length; i += size) {
|
|
47
|
+
chunks.push(array.slice(i, i + size));
|
|
48
|
+
}
|
|
49
|
+
return chunks;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Sleep for a given number of milliseconds
|
|
54
|
+
*/
|
|
55
|
+
export function sleep(ms: number): Promise<void> {
|
|
56
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Execute a bulk operation with concurrency control
|
|
61
|
+
*
|
|
62
|
+
* @param options - Bulk operation options
|
|
63
|
+
* @param operation - Async function to execute for each item
|
|
64
|
+
* @returns Result summary
|
|
65
|
+
*
|
|
66
|
+
* @example
|
|
67
|
+
* ```typescript
|
|
68
|
+
* const result = await executeBulk(
|
|
69
|
+
* {
|
|
70
|
+
* items: users,
|
|
71
|
+
* concurrency: 5,
|
|
72
|
+
* onProgress: (current, total, user) => {
|
|
73
|
+
* console.log(`Processing ${current}/${total}: ${user.email}`);
|
|
74
|
+
* },
|
|
75
|
+
* },
|
|
76
|
+
* async (user) => {
|
|
77
|
+
* await api.updateUser(user.id, { status: 'active' });
|
|
78
|
+
* }
|
|
79
|
+
* );
|
|
80
|
+
* console.log(`Success: ${result.success}, Failed: ${result.failed}`);
|
|
81
|
+
* ```
|
|
82
|
+
*/
|
|
83
|
+
export async function executeBulk<T, R = void>(
|
|
84
|
+
options: BulkOperationOptions<T>,
|
|
85
|
+
operation: (item: T) => Promise<R>
|
|
86
|
+
): Promise<BulkOperationResult<T, R>> {
|
|
87
|
+
const {
|
|
88
|
+
items,
|
|
89
|
+
concurrency = 10,
|
|
90
|
+
dryRun = false,
|
|
91
|
+
onProgress,
|
|
92
|
+
onError,
|
|
93
|
+
batchDelay = 0,
|
|
94
|
+
} = options;
|
|
95
|
+
|
|
96
|
+
const result: BulkOperationResult<T, R> = {
|
|
97
|
+
total: items.length,
|
|
98
|
+
success: 0,
|
|
99
|
+
failed: 0,
|
|
100
|
+
skipped: 0,
|
|
101
|
+
errors: [],
|
|
102
|
+
results: [],
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
if (items.length === 0) {
|
|
106
|
+
return result;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Process in batches with concurrency control
|
|
110
|
+
const chunks = chunkArray(items, concurrency);
|
|
111
|
+
let shouldStop = false;
|
|
112
|
+
|
|
113
|
+
for (let chunkIndex = 0; chunkIndex < chunks.length && !shouldStop; chunkIndex++) {
|
|
114
|
+
const chunk = chunks[chunkIndex];
|
|
115
|
+
|
|
116
|
+
await Promise.all(
|
|
117
|
+
chunk.map(async (item) => {
|
|
118
|
+
if (shouldStop) return;
|
|
119
|
+
|
|
120
|
+
try {
|
|
121
|
+
if (dryRun) {
|
|
122
|
+
result.skipped++;
|
|
123
|
+
result.results.push({ item, result: undefined as R });
|
|
124
|
+
} else {
|
|
125
|
+
const opResult = await operation(item);
|
|
126
|
+
result.success++;
|
|
127
|
+
result.results.push({ item, result: opResult });
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
if (onProgress) {
|
|
131
|
+
onProgress(result.success + result.failed + result.skipped, result.total, item);
|
|
132
|
+
}
|
|
133
|
+
} catch (err) {
|
|
134
|
+
result.failed++;
|
|
135
|
+
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
136
|
+
result.errors.push({ item, error: errorMessage });
|
|
137
|
+
|
|
138
|
+
if (onError) {
|
|
139
|
+
const shouldContinue = onError(err instanceof Error ? err : new Error(errorMessage), item);
|
|
140
|
+
if (shouldContinue === false) {
|
|
141
|
+
shouldStop = true;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
})
|
|
146
|
+
);
|
|
147
|
+
|
|
148
|
+
// Add delay between batches if specified (for rate limiting)
|
|
149
|
+
if (batchDelay > 0 && chunkIndex < chunks.length - 1) {
|
|
150
|
+
await sleep(batchDelay);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
return result;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Execute operations sequentially (one at a time)
|
|
159
|
+
* Useful when operations must be processed in order
|
|
160
|
+
*/
|
|
161
|
+
export async function executeSequential<T, R = void>(
|
|
162
|
+
options: Omit<BulkOperationOptions<T>, 'concurrency'>,
|
|
163
|
+
operation: (item: T) => Promise<R>
|
|
164
|
+
): Promise<BulkOperationResult<T, R>> {
|
|
165
|
+
return executeBulk({ ...options, concurrency: 1 }, operation);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Create a progress reporter for bulk operations
|
|
170
|
+
*/
|
|
171
|
+
export function createProgressReporter(prefix: string = 'Processing') {
|
|
172
|
+
let lastPercent = -1;
|
|
173
|
+
|
|
174
|
+
return (current: number, total: number, _item: unknown): void => {
|
|
175
|
+
const percent = Math.floor((current / total) * 100);
|
|
176
|
+
if (percent !== lastPercent) {
|
|
177
|
+
lastPercent = percent;
|
|
178
|
+
process.stdout.write(`\r${prefix}: ${current}/${total} (${percent}%)`);
|
|
179
|
+
if (current === total) {
|
|
180
|
+
process.stdout.write('\n');
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Format bulk operation result for display
|
|
188
|
+
*/
|
|
189
|
+
export function formatBulkResult<T>(result: BulkOperationResult<T, unknown>): string {
|
|
190
|
+
const lines: string[] = [
|
|
191
|
+
`Total: ${result.total}`,
|
|
192
|
+
`Success: ${result.success}`,
|
|
193
|
+
`Failed: ${result.failed}`,
|
|
194
|
+
];
|
|
195
|
+
|
|
196
|
+
if (result.skipped > 0) {
|
|
197
|
+
lines.push(`Skipped (dry run): ${result.skipped}`);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
if (result.errors.length > 0) {
|
|
201
|
+
lines.push('');
|
|
202
|
+
lines.push('Errors:');
|
|
203
|
+
for (const { error } of result.errors.slice(0, 10)) {
|
|
204
|
+
lines.push(` - ${error}`);
|
|
205
|
+
}
|
|
206
|
+
if (result.errors.length > 10) {
|
|
207
|
+
lines.push(` ... and ${result.errors.length - 10} more errors`);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
return lines.join('\n');
|
|
212
|
+
}
|