mcp-scraper 0.66.3 → 0.66.5
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/CHANGELOG.md +19 -1
- package/README.md +1 -1
- package/dist/bin/api-server.cjs +304 -28
- package/dist/bin/api-server.js +1 -1
- package/dist/bin/mcp-scraper-cli.cjs +1 -1
- package/dist/bin/mcp-scraper-cli.js +1 -1
- package/dist/bin/mcp-scraper-install.cjs +2 -2
- package/dist/bin/mcp-scraper-install.js +2 -2
- package/dist/bin/mcp-stdio-server.cjs +22 -12
- package/dist/bin/mcp-stdio-server.js +3 -3
- package/dist/{chunk-LNLMOBP6.js → chunk-OJFWURGD.js} +21 -11
- package/dist/{chunk-JBFLOJGM.js → chunk-RSFMR7O7.js} +1 -1
- package/dist/{chunk-2XVDLF6H.js → chunk-TCOP5BT7.js} +1 -1
- package/dist/{server-D47PNEXR.js → server-LESUYFDI.js} +283 -18
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,23 @@ All notable changes to MCP Scraper are documented here. The format is based on [
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [0.66.5] - 2026-08-25
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
|
|
11
|
+
- Local Sourcebook submissions now distinguish accepted processing from publication, tell MCP clients to keep checking until the run is terminal, and return a plain-language completion message with the exact live listing URL.
|
|
12
|
+
|
|
13
|
+
## [0.66.4] - 2026-08-25
|
|
14
|
+
|
|
15
|
+
### Added
|
|
16
|
+
|
|
17
|
+
- Connected-service discovery now returns a tenant-owned `userLabel` plus verified provider account ID, email, name, and identity status so agents can select the intended account when one integration has multiple connections.
|
|
18
|
+
- Managed OAuth connections now retain non-secret provider identity metadata and support owner-scoped account names through the dashboard API.
|
|
19
|
+
|
|
20
|
+
### Fixed
|
|
21
|
+
|
|
22
|
+
- Nango tenant tags are no longer presented as the external provider account, preventing the MCP Scraper login from being mistaken for the Google, Slack, GitHub, Zoom, or other authorized identity.
|
|
23
|
+
|
|
7
24
|
## [0.66.3] - 2026-08-25
|
|
8
25
|
|
|
9
26
|
### Changed
|
|
@@ -1098,7 +1115,8 @@ All notable changes to MCP Scraper are documented here. The format is based on [
|
|
|
1098
1115
|
- Write actions remain unavailable until the account owner explicitly enables them.
|
|
1099
1116
|
- Provider-specific connection data is normalized into one agent-facing contract.
|
|
1100
1117
|
|
|
1101
|
-
[Unreleased]: https://github.com/VilovietaSEO/mcp-scraper/compare/v0.66.
|
|
1118
|
+
[Unreleased]: https://github.com/VilovietaSEO/mcp-scraper/compare/v0.66.4...HEAD
|
|
1119
|
+
[0.66.4]: https://github.com/VilovietaSEO/mcp-scraper/compare/v0.66.3...v0.66.4
|
|
1102
1120
|
[0.66.3]: https://github.com/VilovietaSEO/mcp-scraper/compare/v0.66.2...v0.66.3
|
|
1103
1121
|
[0.66.2]: https://github.com/VilovietaSEO/mcp-scraper/compare/v0.66.1...v0.66.2
|
|
1104
1122
|
[0.66.1]: https://github.com/VilovietaSEO/mcp-scraper/compare/v0.66.0...v0.66.1
|
package/README.md
CHANGED
|
@@ -159,7 +159,7 @@ Build the branded one-click bundle:
|
|
|
159
159
|
npm run build:mcpb
|
|
160
160
|
```
|
|
161
161
|
|
|
162
|
-
The generated bundle is written to `build/mcpb/mcp-scraper-<version>.mcpb` and copied to `public/downloads/` for the hosted download. The current public bundle is `https://mcpscraper.dev/downloads/mcp-scraper.mcpb` (`0.66.
|
|
162
|
+
The generated bundle is written to `build/mcpb/mcp-scraper-<version>.mcpb` and copied to `public/downloads/` for the hosted download. The current public bundle is `https://mcpscraper.dev/downloads/mcp-scraper.mcpb` (`0.66.5`, SHA-256 `958313b3c6a41b73a725bac75770f5d429d6a4969966f3051c86d51087f76993`). Install it by opening or dragging it into Claude Desktop. Claude displays the `MCP Scraper` install card, icon, API-key configuration field, and manually curated current-release message from the bundle manifest.
|
|
163
163
|
|
|
164
164
|
The MCPB install exposes every tool — web-intelligence plus all `browser_*` tools — through the one `mcp-scraper` server.
|
|
165
165
|
|
package/dist/bin/api-server.cjs
CHANGED
|
@@ -33148,12 +33148,39 @@ function localSourcebookPublicUrls(category, state, slug4) {
|
|
|
33148
33148
|
};
|
|
33149
33149
|
}
|
|
33150
33150
|
function localSourcebookPublicationLinks(submission) {
|
|
33151
|
+
const isLive = submission.publishedRevision !== null && submission.status !== "rejected" && submission.status !== "unpublished";
|
|
33151
33152
|
return {
|
|
33152
33153
|
...localSourcebookPublicUrls(submission.category, submission.state, submission.slug),
|
|
33153
33154
|
publicationStatus: submission.status,
|
|
33154
|
-
isLive
|
|
33155
|
+
isLive
|
|
33155
33156
|
};
|
|
33156
33157
|
}
|
|
33158
|
+
function localSourcebookPublicationMessage(submission) {
|
|
33159
|
+
const { profileUrl } = localSourcebookPublicUrls(submission.category, submission.state, submission.slug);
|
|
33160
|
+
if (submission.status === "published" && submission.publishedRevision !== null) {
|
|
33161
|
+
return `Your Local Sourcebook listing is live: ${profileUrl}`;
|
|
33162
|
+
}
|
|
33163
|
+
if (submission.status === "failed") {
|
|
33164
|
+
if (submission.publishedRevision !== null) {
|
|
33165
|
+
return `We couldn't refresh this Local Sourcebook listing, but the previous version is still live: ${profileUrl}`;
|
|
33166
|
+
}
|
|
33167
|
+
return "We couldn't publish this Local Sourcebook listing because evidence collection failed. Review the reported issue before trying again.";
|
|
33168
|
+
}
|
|
33169
|
+
if (submission.status === "rejected") {
|
|
33170
|
+
return "This Local Sourcebook listing was rejected and is not live.";
|
|
33171
|
+
}
|
|
33172
|
+
if (submission.status === "unpublished") {
|
|
33173
|
+
return "This Local Sourcebook listing has been unpublished and is not live.";
|
|
33174
|
+
}
|
|
33175
|
+
if (submission.publishedRevision !== null) {
|
|
33176
|
+
return `We are collecting and compiling refreshed evidence. The current Local Sourcebook listing remains live: ${profileUrl}`;
|
|
33177
|
+
}
|
|
33178
|
+
return "Submission received. We are collecting and compiling evidence, so this Local Sourcebook listing is not live yet. Check the submission status again in about 10 seconds.";
|
|
33179
|
+
}
|
|
33180
|
+
function localSourcebookStatusCheck(submission) {
|
|
33181
|
+
const required = submission.status === "queued" || submission.status === "enriching" || submission.status === "needs_review";
|
|
33182
|
+
return required ? { required: true, submissionId: submission.id, pollAfterSeconds: 10 } : { required: false, submissionId: submission.id };
|
|
33183
|
+
}
|
|
33157
33184
|
var DEFAULT_ROOT_DOMAIN, CATEGORY_PUBLIC_SLUGS;
|
|
33158
33185
|
var init_local_sourcebook_public_urls = __esm({
|
|
33159
33186
|
"src/api/local-sourcebook-public-urls.ts"() {
|
|
@@ -45265,7 +45292,7 @@ var init_local_sourcebook_routes = __esm({
|
|
|
45265
45292
|
persisted = true;
|
|
45266
45293
|
await persistLocalSourcebookTags(submission.id, Number(user.id), validation.normalizedTags, parsed.data.tagDecisions);
|
|
45267
45294
|
const dispatched = await dispatchLocalSourcebookAcquisition(submission.id).catch(() => false);
|
|
45268
|
-
return c.json({ ok: true, valid: true, submission, publicUrls: localSourcebookPublicationLinks(submission), normalizedTags: validation.normalizedTags, acquisition: { queued: true, dispatched }, acquisitionPlan: acquisitionPlan(identity.websiteUrl), billing: publicListingWriteBilling(billing) }, 202);
|
|
45295
|
+
return c.json({ ok: true, valid: true, message: localSourcebookPublicationMessage(submission), statusCheck: localSourcebookStatusCheck(submission), submission, publicUrls: localSourcebookPublicationLinks(submission), normalizedTags: validation.normalizedTags, acquisition: { queued: true, dispatched }, acquisitionPlan: acquisitionPlan(identity.websiteUrl), billing: publicListingWriteBilling(billing) }, 202);
|
|
45269
45296
|
} catch (error) {
|
|
45270
45297
|
if (billing && !persisted) await refundListingWrite(billing, "Local Sourcebook creation failed before persistence.").catch(() => null);
|
|
45271
45298
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -45314,7 +45341,7 @@ var init_local_sourcebook_routes = __esm({
|
|
|
45314
45341
|
});
|
|
45315
45342
|
persisted = true;
|
|
45316
45343
|
const dispatched = await dispatchLocalSourcebookAcquisition(submission.id).catch(() => false);
|
|
45317
|
-
return c.json({ ok: true, submission, publicUrls: localSourcebookPublicationLinks(submission), acquisition: { queued: true, dispatched }, acquisitionPlan: acquisitionPlan(input.websiteUrl), billing: publicListingWriteBilling(billing) }, 202);
|
|
45344
|
+
return c.json({ ok: true, message: localSourcebookPublicationMessage(submission), statusCheck: localSourcebookStatusCheck(submission), submission, publicUrls: localSourcebookPublicationLinks(submission), acquisition: { queued: true, dispatched }, acquisitionPlan: acquisitionPlan(input.websiteUrl), billing: publicListingWriteBilling(billing) }, 202);
|
|
45318
45345
|
} catch (error) {
|
|
45319
45346
|
if (billing && !persisted) await refundListingWrite(billing, "Local Sourcebook creation failed before persistence.").catch(() => null);
|
|
45320
45347
|
return c.json({ error: "submission_conflict", message: error instanceof Error ? error.message : String(error) }, 409);
|
|
@@ -45324,7 +45351,7 @@ var init_local_sourcebook_routes = __esm({
|
|
|
45324
45351
|
const userId = Number(c.get("user").id);
|
|
45325
45352
|
const submission = await getLocalSourcebookSubmission(c.req.param("id"), userId);
|
|
45326
45353
|
if (!submission) return c.json({ error: "not_found" }, 404);
|
|
45327
|
-
return c.json({ ok: true, submission, publicUrls: localSourcebookPublicationLinks(submission), draft: await getLocalSourcebookDraft(submission.id, userId) });
|
|
45354
|
+
return c.json({ ok: true, message: localSourcebookPublicationMessage(submission), statusCheck: localSourcebookStatusCheck(submission), submission, publicUrls: localSourcebookPublicationLinks(submission), draft: await getLocalSourcebookDraft(submission.id, userId) });
|
|
45328
45355
|
});
|
|
45329
45356
|
localSourcebookApp.on(["PATCH", "POST"], "/submissions/:id/draft", createApiKeyAuth(), (c) => c.json({
|
|
45330
45357
|
error: "evidence_refresh_required",
|
|
@@ -45359,7 +45386,7 @@ var init_local_sourcebook_routes = __esm({
|
|
|
45359
45386
|
return c.json({ error: "not_found" }, 404);
|
|
45360
45387
|
}
|
|
45361
45388
|
const dispatched = await dispatchLocalSourcebookAcquisition(submission.id).catch(() => false);
|
|
45362
|
-
return c.json({ ok: true, submission, publicUrls: localSourcebookPublicationLinks(submission), acquisition: { queued: true, dispatched }, acquisitionPlan: acquisitionPlan(submission.websiteUrl), billing: publicListingWriteBilling(billing) }, 202);
|
|
45389
|
+
return c.json({ ok: true, message: localSourcebookPublicationMessage(submission), statusCheck: localSourcebookStatusCheck(submission), submission, publicUrls: localSourcebookPublicationLinks(submission), acquisition: { queued: true, dispatched }, acquisitionPlan: acquisitionPlan(submission.websiteUrl), billing: publicListingWriteBilling(billing) }, 202);
|
|
45363
45390
|
} catch (error) {
|
|
45364
45391
|
if (billing) await refundListingWrite(billing, "Local Sourcebook refresh failed before persistence.").catch(() => null);
|
|
45365
45392
|
return c.json({ error: "moderated_listing", message: error instanceof Error ? error.message : String(error) }, 409);
|
|
@@ -49679,7 +49706,7 @@ var PACKAGE_VERSION;
|
|
|
49679
49706
|
var init_version = __esm({
|
|
49680
49707
|
"src/version.ts"() {
|
|
49681
49708
|
"use strict";
|
|
49682
|
-
PACKAGE_VERSION = "0.66.
|
|
49709
|
+
PACKAGE_VERSION = "0.66.5";
|
|
49683
49710
|
}
|
|
49684
49711
|
});
|
|
49685
49712
|
|
|
@@ -49910,11 +49937,16 @@ Multi-step orchestrations \u2014 prefer these over hand-chaining primitives when
|
|
|
49910
49937
|
- A new capture costs 10 Credits total and includes broad website, exact Maps place, maximum-accessible review,
|
|
49911
49938
|
service-area, staff, and genuine-media acquisition. When compilation succeeds, that exact system-authored evidence revision
|
|
49912
49939
|
publishes automatically. Never supply owner-authored services, reviews, images, or other public claims.
|
|
49940
|
+
- An accepted capture is still processing, not complete. While its returned \`statusCheck.required\` is true,
|
|
49941
|
+
wait the suggested interval and call **local_sourcebook_submission_status** again before giving the user a
|
|
49942
|
+
final completion answer. When it publishes, say that the listing is live and include the exact clickable
|
|
49943
|
+
\`publicUrls.profileUrl\`; when it fails, report the returned failure message and cause.
|
|
49913
49944
|
- Read progress and the compiled record with **local_sourcebook_submission_status**. Use
|
|
49914
49945
|
**local_sourcebook_refresh** only when the owner intentionally wants to pay 2 Credits for a new acquisition pass.
|
|
49915
49946
|
Contract, tag, preparation, validation, and status tools are free; idempotent retries are not charged twice.
|
|
49916
|
-
- Capture and status responses return \`
|
|
49917
|
-
|
|
49947
|
+
- Capture and status responses return a plain-language \`message\`, a \`statusCheck\`, and exact public URLs. Do not
|
|
49948
|
+
describe a queued or enriching listing as complete, and do not report a listing as live without including its
|
|
49949
|
+
clickable \`publicUrls.profileUrl\`.
|
|
49918
49950
|
- The last published revision remains public during refresh. Administrators can reject or unpublish an
|
|
49919
49951
|
exceptional record, but routine subscriber publication does not require administrator review.
|
|
49920
49952
|
|
|
@@ -49940,10 +49972,10 @@ Multi-step orchestrations \u2014 prefer these over hand-chaining primitives when
|
|
|
49940
49972
|
artifact, call \`report_artifact_read\` with the returned artifactId and follow nextOffset until null. This
|
|
49941
49973
|
works through the authenticated MCP connection even when the client cannot open the signed download URL;
|
|
49942
49974
|
do not try curl or web_fetch. Use \`archive_read\` for ZIP archives.
|
|
49943
|
-
- Before using a connected account, call \`list_service_connections
|
|
49944
|
-
|
|
49945
|
-
|
|
49946
|
-
which
|
|
49975
|
+
- Before using a connected account, call \`list_service_connections\`. Match an explicit \`userLabel\` first,
|
|
49976
|
+
then verified provider-side \`providerAccountEmail\`, \`providerAccountName\`, or \`providerAccountId\`; never
|
|
49977
|
+
treat the MCP Scraper login as external-provider identity. If identity is pending or unavailable and no
|
|
49978
|
+
distinct user label exists, ask the person which connection they mean instead of assuming.
|
|
49947
49979
|
- For Google Search Console, use \`export_connected_service_data\` with
|
|
49948
49980
|
\`dataset:"search_console_performance"\` when the person asks for a fresh JSONL download. When they
|
|
49949
49981
|
ask to schedule ingestion or repeatedly filter historical rows, create a \`connection_sync\` schedule,
|
|
@@ -53853,7 +53885,12 @@ var init_mcp_tool_schemas = __esm({
|
|
|
53853
53885
|
connectionId: import_zod45.z.string(),
|
|
53854
53886
|
providerConfigKey: import_zod45.z.string(),
|
|
53855
53887
|
provider: import_zod45.z.string().nullable().optional(),
|
|
53856
|
-
label: import_zod45.z.string(),
|
|
53888
|
+
label: import_zod45.z.string().describe("Best available human-facing account label: a userLabel when set, otherwise verified provider identity, then a stable fallback."),
|
|
53889
|
+
userLabel: import_zod45.z.string().nullable().optional().describe("Optional tenant-owned name set in the dashboard to distinguish this connection. This is not asserted provider identity."),
|
|
53890
|
+
providerAccountId: import_zod45.z.string().nullable().optional().describe("Stable provider-side account, user, workspace, channel, or tenant identifier when the provider exposes one."),
|
|
53891
|
+
providerAccountEmail: import_zod45.z.string().email().nullable().optional().describe("Verified provider-side email when the provider exposes one. This is never inferred from the MCP Scraper login."),
|
|
53892
|
+
providerAccountName: import_zod45.z.string().nullable().optional().describe("Provider-side account, user, workspace, channel, or tenant name when the provider exposes one."),
|
|
53893
|
+
providerIdentityStatus: import_zod45.z.enum(["pending", "verified", "unavailable"]).optional().describe("Whether provider identity discovery is pending, returned at least one verified provider identifier, or completed without one."),
|
|
53857
53894
|
status: import_zod45.z.string(),
|
|
53858
53895
|
lifecycleStatus: import_zod45.z.enum(["pending", "connected", "needs_reauth", "disconnecting", "disconnected"]).optional().describe("Credential lifecycle. This is separate from current provider availability."),
|
|
53859
53896
|
operationalStatus: import_zod45.z.enum(["unknown", "available", "degraded", "unavailable"]).optional().describe("Last observed provider transport availability. Unavailable does not imply reconnect is required."),
|
|
@@ -56205,14 +56242,14 @@ function registerPaaExtractorMcpTools(server, executor, options = {}) {
|
|
|
56205
56242
|
}, async (input) => executor.validateLocalSourcebookWrite(input));
|
|
56206
56243
|
server.registerTool("local-sourcebook-capture", {
|
|
56207
56244
|
title: "Capture Governed Local Sourcebook Listing",
|
|
56208
|
-
description: "Strict owner-scoped LocalBusiness write path after list, contract, prepare, and validate. Creating a listing costs 10 Credits total, including website, exact-place, review, service-area, staff, genuine-media, and structured-data compilation; preparation and validation are free, and idempotent retries are not charged twice. Capture registers canonical tags and the accepted Schema.org LocalBusiness subtype,
|
|
56245
|
+
description: "Strict owner-scoped LocalBusiness write path after list, contract, prepare, and validate. Creating a listing costs 10 Credits total, including website, exact-place, review, service-area, staff, genuine-media, and structured-data compilation; preparation and validation are free, and idempotent retries are not charged twice. Capture registers canonical tags and the accepted Schema.org LocalBusiness subtype, then queues acquisition; an accepted capture is not yet live. Poll local_sourcebook_submission_status using statusCheck until it is terminal. When published, tell the user the returned message and exact publicUrls.profileUrl. MCP Scraper generates the final JSON-LD from source-backed evidence and publishes a successful system-compiled revision automatically; owner-authored public claims and arbitrary JSON-LD are not accepted.",
|
|
56209
56246
|
inputSchema: LocalSourcebookCaptureInputSchema,
|
|
56210
56247
|
outputSchema: recordOutputSchema("local-sourcebook-capture", LocalSourcebookOutputSchema),
|
|
56211
56248
|
annotations: liveWebToolAnnotations("Capture Governed Local Sourcebook Listing")
|
|
56212
56249
|
}, async (input) => executor.localSourcebookCapture(input));
|
|
56213
56250
|
server.registerTool("local_sourcebook_submission_status", {
|
|
56214
56251
|
title: "Local Sourcebook Submission Status",
|
|
56215
|
-
description: "Read the caller\u2019s listing draft, enrichment coverage, revision number, publication state, and
|
|
56252
|
+
description: "Read the caller\u2019s listing draft, enrichment coverage, revision number, publication state, and LocalSourcebook.com profile and reviews URLs. While statusCheck.required is true, wait the returned interval and check again before giving the user a final completion answer. When published, present the returned message and exact clickable publicUrls.profileUrl. When failed, present the failure message and read acquisitionError before retrying, since a refresh repeats the same acquisition. Coverage counters are written only on success, so zeroed counters on a failed listing do not mean the crawl never ran.",
|
|
56216
56253
|
inputSchema: LocalSourcebookSubmissionStatusInputSchema,
|
|
56217
56254
|
outputSchema: recordOutputSchema("local_sourcebook_submission_status", LocalSourcebookOutputSchema),
|
|
56218
56255
|
annotations: localPlanningToolAnnotations("Local Sourcebook Submission Status")
|
|
@@ -56357,7 +56394,7 @@ function registerPaaExtractorMcpTools(server, executor, options = {}) {
|
|
|
56357
56394
|
}, async (input) => formatCreditsInfo(await executor.creditsInfo(input), input));
|
|
56358
56395
|
server.registerTool("list_service_connections", {
|
|
56359
56396
|
title: "List Connected Services",
|
|
56360
|
-
description: "List every service connection this MCP Scraper account has authorized, including Resend, GitHub, Google Analytics, Google Search Console, YouTube, Facebook Pages, LinkedIn, X, Meta Marketing, Slack, Gmail, Calendar, Google Drive, Zoom, Xero, and others. Returns the tenant-scoped connectionId, credential transport
|
|
56397
|
+
description: "List every service connection this MCP Scraper account has authorized, including Resend, GitHub, Google Analytics, Google Search Console, YouTube, Facebook Pages, LinkedIn, X, Meta Marketing, Slack, Gmail, Calendar, Google Drive, Zoom, Xero, and others. Returns the tenant-scoped connectionId; a tenant-owned userLabel; verified provider-side account ID, email, or name when available; credential transport; exact live readTools and gated actionTools; permission-aware toolCapabilities with missing OAuth-grant or provider-app-feature blockers; permanently blocked administrative tools; and schema-discovery metadata. Get a connectionId and exact tool name here before calling describe_service_connection_tool, read_service_connection, or call_service_connection_action. Managed OAuth and official remote MCP connections use the same provider-neutral bridges; mutations still require the account action switch and an exact allowed action. A scheduled Search Console connection_sync creates a typed tenant-owned performance table; after it runs, use the returned tableName with table-describe and table-query instead of repeatedly calling Google for historical filtering.",
|
|
56361
56398
|
inputSchema: ListServiceConnectionsInputSchema,
|
|
56362
56399
|
outputSchema: recordOutputSchema("list_service_connections", ListServiceConnectionsOutputSchema),
|
|
56363
56400
|
annotations: { title: "List Connected Services", readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false }
|
|
@@ -81169,6 +81206,12 @@ function ensureServiceConnectionsSchema() {
|
|
|
81169
81206
|
transport TEXT NOT NULL,
|
|
81170
81207
|
upstream_connection_id TEXT,
|
|
81171
81208
|
label TEXT,
|
|
81209
|
+
user_label TEXT,
|
|
81210
|
+
provider_account_id TEXT,
|
|
81211
|
+
provider_account_email TEXT,
|
|
81212
|
+
provider_account_name TEXT,
|
|
81213
|
+
provider_identity_checked_at TEXT,
|
|
81214
|
+
provider_identity_source_updated_at TEXT,
|
|
81172
81215
|
lifecycle_status TEXT NOT NULL DEFAULT 'pending',
|
|
81173
81216
|
operational_status TEXT NOT NULL DEFAULT 'unknown',
|
|
81174
81217
|
reconnect_required INTEGER NOT NULL DEFAULT 0,
|
|
@@ -81190,6 +81233,30 @@ function ensureServiceConnectionsSchema() {
|
|
|
81190
81233
|
UNIQUE(user_id, provider_config_key, upstream_connection_id)
|
|
81191
81234
|
)
|
|
81192
81235
|
`);
|
|
81236
|
+
try {
|
|
81237
|
+
await db.execute(`ALTER TABLE service_connections ADD COLUMN user_label TEXT`);
|
|
81238
|
+
} catch {
|
|
81239
|
+
}
|
|
81240
|
+
try {
|
|
81241
|
+
await db.execute(`ALTER TABLE service_connections ADD COLUMN provider_account_id TEXT`);
|
|
81242
|
+
} catch {
|
|
81243
|
+
}
|
|
81244
|
+
try {
|
|
81245
|
+
await db.execute(`ALTER TABLE service_connections ADD COLUMN provider_account_email TEXT`);
|
|
81246
|
+
} catch {
|
|
81247
|
+
}
|
|
81248
|
+
try {
|
|
81249
|
+
await db.execute(`ALTER TABLE service_connections ADD COLUMN provider_account_name TEXT`);
|
|
81250
|
+
} catch {
|
|
81251
|
+
}
|
|
81252
|
+
try {
|
|
81253
|
+
await db.execute(`ALTER TABLE service_connections ADD COLUMN provider_identity_checked_at TEXT`);
|
|
81254
|
+
} catch {
|
|
81255
|
+
}
|
|
81256
|
+
try {
|
|
81257
|
+
await db.execute(`ALTER TABLE service_connections ADD COLUMN provider_identity_source_updated_at TEXT`);
|
|
81258
|
+
} catch {
|
|
81259
|
+
}
|
|
81193
81260
|
await db.execute(`CREATE INDEX IF NOT EXISTS service_connections_user_updated ON service_connections(user_id, updated_at DESC)`);
|
|
81194
81261
|
await db.execute(`CREATE INDEX IF NOT EXISTS service_connections_upstream ON service_connections(provider_config_key, upstream_connection_id)`);
|
|
81195
81262
|
await db.execute(`
|
|
@@ -81250,6 +81317,12 @@ function rowToRecord(row) {
|
|
|
81250
81317
|
transport: row.transport === "remote_mcp" ? "remote_mcp" : "nango",
|
|
81251
81318
|
upstreamConnectionId: typeof row.upstream_connection_id === "string" ? row.upstream_connection_id : null,
|
|
81252
81319
|
label: typeof row.label === "string" ? row.label : null,
|
|
81320
|
+
userLabel: typeof row.user_label === "string" ? row.user_label : null,
|
|
81321
|
+
providerAccountId: typeof row.provider_account_id === "string" ? row.provider_account_id : null,
|
|
81322
|
+
providerAccountEmail: typeof row.provider_account_email === "string" ? row.provider_account_email : null,
|
|
81323
|
+
providerAccountName: typeof row.provider_account_name === "string" ? row.provider_account_name : null,
|
|
81324
|
+
providerIdentityCheckedAt: typeof row.provider_identity_checked_at === "string" ? row.provider_identity_checked_at : null,
|
|
81325
|
+
providerIdentitySourceUpdatedAt: typeof row.provider_identity_source_updated_at === "string" ? row.provider_identity_source_updated_at : null,
|
|
81253
81326
|
lifecycleStatus: String(row.lifecycle_status),
|
|
81254
81327
|
operationalStatus: String(row.operational_status),
|
|
81255
81328
|
reconnectRequired: Number(row.reconnect_required) === 1,
|
|
@@ -81335,6 +81408,34 @@ async function updateServiceConnectionTools(connectionId, readTools, actionTools
|
|
|
81335
81408
|
args: [JSON.stringify(readTools), JSON.stringify(actionTools), toolRevision, connectionId]
|
|
81336
81409
|
});
|
|
81337
81410
|
}
|
|
81411
|
+
async function setServiceConnectionProviderIdentity(args) {
|
|
81412
|
+
await ensureServiceConnectionsSchema();
|
|
81413
|
+
await getDb().execute({
|
|
81414
|
+
sql: `
|
|
81415
|
+
UPDATE service_connections SET
|
|
81416
|
+
provider_account_id = ?, provider_account_email = ?, provider_account_name = ?,
|
|
81417
|
+
provider_identity_checked_at = datetime('now'), provider_identity_source_updated_at = ?,
|
|
81418
|
+
updated_at = datetime('now')
|
|
81419
|
+
WHERE id = ?
|
|
81420
|
+
`,
|
|
81421
|
+
args: [
|
|
81422
|
+
args.providerAccountId ?? null,
|
|
81423
|
+
args.providerAccountEmail?.toLowerCase() ?? null,
|
|
81424
|
+
args.providerAccountName ?? null,
|
|
81425
|
+
args.sourceUpdatedAt,
|
|
81426
|
+
args.connectionId
|
|
81427
|
+
]
|
|
81428
|
+
});
|
|
81429
|
+
}
|
|
81430
|
+
async function setServiceConnectionUserLabel(identity, connectionId, userLabel) {
|
|
81431
|
+
const connection = await getOwnedServiceConnection(identity, connectionId);
|
|
81432
|
+
if (!connection || connection.lifecycleStatus === "disconnected") throw new Error("service_connection_not_found");
|
|
81433
|
+
await getDb().execute({
|
|
81434
|
+
sql: `UPDATE service_connections SET user_label = ?, updated_at = datetime('now') WHERE id = ? AND user_id = ?`,
|
|
81435
|
+
args: [userLabel, connectionId, connection.userId]
|
|
81436
|
+
});
|
|
81437
|
+
return userLabel;
|
|
81438
|
+
}
|
|
81338
81439
|
async function recordServiceConnectionHealth(args) {
|
|
81339
81440
|
await ensureServiceConnectionsSchema();
|
|
81340
81441
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -84411,7 +84512,8 @@ async function nangoRequest(path6, options = {}) {
|
|
|
84411
84512
|
headers: {
|
|
84412
84513
|
authorization: `Bearer ${nangoSecret()}`,
|
|
84413
84514
|
accept: "application/json",
|
|
84414
|
-
...options.body ? { "content-type": "application/json" } : {}
|
|
84515
|
+
...options.body ? { "content-type": "application/json" } : {},
|
|
84516
|
+
...options.headers
|
|
84415
84517
|
},
|
|
84416
84518
|
body: options.body ? JSON.stringify(options.body) : void 0,
|
|
84417
84519
|
signal: AbortSignal.timeout(NANGO_TIMEOUT_MS)
|
|
@@ -84459,22 +84561,126 @@ function discoveredConnection(value) {
|
|
|
84459
84561
|
const upstreamConnectionId = text(row?.connection_id);
|
|
84460
84562
|
const providerConfigKey = text(row?.provider_config_key);
|
|
84461
84563
|
if (!upstreamConnectionId || !providerConfigKey) return null;
|
|
84462
|
-
const tags = asRecord2(row?.tags);
|
|
84463
|
-
const endUser = asRecord2(row?.end_user);
|
|
84464
84564
|
const errors = Array.isArray(row?.errors) ? row.errors : [];
|
|
84465
84565
|
const authError = errors.some((error) => text(asRecord2(error)?.type) === "auth");
|
|
84466
84566
|
return {
|
|
84467
84567
|
upstreamConnectionId,
|
|
84468
84568
|
providerConfigKey,
|
|
84469
84569
|
provider: text(row?.provider) ?? providerConfigKey,
|
|
84470
|
-
|
|
84570
|
+
// Nango's end-user fields identify the MCP Scraper tenant, not the
|
|
84571
|
+
// provider-side principal that authorized this connection.
|
|
84572
|
+
label: null,
|
|
84471
84573
|
lifecycleStatus: authError ? "needs_reauth" : "connected",
|
|
84472
84574
|
reconnectRequired: authError,
|
|
84473
84575
|
createdAt: text(row?.created_at) ?? text(row?.created),
|
|
84474
84576
|
updatedAt: text(row?.updated_at)
|
|
84475
84577
|
};
|
|
84476
84578
|
}
|
|
84477
|
-
|
|
84579
|
+
function scalarId(value) {
|
|
84580
|
+
const valueText = text(value);
|
|
84581
|
+
if (valueText) return valueText;
|
|
84582
|
+
return typeof value === "number" && Number.isFinite(value) ? String(value) : null;
|
|
84583
|
+
}
|
|
84584
|
+
function providerEmail(value) {
|
|
84585
|
+
const email = text(value)?.toLowerCase() ?? null;
|
|
84586
|
+
return email && /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email) ? email : null;
|
|
84587
|
+
}
|
|
84588
|
+
function compactProviderIdentity(identity) {
|
|
84589
|
+
return identity.id || identity.email || identity.name ? identity : null;
|
|
84590
|
+
}
|
|
84591
|
+
async function providerProxyJson(args) {
|
|
84592
|
+
return nangoRequest(`/proxy${args.path}`, {
|
|
84593
|
+
query: args.query,
|
|
84594
|
+
headers: {
|
|
84595
|
+
"connection-id": args.upstreamConnectionId,
|
|
84596
|
+
"provider-config-key": args.providerConfigKey,
|
|
84597
|
+
...args.baseUrlOverride ? { "base-url-override": args.baseUrlOverride } : {}
|
|
84598
|
+
}
|
|
84599
|
+
});
|
|
84600
|
+
}
|
|
84601
|
+
async function googleProviderAccountIdentity(args) {
|
|
84602
|
+
const request = (path6, query, baseUrlOverride) => providerProxyJson({ ...args, path: path6, query, baseUrlOverride });
|
|
84603
|
+
try {
|
|
84604
|
+
if (args.providerConfigKey === "google-mail") {
|
|
84605
|
+
const payload = asRecord2(await request("/gmail/v1/users/me/profile", void 0, "https://gmail.googleapis.com"));
|
|
84606
|
+
const email = providerEmail(payload?.emailAddress);
|
|
84607
|
+
return compactProviderIdentity({ id: email, email, name: null });
|
|
84608
|
+
}
|
|
84609
|
+
if (args.providerConfigKey === "google-drive") {
|
|
84610
|
+
const payload = asRecord2(await request("/drive/v3/about", { fields: "user(permissionId,displayName,emailAddress)" }, "https://www.googleapis.com"));
|
|
84611
|
+
const user = asRecord2(payload?.user);
|
|
84612
|
+
return compactProviderIdentity({
|
|
84613
|
+
id: text(user?.permissionId),
|
|
84614
|
+
email: providerEmail(user?.emailAddress),
|
|
84615
|
+
name: text(user?.displayName)
|
|
84616
|
+
});
|
|
84617
|
+
}
|
|
84618
|
+
if (args.providerConfigKey === "google-calendar") {
|
|
84619
|
+
const payload = asRecord2(await request("/calendar/v3/calendars/primary", void 0, "https://www.googleapis.com"));
|
|
84620
|
+
const id = text(payload?.id);
|
|
84621
|
+
return compactProviderIdentity({ id, email: providerEmail(id), name: text(payload?.summary) });
|
|
84622
|
+
}
|
|
84623
|
+
if (args.providerConfigKey === "youtube") {
|
|
84624
|
+
const payload = asRecord2(await request("/youtube/v3/channels", { part: "snippet", mine: "true", maxResults: 1 }, "https://www.googleapis.com"));
|
|
84625
|
+
const first = Array.isArray(payload?.items) ? asRecord2(payload.items[0]) : null;
|
|
84626
|
+
return compactProviderIdentity({ id: text(first?.id), email: null, name: text(asRecord2(first?.snippet)?.title) });
|
|
84627
|
+
}
|
|
84628
|
+
} catch {
|
|
84629
|
+
}
|
|
84630
|
+
try {
|
|
84631
|
+
const payload = asRecord2(await request("/v1/userinfo", void 0, "https://openidconnect.googleapis.com"));
|
|
84632
|
+
return compactProviderIdentity({
|
|
84633
|
+
id: text(payload?.sub),
|
|
84634
|
+
email: payload?.email_verified === false ? null : providerEmail(payload?.email),
|
|
84635
|
+
name: text(payload?.name)
|
|
84636
|
+
});
|
|
84637
|
+
} catch {
|
|
84638
|
+
return null;
|
|
84639
|
+
}
|
|
84640
|
+
}
|
|
84641
|
+
async function getNangoProviderAccountIdentity(args) {
|
|
84642
|
+
if (!IDENTITY_SUPPORTED_PROVIDERS.has(args.providerConfigKey)) return null;
|
|
84643
|
+
if (args.providerConfigKey.startsWith("google-") || args.providerConfigKey === "youtube") {
|
|
84644
|
+
return googleProviderAccountIdentity(args);
|
|
84645
|
+
}
|
|
84646
|
+
try {
|
|
84647
|
+
if (args.providerConfigKey === "github-getting-started") {
|
|
84648
|
+
const payload = asRecord2(await providerProxyJson({ ...args, path: "/user" }));
|
|
84649
|
+
return compactProviderIdentity({ id: scalarId(payload?.id), email: providerEmail(payload?.email), name: text(payload?.name) ?? text(payload?.login) });
|
|
84650
|
+
}
|
|
84651
|
+
if (args.providerConfigKey === "slack") {
|
|
84652
|
+
const payload = asRecord2(await providerProxyJson({ ...args, path: "/api/auth.test" }));
|
|
84653
|
+
return compactProviderIdentity({ id: text(payload?.user_id), email: null, name: [text(payload?.user), text(payload?.team)].filter(Boolean).join(" \xB7 ") || null });
|
|
84654
|
+
}
|
|
84655
|
+
if (args.providerConfigKey === "zoom") {
|
|
84656
|
+
const payload = asRecord2(await providerProxyJson({ ...args, path: "/v2/users/me" }));
|
|
84657
|
+
const fullName = [text(payload?.first_name), text(payload?.last_name)].filter(Boolean).join(" ");
|
|
84658
|
+
return compactProviderIdentity({ id: text(payload?.id), email: providerEmail(payload?.email), name: text(payload?.display_name) ?? (fullName || null) });
|
|
84659
|
+
}
|
|
84660
|
+
if (args.providerConfigKey === "linkedin") {
|
|
84661
|
+
const payload = asRecord2(await providerProxyJson({ ...args, path: "/v2/userinfo" }));
|
|
84662
|
+
return compactProviderIdentity({ id: text(payload?.sub), email: payload?.email_verified === false ? null : providerEmail(payload?.email), name: text(payload?.name) });
|
|
84663
|
+
}
|
|
84664
|
+
if (args.providerConfigKey === "meta-marketing-api" || args.providerConfigKey === "facebook") {
|
|
84665
|
+
const payload = asRecord2(await providerProxyJson({ ...args, path: "/me", query: { fields: "id,name,email" } }));
|
|
84666
|
+
return compactProviderIdentity({ id: text(payload?.id), email: providerEmail(payload?.email), name: text(payload?.name) });
|
|
84667
|
+
}
|
|
84668
|
+
if (args.providerConfigKey === "twitter-v2") {
|
|
84669
|
+
const payload = asRecord2(await providerProxyJson({ ...args, path: "/2/users/me", query: { "user.fields": "id,name,username" } }));
|
|
84670
|
+
const data = asRecord2(payload?.data);
|
|
84671
|
+
return compactProviderIdentity({ id: text(data?.id), email: null, name: text(data?.username) ?? text(data?.name) });
|
|
84672
|
+
}
|
|
84673
|
+
if (args.providerConfigKey === "xero") {
|
|
84674
|
+
const payload = await providerProxyJson({ ...args, path: "/connections" });
|
|
84675
|
+
const first = Array.isArray(payload) ? asRecord2(payload[0]) : null;
|
|
84676
|
+
return compactProviderIdentity({ id: text(first?.tenantId), email: null, name: text(first?.tenantName) });
|
|
84677
|
+
}
|
|
84678
|
+
} catch {
|
|
84679
|
+
return null;
|
|
84680
|
+
}
|
|
84681
|
+
return null;
|
|
84682
|
+
}
|
|
84683
|
+
async function discoverOwnedNangoConnections(identity, options = {}) {
|
|
84478
84684
|
const rows = [];
|
|
84479
84685
|
for (let page = 0; page < NANGO_CONNECTION_MAX_PAGES; page += 1) {
|
|
84480
84686
|
const payload = await nangoRequest("/connections", {
|
|
@@ -84485,6 +84691,35 @@ async function discoverOwnedNangoConnections(identity) {
|
|
|
84485
84691
|
if (pageRows.length < NANGO_CONNECTION_PAGE_SIZE) break;
|
|
84486
84692
|
}
|
|
84487
84693
|
const discovered = rows.filter((row) => connectionIdentityMatches(row, identity)).map(discoveredConnection).filter((connection) => connection !== null);
|
|
84694
|
+
const reconciled = await reconcileDiscoveredNangoConnections(identity, discovered);
|
|
84695
|
+
if (options.resolveProviderIdentity === false) return reconciled;
|
|
84696
|
+
const remoteByKey = new Map(discovered.map((connection) => [
|
|
84697
|
+
`${connection.providerConfigKey}\0${connection.upstreamConnectionId}`,
|
|
84698
|
+
connection
|
|
84699
|
+
]));
|
|
84700
|
+
await Promise.all(reconciled.map(async (connection) => {
|
|
84701
|
+
if (connection.lifecycleStatus !== "connected" || !connection.upstreamConnectionId) return;
|
|
84702
|
+
const remote = remoteByKey.get(`${connection.providerConfigKey}\0${connection.upstreamConnectionId}`);
|
|
84703
|
+
if (!remote) return;
|
|
84704
|
+
const sourceUpdatedAt = remote.updatedAt ?? remote.createdAt ?? connection.lifecycleStatus;
|
|
84705
|
+
const hasProviderIdentity = Boolean(
|
|
84706
|
+
connection.providerAccountId || connection.providerAccountEmail || connection.providerAccountName
|
|
84707
|
+
);
|
|
84708
|
+
const checkedAt = connection.providerIdentityCheckedAt ? Date.parse(`${connection.providerIdentityCheckedAt.replace(" ", "T")}Z`) : Number.NaN;
|
|
84709
|
+
const unavailableIdentityIsFresh = !hasProviderIdentity && Number.isFinite(checkedAt) && Date.now() - checkedAt < PROVIDER_IDENTITY_RETRY_MS;
|
|
84710
|
+
if (connection.providerIdentitySourceUpdatedAt === sourceUpdatedAt && (hasProviderIdentity || unavailableIdentityIsFresh)) return;
|
|
84711
|
+
const providerIdentity = await getNangoProviderAccountIdentity({
|
|
84712
|
+
upstreamConnectionId: connection.upstreamConnectionId,
|
|
84713
|
+
providerConfigKey: connection.providerConfigKey
|
|
84714
|
+
});
|
|
84715
|
+
await setServiceConnectionProviderIdentity({
|
|
84716
|
+
connectionId: connection.id,
|
|
84717
|
+
providerAccountId: providerIdentity?.id,
|
|
84718
|
+
providerAccountEmail: providerIdentity?.email,
|
|
84719
|
+
providerAccountName: providerIdentity?.name,
|
|
84720
|
+
sourceUpdatedAt
|
|
84721
|
+
});
|
|
84722
|
+
}));
|
|
84488
84723
|
return reconcileDiscoveredNangoConnections(identity, discovered);
|
|
84489
84724
|
}
|
|
84490
84725
|
async function listNangoCatalogDirect() {
|
|
@@ -84747,7 +84982,7 @@ async function describeNangoToolDirect(identity, connectionId, toolName) {
|
|
|
84747
84982
|
fetchedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
84748
84983
|
};
|
|
84749
84984
|
}
|
|
84750
|
-
var import_node_crypto68, import_client13, DEFAULT_NANGO_MCP_URL, NANGO_TIMEOUT_MS, NANGO_CONNECTION_PAGE_SIZE, NANGO_CONNECTION_MAX_PAGES, MAIN_INTEGRATION_CONTRACT_VERSION, MAIN_INTEGRATION_CONTRACT_HASH, MainNangoTransportError;
|
|
84985
|
+
var import_node_crypto68, import_client13, DEFAULT_NANGO_MCP_URL, NANGO_TIMEOUT_MS, NANGO_CONNECTION_PAGE_SIZE, NANGO_CONNECTION_MAX_PAGES, PROVIDER_IDENTITY_RETRY_MS, MAIN_INTEGRATION_CONTRACT_VERSION, MAIN_INTEGRATION_CONTRACT_HASH, MainNangoTransportError, IDENTITY_SUPPORTED_PROVIDERS;
|
|
84751
84986
|
var init_main_nango_transport = __esm({
|
|
84752
84987
|
"src/api/main-nango-transport.ts"() {
|
|
84753
84988
|
"use strict";
|
|
@@ -84761,6 +84996,7 @@ var init_main_nango_transport = __esm({
|
|
|
84761
84996
|
NANGO_TIMEOUT_MS = 2e4;
|
|
84762
84997
|
NANGO_CONNECTION_PAGE_SIZE = 100;
|
|
84763
84998
|
NANGO_CONNECTION_MAX_PAGES = 20;
|
|
84999
|
+
PROVIDER_IDENTITY_RETRY_MS = 6 * 60 * 60 * 1e3;
|
|
84764
85000
|
MAIN_INTEGRATION_CONTRACT_VERSION = "2026-07-16.1";
|
|
84765
85001
|
MAIN_INTEGRATION_CONTRACT_HASH = "f037859ec80c2ea2fac6e4a67c1a59f3200dd22d3642c97789b9bb3236723d3e";
|
|
84766
85002
|
MainNangoTransportError = class extends Error {
|
|
@@ -84777,6 +85013,22 @@ var init_main_nango_transport = __esm({
|
|
|
84777
85013
|
this.retryAfterMs = retryAfterMs;
|
|
84778
85014
|
}
|
|
84779
85015
|
};
|
|
85016
|
+
IDENTITY_SUPPORTED_PROVIDERS = /* @__PURE__ */ new Set([
|
|
85017
|
+
"facebook",
|
|
85018
|
+
"github-getting-started",
|
|
85019
|
+
"google-analytics",
|
|
85020
|
+
"google-calendar",
|
|
85021
|
+
"google-drive",
|
|
85022
|
+
"google-mail",
|
|
85023
|
+
"google-search-console",
|
|
85024
|
+
"linkedin",
|
|
85025
|
+
"meta-marketing-api",
|
|
85026
|
+
"slack",
|
|
85027
|
+
"twitter-v2",
|
|
85028
|
+
"xero",
|
|
85029
|
+
"youtube",
|
|
85030
|
+
"zoom"
|
|
85031
|
+
]);
|
|
84780
85032
|
}
|
|
84781
85033
|
});
|
|
84782
85034
|
|
|
@@ -85067,7 +85319,7 @@ async function getNangoConnections(identity, options = {}) {
|
|
|
85067
85319
|
if (mainOwnsIntegrations()) {
|
|
85068
85320
|
let connections;
|
|
85069
85321
|
try {
|
|
85070
|
-
connections = await discoverOwnedNangoConnections(identity);
|
|
85322
|
+
connections = await discoverOwnedNangoConnections(identity, { resolveProviderIdentity: !options.summaryOnly });
|
|
85071
85323
|
} catch (error) {
|
|
85072
85324
|
try {
|
|
85073
85325
|
connections = await listServiceConnections(identity, "nango");
|
|
@@ -85088,11 +85340,12 @@ async function getNangoConnections(identity, options = {}) {
|
|
|
85088
85340
|
connectionId: connection.id,
|
|
85089
85341
|
providerConfigKey: connection.providerConfigKey,
|
|
85090
85342
|
provider: connection.provider,
|
|
85091
|
-
label: connection.label || connection.providerConfigKey,
|
|
85092
|
-
|
|
85093
|
-
|
|
85094
|
-
|
|
85095
|
-
|
|
85343
|
+
label: connection.userLabel || connection.providerAccountEmail || connection.providerAccountName || (connection.label && connection.label.toLowerCase() !== identity.trim().toLowerCase() ? connection.label : null) || connection.providerConfigKey,
|
|
85344
|
+
userLabel: connection.userLabel,
|
|
85345
|
+
providerAccountId: connection.providerAccountId,
|
|
85346
|
+
providerAccountEmail: connection.providerAccountEmail,
|
|
85347
|
+
providerAccountName: connection.providerAccountName,
|
|
85348
|
+
providerIdentityStatus: connection.providerIdentityCheckedAt ? connection.providerAccountId || connection.providerAccountEmail || connection.providerAccountName ? "verified" : "unavailable" : "pending",
|
|
85096
85349
|
status: connection.reconnectRequired ? "needs_reauth" : "connected",
|
|
85097
85350
|
lifecycleStatus: connection.lifecycleStatus,
|
|
85098
85351
|
operationalStatus: connection.operationalStatus,
|
|
@@ -85193,6 +85446,7 @@ async function getNangoConnections(identity, options = {}) {
|
|
|
85193
85446
|
providerConfigKey,
|
|
85194
85447
|
provider,
|
|
85195
85448
|
label,
|
|
85449
|
+
userLabel: null,
|
|
85196
85450
|
providerAccountId,
|
|
85197
85451
|
providerAccountEmail,
|
|
85198
85452
|
providerAccountName,
|
|
@@ -86968,6 +87222,7 @@ var init_server = __esm({
|
|
|
86968
87222
|
init_scheduling_access();
|
|
86969
87223
|
init_memory_db();
|
|
86970
87224
|
init_nango_control();
|
|
87225
|
+
init_service_connections();
|
|
86971
87226
|
init_resend_control();
|
|
86972
87227
|
init_scheduler_integration_auth();
|
|
86973
87228
|
init_connected_account_billing();
|
|
@@ -87559,6 +87814,26 @@ var init_server = __esm({
|
|
|
87559
87814
|
return scheduleConnectionError(c, err, "Unable to load service connections.");
|
|
87560
87815
|
}
|
|
87561
87816
|
});
|
|
87817
|
+
app.patch("/schedule-connections/:id/label", auth4, async (c) => {
|
|
87818
|
+
const user = c.get("user");
|
|
87819
|
+
const body = await c.req.json().catch(() => ({}));
|
|
87820
|
+
if (body.label !== null && typeof body.label !== "string") {
|
|
87821
|
+
return c.json({ ok: false, error: "label must be a string or null." }, 400);
|
|
87822
|
+
}
|
|
87823
|
+
const label = typeof body.label === "string" ? body.label.normalize("NFKC").trim().replace(/\s+/g, " ") : null;
|
|
87824
|
+
if (label && label.length > 80) {
|
|
87825
|
+
return c.json({ ok: false, error: "label must contain at most 80 characters." }, 400);
|
|
87826
|
+
}
|
|
87827
|
+
try {
|
|
87828
|
+
const userLabel = await setServiceConnectionUserLabel(user.email, c.req.param("id"), label || null);
|
|
87829
|
+
return c.json({ ok: true, userLabel });
|
|
87830
|
+
} catch (err) {
|
|
87831
|
+
if (err instanceof Error && err.message === "service_connection_not_found") {
|
|
87832
|
+
return c.json({ ok: false, error: "Service connection not found." }, 404);
|
|
87833
|
+
}
|
|
87834
|
+
return scheduleConnectionError(c, err, "Unable to rename this service connection.");
|
|
87835
|
+
}
|
|
87836
|
+
});
|
|
87562
87837
|
app.post("/schedule-connections/billing/reconcile", auth4, requireIntegrationsTier, async (c) => {
|
|
87563
87838
|
const user = c.get("user");
|
|
87564
87839
|
try {
|
|
@@ -88117,6 +88392,7 @@ var init_server = __esm({
|
|
|
88117
88392
|
app.post("/integrations/:id/reconnect-session", (c) => forwardIntegrationAlias(c, `/schedule-connections/${encodeURIComponent(c.req.param("id"))}/reconnect-session`));
|
|
88118
88393
|
app.post("/integrations/:id/enable-actions", (c) => forwardIntegrationAlias(c, `/schedule-connections/${encodeURIComponent(c.req.param("id"))}/enable-actions`));
|
|
88119
88394
|
app.post("/integrations/:id/test", (c) => forwardIntegrationAlias(c, `/schedule-connections/${encodeURIComponent(c.req.param("id"))}/test`));
|
|
88395
|
+
app.patch("/integrations/:id/label", (c) => forwardIntegrationAlias(c, `/schedule-connections/${encodeURIComponent(c.req.param("id"))}/label`));
|
|
88120
88396
|
app.delete("/integrations/:id", (c) => forwardIntegrationAlias(c, `/schedule-connections/${encodeURIComponent(c.req.param("id"))}`));
|
|
88121
88397
|
app.post("/integrations/actions/read", (c) => forwardIntegrationAlias(c, "/schedule-connections/actions/read"));
|
|
88122
88398
|
app.post("/integrations/actions/describe", (c) => forwardIntegrationAlias(c, "/schedule-connections/actions/describe"));
|
package/dist/bin/api-server.js
CHANGED
|
@@ -17,7 +17,7 @@ loadDotEnv();
|
|
|
17
17
|
async function main() {
|
|
18
18
|
const [{ serve }, { app }, { startWorker }, { migrate }] = await Promise.all([
|
|
19
19
|
import("@hono/node-server"),
|
|
20
|
-
import("../server-
|
|
20
|
+
import("../server-LESUYFDI.js"),
|
|
21
21
|
import("../worker-UTBUWEXQ.js"),
|
|
22
22
|
import("../db-4RVZ3NYZ.js")
|
|
23
23
|
]);
|