@agent-native/core 0.49.24 → 0.49.26
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent/production-agent.d.ts.map +1 -1
- package/dist/agent/production-agent.js +8 -1
- package/dist/agent/production-agent.js.map +1 -1
- package/dist/cli/recap.d.ts.map +1 -1
- package/dist/cli/recap.js +43 -11
- package/dist/cli/recap.js.map +1 -1
- package/dist/client/agent-chat-adapter.d.ts.map +1 -1
- package/dist/client/agent-chat-adapter.js +2 -1
- package/dist/client/agent-chat-adapter.js.map +1 -1
- package/dist/client/blocks/library/AnnotatedCodeBlock.js +7 -7
- package/dist/client/blocks/library/AnnotatedCodeBlock.js.map +1 -1
- package/dist/client/blocks/library/DiffBlock.js +3 -3
- package/dist/client/blocks/library/DiffBlock.js.map +1 -1
- package/dist/client/blocks/library/annotation-rail.d.ts +4 -3
- package/dist/client/blocks/library/annotation-rail.d.ts.map +1 -1
- package/dist/client/blocks/library/annotation-rail.js +16 -9
- package/dist/client/blocks/library/annotation-rail.js.map +1 -1
- package/dist/client/blocks/types.d.ts +2 -2
- package/dist/client/blocks/types.js.map +1 -1
- package/dist/coding-tools/run-code.d.ts.map +1 -1
- package/dist/coding-tools/run-code.js +198 -15
- package/dist/coding-tools/run-code.js.map +1 -1
- package/dist/extensions/fetch-tool.js +1 -1
- package/dist/extensions/fetch-tool.js.map +1 -1
- package/dist/mcp/build-server.d.ts.map +1 -1
- package/dist/mcp/build-server.js +1 -0
- package/dist/mcp/build-server.js.map +1 -1
- package/dist/mcp/builtin-tools.d.ts +8 -4
- package/dist/mcp/builtin-tools.d.ts.map +1 -1
- package/dist/mcp/builtin-tools.js +247 -13
- package/dist/mcp/builtin-tools.js.map +1 -1
- package/dist/provider-api/actions/query-staged-dataset.d.ts.map +1 -1
- package/dist/provider-api/actions/query-staged-dataset.js +1 -0
- package/dist/provider-api/actions/query-staged-dataset.js.map +1 -1
- package/dist/provider-api/index.d.ts +15 -4
- package/dist/provider-api/index.d.ts.map +1 -1
- package/dist/provider-api/index.js +191 -43
- package/dist/provider-api/index.js.map +1 -1
- package/dist/provider-api/staged-datasets-store.d.ts.map +1 -1
- package/dist/provider-api/staged-datasets-store.js +29 -6
- package/dist/provider-api/staged-datasets-store.js.map +1 -1
- package/dist/provider-api/staging.d.ts +6 -1
- package/dist/provider-api/staging.d.ts.map +1 -1
- package/dist/provider-api/staging.js +35 -6
- package/dist/provider-api/staging.js.map +1 -1
- package/dist/server/agent-chat-plugin.d.ts +1 -1
- package/dist/server/agent-chat-plugin.d.ts.map +1 -1
- package/dist/server/agent-chat-plugin.js +157 -80
- package/dist/server/agent-chat-plugin.js.map +1 -1
- package/dist/server/prompts/shared-rules.d.ts +1 -1
- package/dist/server/prompts/shared-rules.d.ts.map +1 -1
- package/dist/server/prompts/shared-rules.js +5 -7
- package/dist/server/prompts/shared-rules.js.map +1 -1
- package/dist/server/schema-prompt.js +1 -1
- package/dist/server/schema-prompt.js.map +1 -1
- package/dist/templates/default/.agents/skills/actions/SKILL.md +37 -9
- package/dist/templates/default/.agents/skills/adding-a-feature/SKILL.md +7 -1
- package/dist/templates/workspace-core/.agents/skills/actions/SKILL.md +37 -9
- package/dist/templates/workspace-core/.agents/skills/adding-a-feature/SKILL.md +7 -1
- package/package.json +1 -1
- package/src/templates/default/.agents/skills/actions/SKILL.md +37 -9
- package/src/templates/default/.agents/skills/adding-a-feature/SKILL.md +7 -1
- package/src/templates/workspace-core/.agents/skills/actions/SKILL.md +37 -9
- package/src/templates/workspace-core/.agents/skills/adding-a-feature/SKILL.md +7 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
import { resolveCredential, } from "../credentials/index.js";
|
|
3
3
|
import { createSsrfSafeDispatcher, isBlockedExtensionUrlWithDns, } from "../extensions/url-safety.js";
|
|
4
|
-
import { listOAuthAccountsByOwner, saveOAuthTokens, } from "../oauth-tokens/index.js";
|
|
4
|
+
import { deleteOAuthTokens, listOAuthAccountsByOwner, saveOAuthTokens, } from "../oauth-tokens/index.js";
|
|
5
5
|
import { getCredentialContext } from "../server/request-context.js";
|
|
6
6
|
import { resolveWorkspaceConnectionCredentialForApp } from "../workspace-connections/credentials.js";
|
|
7
7
|
export { upsertCustomProvider, deleteCustomProvider, listCustomProviders, getCustomProvider, validateCustomBaseUrl, } from "./custom-registry.js";
|
|
@@ -32,6 +32,11 @@ export const PROVIDER_API_IDS = [
|
|
|
32
32
|
"stripe",
|
|
33
33
|
"twitter",
|
|
34
34
|
];
|
|
35
|
+
const PERMANENT_GOOGLE_OAUTH_REFRESH_ERRORS = new Set([
|
|
36
|
+
"invalid_grant",
|
|
37
|
+
"unauthorized_client",
|
|
38
|
+
"invalid_client",
|
|
39
|
+
]);
|
|
35
40
|
const DEFAULT_TIMEOUT_MS = 30_000;
|
|
36
41
|
const MAX_TIMEOUT_MS = 120_000;
|
|
37
42
|
const DEFAULT_MAX_BYTES = 1024 * 1024;
|
|
@@ -94,7 +99,7 @@ const PROVIDER_CONFIGS = {
|
|
|
94
99
|
},
|
|
95
100
|
credentialKeys: ["APOLLO_API_KEY"],
|
|
96
101
|
docsUrls: ["https://docs.apollo.io/reference/api-reference"],
|
|
97
|
-
templateUses: ["analytics"],
|
|
102
|
+
templateUses: ["analytics", "calendar"],
|
|
98
103
|
examples: [
|
|
99
104
|
{
|
|
100
105
|
label: "Search people",
|
|
@@ -270,7 +275,7 @@ const PROVIDER_CONFIGS = {
|
|
|
270
275
|
Accept: "application/vnd.github+json",
|
|
271
276
|
"X-GitHub-Api-Version": "2022-11-28",
|
|
272
277
|
},
|
|
273
|
-
templateUses: ["analytics", "brain", "dispatch"],
|
|
278
|
+
templateUses: ["analytics", "brain", "design", "dispatch"],
|
|
274
279
|
examples: [
|
|
275
280
|
{ label: "Authenticated user", method: "GET", path: "/user" },
|
|
276
281
|
{ label: "Search issues", method: "GET", path: "/search/issues" },
|
|
@@ -319,7 +324,7 @@ const PROVIDER_CONFIGS = {
|
|
|
319
324
|
},
|
|
320
325
|
credentialKeys: ["GONG_ACCESS_KEY", "GONG_ACCESS_SECRET", "GONG_API_BASE"],
|
|
321
326
|
docsUrls: ["https://gong.app.gong.io/settings/api/documentation"],
|
|
322
|
-
templateUses: ["analytics"],
|
|
327
|
+
templateUses: ["analytics", "calendar"],
|
|
323
328
|
examples: [
|
|
324
329
|
{ label: "List calls", method: "GET", path: "/calls" },
|
|
325
330
|
{
|
|
@@ -328,6 +333,24 @@ const PROVIDER_CONFIGS = {
|
|
|
328
333
|
path: "/calls/transcript",
|
|
329
334
|
body: { filter: { callIds: ["<call-id>"] } },
|
|
330
335
|
},
|
|
336
|
+
{
|
|
337
|
+
label: "Calls with parties/content",
|
|
338
|
+
method: "POST",
|
|
339
|
+
path: "/calls/extensive",
|
|
340
|
+
body: {
|
|
341
|
+
filter: { fromDateTime: "<iso-date-time>" },
|
|
342
|
+
contentSelector: {
|
|
343
|
+
exposedFields: {
|
|
344
|
+
parties: true,
|
|
345
|
+
content: { brief: true, keyPoints: true },
|
|
346
|
+
},
|
|
347
|
+
},
|
|
348
|
+
},
|
|
349
|
+
},
|
|
350
|
+
],
|
|
351
|
+
notes: [
|
|
352
|
+
"For broad corpus work, call /calls/extensive with provider-api-request and stageAs/saveToFile. Gong returns the next cursor at records.cursor and expects the next cursor in the POST body at cursor, so use pagination { nextCursorPath: 'records.cursor', cursorBodyPath: 'cursor' } for stageAs or fetchAllPages { cursorPath: 'records.cursor', cursorBodyPath: 'cursor' } for saveToFile.",
|
|
353
|
+
"Batch transcripts with POST /calls/transcript and body { filter: { callIds: [...] } } after narrowing or staging call ids.",
|
|
331
354
|
],
|
|
332
355
|
},
|
|
333
356
|
google_calendar: {
|
|
@@ -343,7 +366,7 @@ const PROVIDER_CONFIGS = {
|
|
|
343
366
|
docsUrls: ["https://developers.google.com/calendar/api/v3/reference"],
|
|
344
367
|
specUrls: ["https://www.googleapis.com/discovery/v1/apis/calendar/v3/rest"],
|
|
345
368
|
allowedHostSuffixes: ["googleapis.com"],
|
|
346
|
-
templateUses: ["brain", "calendar", "dispatch"],
|
|
369
|
+
templateUses: ["brain", "calendar", "dispatch", "mail"],
|
|
347
370
|
examples: [
|
|
348
371
|
{
|
|
349
372
|
label: "List calendars",
|
|
@@ -426,7 +449,7 @@ const PROVIDER_CONFIGS = {
|
|
|
426
449
|
},
|
|
427
450
|
credentialKeys: ["HUBSPOT_PRIVATE_APP_TOKEN", "HUBSPOT_ACCESS_TOKEN"],
|
|
428
451
|
docsUrls: ["https://developers.hubspot.com/docs/api/overview"],
|
|
429
|
-
templateUses: ["analytics", "brain", "mail", "dispatch"],
|
|
452
|
+
templateUses: ["analytics", "brain", "calendar", "mail", "dispatch"],
|
|
430
453
|
examples: [
|
|
431
454
|
{
|
|
432
455
|
label: "Search deals with any HubSpot CRM filter",
|
|
@@ -585,7 +608,7 @@ const PROVIDER_CONFIGS = {
|
|
|
585
608
|
},
|
|
586
609
|
credentialKeys: ["PYLON_API_KEY"],
|
|
587
610
|
docsUrls: ["https://docs.usepylon.com/pylon-docs/developer/api-reference"],
|
|
588
|
-
templateUses: ["analytics"],
|
|
611
|
+
templateUses: ["analytics", "calendar"],
|
|
589
612
|
examples: [{ label: "List issues", method: "GET", path: "/issues" }],
|
|
590
613
|
},
|
|
591
614
|
sentry: {
|
|
@@ -822,9 +845,9 @@ export async function executeProviderApiRequest(args, runtime) {
|
|
|
822
845
|
// --- fetchAllPages mode ---
|
|
823
846
|
if (args.fetchAllPages) {
|
|
824
847
|
const pageCfg = args.fetchAllPages;
|
|
825
|
-
const { items, pageCount, lastStatus, lastContentType } = await fetchAllPages(pageCfg, async (
|
|
826
|
-
const queryWithCursor =
|
|
827
|
-
? mergeQueryObjects(substituteUnknown(args.query, placeholders),
|
|
848
|
+
const { items, pageCount, lastStatus, lastContentType } = await fetchAllPages(pageCfg, async (extra) => {
|
|
849
|
+
const queryWithCursor = extra?.query
|
|
850
|
+
? mergeQueryObjects(substituteUnknown(args.query, placeholders), extra.query)
|
|
828
851
|
: substituteUnknown(args.query, placeholders);
|
|
829
852
|
const pageUrl = buildProviderUrl({
|
|
830
853
|
config,
|
|
@@ -832,7 +855,10 @@ export async function executeProviderApiRequest(args, runtime) {
|
|
|
832
855
|
rawPath: substituteString(args.path, placeholders),
|
|
833
856
|
query: queryWithCursor,
|
|
834
857
|
});
|
|
835
|
-
const
|
|
858
|
+
const bodyWithCursor = extra?.bodyCursor
|
|
859
|
+
? setValueAtPath(substituteUnknown(args.body, placeholders), extra.bodyCursor.path, extra.bodyCursor.value)
|
|
860
|
+
: substituteUnknown(args.body, placeholders);
|
|
861
|
+
const pageBody = prepareBody(bodyWithCursor, { ...headers });
|
|
836
862
|
const resp = await fetchWithTimeout(pageUrl.href, {
|
|
837
863
|
method,
|
|
838
864
|
headers,
|
|
@@ -955,9 +981,9 @@ async function executeCustomProviderApiRequest(args, customConfig, runtime) {
|
|
|
955
981
|
// --- fetchAllPages mode (same cursor pagination as built-in providers) ---
|
|
956
982
|
if (args.fetchAllPages) {
|
|
957
983
|
const pageCfg = args.fetchAllPages;
|
|
958
|
-
const { items, pageCount, lastStatus, lastContentType } = await fetchAllPages(pageCfg, async (
|
|
959
|
-
const queryWithCursor =
|
|
960
|
-
? mergeQueryObjects(args.query,
|
|
984
|
+
const { items, pageCount, lastStatus, lastContentType } = await fetchAllPages(pageCfg, async (extra) => {
|
|
985
|
+
const queryWithCursor = extra?.query
|
|
986
|
+
? mergeQueryObjects(args.query, extra.query)
|
|
961
987
|
: args.query;
|
|
962
988
|
const pageUrl = buildProviderUrl({
|
|
963
989
|
config: syntheticConfig,
|
|
@@ -965,7 +991,10 @@ async function executeCustomProviderApiRequest(args, customConfig, runtime) {
|
|
|
965
991
|
rawPath: args.path,
|
|
966
992
|
query: queryWithCursor,
|
|
967
993
|
});
|
|
968
|
-
const
|
|
994
|
+
const bodyWithCursor = extra?.bodyCursor
|
|
995
|
+
? setValueAtPath(args.body, extra.bodyCursor.path, extra.bodyCursor.value)
|
|
996
|
+
: args.body;
|
|
997
|
+
const pageBody = prepareBody(bodyWithCursor, { ...headers });
|
|
969
998
|
const resp = await fetchWithTimeout(pageUrl.href, {
|
|
970
999
|
method,
|
|
971
1000
|
headers,
|
|
@@ -1394,7 +1423,7 @@ function buildProviderUrl(options) {
|
|
|
1394
1423
|
const rawPath = options.rawPath.trim();
|
|
1395
1424
|
const url = /^https?:\/\//i.test(rawPath)
|
|
1396
1425
|
? new URL(rawPath)
|
|
1397
|
-
: new URL(
|
|
1426
|
+
: new URL(joinProviderUrlPath(base, rawPath), base.origin);
|
|
1398
1427
|
if (!isAllowedProviderUrl(url, base, options.config)) {
|
|
1399
1428
|
throw new Error(`${options.config.label} API requests must stay on the configured provider host or registered provider host suffix.`);
|
|
1400
1429
|
}
|
|
@@ -1403,6 +1432,20 @@ function buildProviderUrl(options) {
|
|
|
1403
1432
|
}
|
|
1404
1433
|
return url;
|
|
1405
1434
|
}
|
|
1435
|
+
function joinProviderUrlPath(base, rawPath) {
|
|
1436
|
+
const basePath = base.pathname.replace(/\/+$/, "");
|
|
1437
|
+
const providerPath = rawPath.replace(/^\/+/, "");
|
|
1438
|
+
if (!providerPath)
|
|
1439
|
+
return basePath || "/";
|
|
1440
|
+
const baseSegments = basePath.split("/").filter(Boolean);
|
|
1441
|
+
const providerSegments = providerPath.split("/").filter(Boolean);
|
|
1442
|
+
if (baseSegments.length > 0 &&
|
|
1443
|
+
providerSegments.length >= baseSegments.length &&
|
|
1444
|
+
baseSegments.every((segment, index) => segment === providerSegments[index])) {
|
|
1445
|
+
return `/${providerPath}`;
|
|
1446
|
+
}
|
|
1447
|
+
return `${basePath}/${providerPath}`;
|
|
1448
|
+
}
|
|
1406
1449
|
function isAllowedProviderUrl(url, base, config) {
|
|
1407
1450
|
if (url.protocol !== "https:" && url.protocol !== "http:")
|
|
1408
1451
|
return false;
|
|
@@ -1533,8 +1576,9 @@ async function resolveAuth(config, runtime, ctx, args) {
|
|
|
1533
1576
|
};
|
|
1534
1577
|
}
|
|
1535
1578
|
if (auth.type === "oauth-bearer") {
|
|
1579
|
+
const oauthProvider = runtime.oauthProviderOverrides?.[config.id] ?? auth.oauthProvider;
|
|
1536
1580
|
const credential = await resolveOAuthBearerToken({
|
|
1537
|
-
auth,
|
|
1581
|
+
auth: { ...auth, oauthProvider },
|
|
1538
1582
|
ctx,
|
|
1539
1583
|
accountId: args.accountId,
|
|
1540
1584
|
});
|
|
@@ -1733,7 +1777,8 @@ async function getValidOAuthAccessToken(options) {
|
|
|
1733
1777
|
const refreshToken = options.tokens.refresh_token ?? options.tokens.refreshToken;
|
|
1734
1778
|
if (!refreshToken)
|
|
1735
1779
|
return accessToken;
|
|
1736
|
-
if (options.oauthProvider === "google"
|
|
1780
|
+
if (options.oauthProvider === "google" ||
|
|
1781
|
+
options.oauthProvider === "google-docs") {
|
|
1737
1782
|
return refreshGoogleOAuthToken(options, refreshToken);
|
|
1738
1783
|
}
|
|
1739
1784
|
throw new Error(`${options.oauthProvider} OAuth token is expired and automatic refresh is not configured for provider-api.`);
|
|
@@ -1756,6 +1801,9 @@ async function refreshGoogleOAuthToken(options, refreshToken) {
|
|
|
1756
1801
|
});
|
|
1757
1802
|
const data = (await res.json());
|
|
1758
1803
|
if (!res.ok || !data.access_token) {
|
|
1804
|
+
if (data.error && PERMANENT_GOOGLE_OAUTH_REFRESH_ERRORS.has(data.error)) {
|
|
1805
|
+
await deleteOAuthTokens(options.oauthProvider, options.accountId);
|
|
1806
|
+
}
|
|
1759
1807
|
const detail = data.error_description ?? data.error ?? res.statusText;
|
|
1760
1808
|
throw new Error(`Google OAuth refresh failed: ${detail}`);
|
|
1761
1809
|
}
|
|
@@ -1811,11 +1859,14 @@ function prepareBody(body, headers) {
|
|
|
1811
1859
|
}
|
|
1812
1860
|
async function fetchWithTimeout(optionsUrl, options) {
|
|
1813
1861
|
const controller = new AbortController();
|
|
1814
|
-
const
|
|
1862
|
+
const timeoutMs = clampTimeout(options.timeoutMs);
|
|
1863
|
+
const timeout = setTimeout(() => controller.abort(), timeoutMs);
|
|
1864
|
+
const method = options.method ?? "GET";
|
|
1865
|
+
const secretValues = options.secretValues ?? [];
|
|
1815
1866
|
try {
|
|
1816
1867
|
const dispatcher = (await createSsrfSafeDispatcher()) ?? undefined;
|
|
1817
1868
|
const fetchOptions = {
|
|
1818
|
-
method
|
|
1869
|
+
method,
|
|
1819
1870
|
headers: options.headers,
|
|
1820
1871
|
body: options.body,
|
|
1821
1872
|
signal: controller.signal,
|
|
@@ -1823,30 +1874,93 @@ async function fetchWithTimeout(optionsUrl, options) {
|
|
|
1823
1874
|
};
|
|
1824
1875
|
if (dispatcher)
|
|
1825
1876
|
fetchOptions.dispatcher = dispatcher;
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1877
|
+
try {
|
|
1878
|
+
return await fetchProviderResponse(optionsUrl, fetchOptions, {
|
|
1879
|
+
maxBytes: options.maxBytes,
|
|
1880
|
+
secretValues,
|
|
1881
|
+
});
|
|
1882
|
+
}
|
|
1883
|
+
catch (error) {
|
|
1884
|
+
if (dispatcher && isDispatcherCompatibilityError(error)) {
|
|
1885
|
+
const fallbackOptions = { ...fetchOptions };
|
|
1886
|
+
delete fallbackOptions.dispatcher;
|
|
1887
|
+
return await fetchProviderResponse(optionsUrl, fallbackOptions, {
|
|
1888
|
+
maxBytes: options.maxBytes,
|
|
1889
|
+
secretValues,
|
|
1890
|
+
});
|
|
1891
|
+
}
|
|
1892
|
+
throw error;
|
|
1893
|
+
}
|
|
1894
|
+
}
|
|
1895
|
+
catch (error) {
|
|
1896
|
+
throw normalizeFetchError(error, {
|
|
1897
|
+
method,
|
|
1898
|
+
url: optionsUrl,
|
|
1899
|
+
timeoutMs,
|
|
1900
|
+
secretValues,
|
|
1901
|
+
});
|
|
1845
1902
|
}
|
|
1846
1903
|
finally {
|
|
1847
1904
|
clearTimeout(timeout);
|
|
1848
1905
|
}
|
|
1849
1906
|
}
|
|
1907
|
+
async function fetchProviderResponse(url, fetchOptions, options) {
|
|
1908
|
+
const startedAt = Date.now();
|
|
1909
|
+
const res = await fetch(url, fetchOptions);
|
|
1910
|
+
const elapsedMs = Date.now() - startedAt;
|
|
1911
|
+
const rawText = await readResponseTextWithLimit(res, clampMaxBytes(options.maxBytes));
|
|
1912
|
+
const redactedText = redactString(rawText.text, options.secretValues);
|
|
1913
|
+
const parsed = tryParseJson(redactedText);
|
|
1914
|
+
return {
|
|
1915
|
+
status: res.status,
|
|
1916
|
+
statusText: res.statusText,
|
|
1917
|
+
ok: res.ok,
|
|
1918
|
+
elapsedMs,
|
|
1919
|
+
headers: redactSecrets(headersToObject(res.headers), options.secretValues),
|
|
1920
|
+
contentType: res.headers.get("content-type") ?? null,
|
|
1921
|
+
size: rawText.size,
|
|
1922
|
+
truncated: rawText.truncated,
|
|
1923
|
+
text: parsed === undefined ? redactedText : undefined,
|
|
1924
|
+
json: parsed,
|
|
1925
|
+
};
|
|
1926
|
+
}
|
|
1927
|
+
function isDispatcherCompatibilityError(error) {
|
|
1928
|
+
const err = error;
|
|
1929
|
+
const code = typeof err?.cause?.code === "string" ? err.cause.code : "";
|
|
1930
|
+
const detail = [
|
|
1931
|
+
typeof err?.message === "string" ? err.message : "",
|
|
1932
|
+
typeof err?.cause?.message === "string" ? err.cause.message : "",
|
|
1933
|
+
]
|
|
1934
|
+
.join(" ")
|
|
1935
|
+
.toLowerCase();
|
|
1936
|
+
return (code === "UND_ERR_INVALID_ARG" &&
|
|
1937
|
+
detail.includes("invalid onrequeststart method"));
|
|
1938
|
+
}
|
|
1939
|
+
function normalizeFetchError(error, options) {
|
|
1940
|
+
const target = describeProviderRequestTarget(options.url, options.secretValues);
|
|
1941
|
+
const err = error;
|
|
1942
|
+
if (err?.name === "AbortError") {
|
|
1943
|
+
return new Error(`Provider API request timed out after ${options.timeoutMs}ms: ${options.method} ${target}`, { cause: error });
|
|
1944
|
+
}
|
|
1945
|
+
const causeCode = typeof err?.cause?.code === "string" && err.cause.code
|
|
1946
|
+
? ` (${err.cause.code})`
|
|
1947
|
+
: "";
|
|
1948
|
+
const detail = typeof err?.cause?.message === "string" && err.cause.message
|
|
1949
|
+
? err.cause.message
|
|
1950
|
+
: typeof err?.message === "string" && err.message
|
|
1951
|
+
? err.message
|
|
1952
|
+
: String(error);
|
|
1953
|
+
return new Error(`Provider API request failed${causeCode}: ${options.method} ${target}: ${redactString(detail, options.secretValues)}`, { cause: error });
|
|
1954
|
+
}
|
|
1955
|
+
function describeProviderRequestTarget(rawUrl, secretValues) {
|
|
1956
|
+
try {
|
|
1957
|
+
const url = new URL(rawUrl);
|
|
1958
|
+
return redactString(`${url.host}${url.pathname}${url.search}`, secretValues);
|
|
1959
|
+
}
|
|
1960
|
+
catch {
|
|
1961
|
+
return redactString(rawUrl, secretValues);
|
|
1962
|
+
}
|
|
1963
|
+
}
|
|
1850
1964
|
async function readResponseTextWithLimit(response, maxBytes) {
|
|
1851
1965
|
const contentLength = response.headers.get("content-length");
|
|
1852
1966
|
if (contentLength && Number(contentLength) > maxBytes) {
|
|
@@ -1928,6 +2042,25 @@ function mergeQueryObjects(base, extra) {
|
|
|
1928
2042
|
}
|
|
1929
2043
|
return extra;
|
|
1930
2044
|
}
|
|
2045
|
+
function setValueAtPath(base, path, value) {
|
|
2046
|
+
const root = base && typeof base === "object" && !Array.isArray(base)
|
|
2047
|
+
? { ...base }
|
|
2048
|
+
: {};
|
|
2049
|
+
const parts = path.split(".").filter(Boolean);
|
|
2050
|
+
if (!parts.length)
|
|
2051
|
+
return root;
|
|
2052
|
+
let current = root;
|
|
2053
|
+
for (const part of parts.slice(0, -1)) {
|
|
2054
|
+
const existing = current[part];
|
|
2055
|
+
const next = existing && typeof existing === "object" && !Array.isArray(existing)
|
|
2056
|
+
? { ...existing }
|
|
2057
|
+
: {};
|
|
2058
|
+
current[part] = next;
|
|
2059
|
+
current = next;
|
|
2060
|
+
}
|
|
2061
|
+
current[parts[parts.length - 1]] = value;
|
|
2062
|
+
return root;
|
|
2063
|
+
}
|
|
1931
2064
|
function clampTimeout(timeoutMs) {
|
|
1932
2065
|
if (!Number.isFinite(timeoutMs))
|
|
1933
2066
|
return DEFAULT_TIMEOUT_MS;
|
|
@@ -1995,14 +2128,29 @@ async function fetchAllPages(config, executeOnePage) {
|
|
|
1995
2128
|
let pageCount = 0;
|
|
1996
2129
|
let lastStatus = 0;
|
|
1997
2130
|
let lastContentType = null;
|
|
2131
|
+
if (!config.cursorParam && !config.cursorBodyPath) {
|
|
2132
|
+
throw new Error("fetchAllPages requires cursorParam or cursorBodyPath to send the next cursor.");
|
|
2133
|
+
}
|
|
2134
|
+
if (config.cursorParam && config.cursorBodyPath) {
|
|
2135
|
+
throw new Error("fetchAllPages accepts exactly one cursor method: cursorParam or cursorBodyPath.");
|
|
2136
|
+
}
|
|
1998
2137
|
while (pageCount < maxPages) {
|
|
1999
|
-
const
|
|
2000
|
-
if (cursor)
|
|
2001
|
-
|
|
2002
|
-
|
|
2138
|
+
const extra = {};
|
|
2139
|
+
if (cursor) {
|
|
2140
|
+
if (config.cursorParam)
|
|
2141
|
+
extra.query = { [config.cursorParam]: cursor };
|
|
2142
|
+
if (config.cursorBodyPath) {
|
|
2143
|
+
extra.bodyCursor = { path: config.cursorBodyPath, value: cursor };
|
|
2144
|
+
}
|
|
2145
|
+
}
|
|
2146
|
+
const page = await executeOnePage(pageCount > 0 ? extra : undefined);
|
|
2003
2147
|
lastStatus = page.status;
|
|
2004
2148
|
lastContentType = page.contentType;
|
|
2005
2149
|
pageCount++;
|
|
2150
|
+
if (!page.ok) {
|
|
2151
|
+
const preview = page.text.replace(/\s+/g, " ").trim().slice(0, 500);
|
|
2152
|
+
throw new Error(`fetchAllPages request failed with HTTP ${page.status}${preview ? `: ${preview}` : ""}`);
|
|
2153
|
+
}
|
|
2006
2154
|
let body;
|
|
2007
2155
|
try {
|
|
2008
2156
|
body = JSON.parse(page.text);
|