@oomol-lab/open-connector 1.6.4 → 1.7.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/assets/open-connector/catalog/apps/aisa.json +6394 -0
- package/assets/open-connector/catalog/apps/baizhi_mcp.json +135 -0
- package/assets/open-connector/catalog/apps/bilibili.json +2586 -0
- package/assets/open-connector/catalog/apps/bitwarden.json +1423 -0
- package/assets/open-connector/catalog/apps/cronfree.json +303 -0
- package/assets/open-connector/catalog/apps/cufinder.json +368 -0
- package/assets/open-connector/catalog/apps/discordbot.json +2888 -248
- package/assets/open-connector/catalog/apps/drawio_mcp.json +192 -0
- package/assets/open-connector/catalog/apps/firecrawl.json +176 -32
- package/assets/open-connector/catalog/apps/framejet.json +273 -0
- package/assets/open-connector/catalog/apps/futunn.json +2967 -0
- package/assets/open-connector/catalog/apps/gmail.json +298 -0
- package/assets/open-connector/catalog/apps/google_analytics.json +4 -1
- package/assets/open-connector/catalog/apps/google_bigquery.json +4 -1
- package/assets/open-connector/catalog/apps/google_search_console.json +4 -1
- package/assets/open-connector/catalog/apps/googlecalendar.json +4 -1
- package/assets/open-connector/catalog/apps/googlechat.json +694 -7
- package/assets/open-connector/catalog/apps/googledocs.json +4 -1
- package/assets/open-connector/catalog/apps/googlesheets.json +4 -1
- package/assets/open-connector/catalog/apps/granola.json +23 -0
- package/assets/open-connector/catalog/apps/investoday_mcp.json +22228 -197
- package/assets/open-connector/catalog/apps/ixspy.json +2274 -0
- package/assets/open-connector/catalog/apps/keepa.json +3534 -987
- package/assets/open-connector/catalog/apps/linear.json +17 -0
- package/assets/open-connector/catalog/apps/linear_mcp.json +510 -0
- package/assets/open-connector/catalog/apps/moneyforward_mcp.json +1432 -0
- package/assets/open-connector/catalog/apps/notion.json +446 -1
- package/assets/open-connector/catalog/apps/notion_mcp.json +490 -0
- package/assets/open-connector/catalog/apps/odoo.json +619 -0
- package/assets/open-connector/catalog/apps/osv.json +663 -0
- package/assets/open-connector/catalog/apps/outlook.json +30 -8
- package/assets/open-connector/catalog/apps/outlook_calendar.json +23 -2
- package/assets/open-connector/catalog/apps/pdf_vector.json +329 -0
- package/assets/open-connector/catalog/apps/postiz.json +334 -0
- package/assets/open-connector/catalog/apps/proworkflow.json +529 -0
- package/assets/open-connector/catalog/apps/pulsetic.json +406 -0
- package/assets/open-connector/catalog/apps/sellersprite_mcp.json +2 -1
- package/assets/open-connector/catalog/apps/shopee.json +1785 -0
- package/assets/open-connector/catalog/apps/slack.json +304 -0
- package/assets/open-connector/catalog/apps/slackbot.json +295 -0
- package/assets/open-connector/catalog/apps/speak_ai.json +360 -0
- package/assets/open-connector/catalog/apps/tencent_docs.json +3 -0
- package/assets/open-connector/catalog/apps/wisburg.json +1528 -0
- package/assets/open-connector/catalog/apps/youtube.json +4 -1
- package/assets/open-connector/catalog/apps/zerotier.json +3319 -0
- package/assets/open-connector/catalog/apps/zyhub.json +1585 -0
- package/assets/open-connector/catalog/apps-index.json +1 -1
- package/package.json +1 -1
- package/src/core/cast.d.ts +10 -0
- package/src/core/cast.js +14 -0
- package/src/core/guarded-fetch.d.ts +6 -3
- package/src/core/guarded-fetch.js +6 -3
- package/src/core/json-schema.d.ts +5 -1
- package/src/core/json-schema.js +14 -2
- package/src/core/provider-scenarios.js +67 -13
- package/src/core/request.js +4 -1
- package/src/core/types.d.ts +10 -1
- package/src/mcp.js +10 -7
- package/src/oauth/oauth-client-config-service.d.ts +16 -1
- package/src/oauth/oauth-client-config-service.js +59 -3
- package/src/oauth/oauth-credential-refresh-service.js +3 -1
- package/src/oauth/oauth-flow-service.d.ts +6 -0
- package/src/oauth/oauth-flow-service.js +21 -4
- package/src/oauth/oauth-token.d.ts +13 -0
- package/src/providers/17track/runtime.js +1 -1
- package/src/providers/action-contracts.generated.d.ts +32 -8
- package/src/providers/aifinmarket/runtime.js +1 -1
- package/src/providers/aisa/executors.d.ts +4 -0
- package/src/providers/aisa/executors.js +864 -0
- package/src/providers/aliyun_ack/runtime.js +1 -1
- package/src/providers/aliyun_sls/runtime.js +1 -1
- package/src/providers/app_store_server/runtime-helpers.js +1 -1
- package/src/providers/baizhi_mcp/actions.d.ts +2 -0
- package/src/providers/baizhi_mcp/actions.js +40 -0
- package/src/providers/baizhi_mcp/executors.d.ts +3 -0
- package/src/providers/baizhi_mcp/executors.js +102 -0
- package/src/providers/bazhuayu/runtime.js +2 -2
- package/src/providers/bilibili/articles.d.ts +3 -0
- package/src/providers/bilibili/articles.js +369 -0
- package/src/providers/bilibili/executors.d.ts +8 -0
- package/src/providers/bilibili/executors.js +262 -0
- package/src/providers/bilibili/runtime.d.ts +95 -0
- package/src/providers/bilibili/runtime.js +236 -0
- package/src/providers/bilibili/upload.d.ts +14 -0
- package/src/providers/bilibili/upload.js +107 -0
- package/src/providers/bitwarden/executors.d.ts +3 -0
- package/src/providers/bitwarden/executors.js +146 -0
- package/src/providers/cloudflare_browser_rendering/executors.js +1 -1
- package/src/providers/cloudflare_dns/executors.js +1 -1
- package/src/providers/{cloudflare-current-user.js → cloudflare_dns/runtime-user.js} +2 -2
- package/src/providers/cloudflare_dns/runtime.d.ts +1 -1
- package/src/providers/cloudflare_dns/runtime.js +1 -1
- package/src/providers/cloudflare_r2/executors.js +1 -1
- package/src/providers/cloudflare_r2/runtime.d.ts +1 -1
- package/src/providers/cloudflare_r2/runtime.js +1 -1
- package/src/providers/cloudflare_worker/executors.js +1 -1
- package/src/providers/cloudflare_worker/runtime.d.ts +1 -1
- package/src/providers/cloudflare_worker/runtime.js +1 -1
- package/src/providers/cronfree/executors.d.ts +4 -0
- package/src/providers/cronfree/executors.js +89 -0
- package/src/providers/cufinder/executors.d.ts +4 -0
- package/src/providers/cufinder/executors.js +105 -0
- package/src/providers/dingtalk_mcp/runtime.js +1 -1
- package/src/providers/discordbot/executors.d.ts +0 -9
- package/src/providers/discordbot/executors.js +9 -101
- package/src/providers/discordbot/runtime-guilds.d.ts +3 -0
- package/src/providers/discordbot/runtime-guilds.js +464 -0
- package/src/providers/discordbot/runtime.d.ts +42 -0
- package/src/providers/discordbot/runtime.js +124 -0
- package/src/providers/drawio_mcp/executors.d.ts +3 -0
- package/src/providers/drawio_mcp/executors.js +19 -0
- package/src/providers/drawio_mcp/runtime.d.ts +8 -0
- package/src/providers/drawio_mcp/runtime.js +175 -0
- package/src/providers/elevenreader/runtime.js +6 -5
- package/src/providers/fastmoss_mcp/executors.js +2 -2
- package/src/providers/finchina/executors.js +1 -1
- package/src/providers/framejet/executors.d.ts +4 -0
- package/src/providers/framejet/executors.js +19 -0
- package/src/providers/framejet/runtime.d.ts +16 -0
- package/src/providers/framejet/runtime.js +148 -0
- package/src/providers/futunn/actions.d.ts +2 -0
- package/src/providers/futunn/actions.js +152 -0
- package/src/providers/futunn/actions.research.d.ts +2 -0
- package/src/providers/futunn/actions.research.js +423 -0
- package/src/providers/futunn/actions.trading.d.ts +2 -0
- package/src/providers/futunn/actions.trading.js +204 -0
- package/src/providers/futunn/executors.d.ts +3 -0
- package/src/providers/futunn/executors.js +24 -0
- package/src/providers/futunn/runtime-research.d.ts +3 -0
- package/src/providers/futunn/runtime-research.js +133 -0
- package/src/providers/futunn/runtime-trading.d.ts +3 -0
- package/src/providers/futunn/runtime-trading.js +39 -0
- package/src/providers/futunn/runtime.d.ts +2 -0
- package/src/providers/futunn/runtime.js +50 -0
- package/src/providers/futunn/transport.d.ts +11 -0
- package/src/providers/futunn/transport.js +76 -0
- package/src/providers/gildata/executors.js +1 -1
- package/src/providers/gmail/attachment-stream.d.ts +2 -0
- package/src/providers/gmail/attachment-stream.js +162 -0
- package/src/providers/gmail/executors.d.ts +3 -1
- package/src/providers/gmail/executors.js +54 -26
- package/src/providers/gmail/message.d.ts +6 -8
- package/src/providers/gmail/message.js +4 -0
- package/src/providers/gmail/scopes.d.ts +32 -0
- package/src/providers/gmail/scopes.js +37 -0
- package/src/providers/google_analytics/executors.js +2 -2
- package/src/providers/google_analytics/scopes.js +6 -1
- package/src/providers/google_bigquery/executors.js +2 -2
- package/src/providers/google_bigquery/scopes.js +2 -0
- package/src/providers/google_search_console/executors.js +2 -2
- package/src/providers/google_search_console/scopes.js +2 -0
- package/src/providers/googleads/executors.js +1 -1
- package/src/providers/googleads/scopes.d.ts +1 -3
- package/src/providers/googleads/scopes.js +2 -4
- package/src/providers/googlecalendar/executors.js +1 -1
- package/src/providers/googlecalendar/runtime-shared.js +2 -2
- package/src/providers/googlecalendar/scopes.js +2 -0
- package/src/providers/googlechat/executors.d.ts +1 -3
- package/src/providers/googlechat/executors.js +219 -28
- package/src/providers/googlechat/members.d.ts +55 -0
- package/src/providers/googlechat/members.js +293 -0
- package/src/providers/googlechat/runtime.d.ts +16 -0
- package/src/providers/googlechat/runtime.js +25 -0
- package/src/providers/googlechat/scopes.d.ts +22 -3
- package/src/providers/googlechat/scopes.js +30 -7
- package/src/providers/googledocs/executors.js +1 -1
- package/src/providers/googledrive/executors.js +2 -2
- package/src/providers/{google-auth.d.ts → googledrive/runtime-auth.d.ts} +2 -2
- package/src/providers/{google-auth.js → googledrive/runtime-auth.js} +3 -3
- package/src/providers/googledrive/runtime-collaboration.js +1 -1
- package/src/providers/{google-runtime.d.ts → googledrive/runtime-request.d.ts} +1 -1
- package/src/providers/{google-runtime.js → googledrive/runtime-request.js} +2 -2
- package/src/providers/googleforms/executors.js +2 -2
- package/src/providers/googleforms/scopes.d.ts +0 -3
- package/src/providers/googleforms/scopes.js +2 -6
- package/src/providers/googlemeet/executors.js +2 -2
- package/src/providers/googlemeet/scopes.d.ts +0 -3
- package/src/providers/googlemeet/scopes.js +2 -6
- package/src/providers/googlesheets/executors.js +1 -1
- package/src/providers/googlesheets/scopes.js +2 -0
- package/src/providers/googleslides/executors.js +2 -2
- package/src/providers/googleslides/scopes.d.ts +0 -3
- package/src/providers/googleslides/scopes.js +2 -6
- package/src/providers/googletasks/executors.js +2 -2
- package/src/providers/googletasks/scopes.d.ts +0 -3
- package/src/providers/googletasks/scopes.js +2 -6
- package/src/providers/granola/actions.js +2 -0
- package/src/providers/granola/runtime-mcp.js +2 -0
- package/src/providers/granola/runtime.js +2 -2
- package/src/providers/indiegogo/executors.js +1 -1
- package/src/providers/investoday_mcp/executors.js +2 -2
- package/src/providers/investoday_mcp/official-actions.js +19 -372
- package/src/providers/investoday_mcp/official-tool-types.d.ts +10 -0
- package/src/providers/investoday_mcp/official-tool-types.js +4 -0
- package/src/providers/investoday_mcp/official-tools/charts.d.ts +2 -0
- package/src/providers/investoday_mcp/official-tools/charts.js +32 -0
- package/src/providers/investoday_mcp/official-tools/fixed-income.d.ts +2 -0
- package/src/providers/investoday_mcp/official-tools/fixed-income.js +326 -0
- package/src/providers/investoday_mcp/official-tools/funds.d.ts +2 -0
- package/src/providers/investoday_mcp/official-tools/funds.js +1085 -0
- package/src/providers/investoday_mcp/official-tools/hong-kong.d.ts +2 -0
- package/src/providers/investoday_mcp/official-tools/hong-kong.js +275 -0
- package/src/providers/investoday_mcp/official-tools/indices-industries.d.ts +2 -0
- package/src/providers/investoday_mcp/official-tools/indices-industries.js +642 -0
- package/src/providers/investoday_mcp/official-tools/macro-calendar.d.ts +2 -0
- package/src/providers/investoday_mcp/official-tools/macro-calendar.js +398 -0
- package/src/providers/investoday_mcp/official-tools/other.d.ts +2 -0
- package/src/providers/investoday_mcp/official-tools/other.js +131 -0
- package/src/providers/investoday_mcp/official-tools/research-news.d.ts +2 -0
- package/src/providers/investoday_mcp/official-tools/research-news.js +302 -0
- package/src/providers/investoday_mcp/official-tools/stocks.d.ts +2 -0
- package/src/providers/investoday_mcp/official-tools/stocks.js +2829 -0
- package/src/providers/ixspy/executors.d.ts +3 -0
- package/src/providers/ixspy/executors.js +220 -0
- package/src/providers/iyunbiao/runtime.js +2 -2
- package/src/providers/keepa/actions.d.ts +1 -1
- package/src/providers/keepa/actions.js +371 -149
- package/src/providers/keepa/deal-attributes.d.ts +1 -0
- package/src/providers/keepa/deal-attributes.js +26 -0
- package/src/providers/keepa/runtime.js +212 -18
- package/src/providers/kingdee/executors.js +1 -1
- package/src/providers/kingdee/runtime.js +2 -2
- package/src/providers/kuaidi100/runtime.js +1 -1
- package/src/providers/linear/executors.js +12 -9
- package/src/providers/linear_mcp/actions.d.ts +47 -0
- package/src/providers/linear_mcp/actions.js +102 -0
- package/src/providers/linear_mcp/endpoints.d.ts +3 -0
- package/src/providers/linear_mcp/endpoints.js +3 -0
- package/src/providers/linear_mcp/executors.d.ts +3 -0
- package/src/providers/linear_mcp/executors.js +9 -0
- package/src/providers/linear_mcp/runtime-mcp.d.ts +15 -0
- package/src/providers/linear_mcp/runtime-mcp.js +238 -0
- package/src/providers/lingxing_mcp/runtime.js +1 -1
- package/src/providers/mcdonalds_cn_mcp/runtime.js +1 -1
- package/src/providers/metabase/mcp-runtime.js +1 -1
- package/src/providers/moneyforward_mcp/executors.d.ts +3 -0
- package/src/providers/moneyforward_mcp/executors.js +11 -0
- package/src/providers/moneyforward_mcp/manifest.d.ts +31 -0
- package/src/providers/moneyforward_mcp/manifest.js +204 -0
- package/src/providers/moneyforward_mcp/runtime.d.ts +18 -0
- package/src/providers/moneyforward_mcp/runtime.js +296 -0
- package/src/providers/notion/executors.js +39 -3
- package/src/providers/notion_mcp/endpoints.d.ts +3 -0
- package/src/providers/notion_mcp/endpoints.js +3 -0
- package/src/providers/notion_mcp/executors.d.ts +3 -0
- package/src/providers/notion_mcp/executors.js +176 -0
- package/src/providers/notion_mcp/mcp-response.d.ts +86 -0
- package/src/providers/notion_mcp/mcp-response.js +314 -0
- package/src/providers/odoo/executors.d.ts +3 -0
- package/src/providers/odoo/executors.js +32 -0
- package/src/providers/odoo/runtime.d.ts +16 -0
- package/src/providers/odoo/runtime.js +140 -0
- package/src/providers/osv/executors.d.ts +2 -0
- package/src/providers/osv/executors.js +11 -0
- package/src/providers/osv/runtime.d.ts +9 -0
- package/src/providers/osv/runtime.js +66 -0
- package/src/providers/outlook/executors.js +27 -6
- package/src/providers/outlook/microsoft-graph.js +17 -6
- package/src/providers/outlook_calendar/executors.js +24 -7
- package/src/providers/patsnap_mcp/executors.js +2 -2
- package/src/providers/pdf_vector/executors.d.ts +4 -0
- package/src/providers/pdf_vector/executors.js +108 -0
- package/src/providers/postiz/executors.d.ts +4 -0
- package/src/providers/postiz/executors.js +83 -0
- package/src/providers/provider-runtime.d.ts +18 -0
- package/src/providers/provider-runtime.js +60 -4
- package/src/providers/proworkflow/executors.d.ts +4 -0
- package/src/providers/proworkflow/executors.js +135 -0
- package/src/providers/pulsetic/executors.d.ts +4 -0
- package/src/providers/pulsetic/executors.js +126 -0
- package/src/providers/registry.generated.js +24 -0
- package/src/providers/scrape_creators/catalog.js +1 -1
- package/src/providers/scrape_creators/runtime.js +1 -1
- package/src/providers/sellersprite_mcp/executors.js +2 -2
- package/src/providers/sellersprite_mcp/official-actions.js +3 -2
- package/src/providers/sentry/runtime.js +6 -7
- package/src/providers/shopee/actions.d.ts +2 -0
- package/src/providers/shopee/actions.js +474 -0
- package/src/providers/shopee/executors.d.ts +3 -0
- package/src/providers/shopee/executors.js +85 -0
- package/src/providers/shopee/runtime.d.ts +79 -0
- package/src/providers/shopee/runtime.js +693 -0
- package/src/providers/slack/actions.js +69 -3
- package/src/providers/slack/runtime.js +116 -21
- package/src/providers/slickdeals/runtime.js +1 -1
- package/src/providers/sorftime_mcp/executors.js +2 -2
- package/src/providers/speak_ai/executors.d.ts +3 -0
- package/src/providers/speak_ai/executors.js +187 -0
- package/src/providers/tikhub/bounded-response.d.ts +1 -1
- package/src/providers/tikhub/bounded-response.js +8 -34
- package/src/providers/tikhub/endpoint-catalog.js +1 -1
- package/src/providers/tikhub/runtime.js +2 -2
- package/src/providers/tongdaxin/executors.js +79 -15
- package/src/providers/tushare/runtime.js +22 -9
- package/src/providers/uspto/executors.js +1 -1
- package/src/providers/waffo/executors.js +1 -1
- package/src/providers/waffo/runtime.js +1 -1
- package/src/providers/wecom_mcp/runtime.js +1 -1
- package/src/providers/wisburg/executors.d.ts +4 -0
- package/src/providers/wisburg/executors.js +13 -0
- package/src/providers/wisburg/runtime.d.ts +12 -0
- package/src/providers/wisburg/runtime.js +133 -0
- package/src/providers/xydc_mcp/executors.js +2 -2
- package/src/providers/youtube/executors.js +1 -1
- package/src/providers/youtube/runtime.js +1 -1
- package/src/providers/zerotier/executors.d.ts +9 -0
- package/src/providers/zerotier/executors.js +629 -0
- package/src/providers/zerotier/runtime.d.ts +54 -0
- package/src/providers/zerotier/runtime.js +207 -0
- package/src/providers/zyhub/executors.d.ts +3 -0
- package/src/providers/zyhub/executors.js +240 -0
- package/src/server/api/openapi.js +12 -2
- package/src/server/api/runtime-api.d.ts +8 -1
- package/src/server/api/runtime-api.js +19 -1
- package/src/server/connect-server.js +10 -2
- package/src/server/files/transit-files.d.ts +3 -2
- package/src/server/files/transit-files.js +31 -35
- package/src/providers/sentry/scopes.d.ts +0 -1
- package/src/providers/sentry/scopes.js +0 -7
- /package/src/providers/{cloudflare-current-user.d.ts → cloudflare_dns/runtime-user.d.ts} +0 -0
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { optionalRecord, optionalString } from "../../core/cast.js";
|
|
2
|
-
import { ProviderRequestError, readProviderErrorTextBody, readProviderJsonBody, runProviderRequest, setSearchParams, } from "../provider-runtime.js";
|
|
2
|
+
import { ProviderRequestError, readProviderErrorTextBody, readProviderJsonBody, runProviderRequest, setSearchParams, withRetryAfterSeconds, } from "../provider-runtime.js";
|
|
3
3
|
const graphBaseUrl = "https://graph.microsoft.com/v1.0/";
|
|
4
4
|
const graphOrigin = "https://graph.microsoft.com";
|
|
5
5
|
/**
|
|
@@ -41,10 +41,13 @@ export async function microsoftGraphJson(pathOrUrl, options) {
|
|
|
41
41
|
}));
|
|
42
42
|
}
|
|
43
43
|
function buildMicrosoftGraphUrl(pathOrUrl, query, allowNextLink) {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
44
|
+
// Code passes plain relative paths such as `me/messages`; anything else is a
|
|
45
|
+
// caller-supplied link that must pass the allowlist. A value with any scheme
|
|
46
|
+
// is parsed on its own: resolved against the base, `https:me/drive` would
|
|
47
|
+
// turn into a same-origin path that never reaches the allowlist.
|
|
48
|
+
const absolute = /^[a-z][a-z\d+.-]*:/i.test(pathOrUrl);
|
|
49
|
+
const requiresNextLinkValidation = absolute || !/^[a-z\d]/i.test(pathOrUrl);
|
|
50
|
+
const url = parseMicrosoftGraphUrl(pathOrUrl, absolute ? undefined : graphBaseUrl);
|
|
48
51
|
if (url.origin !== graphOrigin || url.protocol !== "https:") {
|
|
49
52
|
throw new ProviderRequestError(400, "Microsoft Graph URL must target https://graph.microsoft.com");
|
|
50
53
|
}
|
|
@@ -54,6 +57,14 @@ function buildMicrosoftGraphUrl(pathOrUrl, query, allowNextLink) {
|
|
|
54
57
|
setSearchParams(url, query ?? {});
|
|
55
58
|
return url;
|
|
56
59
|
}
|
|
60
|
+
function parseMicrosoftGraphUrl(pathOrUrl, base) {
|
|
61
|
+
try {
|
|
62
|
+
return new URL(pathOrUrl, base);
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
throw new ProviderRequestError(400, "Microsoft Graph URL must target https://graph.microsoft.com");
|
|
66
|
+
}
|
|
67
|
+
}
|
|
57
68
|
async function microsoftGraphResponseError(response, options) {
|
|
58
69
|
const text = await readProviderErrorTextBody(response, `${options.label} error response`);
|
|
59
70
|
let code = "";
|
|
@@ -69,5 +80,5 @@ async function microsoftGraphResponseError(response, options) {
|
|
|
69
80
|
// Keep the upstream text when it is not JSON.
|
|
70
81
|
}
|
|
71
82
|
}
|
|
72
|
-
return new ProviderRequestError(response.status, options.refineErrorMessage?.(code, message) ?? message);
|
|
83
|
+
return new ProviderRequestError(response.status, options.refineErrorMessage?.(code, message) ?? message, withRetryAfterSeconds(response));
|
|
73
84
|
}
|
|
@@ -97,19 +97,26 @@ async function getCalendar(input, context) {
|
|
|
97
97
|
return microsoftGraphJson(`me/calendars/${encodePathSegment(id)}`, requestOptions(context, "get calendar", { $select: stringList(input.select) }));
|
|
98
98
|
}
|
|
99
99
|
async function listEvents(input, context, calendarView) {
|
|
100
|
-
|
|
100
|
+
// Graph v1.0 tracks calendar changes through calendarView/delta only
|
|
101
|
+
// (events/delta is a beta preview); a nextLink continues the current round,
|
|
102
|
+
// a deltaLink starts the next one.
|
|
103
|
+
const followUpLink = optionalString(input.nextLink) ?? (calendarView ? optionalString(input.deltaLink) : undefined);
|
|
104
|
+
const delta = calendarView && input.delta === true;
|
|
101
105
|
const calendar = optionalString(input.calendarId);
|
|
102
106
|
const collection = calendarView ? "calendarView" : "events";
|
|
103
|
-
const path = calendar ? `me/calendars/${encodePathSegment(calendar)}/${collection}` : `me/${collection}`;
|
|
104
|
-
const query =
|
|
107
|
+
const path = `${calendar ? `me/calendars/${encodePathSegment(calendar)}/${collection}` : `me/${collection}`}${delta ? "/delta" : ""}`;
|
|
108
|
+
const query = followUpLink
|
|
105
109
|
? undefined
|
|
106
110
|
: compactObject({
|
|
107
111
|
...listQuery(input),
|
|
108
112
|
startDateTime: calendarView ? optionalString(input.startDateTime) : undefined,
|
|
109
113
|
endDateTime: calendarView ? optionalString(input.endDateTime) : undefined,
|
|
110
114
|
});
|
|
111
|
-
const payload = await microsoftGraphJson(
|
|
112
|
-
|
|
115
|
+
const payload = await microsoftGraphJson(followUpLink ?? path, requestOptions(context, `list ${calendarView ? "calendar view" : "events"}`, query, allowEventNextLink, preferTimeZone(input)));
|
|
116
|
+
// Rows stay raw so a delta round's `@removed` markers pass through. Only the
|
|
117
|
+
// calendar view has delta rounds, so only it reports a deltaLink.
|
|
118
|
+
const output = listOutput(payload, "events");
|
|
119
|
+
return calendarView ? { ...output, deltaLink: optionalString(payload["@odata.deltaLink"]) ?? null } : output;
|
|
113
120
|
}
|
|
114
121
|
async function getEvent(input, context) {
|
|
115
122
|
const id = requiredInputString(input.eventId, "eventId");
|
|
@@ -212,16 +219,26 @@ function allowEventNextLink(pathname) {
|
|
|
212
219
|
return true;
|
|
213
220
|
}
|
|
214
221
|
const segments = pathname.split("/").filter(Boolean);
|
|
222
|
+
// Delta links end in a trailing `delta` segment on the same three shapes,
|
|
223
|
+
// and only for calendarView: events/delta is not on Graph v1.0.
|
|
224
|
+
const delta = segments.length > 3 && segments[segments.length - 1] === "delta";
|
|
225
|
+
if (delta) {
|
|
226
|
+
segments.pop();
|
|
227
|
+
}
|
|
228
|
+
const isEventCollection = (segment) => segment === "calendarView" || (!delta && segment === "events");
|
|
229
|
+
if (delta && segments.length === 3 && segments[0] === "v1.0" && segments[1] === "me") {
|
|
230
|
+
return isEventCollection(segments[2]);
|
|
231
|
+
}
|
|
215
232
|
if (segments.length === 4 &&
|
|
216
233
|
segments[0] === "v1.0" &&
|
|
217
234
|
segments[1] === "me" &&
|
|
218
235
|
segments[2]?.startsWith("calendars('") &&
|
|
219
236
|
segments[2].endsWith("')")) {
|
|
220
|
-
return segments[3]
|
|
237
|
+
return isEventCollection(segments[3]);
|
|
221
238
|
}
|
|
222
239
|
return (segments.length === 5 &&
|
|
223
240
|
segments[0] === "v1.0" &&
|
|
224
241
|
segments[1] === "me" &&
|
|
225
242
|
segments[2] === "calendars" &&
|
|
226
|
-
(segments[4]
|
|
243
|
+
isEventCollection(segments[4]));
|
|
227
244
|
}
|
|
@@ -37,7 +37,7 @@ export const proxy = defineProviderProxy({
|
|
|
37
37
|
return endpoints.has(new URL(endpoint, patsnapMcpOrigin).href);
|
|
38
38
|
},
|
|
39
39
|
skipDnsValidation: true,
|
|
40
|
-
redirect: "
|
|
40
|
+
redirect: "manual",
|
|
41
41
|
customizeRequest({ headers }) {
|
|
42
42
|
headers.set("accept", "application/json, text/event-stream");
|
|
43
43
|
},
|
|
@@ -95,7 +95,7 @@ async function withClient(context, server, run) {
|
|
|
95
95
|
transport: "streamable_http",
|
|
96
96
|
fetcher: context.fetcher,
|
|
97
97
|
headers: { authorization: `Bearer ${context.apiKey}`, "user-agent": providerUserAgent },
|
|
98
|
-
redirect: "
|
|
98
|
+
redirect: "manual",
|
|
99
99
|
signal: context.signal,
|
|
100
100
|
mapError: mapMcpError,
|
|
101
101
|
}, run);
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { CredentialValidators, ProviderExecutors, ProviderProxyExecutor } from "../../core/types.ts";
|
|
2
|
+
export declare const executors: ProviderExecutors;
|
|
3
|
+
export declare const proxy: ProviderProxyExecutor;
|
|
4
|
+
export declare const credentialValidators: CredentialValidators;
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { compactObject, optionalRecord, optionalString } from "../../core/cast.js";
|
|
2
|
+
import { defineApiKeyProviderExecutors, defineProviderProxy, providerInputError, ProviderRequestError, providerUserAgent, readProviderJsonBody, requiredInputString, runProviderRequest, } from "../provider-runtime.js";
|
|
3
|
+
const service = "pdf_vector";
|
|
4
|
+
const pdfVectorApiBaseUrl = "https://global.pdfvector.com/api";
|
|
5
|
+
const credentialUrl = "https://global.pdfvector.com/rpc/authenticate/validateCredential";
|
|
6
|
+
const handlers = {
|
|
7
|
+
parse_document(input, context) {
|
|
8
|
+
return executeDocumentAction("/document/parse", input, context);
|
|
9
|
+
},
|
|
10
|
+
ask_document(input, context) {
|
|
11
|
+
return executeDocumentAction("/document/ask", input, context);
|
|
12
|
+
},
|
|
13
|
+
extract_document(input, context) {
|
|
14
|
+
return executeDocumentAction("/document/extract", input, context);
|
|
15
|
+
},
|
|
16
|
+
};
|
|
17
|
+
export const executors = defineApiKeyProviderExecutors(service, handlers, {
|
|
18
|
+
skipDnsValidation: true,
|
|
19
|
+
});
|
|
20
|
+
export const proxy = defineProviderProxy({
|
|
21
|
+
service,
|
|
22
|
+
baseUrl: pdfVectorApiBaseUrl,
|
|
23
|
+
auth: { type: "bearer" },
|
|
24
|
+
skipDnsValidation: true,
|
|
25
|
+
customizeRequest({ headers }) {
|
|
26
|
+
headers.set("accept", "application/json");
|
|
27
|
+
headers.set("x-pdfvector-source", "oomol-connector");
|
|
28
|
+
},
|
|
29
|
+
});
|
|
30
|
+
export const credentialValidators = {
|
|
31
|
+
async apiKey(input, { fetcher, signal }) {
|
|
32
|
+
const payload = await requestJson({
|
|
33
|
+
url: credentialUrl,
|
|
34
|
+
body: {},
|
|
35
|
+
context: { apiKey: input.apiKey, fetcher, signal },
|
|
36
|
+
phase: "validate",
|
|
37
|
+
});
|
|
38
|
+
const version = optionalString(optionalRecord(optionalRecord(payload)?.json)?.version);
|
|
39
|
+
if (!version)
|
|
40
|
+
throw new ProviderRequestError(502, "PDF Vector credential validation response did not include a server version");
|
|
41
|
+
return {
|
|
42
|
+
profile: { accountId: "pdf-vector-api-key", displayName: "PDF Vector API Key" },
|
|
43
|
+
grantedScopes: [],
|
|
44
|
+
metadata: {
|
|
45
|
+
apiBaseUrl: pdfVectorApiBaseUrl,
|
|
46
|
+
validationEndpoint: "/rpc/authenticate/validateCredential",
|
|
47
|
+
serverVersion: version,
|
|
48
|
+
},
|
|
49
|
+
};
|
|
50
|
+
},
|
|
51
|
+
};
|
|
52
|
+
function executeDocumentAction(path, input, context) {
|
|
53
|
+
return requestJson({
|
|
54
|
+
url: `${pdfVectorApiBaseUrl}${path}`,
|
|
55
|
+
body: compactObject({
|
|
56
|
+
url: requiredInputString(input.documentUrl, "documentUrl"),
|
|
57
|
+
model: optionalString(input.model),
|
|
58
|
+
includePages: input.includePages,
|
|
59
|
+
question: optionalString(input.question),
|
|
60
|
+
prompt: optionalString(input.prompt),
|
|
61
|
+
schema: optionalRecord(input.schema),
|
|
62
|
+
}),
|
|
63
|
+
documentId: optionalString(input.documentId),
|
|
64
|
+
context,
|
|
65
|
+
phase: "execute",
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
async function requestJson(input) {
|
|
69
|
+
return runProviderRequest({ signal: input.context.signal, label: "PDF Vector" }, async (signal) => {
|
|
70
|
+
const response = await input.context.fetcher(input.url, {
|
|
71
|
+
method: "POST",
|
|
72
|
+
headers: {
|
|
73
|
+
accept: "application/json",
|
|
74
|
+
authorization: `Bearer ${input.context.apiKey}`,
|
|
75
|
+
"content-type": "application/json",
|
|
76
|
+
"user-agent": providerUserAgent,
|
|
77
|
+
"x-pdfvector-source": "oomol-connector",
|
|
78
|
+
...(input.documentId ? { "x-pdfvector-document-id": input.documentId } : {}),
|
|
79
|
+
},
|
|
80
|
+
body: JSON.stringify(input.body),
|
|
81
|
+
signal,
|
|
82
|
+
});
|
|
83
|
+
const payload = await readProviderJsonBody(response, {
|
|
84
|
+
emptyBody: null,
|
|
85
|
+
invalidJsonMessage: "PDF Vector returned invalid JSON",
|
|
86
|
+
});
|
|
87
|
+
if (!response.ok)
|
|
88
|
+
throw createError(response.status, payload, input.phase);
|
|
89
|
+
return payload;
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
function createError(status, payload, phase) {
|
|
93
|
+
const record = optionalRecord(payload);
|
|
94
|
+
const json = optionalRecord(record?.json);
|
|
95
|
+
const error = optionalRecord(record?.error);
|
|
96
|
+
const message = optionalString(json?.message) ??
|
|
97
|
+
optionalString(error?.message) ??
|
|
98
|
+
optionalString(record?.message) ??
|
|
99
|
+
`PDF Vector request failed with status ${status}`;
|
|
100
|
+
const code = optionalString(json?.code) ?? optionalString(error?.code) ?? optionalString(record?.code);
|
|
101
|
+
if (phase === "validate" && status === 401 && code === "UNAUTHORIZED")
|
|
102
|
+
return providerInputError(message);
|
|
103
|
+
if (status === 429)
|
|
104
|
+
return new ProviderRequestError(429, message, payload);
|
|
105
|
+
if (phase === "execute" && [400, 404, 422].includes(status))
|
|
106
|
+
return providerInputError(message);
|
|
107
|
+
return new ProviderRequestError(status || 502, message, payload);
|
|
108
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { CredentialValidators, ProviderExecutors, ProviderProxyExecutor } from "../../core/types.ts";
|
|
2
|
+
export declare const executors: ProviderExecutors;
|
|
3
|
+
export declare const credentialValidators: CredentialValidators;
|
|
4
|
+
export declare const proxy: ProviderProxyExecutor;
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { optionalRecord, optionalString } from "../../core/cast.js";
|
|
2
|
+
import { defineApiKeyProviderExecutors, defineProviderProxy, ProviderRequestError, providerUserAgent, readProviderJsonBody, requiredResponseRecord, runProviderRequest, } from "../provider-runtime.js";
|
|
3
|
+
const service = "postiz";
|
|
4
|
+
const baseUrl = "https://api.postiz.com/public/v1";
|
|
5
|
+
const handlers = {
|
|
6
|
+
list_integrations: async (input, context) => readPostizCollection(await request(context, "/integrations", { query: { group: optionalString(input.group) } }), "integrations"),
|
|
7
|
+
list_posts: async (input, context) => requiredResponseRecord(await request(context, "/posts", {
|
|
8
|
+
query: {
|
|
9
|
+
startDate: optionalString(input.startDate),
|
|
10
|
+
endDate: optionalString(input.endDate),
|
|
11
|
+
customer: optionalString(input.customer),
|
|
12
|
+
},
|
|
13
|
+
}), "Postiz posts"),
|
|
14
|
+
upload_from_url: async (input, context) => requiredResponseRecord(await request(context, "/upload-from-url", { method: "POST", body: { url: input.url } }), "Postiz uploaded media"),
|
|
15
|
+
create_post: async (input, context) => readPostizCollection(await request(context, "/posts", { method: "POST", body: input }), "created posts"),
|
|
16
|
+
};
|
|
17
|
+
export const executors = defineApiKeyProviderExecutors(service, handlers, {
|
|
18
|
+
skipDnsValidation: true,
|
|
19
|
+
});
|
|
20
|
+
export const credentialValidators = {
|
|
21
|
+
async apiKey(input, { fetcher, signal }) {
|
|
22
|
+
const result = optionalRecord(await request({ apiKey: input.apiKey, fetcher, signal }, "/is-connected"));
|
|
23
|
+
if (result?.connected !== true) {
|
|
24
|
+
throw new ProviderRequestError(502, "Postiz did not confirm the API key connection");
|
|
25
|
+
}
|
|
26
|
+
return {
|
|
27
|
+
profile: { accountId: "api_key", displayName: "Postiz API Key" },
|
|
28
|
+
grantedScopes: [],
|
|
29
|
+
metadata: { apiBaseUrl: baseUrl },
|
|
30
|
+
};
|
|
31
|
+
},
|
|
32
|
+
};
|
|
33
|
+
export const proxy = defineProviderProxy({
|
|
34
|
+
service,
|
|
35
|
+
baseUrl,
|
|
36
|
+
auth: { type: "api_key_header", name: "Authorization" },
|
|
37
|
+
skipDnsValidation: true,
|
|
38
|
+
customizeRequest({ headers }) {
|
|
39
|
+
if (!headers.has("accept"))
|
|
40
|
+
headers.set("accept", "application/json");
|
|
41
|
+
},
|
|
42
|
+
});
|
|
43
|
+
async function request(context, path, options = {}) {
|
|
44
|
+
return runProviderRequest({ signal: context.signal, label: "Postiz" }, async (signal) => {
|
|
45
|
+
const url = new URL(`${baseUrl}${path}`);
|
|
46
|
+
for (const [key, value] of Object.entries(options.query ?? {})) {
|
|
47
|
+
if (value !== undefined)
|
|
48
|
+
url.searchParams.set(key, value);
|
|
49
|
+
}
|
|
50
|
+
const headers = new Headers({
|
|
51
|
+
accept: "application/json",
|
|
52
|
+
authorization: context.apiKey,
|
|
53
|
+
"user-agent": providerUserAgent,
|
|
54
|
+
});
|
|
55
|
+
if (options.body !== undefined)
|
|
56
|
+
headers.set("content-type", "application/json");
|
|
57
|
+
const response = await context.fetcher(url, {
|
|
58
|
+
method: options.method,
|
|
59
|
+
headers,
|
|
60
|
+
body: options.body === undefined ? undefined : JSON.stringify(options.body),
|
|
61
|
+
signal,
|
|
62
|
+
});
|
|
63
|
+
const payload = await readProviderJsonBody(response, {
|
|
64
|
+
emptyBody: null,
|
|
65
|
+
invalidJsonMessage: "Postiz returned invalid JSON",
|
|
66
|
+
invalidJsonFallback: response.ok ? undefined : (text) => text,
|
|
67
|
+
});
|
|
68
|
+
if (!response.ok) {
|
|
69
|
+
const details = optionalRecord(payload);
|
|
70
|
+
const message = optionalString(details?.message) ??
|
|
71
|
+
optionalString(details?.error) ??
|
|
72
|
+
optionalString(payload) ??
|
|
73
|
+
`Postiz request failed with HTTP ${response.status}`;
|
|
74
|
+
throw new ProviderRequestError(response.status, message, payload);
|
|
75
|
+
}
|
|
76
|
+
return payload;
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
function readPostizCollection(value, label) {
|
|
80
|
+
if (!Array.isArray(value))
|
|
81
|
+
throw new ProviderRequestError(502, `Postiz returned invalid ${label}`);
|
|
82
|
+
return value;
|
|
83
|
+
}
|
|
@@ -284,6 +284,8 @@ export interface ProviderProxyDefinition {
|
|
|
284
284
|
auth: ProviderProxyAuth;
|
|
285
285
|
allowedEndpoint?: (endpoint: string) => boolean;
|
|
286
286
|
customizeRequest?: (input: ProviderProxyRequestCustomizationInput) => Promise<void> | void;
|
|
287
|
+
/** Parse a failed HTTP response using the same provider error rules as actions. */
|
|
288
|
+
readError?: (response: Response) => Promise<ProviderRequestError>;
|
|
287
289
|
/** Provider-specific credential/signature headers that redirects must not forward cross-origin. */
|
|
288
290
|
sensitiveHeaders?: readonly string[];
|
|
289
291
|
/** Exact code-controlled origins that `customizeRequest` may select in addition to the resolved base origin. */
|
|
@@ -299,6 +301,7 @@ export interface ProviderProxyDefinition {
|
|
|
299
301
|
/** Deliberate provider-specific response byte cap; defaults to the shared 20 MiB limit. */
|
|
300
302
|
maxResponseBytes?: number;
|
|
301
303
|
}
|
|
304
|
+
export declare const defaultProviderJsonMaxResponseBytes: number;
|
|
302
305
|
export declare function createProviderProxyUrl(baseUrl: string, endpointInput: unknown, queryInput?: unknown, allowedOrigins?: readonly string[]): URL;
|
|
303
306
|
export declare function normalizeProviderProxyEndpoint(endpointInput: unknown, allowedOrigins?: readonly string[]): string;
|
|
304
307
|
export declare function normalizeProviderProxyQuery(queryInput: unknown): Record<string, string>;
|
|
@@ -369,6 +372,21 @@ export declare function setSearchParams(url: URL, query: Record<string, string |
|
|
|
369
372
|
* Read a bounded provider error response body as text.
|
|
370
373
|
*/
|
|
371
374
|
export declare function readProviderErrorTextBody(response: Response, fieldName: string): Promise<string>;
|
|
375
|
+
/**
|
|
376
|
+
* Read a `Retry-After` header as whole seconds from now: an integer delay
|
|
377
|
+
* verbatim, an HTTP-date as the seconds until that instant (never negative).
|
|
378
|
+
* Examples: `"73" => 73`; a date 90 s ahead `=> 90`; absent or unparseable
|
|
379
|
+
* `=> undefined`.
|
|
380
|
+
*/
|
|
381
|
+
export declare function readRetryAfterSeconds(headers: Headers, now?: number): number | undefined;
|
|
382
|
+
/**
|
|
383
|
+
* Attach a rate-limited response's `Retry-After` to its error details in the
|
|
384
|
+
* shape Slack established, `details.retryAfterSeconds`, so every provider's
|
|
385
|
+
* 429 (and 503) reaches the action envelope with the same pacing hint.
|
|
386
|
+
* Other statuses, and responses without a usable header, return `details`
|
|
387
|
+
* untouched; non-record details are kept under `body`.
|
|
388
|
+
*/
|
|
389
|
+
export declare function withRetryAfterSeconds(response: Response, details?: unknown): unknown;
|
|
372
390
|
/**
|
|
373
391
|
* Read a JSON provider response or raise a structured provider request error.
|
|
374
392
|
*/
|
|
@@ -125,7 +125,7 @@ const blockedProxyRequestHeaders = new Set([
|
|
|
125
125
|
"transfer-encoding",
|
|
126
126
|
]);
|
|
127
127
|
const defaultProviderProxyMaxResponseBytes = 20 * 1024 * 1024;
|
|
128
|
-
const defaultProviderJsonMaxResponseBytes = 20 * 1024 * 1024;
|
|
128
|
+
export const defaultProviderJsonMaxResponseBytes = 20 * 1024 * 1024;
|
|
129
129
|
const defaultProviderErrorMaxResponseBytes = 64 * 1024;
|
|
130
130
|
const defaultProviderRequestTimeoutMs = 30_000;
|
|
131
131
|
export function createProviderProxyUrl(baseUrl, endpointInput, queryInput, allowedOrigins) {
|
|
@@ -264,6 +264,11 @@ export async function readProviderProxyResponse(response, options = {}) {
|
|
|
264
264
|
};
|
|
265
265
|
}
|
|
266
266
|
export async function readProviderProxyErrorMessage(response, fallbackMessage) {
|
|
267
|
+
// An unfollowed redirect's body usually echoes its `Location`, which may carry a signed target URL.
|
|
268
|
+
if (response.status >= 300 && response.status < 400) {
|
|
269
|
+
await response.body?.cancel().catch(() => undefined);
|
|
270
|
+
return fallbackMessage;
|
|
271
|
+
}
|
|
267
272
|
const bytes = await readBoundedResponseBytes(response, {
|
|
268
273
|
maxBytes: defaultProviderProxyMaxResponseBytes,
|
|
269
274
|
fieldName: "proxy error response",
|
|
@@ -356,6 +361,10 @@ export function defineProviderProxy(input) {
|
|
|
356
361
|
}
|
|
357
362
|
const response = await egressFetch(url, init);
|
|
358
363
|
if (!response.ok) {
|
|
364
|
+
// A provider error parser would surface an unfollowed redirect's body; the shared reader withholds it.
|
|
365
|
+
if (input.readError && (response.status < 300 || response.status >= 400)) {
|
|
366
|
+
throw await input.readError(response);
|
|
367
|
+
}
|
|
359
368
|
throw new ProviderRequestError(response.status, await readProviderProxyErrorMessage(response, `provider request failed with HTTP ${response.status}`));
|
|
360
369
|
}
|
|
361
370
|
return {
|
|
@@ -676,6 +685,52 @@ export async function readProviderErrorTextBody(response, fieldName) {
|
|
|
676
685
|
return "";
|
|
677
686
|
}
|
|
678
687
|
}
|
|
688
|
+
// The three HTTP-date forms of RFC 9110 section 5.6.7. Date.parse alone is too
|
|
689
|
+
// lenient for a header: V8 reads "wait 5" as 1 May 2001, and reads the zone-less
|
|
690
|
+
// asctime form as local time although every HTTP-date is UTC.
|
|
691
|
+
const imfFixdatePattern = /^(?:Mon|Tue|Wed|Thu|Fri|Sat|Sun), \d{2} (?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) \d{4} \d{2}:\d{2}:\d{2} GMT$/;
|
|
692
|
+
const rfc850DatePattern = /^(?:Mon|Tues|Wednes|Thurs|Fri|Satur|Sun)day, \d{2}-(?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)-\d{2} \d{2}:\d{2}:\d{2} GMT$/;
|
|
693
|
+
const asctimeDatePattern = /^(?:Mon|Tue|Wed|Thu|Fri|Sat|Sun) (?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) [ \d]\d \d{2}:\d{2}:\d{2} \d{4}$/;
|
|
694
|
+
/**
|
|
695
|
+
* Read a `Retry-After` header as whole seconds from now: an integer delay
|
|
696
|
+
* verbatim, an HTTP-date as the seconds until that instant (never negative).
|
|
697
|
+
* Examples: `"73" => 73`; a date 90 s ahead `=> 90`; absent or unparseable
|
|
698
|
+
* `=> undefined`.
|
|
699
|
+
*/
|
|
700
|
+
export function readRetryAfterSeconds(headers, now = Date.now()) {
|
|
701
|
+
const value = headers.get("retry-after")?.trim();
|
|
702
|
+
if (!value) {
|
|
703
|
+
return undefined;
|
|
704
|
+
}
|
|
705
|
+
if (/^\d+$/.test(value)) {
|
|
706
|
+
const seconds = Number(value);
|
|
707
|
+
return Number.isSafeInteger(seconds) ? seconds : undefined;
|
|
708
|
+
}
|
|
709
|
+
const retryAt = imfFixdatePattern.test(value) || rfc850DatePattern.test(value)
|
|
710
|
+
? Date.parse(value)
|
|
711
|
+
: asctimeDatePattern.test(value)
|
|
712
|
+
? Date.parse(`${value} GMT`)
|
|
713
|
+
: Number.NaN;
|
|
714
|
+
return Number.isFinite(retryAt) ? Math.max(0, Math.ceil((retryAt - now) / 1000)) : undefined;
|
|
715
|
+
}
|
|
716
|
+
/**
|
|
717
|
+
* Attach a rate-limited response's `Retry-After` to its error details in the
|
|
718
|
+
* shape Slack established, `details.retryAfterSeconds`, so every provider's
|
|
719
|
+
* 429 (and 503) reaches the action envelope with the same pacing hint.
|
|
720
|
+
* Other statuses, and responses without a usable header, return `details`
|
|
721
|
+
* untouched; non-record details are kept under `body`.
|
|
722
|
+
*/
|
|
723
|
+
export function withRetryAfterSeconds(response, details) {
|
|
724
|
+
if (response.status !== 429 && response.status !== 503) {
|
|
725
|
+
return details;
|
|
726
|
+
}
|
|
727
|
+
const retryAfterSeconds = readRetryAfterSeconds(response.headers);
|
|
728
|
+
if (retryAfterSeconds === undefined) {
|
|
729
|
+
return details;
|
|
730
|
+
}
|
|
731
|
+
const record = optionalRecord(details) ?? (details == null ? {} : { body: details });
|
|
732
|
+
return { ...record, retryAfterSeconds };
|
|
733
|
+
}
|
|
679
734
|
/**
|
|
680
735
|
* Read a JSON provider response or raise a structured provider request error.
|
|
681
736
|
*/
|
|
@@ -803,9 +858,10 @@ export function toProviderExecutionError(error, fallbackMessage) {
|
|
|
803
858
|
? "authorization_failed"
|
|
804
859
|
: error.status === 429
|
|
805
860
|
? "rate_limited"
|
|
806
|
-
:
|
|
807
|
-
|
|
808
|
-
|
|
861
|
+
: // A 3xx is an upstream redirect that `redirect: "manual"` surfaced unfollowed, not bad input.
|
|
862
|
+
error.status < 500 && (error.status < 300 || error.status >= 400)
|
|
863
|
+
? "invalid_input"
|
|
864
|
+
: "provider_error"),
|
|
809
865
|
message: error.message,
|
|
810
866
|
details: {
|
|
811
867
|
status: error.status,
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { CredentialValidators, ProviderExecutors, ProviderProxyExecutor } from "../../core/types.ts";
|
|
2
|
+
export declare const executors: ProviderExecutors;
|
|
3
|
+
export declare const credentialValidators: CredentialValidators;
|
|
4
|
+
export declare const proxy: ProviderProxyExecutor;
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { compactObject, looseArray, optionalRecord, optionalString } from "../../core/cast.js";
|
|
2
|
+
import { defineApiKeyProviderExecutors, defineProviderProxy, providerInputError, ProviderRequestError, providerUserAgent, readProviderJsonBody, requiredResponseRecord, runProviderRequest, } from "../provider-runtime.js";
|
|
3
|
+
const service = "proworkflow";
|
|
4
|
+
const baseUrl = "https://api.proworkflow.com/api/v4";
|
|
5
|
+
const handlers = {
|
|
6
|
+
list_projects: (input, context) => request(context, "/projects", { query: buildListQuery(input) }),
|
|
7
|
+
get_project: (input, context) => request(context, `/projects/${positiveInteger(input.projectId, "projectId")}`, {
|
|
8
|
+
query: { fields: input.fields },
|
|
9
|
+
}),
|
|
10
|
+
update_project: (input, context) => request(context, `/projects/${positiveInteger(input.projectId, "projectId")}`, {
|
|
11
|
+
method: "PUT",
|
|
12
|
+
body: requireUpdateFields(compactObject({
|
|
13
|
+
title: input.title,
|
|
14
|
+
number: input.number,
|
|
15
|
+
description: input.description,
|
|
16
|
+
startdate: input.startDate,
|
|
17
|
+
duedate: input.dueDate,
|
|
18
|
+
completedate: input.completedDate,
|
|
19
|
+
priorityid: input.priorityId,
|
|
20
|
+
managerid: input.managerId,
|
|
21
|
+
companyid: input.companyId,
|
|
22
|
+
notification: input.notification,
|
|
23
|
+
})),
|
|
24
|
+
}),
|
|
25
|
+
list_project_items: (input, context) => request(context, "/projects/items", {
|
|
26
|
+
query: { ...buildListQuery(input), projectid: input.projectId },
|
|
27
|
+
}),
|
|
28
|
+
get_project_item: (input, context) => request(context, `/projects/items/${positiveInteger(input.itemId, "itemId")}`),
|
|
29
|
+
update_project_item: (input, context) => request(context, `/projects/items/${positiveInteger(input.itemId, "itemId")}`, {
|
|
30
|
+
method: "PUT",
|
|
31
|
+
body: requireUpdateFields(compactObject({
|
|
32
|
+
name: input.name,
|
|
33
|
+
code: input.code,
|
|
34
|
+
description: input.description,
|
|
35
|
+
startdate: input.startDate,
|
|
36
|
+
duedate: input.dueDate,
|
|
37
|
+
completedate: input.completedDate,
|
|
38
|
+
status: input.status,
|
|
39
|
+
priorityid: input.priorityId,
|
|
40
|
+
manualPercentageComplete: input.percentComplete,
|
|
41
|
+
})),
|
|
42
|
+
}),
|
|
43
|
+
};
|
|
44
|
+
export const executors = defineApiKeyProviderExecutors(service, handlers, {
|
|
45
|
+
skipDnsValidation: true,
|
|
46
|
+
});
|
|
47
|
+
export const credentialValidators = {
|
|
48
|
+
async apiKey(input, { fetcher, signal }) {
|
|
49
|
+
const payload = requiredResponseRecord(await request({ apiKey: input.apiKey, fetcher, signal }, "/settings/account", {}, "validate"), "ProWorkflow account settings response");
|
|
50
|
+
const data = requiredResponseRecord(payload.data, "ProWorkflow account settings data");
|
|
51
|
+
return {
|
|
52
|
+
profile: { accountId: "api_key", displayName: optionalString(data.accounturl) ?? "ProWorkflow API Key" },
|
|
53
|
+
grantedScopes: [],
|
|
54
|
+
metadata: { apiBaseUrl: baseUrl, validationEndpoint: "/settings/account" },
|
|
55
|
+
};
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
export const proxy = defineProviderProxy({
|
|
59
|
+
service,
|
|
60
|
+
baseUrl,
|
|
61
|
+
auth: { type: "api_key_header", name: "apikey" },
|
|
62
|
+
skipDnsValidation: true,
|
|
63
|
+
customizeRequest({ headers }) {
|
|
64
|
+
if (!headers.has("accept"))
|
|
65
|
+
headers.set("accept", "application/json");
|
|
66
|
+
},
|
|
67
|
+
});
|
|
68
|
+
async function request(context, path, options = {}, phase = "execute") {
|
|
69
|
+
return runProviderRequest({ signal: context.signal, label: "ProWorkflow" }, async (signal) => {
|
|
70
|
+
const url = new URL(`${baseUrl}${path}`);
|
|
71
|
+
for (const [key, value] of Object.entries(options.query ?? {})) {
|
|
72
|
+
if (value != null && value !== "")
|
|
73
|
+
url.searchParams.set(key, String(value));
|
|
74
|
+
}
|
|
75
|
+
const response = await context.fetcher(url, {
|
|
76
|
+
method: options.method,
|
|
77
|
+
headers: {
|
|
78
|
+
accept: "application/json",
|
|
79
|
+
apikey: context.apiKey,
|
|
80
|
+
"content-type": "application/json",
|
|
81
|
+
"user-agent": providerUserAgent,
|
|
82
|
+
},
|
|
83
|
+
body: options.body === undefined ? undefined : JSON.stringify(options.body),
|
|
84
|
+
signal,
|
|
85
|
+
});
|
|
86
|
+
const payload = await readProviderJsonBody(response, {
|
|
87
|
+
emptyBody: {},
|
|
88
|
+
invalidJsonMessage: "ProWorkflow returned invalid JSON",
|
|
89
|
+
trimEmptyBody: false,
|
|
90
|
+
});
|
|
91
|
+
if (!response.ok)
|
|
92
|
+
throw createError(response.status, payload, phase);
|
|
93
|
+
return requiredResponseRecord(payload, "ProWorkflow response");
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
function buildListQuery(input) {
|
|
97
|
+
if ((input.pageNumber == null) !== (input.pageSize == null)) {
|
|
98
|
+
throw providerInputError("pageNumber and pageSize must be provided together");
|
|
99
|
+
}
|
|
100
|
+
return {
|
|
101
|
+
fields: input.fields,
|
|
102
|
+
q: input.q,
|
|
103
|
+
status: input.status,
|
|
104
|
+
sortby: input.sortBy,
|
|
105
|
+
sortorder: input.sortOrder,
|
|
106
|
+
pagenumber: input.pageNumber,
|
|
107
|
+
pagesize: input.pageSize,
|
|
108
|
+
includetotalrows: input.includeTotalRows,
|
|
109
|
+
companyid: input.companyId,
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
function requireUpdateFields(body) {
|
|
113
|
+
if (Object.keys(body).length === 0)
|
|
114
|
+
throw providerInputError("at least one update field is required");
|
|
115
|
+
return body;
|
|
116
|
+
}
|
|
117
|
+
function positiveInteger(value, fieldName) {
|
|
118
|
+
if (typeof value !== "number" || !Number.isInteger(value) || value < 1) {
|
|
119
|
+
throw providerInputError(`${fieldName} must be a positive integer`);
|
|
120
|
+
}
|
|
121
|
+
return value;
|
|
122
|
+
}
|
|
123
|
+
function createError(status, payload, phase) {
|
|
124
|
+
const record = optionalRecord(payload);
|
|
125
|
+
const messages = looseArray(record?.data)
|
|
126
|
+
.filter((item) => typeof item === "string" && item.trim() !== "")
|
|
127
|
+
.join("; ");
|
|
128
|
+
const message = messages || optionalString(record?.message) || `ProWorkflow request failed with ${status}`;
|
|
129
|
+
if (status === 429)
|
|
130
|
+
return new ProviderRequestError(429, message, payload);
|
|
131
|
+
if (phase === "execute" && [400, 404, 422].includes(status)) {
|
|
132
|
+
return new ProviderRequestError(400, message, payload, "invalid_input");
|
|
133
|
+
}
|
|
134
|
+
return new ProviderRequestError(status || 500, message, payload);
|
|
135
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { CredentialValidators, ProviderExecutors, ProviderProxyExecutor } from "../../core/types.ts";
|
|
2
|
+
export declare const executors: ProviderExecutors;
|
|
3
|
+
export declare const proxy: ProviderProxyExecutor;
|
|
4
|
+
export declare const credentialValidators: CredentialValidators;
|