@zapier/zapier-sdk-cli 0.30.0 → 0.31.1

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/index.cjs CHANGED
@@ -302,7 +302,7 @@ var LoginSchema = zod.z.object({
302
302
 
303
303
  // package.json
304
304
  var package_default = {
305
- version: "0.30.0"};
305
+ version: "0.31.1"};
306
306
 
307
307
  // src/telemetry/builders.ts
308
308
  function createCliBaseEvent(context = {}) {
@@ -1638,12 +1638,444 @@ var feedbackPlugin = ({
1638
1638
  }
1639
1639
  };
1640
1640
  };
1641
+ var CurlSchema = zod.z.object({
1642
+ url: zod.z.string().describe("Request URL"),
1643
+ request: zod.z.enum(["GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS"]).optional().describe("HTTP method (defaults to GET, or POST if data is provided)"),
1644
+ header: zod.z.array(zod.z.string()).optional().describe("HTTP headers in 'Key: Value' format (repeatable)"),
1645
+ data: zod.z.array(zod.z.string()).optional().describe("HTTP POST data (repeatable, joined with &)"),
1646
+ dataRaw: zod.z.array(zod.z.string()).optional().describe("HTTP POST data without special interpretation (repeatable)"),
1647
+ dataAscii: zod.z.array(zod.z.string()).optional().describe("HTTP POST ASCII data (repeatable)"),
1648
+ dataBinary: zod.z.array(zod.z.string()).optional().describe("HTTP POST binary data (repeatable)"),
1649
+ dataUrlencode: zod.z.array(zod.z.string()).optional().describe("HTTP POST data, URL-encoded (repeatable)"),
1650
+ json: zod.z.string().optional().describe("Send JSON body (sets Content-Type and Accept headers)"),
1651
+ form: zod.z.array(zod.z.string()).optional().describe("Multipart form data as 'name=value' (repeatable)"),
1652
+ formString: zod.z.array(zod.z.string()).optional().describe("Multipart form string field (repeatable)"),
1653
+ get: zod.z.boolean().optional().describe("Force GET method and append data to query string"),
1654
+ head: zod.z.boolean().optional().describe("Fetch headers only (HEAD request)"),
1655
+ location: zod.z.boolean().optional().describe("Follow redirects"),
1656
+ include: zod.z.boolean().optional().describe("Include response headers in output"),
1657
+ output: zod.z.string().optional().describe("Write output to file instead of stdout"),
1658
+ remoteName: zod.z.boolean().optional().describe("Write output to file named like the remote file"),
1659
+ verbose: zod.z.boolean().optional().describe("Verbose output (show request/response headers on stderr)"),
1660
+ silent: zod.z.boolean().optional().describe("Silent mode (suppress errors)"),
1661
+ showError: zod.z.boolean().optional().describe("Show errors even when in silent mode"),
1662
+ fail: zod.z.boolean().optional().describe("Fail silently on HTTP errors (exit code 22)"),
1663
+ failWithBody: zod.z.boolean().optional().describe("Fail on HTTP errors but still output the body"),
1664
+ writeOut: zod.z.string().optional().describe("Output format string after completion (e.g., '%{http_code}')"),
1665
+ maxTime: zod.z.number().optional().describe("Maximum time in seconds for the request"),
1666
+ user: zod.z.string().optional().describe("Basic auth credentials as 'user:password'"),
1667
+ compressed: zod.z.boolean().optional().describe("Request compressed response (sends Accept-Encoding header)"),
1668
+ connectionId: zod.z.union([zod.z.string(), zod.z.number()]).optional().describe("Zapier connection ID for authentication")
1669
+ }).describe("Make HTTP requests through Zapier Relay with curl-like options");
1670
+ var CurlExitError = class extends Error {
1671
+ constructor(message, exitCode) {
1672
+ super(message);
1673
+ this.exitCode = exitCode;
1674
+ this.name = "CurlExitError";
1675
+ }
1676
+ };
1677
+ function parseHeaderLine(input) {
1678
+ const idx = input.indexOf(":");
1679
+ if (idx === -1) {
1680
+ return null;
1681
+ }
1682
+ const key = input.slice(0, idx).trim();
1683
+ const value = input.slice(idx + 1).trim();
1684
+ if (!key) {
1685
+ return null;
1686
+ }
1687
+ return { key, value };
1688
+ }
1689
+ function basicAuthHeader(userpass) {
1690
+ const idx = userpass.indexOf(":");
1691
+ const user = idx === -1 ? userpass : userpass.slice(0, idx);
1692
+ const pass = idx === -1 ? "" : userpass.slice(idx + 1);
1693
+ const token = Buffer.from(`${user}:${pass}`, "utf8").toString("base64");
1694
+ return `Basic ${token}`;
1695
+ }
1696
+ function deriveRemoteFilename(url) {
1697
+ const path2 = url.pathname;
1698
+ const candidate = path2.endsWith("/") ? "index.html" : path.basename(path2);
1699
+ return candidate || "index.html";
1700
+ }
1701
+ function decodeWriteOutEscapes(input) {
1702
+ return input.replace(/\\n/g, "\n").replace(/\\r/g, "\r").replace(/\\t/g, " ");
1703
+ }
1704
+ function formatWriteOut(params) {
1705
+ const { template } = params;
1706
+ const replacements = {
1707
+ "%{http_code}": String(params.httpCode).padStart(3, "0"),
1708
+ "%{time_total}": params.timeTotalSeconds.toFixed(3),
1709
+ "%{size_download}": String(params.sizeDownloadBytes),
1710
+ "%{url_effective}": params.urlEffective,
1711
+ "%{content_type}": params.contentType ?? ""
1712
+ };
1713
+ let out = template;
1714
+ for (const [token, value] of Object.entries(replacements)) {
1715
+ out = out.split(token).join(value);
1716
+ }
1717
+ return decodeWriteOutEscapes(out);
1718
+ }
1719
+ function appendQueryParams(url, dataParts) {
1720
+ const out = new URL(url.toString());
1721
+ for (const part of dataParts) {
1722
+ const segments = part.split("&");
1723
+ for (const seg of segments) {
1724
+ if (!seg) continue;
1725
+ const idx = seg.indexOf("=");
1726
+ if (idx === -1) {
1727
+ out.searchParams.append(seg, "");
1728
+ } else {
1729
+ out.searchParams.append(seg.slice(0, idx), seg.slice(idx + 1));
1730
+ }
1731
+ }
1732
+ }
1733
+ return out;
1734
+ }
1735
+ async function readAllStdin() {
1736
+ const chunks = [];
1737
+ for await (const chunk of process.stdin) {
1738
+ chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
1739
+ }
1740
+ return Buffer.concat(chunks);
1741
+ }
1742
+ async function resolveDataArgText(raw) {
1743
+ if (!raw.startsWith("@")) {
1744
+ return raw;
1745
+ }
1746
+ const source = raw.slice(1);
1747
+ if (source === "-") {
1748
+ const buf2 = await readAllStdin();
1749
+ return buf2.toString("utf8");
1750
+ }
1751
+ const buf = await fs.promises.readFile(source);
1752
+ return buf.toString("utf8");
1753
+ }
1754
+ async function resolveDataArgBinary(raw) {
1755
+ if (!raw.startsWith("@")) {
1756
+ return Buffer.from(raw, "utf8");
1757
+ }
1758
+ const source = raw.slice(1);
1759
+ if (source === "-") {
1760
+ return await readAllStdin();
1761
+ }
1762
+ return await fs.promises.readFile(source);
1763
+ }
1764
+ async function buildFormData(formArgs, formStringArgs) {
1765
+ if (typeof FormData === "undefined") {
1766
+ throw new CurlExitError(
1767
+ "FormData is not available in this runtime; cannot use --form.",
1768
+ 2
1769
+ );
1770
+ }
1771
+ const fd = new FormData();
1772
+ const addField = async (item, forceString) => {
1773
+ const idx = item.indexOf("=");
1774
+ if (idx === -1) {
1775
+ throw new CurlExitError(
1776
+ `Invalid form field: '${item}'. Expected 'name=value' or 'name=@file'.`,
1777
+ 2
1778
+ );
1779
+ }
1780
+ const name = item.slice(0, idx);
1781
+ const value = item.slice(idx + 1);
1782
+ if (!name) {
1783
+ throw new CurlExitError(
1784
+ `Invalid form field: '${item}'. Field name cannot be empty.`,
1785
+ 2
1786
+ );
1787
+ }
1788
+ if (!forceString && value.startsWith("@")) {
1789
+ const filePath = value.slice(1);
1790
+ const buf = filePath === "-" ? await readAllStdin() : await fs.promises.readFile(filePath);
1791
+ if (typeof Blob === "undefined") {
1792
+ fd.append(name, buf.toString("utf8"));
1793
+ return;
1794
+ }
1795
+ const blob = new Blob([buf]);
1796
+ const filename = filePath === "-" ? `stdin-${crypto.createHash("sha1").update(buf).digest("hex").slice(0, 8)}` : path.basename(filePath);
1797
+ fd.append(name, blob, filename);
1798
+ return;
1799
+ }
1800
+ fd.append(name, value);
1801
+ };
1802
+ for (const item of formArgs) {
1803
+ await addField(item, false);
1804
+ }
1805
+ for (const item of formStringArgs) {
1806
+ await addField(item, true);
1807
+ }
1808
+ return fd;
1809
+ }
1810
+
1811
+ // src/plugins/curl/index.ts
1812
+ var curlPlugin = ({
1813
+ sdk
1814
+ }) => {
1815
+ async function curl(options) {
1816
+ const {
1817
+ url: rawUrl,
1818
+ request,
1819
+ header = [],
1820
+ data = [],
1821
+ dataRaw = [],
1822
+ dataAscii = [],
1823
+ dataBinary = [],
1824
+ dataUrlencode = [],
1825
+ json,
1826
+ form = [],
1827
+ formString = [],
1828
+ get: forceGet,
1829
+ head: forceHead,
1830
+ location,
1831
+ include,
1832
+ output,
1833
+ remoteName,
1834
+ verbose,
1835
+ silent,
1836
+ showError,
1837
+ fail,
1838
+ failWithBody,
1839
+ writeOut,
1840
+ maxTime,
1841
+ user,
1842
+ compressed,
1843
+ connectionId
1844
+ } = options;
1845
+ const parsedUrl = new URL(rawUrl);
1846
+ const headers = {};
1847
+ for (const h of header) {
1848
+ const parsed = parseHeaderLine(h);
1849
+ if (parsed) {
1850
+ headers[parsed.key] = parsed.value;
1851
+ }
1852
+ }
1853
+ if (user) {
1854
+ headers["Authorization"] = basicAuthHeader(user);
1855
+ }
1856
+ if (compressed) {
1857
+ headers["Accept-Encoding"] = "gzip, deflate, br";
1858
+ }
1859
+ const rawTextDataArgs = [...data, ...dataRaw, ...dataAscii];
1860
+ const rawBinaryDataArgs = [...dataBinary];
1861
+ const rawUrlencodeArgs = [...dataUrlencode];
1862
+ const hasForm = form.length > 0 || formString.length > 0;
1863
+ const hasJson = json !== void 0;
1864
+ const hasAnyData = hasJson || hasForm || rawTextDataArgs.length > 0 || rawBinaryDataArgs.length > 0 || rawUrlencodeArgs.length > 0;
1865
+ let method = "GET";
1866
+ if (forceHead) {
1867
+ method = "HEAD";
1868
+ } else if (request) {
1869
+ method = request;
1870
+ } else if (forceGet) {
1871
+ method = "GET";
1872
+ } else if (hasAnyData) {
1873
+ method = "POST";
1874
+ }
1875
+ let body;
1876
+ let effectiveUrl = parsedUrl;
1877
+ if (hasJson) {
1878
+ if (!headers["Content-Type"]) {
1879
+ headers["Content-Type"] = "application/json";
1880
+ }
1881
+ if (!headers["Accept"]) {
1882
+ headers["Accept"] = "application/json";
1883
+ }
1884
+ body = json;
1885
+ } else if (hasForm) {
1886
+ body = await buildFormData(form, formString);
1887
+ } else {
1888
+ const resolvedTextParts = [];
1889
+ for (const raw of rawTextDataArgs) {
1890
+ resolvedTextParts.push(await resolveDataArgText(raw));
1891
+ }
1892
+ const resolvedUrlEncodeParts = [];
1893
+ for (const raw of rawUrlencodeArgs) {
1894
+ if (raw.startsWith("@")) {
1895
+ const content = await resolveDataArgText(raw);
1896
+ resolvedUrlEncodeParts.push(encodeURIComponent(content));
1897
+ continue;
1898
+ }
1899
+ const atIdx = raw.indexOf("@");
1900
+ const eqIdx = raw.indexOf("=");
1901
+ if (eqIdx !== -1) {
1902
+ const key = raw.slice(0, eqIdx);
1903
+ const value = raw.slice(eqIdx + 1);
1904
+ resolvedUrlEncodeParts.push(
1905
+ `${encodeURIComponent(key)}=${encodeURIComponent(value)}`
1906
+ );
1907
+ } else if (atIdx !== -1) {
1908
+ const key = raw.slice(0, atIdx);
1909
+ const filePath = raw.slice(atIdx + 1);
1910
+ const buf2 = filePath === "-" ? await readAllStdin() : await fs.promises.readFile(filePath);
1911
+ resolvedUrlEncodeParts.push(
1912
+ `${encodeURIComponent(key)}=${encodeURIComponent(buf2.toString("utf8"))}`
1913
+ );
1914
+ } else {
1915
+ resolvedUrlEncodeParts.push(encodeURIComponent(raw));
1916
+ }
1917
+ }
1918
+ const resolvedBinaryParts = [];
1919
+ for (const raw of rawBinaryDataArgs) {
1920
+ resolvedBinaryParts.push(await resolveDataArgBinary(raw));
1921
+ }
1922
+ const allTextParts = [...resolvedTextParts, ...resolvedUrlEncodeParts];
1923
+ if (forceGet && allTextParts.length > 0) {
1924
+ effectiveUrl = appendQueryParams(parsedUrl, allTextParts);
1925
+ } else if (resolvedBinaryParts.length > 0) {
1926
+ body = Buffer.concat(resolvedBinaryParts);
1927
+ } else if (allTextParts.length > 0) {
1928
+ body = allTextParts.join("&");
1929
+ if (!headers["Content-Type"]) {
1930
+ headers["Content-Type"] = "application/x-www-form-urlencoded";
1931
+ }
1932
+ }
1933
+ }
1934
+ const redirect = location ? "follow" : "manual";
1935
+ if (verbose && !silent) {
1936
+ process.stderr.write(`> ${method} ${effectiveUrl.toString()}
1937
+ `);
1938
+ for (const [k, v] of Object.entries(headers)) {
1939
+ process.stderr.write(`> ${k}: ${v}
1940
+ `);
1941
+ }
1942
+ process.stderr.write(">\n");
1943
+ }
1944
+ const signal = maxTime ? AbortSignal.timeout(maxTime * 1e3) : void 0;
1945
+ const start = performance.now();
1946
+ const response = await sdk.fetch(effectiveUrl.toString(), {
1947
+ method,
1948
+ headers,
1949
+ body,
1950
+ redirect,
1951
+ signal,
1952
+ connectionId
1953
+ });
1954
+ const timeTotalSeconds = (performance.now() - start) / 1e3;
1955
+ if (verbose && !silent) {
1956
+ process.stderr.write(
1957
+ `< HTTP ${response.status} ${response.statusText}
1958
+ `
1959
+ );
1960
+ response.headers.forEach((value, key) => {
1961
+ process.stderr.write(`< ${key}: ${value}
1962
+ `);
1963
+ });
1964
+ process.stderr.write("<\n");
1965
+ }
1966
+ const isHttpError = response.status >= 400;
1967
+ const shouldFail = (fail || failWithBody) && isHttpError;
1968
+ const shouldOutputBody = !shouldFail || !!failWithBody;
1969
+ const headerText = include ? `HTTP ${response.status} ${response.statusText}
1970
+ ${Array.from(
1971
+ response.headers.entries()
1972
+ ).map(([k, v]) => `${k}: ${v}`).join("\n")}
1973
+
1974
+ ` : "";
1975
+ let bodyBytes = 0;
1976
+ const buf = shouldOutputBody ? Buffer.from(await response.arrayBuffer()) : Buffer.alloc(0);
1977
+ bodyBytes = buf.length;
1978
+ const outputFile = output && output !== "-" ? output : remoteName ? deriveRemoteFilename(parsedUrl) : void 0;
1979
+ if (outputFile) {
1980
+ const dir = path.dirname(outputFile);
1981
+ if (dir !== ".") {
1982
+ await fs.promises.mkdir(dir, { recursive: true });
1983
+ }
1984
+ const ws = fs.createWriteStream(outputFile);
1985
+ if (headerText) {
1986
+ ws.write(headerText);
1987
+ }
1988
+ if (buf.length) {
1989
+ ws.write(buf);
1990
+ }
1991
+ await new Promise((resolve4, reject) => {
1992
+ ws.end(() => resolve4());
1993
+ ws.on("error", reject);
1994
+ });
1995
+ } else {
1996
+ if (headerText) {
1997
+ process.stdout.write(headerText);
1998
+ }
1999
+ if (buf.length) {
2000
+ process.stdout.write(buf);
2001
+ }
2002
+ }
2003
+ if (writeOut) {
2004
+ const formatted = formatWriteOut({
2005
+ template: writeOut,
2006
+ urlEffective: response.url || effectiveUrl.toString(),
2007
+ httpCode: response.status,
2008
+ timeTotalSeconds,
2009
+ sizeDownloadBytes: bodyBytes,
2010
+ contentType: response.headers.get("content-type")
2011
+ });
2012
+ process.stdout.write(formatted);
2013
+ }
2014
+ if (shouldFail) {
2015
+ if (!silent || showError) {
2016
+ process.stderr.write(
2017
+ `curl: (22) The requested URL returned error: ${response.status}
2018
+ `
2019
+ );
2020
+ }
2021
+ throw new CurlExitError("HTTP request failed", 22);
2022
+ }
2023
+ return void 0;
2024
+ }
2025
+ return {
2026
+ curl,
2027
+ context: {
2028
+ meta: {
2029
+ curl: {
2030
+ description: "Make authenticated HTTP requests to any API through Zapier. Pass a connection ID to automatically inject the user's stored credentials (OAuth tokens, API keys, etc.) into the outgoing request. Use it in place of the native curl command with additional Zapier-specific options.",
2031
+ categories: ["http"],
2032
+ inputSchema: CurlSchema,
2033
+ aliases: {
2034
+ request: "X",
2035
+ header: "H",
2036
+ data: "d",
2037
+ form: "F",
2038
+ get: "G",
2039
+ head: "I",
2040
+ location: "L",
2041
+ include: "i",
2042
+ output: "o",
2043
+ remoteName: "O",
2044
+ verbose: "v",
2045
+ silent: "s",
2046
+ showError: "S",
2047
+ writeOut: "w",
2048
+ maxTime: "m",
2049
+ user: "u"
2050
+ }
2051
+ }
2052
+ }
2053
+ }
2054
+ };
2055
+ };
2056
+
2057
+ // src/plugins/cliOverrides/index.ts
2058
+ var cliOverridesPlugin = ({ context }) => {
2059
+ if (!context.meta.fetch) {
2060
+ return { context: { meta: {} } };
2061
+ }
2062
+ return {
2063
+ context: {
2064
+ meta: {
2065
+ fetch: {
2066
+ ...context.meta.fetch,
2067
+ categories: [...context.meta.fetch.categories || [], "deprecated"]
2068
+ }
2069
+ }
2070
+ }
2071
+ };
2072
+ };
1641
2073
 
1642
2074
  // src/sdk.ts
1643
2075
  function createZapierCliSdk(options = {}) {
1644
2076
  return zapierSdk.createZapierSdkWithoutRegistry({
1645
2077
  ...options
1646
- }).addPlugin(generateAppTypesPlugin).addPlugin(buildManifestPlugin).addPlugin(bundleCodePlugin).addPlugin(getLoginConfigPathPlugin).addPlugin(addPlugin).addPlugin(feedbackPlugin).addPlugin(mcpPlugin).addPlugin(loginPlugin).addPlugin(logoutPlugin).addPlugin(zapierSdk.registryPlugin);
2078
+ }).addPlugin(generateAppTypesPlugin).addPlugin(buildManifestPlugin).addPlugin(bundleCodePlugin).addPlugin(getLoginConfigPathPlugin).addPlugin(addPlugin).addPlugin(feedbackPlugin).addPlugin(curlPlugin).addPlugin(mcpPlugin).addPlugin(loginPlugin).addPlugin(logoutPlugin).addPlugin(cliOverridesPlugin).addPlugin(zapierSdk.registryPlugin);
1647
2079
  }
1648
2080
 
1649
2081
  exports.buildCliCommandExecutedEvent = buildCliCommandExecutedEvent;