zapier-platform-cli 16.3.0 → 16.3.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.
@@ -349,6 +349,7 @@
349
349
  "minimal",
350
350
  "oauth1-trello",
351
351
  "oauth2",
352
+ "openai",
352
353
  "search-or-create",
353
354
  "session-auth",
354
355
  "typescript"
@@ -1540,13 +1541,44 @@
1540
1541
  "clear.js"
1541
1542
  ]
1542
1543
  },
1543
- "delete:integration": {
1544
- "aliases": [
1545
- "delete:app"
1544
+ "canary:create": {
1545
+ "aliases": [],
1546
+ "args": {
1547
+ "versionFrom": {
1548
+ "description": "Version to route traffic from",
1549
+ "name": "versionFrom",
1550
+ "required": true
1551
+ },
1552
+ "versionTo": {
1553
+ "description": "Version to canary traffic to",
1554
+ "name": "versionTo",
1555
+ "required": true
1556
+ }
1557
+ },
1558
+ "description": "Create a new canary deployment, diverting a specified percentage of traffic from one version to another for a specified duration.\n\nOnly one canary can be active at the same time. You can run `zapier canary:list` to check. If you would like to create a new canary with different parameters, you can wait for the canary to finish, or delete it using `zapier canary:delete a.b.c x.y.z`.\n\nNote: this is similar to `zapier migrate` but different in that this is temporary and will \"revert\" the changes once the specified duration is expired.\n\n**Only use this command to canary traffic between non-breaking versions!**",
1559
+ "examples": [
1560
+ "zapier canary:create 1.0.0 1.1.0 -p 25 -d 720",
1561
+ "zapier canary:create 2.0.0 2.1.0 --percent 50 --duration 300"
1546
1562
  ],
1547
- "args": {},
1548
- "description": "Delete your integration (including all versions).\n\nThis only works if there are no active users or Zaps on any version. If you only want to delete certain versions, use the `zapier delete:version` command instead. It's unlikely that you'll be able to run this on an app that you've pushed publicly, since there are usually still users.",
1549
1563
  "flags": {
1564
+ "percent": {
1565
+ "char": "p",
1566
+ "description": "Percent of traffic to route to new version",
1567
+ "name": "percent",
1568
+ "required": true,
1569
+ "hasDynamicHelp": false,
1570
+ "multiple": false,
1571
+ "type": "option"
1572
+ },
1573
+ "duration": {
1574
+ "char": "d",
1575
+ "description": "Duration of the canary in seconds",
1576
+ "name": "duration",
1577
+ "required": true,
1578
+ "hasDynamicHelp": false,
1579
+ "multiple": false,
1580
+ "type": "option"
1581
+ },
1550
1582
  "debug": {
1551
1583
  "char": "d",
1552
1584
  "description": "Show extra debugging output.",
@@ -1563,7 +1595,7 @@
1563
1595
  },
1564
1596
  "hasDynamicHelp": false,
1565
1597
  "hiddenAliases": [],
1566
- "id": "delete:integration",
1598
+ "id": "canary:create",
1567
1599
  "pluginAlias": "zapier-platform-cli",
1568
1600
  "pluginName": "zapier-platform-cli",
1569
1601
  "pluginType": "core",
@@ -1575,20 +1607,54 @@
1575
1607
  "src",
1576
1608
  "oclif",
1577
1609
  "commands",
1578
- "delete",
1579
- "integration.js"
1610
+ "canary",
1611
+ "create.js"
1580
1612
  ]
1581
1613
  },
1582
- "delete:version": {
1614
+ "canary:delete": {
1583
1615
  "aliases": [],
1584
1616
  "args": {
1585
- "version": {
1586
- "description": "Specify the version to delete. It must have no users or Zaps.",
1587
- "name": "version",
1617
+ "versionFrom": {
1618
+ "description": "Version to route traffic from",
1619
+ "name": "versionFrom",
1620
+ "required": true
1621
+ },
1622
+ "versionTo": {
1623
+ "description": "Version canary traffic is routed to",
1624
+ "name": "versionTo",
1588
1625
  "required": true
1589
1626
  }
1590
1627
  },
1591
- "description": "Delete a specific version of your integration.\n\nThis only works if there are no users or Zaps on that version. You will probably need to have run `zapier migrate` and `zapier deprecate` before this command will work.",
1628
+ "description": "Delete an active canary deployment",
1629
+ "examples": [
1630
+ "zapier canary:delete 1.0.0 1.1.0"
1631
+ ],
1632
+ "flags": {},
1633
+ "hasDynamicHelp": false,
1634
+ "hiddenAliases": [],
1635
+ "id": "canary:delete",
1636
+ "pluginAlias": "zapier-platform-cli",
1637
+ "pluginName": "zapier-platform-cli",
1638
+ "pluginType": "core",
1639
+ "strict": true,
1640
+ "enableJsonFlag": false,
1641
+ "skipValidInstallCheck": true,
1642
+ "isESM": false,
1643
+ "relativePath": [
1644
+ "src",
1645
+ "oclif",
1646
+ "commands",
1647
+ "canary",
1648
+ "delete.js"
1649
+ ]
1650
+ },
1651
+ "canary:list": {
1652
+ "aliases": [],
1653
+ "args": {},
1654
+ "description": "List all active canary deployments",
1655
+ "examples": [
1656
+ "zapier canary:list"
1657
+ ],
1592
1658
  "flags": {
1593
1659
  "debug": {
1594
1660
  "char": "d",
@@ -1597,6 +1663,22 @@
1597
1663
  "allowNo": false,
1598
1664
  "type": "boolean"
1599
1665
  },
1666
+ "format": {
1667
+ "char": "f",
1668
+ "description": "Change the way structured data is presented. If \"json\" or \"raw\", you can pipe the output of the command into other tools, such as jq.",
1669
+ "name": "format",
1670
+ "default": "table",
1671
+ "hasDynamicHelp": false,
1672
+ "multiple": false,
1673
+ "options": [
1674
+ "plain",
1675
+ "json",
1676
+ "raw",
1677
+ "row",
1678
+ "table"
1679
+ ],
1680
+ "type": "option"
1681
+ },
1600
1682
  "invokedFromAnotherCommand": {
1601
1683
  "hidden": true,
1602
1684
  "name": "invokedFromAnotherCommand",
@@ -1606,7 +1688,7 @@
1606
1688
  },
1607
1689
  "hasDynamicHelp": false,
1608
1690
  "hiddenAliases": [],
1609
- "id": "delete:version",
1691
+ "id": "canary:list",
1610
1692
  "pluginAlias": "zapier-platform-cli",
1611
1693
  "pluginName": "zapier-platform-cli",
1612
1694
  "pluginType": "core",
@@ -1618,8 +1700,8 @@
1618
1700
  "src",
1619
1701
  "oclif",
1620
1702
  "commands",
1621
- "delete",
1622
- "version.js"
1703
+ "canary",
1704
+ "list.js"
1623
1705
  ]
1624
1706
  },
1625
1707
  "env:get": {
@@ -1798,44 +1880,13 @@
1798
1880
  "unset.js"
1799
1881
  ]
1800
1882
  },
1801
- "canary:create": {
1802
- "aliases": [],
1803
- "args": {
1804
- "versionFrom": {
1805
- "description": "Version to route traffic from",
1806
- "name": "versionFrom",
1807
- "required": true
1808
- },
1809
- "versionTo": {
1810
- "description": "Version to canary traffic to",
1811
- "name": "versionTo",
1812
- "required": true
1813
- }
1814
- },
1815
- "description": "Create a new canary deployment, diverting a specified percentage of traffic from one version to another for a specified duration.\n\nOnly one canary can be active at the same time. You can run `zapier canary:list` to check. If you would like to create a new canary with different parameters, you can wait for the canary to finish, or delete it using `zapier canary:delete a.b.c x.y.z`.\n\nNote: this is similar to `zapier migrate` but different in that this is temporary and will \"revert\" the changes once the specified duration is expired.\n\n**Only use this command to canary traffic between non-breaking versions!**",
1816
- "examples": [
1817
- "zapier canary:create 1.0.0 1.1.0 -p 25 -d 720",
1818
- "zapier canary:create 2.0.0 2.1.0 --percent 50 --duration 300"
1883
+ "delete:integration": {
1884
+ "aliases": [
1885
+ "delete:app"
1819
1886
  ],
1887
+ "args": {},
1888
+ "description": "Delete your integration (including all versions).\n\nThis only works if there are no active users or Zaps on any version. If you only want to delete certain versions, use the `zapier delete:version` command instead. It's unlikely that you'll be able to run this on an app that you've pushed publicly, since there are usually still users.",
1820
1889
  "flags": {
1821
- "percent": {
1822
- "char": "p",
1823
- "description": "Percent of traffic to route to new version",
1824
- "name": "percent",
1825
- "required": true,
1826
- "hasDynamicHelp": false,
1827
- "multiple": false,
1828
- "type": "option"
1829
- },
1830
- "duration": {
1831
- "char": "d",
1832
- "description": "Duration of the canary in seconds",
1833
- "name": "duration",
1834
- "required": true,
1835
- "hasDynamicHelp": false,
1836
- "multiple": false,
1837
- "type": "option"
1838
- },
1839
1890
  "debug": {
1840
1891
  "char": "d",
1841
1892
  "description": "Show extra debugging output.",
@@ -1852,7 +1903,7 @@
1852
1903
  },
1853
1904
  "hasDynamicHelp": false,
1854
1905
  "hiddenAliases": [],
1855
- "id": "canary:create",
1906
+ "id": "delete:integration",
1856
1907
  "pluginAlias": "zapier-platform-cli",
1857
1908
  "pluginName": "zapier-platform-cli",
1858
1909
  "pluginType": "core",
@@ -1864,54 +1915,20 @@
1864
1915
  "src",
1865
1916
  "oclif",
1866
1917
  "commands",
1867
- "canary",
1868
- "create.js"
1918
+ "delete",
1919
+ "integration.js"
1869
1920
  ]
1870
1921
  },
1871
- "canary:delete": {
1922
+ "delete:version": {
1872
1923
  "aliases": [],
1873
1924
  "args": {
1874
- "versionFrom": {
1875
- "description": "Version to route traffic from",
1876
- "name": "versionFrom",
1877
- "required": true
1878
- },
1879
- "versionTo": {
1880
- "description": "Version canary traffic is routed to",
1881
- "name": "versionTo",
1925
+ "version": {
1926
+ "description": "Specify the version to delete. It must have no users or Zaps.",
1927
+ "name": "version",
1882
1928
  "required": true
1883
1929
  }
1884
1930
  },
1885
- "description": "Delete an active canary deployment",
1886
- "examples": [
1887
- "zapier canary:delete 1.0.0 1.1.0"
1888
- ],
1889
- "flags": {},
1890
- "hasDynamicHelp": false,
1891
- "hiddenAliases": [],
1892
- "id": "canary:delete",
1893
- "pluginAlias": "zapier-platform-cli",
1894
- "pluginName": "zapier-platform-cli",
1895
- "pluginType": "core",
1896
- "strict": true,
1897
- "enableJsonFlag": false,
1898
- "skipValidInstallCheck": true,
1899
- "isESM": false,
1900
- "relativePath": [
1901
- "src",
1902
- "oclif",
1903
- "commands",
1904
- "canary",
1905
- "delete.js"
1906
- ]
1907
- },
1908
- "canary:list": {
1909
- "aliases": [],
1910
- "args": {},
1911
- "description": "List all active canary deployments",
1912
- "examples": [
1913
- "zapier canary:list"
1914
- ],
1931
+ "description": "Delete a specific version of your integration.\n\nThis only works if there are no users or Zaps on that version. You will probably need to have run `zapier migrate` and `zapier deprecate` before this command will work.",
1915
1932
  "flags": {
1916
1933
  "debug": {
1917
1934
  "char": "d",
@@ -1920,22 +1937,6 @@
1920
1937
  "allowNo": false,
1921
1938
  "type": "boolean"
1922
1939
  },
1923
- "format": {
1924
- "char": "f",
1925
- "description": "Change the way structured data is presented. If \"json\" or \"raw\", you can pipe the output of the command into other tools, such as jq.",
1926
- "name": "format",
1927
- "default": "table",
1928
- "hasDynamicHelp": false,
1929
- "multiple": false,
1930
- "options": [
1931
- "plain",
1932
- "json",
1933
- "raw",
1934
- "row",
1935
- "table"
1936
- ],
1937
- "type": "option"
1938
- },
1939
1940
  "invokedFromAnotherCommand": {
1940
1941
  "hidden": true,
1941
1942
  "name": "invokedFromAnotherCommand",
@@ -1945,7 +1946,7 @@
1945
1946
  },
1946
1947
  "hasDynamicHelp": false,
1947
1948
  "hiddenAliases": [],
1948
- "id": "canary:list",
1949
+ "id": "delete:version",
1949
1950
  "pluginAlias": "zapier-platform-cli",
1950
1951
  "pluginName": "zapier-platform-cli",
1951
1952
  "pluginType": "core",
@@ -1957,8 +1958,8 @@
1957
1958
  "src",
1958
1959
  "oclif",
1959
1960
  "commands",
1960
- "canary",
1961
- "list.js"
1961
+ "delete",
1962
+ "version.js"
1962
1963
  ]
1963
1964
  },
1964
1965
  "team:add": {
@@ -2340,5 +2341,5 @@
2340
2341
  ]
2341
2342
  }
2342
2343
  },
2343
- "version": "16.3.0"
2344
+ "version": "16.3.1"
2344
2345
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zapier-platform-cli",
3
- "version": "16.3.0",
3
+ "version": "16.3.1",
4
4
  "description": "The CLI for managing integrations in Zapier Developer Platform.",
5
5
  "repository": "zapier/zapier-platform",
6
6
  "homepage": "https://platform.zapier.com/",
@@ -201,6 +201,7 @@ const TEMPLATE_ROUTES = {
201
201
  minimal: writeForMinimalTemplate,
202
202
  'oauth1-trello': writeForAuthTemplate,
203
203
  oauth2: writeForAuthTemplate,
204
+ openai: writeForStandaloneTemplate,
204
205
  'search-or-create': writeForStandaloneTemplate,
205
206
  'session-auth': writeForAuthTemplate,
206
207
  typescript: writeForStandaloneTypeScriptTemplate,
@@ -0,0 +1,3 @@
1
+ # OpenAI
2
+
3
+ This Zapier integration project is generated by the `zapier init` CLI command. This integration in particular is using the OpenAI API to generate responses to prompts from users. For this integration, there is a `constants.js` file that will allow you to swap out the base URL and version of the API to swap if you are using an OpenAI compatible API to get started.
@@ -0,0 +1,46 @@
1
+ const { API_URL } = require('./constants');
2
+ // You want to make a request to an endpoint that is either specifically designed
3
+ // to test auth, or one that every user will have access to. eg: `/me`.
4
+ // By returning the entire request object, you have access to the request and
5
+ // response data for testing purposes. Your connection label can access any data
6
+ // from the returned response using the `json.` prefix. eg: `{{json.username}}`.
7
+ const test = (z, bundle) => z.request({ url: `${API_URL}/me` });
8
+
9
+ module.exports = {
10
+ // "custom" is the catch-all auth type. The user supplies some info and Zapier can
11
+ // make authenticated requests with it
12
+ type: 'custom',
13
+
14
+ // Define any input app's auth requires here. The user will be prompted to enter
15
+ // this info when they connect their account.
16
+ fields: [
17
+ {
18
+ key: 'api_key',
19
+ label: 'API Key',
20
+ required: true,
21
+ helpText:
22
+ 'Generate an API Key in your [Platform settings page](https://platform.openai.com/api-keys).',
23
+ },
24
+ // This field is optional and can be removed if not needed
25
+ {
26
+ key: 'organization_id',
27
+ required: false,
28
+ label: 'Organization ID',
29
+ helpText:
30
+ '**Optional** Only required if your OpenAI account belongs to multiple organizations. If not using OpenAI, this field will be disregarded. If your OpenAI account belongs to multiple organizations, optionally add the [Organization ID](https://platform.openai.com/account/org-settings) that this connection should use. If left blank, your [default organization](https://platform.openai.com/account/api-keys) will be used.',
31
+ },
32
+ ],
33
+
34
+ // The test method allows Zapier to verify that the credentials a user provides
35
+ // are valid. We'll execute this method whenever a user connects their account for
36
+ // the first time.
37
+ test,
38
+
39
+ // This template string can access all the data returned from the auth test. If
40
+ // you return the test object, you'll access the returned data with a label like
41
+ // `{{json.X}}`. If you return `response.data` from your test, then your label can
42
+ // be `{{X}}`. This can also be a function that returns a label. That function has
43
+ // the standard args `(z, bundle)` and data returned from the test can be accessed
44
+ // in `bundle.inputData.X`.
45
+ connectionLabel: '{{json.email}}',
46
+ };
@@ -0,0 +1,10 @@
1
+ const BASE_URL = 'https://api.openai.com';
2
+ const VERSION = 'v1';
3
+ const API_URL = `${BASE_URL}/${VERSION}`;
4
+
5
+ const DEFAULT_MODEL = 'gpt-4o-mini';
6
+
7
+ module.exports = {
8
+ API_URL,
9
+ DEFAULT_MODEL,
10
+ };
@@ -0,0 +1,164 @@
1
+ /* eslint-disable camelcase */
2
+ const { API_URL, DEFAULT_MODEL } = require('../constants');
3
+
4
+ const sample = require('../samples/chat.json');
5
+
6
+ async function getAdvancedFields(_z, bundle) {
7
+ if (bundle.inputData.show_advanced === true) {
8
+ return [
9
+ {
10
+ key: 'info_advanced',
11
+ type: 'copy',
12
+ helpText:
13
+ "The following fields are for advanced users and should be used with caution as they may affect performance. In most cases, the default options are sufficient. If you'd like to explore these options further, you can [learn more here](https://help.zapier.com/hc/en-us/articles/22497191078797).",
14
+ },
15
+ {
16
+ key: 'developer_message',
17
+ label: 'Developer/System Message',
18
+ type: 'text',
19
+ helpText:
20
+ 'Instructions to the model that are prioritized ahead of user messages, following [chain of command](https://cdn.openai.com/spec/model-spec-2024-05-08.html#follow-the-chain-of-command).',
21
+ },
22
+ {
23
+ key: 'temperature',
24
+ label: 'Temperature',
25
+ type: 'number',
26
+ helpText:
27
+ 'Higher values mean the model will take more risks. Try 0.9 for more creative applications, and 0 for ones with a well-defined answer.\n\nUse a decimal between 0 and 1.',
28
+ },
29
+ {
30
+ key: 'max_completion_tokens',
31
+ label: 'Maximum Length',
32
+ type: 'integer',
33
+ helpText: 'The maximum number of tokens for the completion.',
34
+ },
35
+ ];
36
+ }
37
+ return [];
38
+ }
39
+
40
+ async function perform(z, bundle) {
41
+ const {
42
+ user_message,
43
+ model,
44
+ files,
45
+ developer_message,
46
+ temperature,
47
+ max_completion_tokens,
48
+ } = bundle.inputData;
49
+
50
+ const developerMessage = {
51
+ role: 'developer',
52
+ content: [
53
+ {
54
+ type: 'text',
55
+ text: developer_message || 'You are a helpful assistant.',
56
+ },
57
+ ],
58
+ };
59
+
60
+ const userMessage = {
61
+ role: 'user',
62
+ content: [
63
+ {
64
+ type: 'text',
65
+ text: user_message,
66
+ },
67
+ ...(files
68
+ ? files.map((file) => ({
69
+ type: 'image_url',
70
+ image_url: {
71
+ url: file,
72
+ },
73
+ }))
74
+ : []),
75
+ ],
76
+ };
77
+
78
+ const messages = [developerMessage, userMessage];
79
+
80
+ const response = await z.request({
81
+ url: `${API_URL}/chat/completions`,
82
+ method: 'POST',
83
+ body: JSON.stringify({
84
+ model,
85
+ messages,
86
+ temperature,
87
+ max_completion_tokens,
88
+ }),
89
+ });
90
+ return response.data;
91
+ }
92
+
93
+ module.exports = {
94
+ key: 'chat_completion',
95
+ noun: 'Chat',
96
+ display: {
97
+ label: 'Chat Completion',
98
+ description: 'Sends a Chat to OpenAI and generates a Completion.',
99
+ },
100
+ operation: {
101
+ perform,
102
+ inputFields: [
103
+ {
104
+ key: 'info_data_usage',
105
+ type: 'copy',
106
+ helpText:
107
+ "Data sent to OpenAI through this Zap is via an API. Under OpenAI's [API data usage policy](https://openai.com/policies/api-data-usage-policies), OpenAI will not use API-submitted data to train or improve their models unless you explicitly decide to share your data with them for that purpose (such as by opting in). For more information, please review OpenAI's article about [when/how data may be used to improve model performance](https://help.openai.com/en/articles/5722486-how-your-data-is-used-to-improve-model-performance).",
108
+ },
109
+ {
110
+ key: 'user_message',
111
+ label: 'User Message',
112
+ type: 'text',
113
+ helpText:
114
+ "Instructions that request some output from the model. Similar to messages you'd type in [ChatGPT](https://chatgpt.com) as an end user.",
115
+ required: true,
116
+ },
117
+ {
118
+ key: 'files',
119
+ label: 'Images',
120
+ type: 'file',
121
+ helpText: 'Images to include along with your message.',
122
+ list: true,
123
+ },
124
+ {
125
+ key: 'model',
126
+ label: 'Model',
127
+ type: 'string',
128
+ required: true,
129
+ default: DEFAULT_MODEL, // Optional to default to a specific model for most users
130
+ dynamic: 'list_models.id.name',
131
+ altersDynamicFields: false,
132
+ },
133
+ {
134
+ key: 'show_advanced',
135
+ label: 'Show Advanced Options',
136
+ type: 'boolean',
137
+ default: 'false',
138
+ altersDynamicFields: true,
139
+ },
140
+ getAdvancedFields,
141
+ ],
142
+ // Rename some of the output fields to be more descriptive for a user
143
+ outputFields: [
144
+ { key: 'id', type: 'string', label: 'Completion ID' },
145
+ { key: 'model', type: 'string', label: 'Model' },
146
+ {
147
+ key: 'usage__prompt_tokens',
148
+ type: 'number',
149
+ label: 'Usage: Prompt Tokens',
150
+ },
151
+ {
152
+ key: 'usage__completion_tokens',
153
+ type: 'number',
154
+ label: 'Usage: Completion Tokens',
155
+ },
156
+ {
157
+ key: 'usage__total_tokens',
158
+ type: 'number',
159
+ label: 'Usage: Total Tokens',
160
+ },
161
+ ],
162
+ sample,
163
+ },
164
+ };
@@ -0,0 +1,7 @@
1
+ /* eslint-disable camelcase */
2
+ const chat_completion = require('./chat_completion');
3
+
4
+ // If you add a new create, make sure it is exported here to display in the Zapier Editor
5
+ module.exports = {
6
+ [chat_completion.key]: chat_completion,
7
+ };
@@ -0,0 +1,7 @@
1
+ /* eslint-disable camelcase */
2
+ const list_models = require('./list_models.js');
3
+
4
+ // If you add a new Dynamic Dropdown, make sure it is exported here to display in the Zapier Editor
5
+ module.exports = {
6
+ [list_models.key]: list_models,
7
+ };
@@ -0,0 +1,24 @@
1
+ const { API_URL } = require('../constants');
2
+
3
+ const perform = async (z, bundle) => {
4
+ const response = await z.request({ url: `${API_URL}/models` });
5
+
6
+ const responseData = response.data;
7
+
8
+ return responseData.data.map((model) => ({
9
+ id: model.id,
10
+ name: model.id,
11
+ }));
12
+ };
13
+
14
+ module.exports = {
15
+ key: 'list_models',
16
+ noun: 'Model',
17
+ display: {
18
+ label: 'List of Models',
19
+ description:
20
+ 'This is a hidden trigger, and is used in a Dynamic Dropdown of another trigger.',
21
+ hidden: true,
22
+ },
23
+ operation: { perform },
24
+ };
@@ -0,0 +1,33 @@
1
+ /* eslint-disable camelcase */
2
+ const authentication = require('./authentication');
3
+ const middleware = require('./middleware');
4
+ const dynamic_dropdowns = require('./dynamic_dropdowns');
5
+ const creates = require('./creates');
6
+
7
+ module.exports = {
8
+ // This is just shorthand to reference the installed dependencies you have.
9
+ // Zapier will need to know these before we can upload.
10
+ version: require('./package.json').version,
11
+ platformVersion: require('zapier-platform-core').version,
12
+
13
+ authentication,
14
+
15
+ beforeRequest: [...middleware.befores],
16
+
17
+ afterResponse: [...middleware.afters],
18
+
19
+ // If you want your trigger to show up, you better include it here!
20
+ triggers: {
21
+ ...dynamic_dropdowns,
22
+ },
23
+
24
+ // If you want your searches to show up, you better include it here!
25
+ searches: {},
26
+
27
+ // If you want your creates to show up, you better include it here!
28
+ creates: {
29
+ ...creates,
30
+ },
31
+
32
+ resources: {},
33
+ };
@@ -0,0 +1,50 @@
1
+ /* eslint-disable camelcase */
2
+ // This function runs after every outbound request. You can use it to check for
3
+ // errors or modify the response. You can have as many as you need. They'll need
4
+ // to each be registered in your index.js file.
5
+ const handleBadResponses = (response, z, bundle) => {
6
+ if (response.data.error) {
7
+ throw new z.errors.Error(
8
+ response.data.error.message,
9
+ response.data.error.code,
10
+ response.status,
11
+ );
12
+ }
13
+
14
+ return response;
15
+ };
16
+
17
+ const includeOrgId = (request, z, bundle) => {
18
+ const { organization_id } = bundle.authData;
19
+ if (organization_id) {
20
+ request.headers['OpenAI-Organization'] = organization_id;
21
+ }
22
+ return request;
23
+ };
24
+
25
+ // This function runs before every outbound request. You can have as many as you
26
+ // need. They'll need to each be registered in your index.js file.
27
+ const includeApiKey = (request, z, bundle) => {
28
+ const { api_key } = bundle.authData;
29
+ if (api_key) {
30
+ // Use these lines to include the API key in the querystring
31
+ // request.params = request.params || {};
32
+ // request.params.api_key = api_key;
33
+
34
+ // If you want to include the API key in the header:
35
+ request.headers.Authorization = `Bearer ${api_key}`;
36
+ }
37
+
38
+ return request;
39
+ };
40
+
41
+ const jsonHeaders = (request) => {
42
+ request.headers['Content-Type'] = 'application/json';
43
+ request.headers.Accept = 'application/json';
44
+ return request;
45
+ };
46
+
47
+ module.exports = {
48
+ befores: [includeApiKey, includeOrgId, jsonHeaders],
49
+ afters: [handleBadResponses],
50
+ };
@@ -0,0 +1,29 @@
1
+ {
2
+ "id": "chatcmpl-123",
3
+ "object": "chat.completion",
4
+ "created": 1677652288,
5
+ "model": "gpt-4o-mini",
6
+ "system_fingerprint": "fp_44709d6fcb",
7
+ "choices": [
8
+ {
9
+ "index": 0,
10
+ "message": {
11
+ "role": "assistant",
12
+ "content": "\n\nHello there, how may I assist you today?"
13
+ },
14
+ "logprobs": null,
15
+ "finish_reason": "stop"
16
+ }
17
+ ],
18
+ "service_tier": "default",
19
+ "usage": {
20
+ "prompt_tokens": 9,
21
+ "completion_tokens": 12,
22
+ "total_tokens": 21,
23
+ "completion_tokens_details": {
24
+ "reasoning_tokens": 0,
25
+ "accepted_prediction_tokens": 0,
26
+ "rejected_prediction_tokens": 0
27
+ }
28
+ }
29
+ }
@@ -0,0 +1,66 @@
1
+ /* globals describe, it, expect */
2
+ /* eslint-disable no-undef */
3
+
4
+ const App = require('../index');
5
+
6
+ describe('custom auth', () => {
7
+ beforeEach(() => {
8
+ jest.clearAllMocks();
9
+ });
10
+
11
+ it('passes authentication and returns json', async () => {
12
+ const bundle = {
13
+ authData: {
14
+ api_key: 'secret',
15
+ },
16
+ };
17
+
18
+ // Mock successful response
19
+ const mockResponse = {
20
+ status: 200,
21
+ data: {
22
+ email: 'test@example.com',
23
+ },
24
+ };
25
+
26
+ // Mock the request client
27
+ const mockRequest = jest.fn().mockResolvedValue(mockResponse);
28
+ const z = {
29
+ request: mockRequest,
30
+ };
31
+
32
+ const response = await App.authentication.test(z, bundle);
33
+
34
+ expect(mockRequest).toHaveBeenCalledTimes(1);
35
+ expect(mockRequest).toHaveBeenCalledWith({
36
+ url: expect.stringContaining('/me'),
37
+ });
38
+ expect(response.status).toBe(200);
39
+ expect(response.data).toHaveProperty('email', 'test@example.com');
40
+ });
41
+
42
+ it('fails on bad auth', async () => {
43
+ const bundle = {
44
+ authData: {
45
+ api_key: 'bad',
46
+ },
47
+ };
48
+
49
+ // Mock failed response
50
+ const mockRequest = jest
51
+ .fn()
52
+ .mockRejectedValue(new Error('Incorrect API key provided'));
53
+ const z = {
54
+ request: mockRequest,
55
+ };
56
+
57
+ try {
58
+ await App.authentication.test(z, bundle);
59
+ } catch (error) {
60
+ expect(mockRequest).toHaveBeenCalledTimes(1);
61
+ expect(error.message).toContain('Incorrect API key provided');
62
+ return;
63
+ }
64
+ throw new Error('appTester should have thrown');
65
+ });
66
+ });
@@ -0,0 +1,150 @@
1
+ /* globals describe, it, expect */
2
+ /* eslint-disable no-undef */
3
+
4
+ const chatCompletion = require('../creates/chat_completion');
5
+ const { DEFAULT_MODEL } = require('../constants');
6
+
7
+ describe('chat_completion', () => {
8
+ beforeEach(() => {
9
+ jest.clearAllMocks();
10
+ });
11
+
12
+ it('creates a basic chat completion', async () => {
13
+ const bundle = {
14
+ inputData: {
15
+ user_message: 'Hello, how are you?',
16
+ model: DEFAULT_MODEL,
17
+ },
18
+ };
19
+
20
+ const mockResponse = {
21
+ data: {
22
+ id: 'chatcmpl-123',
23
+ model: DEFAULT_MODEL,
24
+ usage: {
25
+ prompt_tokens: 20,
26
+ completion_tokens: 15,
27
+ total_tokens: 35,
28
+ },
29
+ choices: [
30
+ {
31
+ message: {
32
+ content: 'I am doing well, thank you for asking!',
33
+ },
34
+ },
35
+ ],
36
+ },
37
+ };
38
+
39
+ const mockRequest = jest.fn().mockResolvedValue(mockResponse);
40
+ const z = { request: mockRequest };
41
+
42
+ const result = await chatCompletion.operation.perform(z, bundle);
43
+
44
+ expect(mockRequest).toHaveBeenCalledTimes(1);
45
+ expect(mockRequest).toHaveBeenCalledWith({
46
+ url: expect.stringContaining('/chat/completions'),
47
+ method: 'POST',
48
+ body: expect.stringContaining(bundle.inputData.user_message),
49
+ });
50
+
51
+ expect(result).toEqual(mockResponse.data);
52
+ });
53
+
54
+ it('creates a chat completion with advanced options', async () => {
55
+ const bundle = {
56
+ inputData: {
57
+ user_message: 'Write a story',
58
+ model: DEFAULT_MODEL,
59
+ developer_message: 'You are a creative writer',
60
+ temperature: 0.9,
61
+ max_completion_tokens: 100,
62
+ },
63
+ };
64
+
65
+ const mockResponse = {
66
+ data: {
67
+ id: 'chatcmpl-456',
68
+ model: DEFAULT_MODEL,
69
+ usage: {
70
+ prompt_tokens: 25,
71
+ completion_tokens: 50,
72
+ total_tokens: 75,
73
+ },
74
+ },
75
+ };
76
+
77
+ const mockRequest = jest.fn().mockResolvedValue(mockResponse);
78
+ const z = { request: mockRequest };
79
+
80
+ const result = await chatCompletion.operation.perform(z, bundle);
81
+
82
+ expect(mockRequest).toHaveBeenCalledTimes(1);
83
+ expect(mockRequest).toHaveBeenCalledWith({
84
+ url: expect.stringContaining('/chat/completions'),
85
+ method: 'POST',
86
+ body: expect.stringMatching(/temperature.*0.9/),
87
+ });
88
+
89
+ expect(result).toEqual(mockResponse.data);
90
+ });
91
+
92
+ it('creates a chat completion with image', async () => {
93
+ const bundle = {
94
+ inputData: {
95
+ user_message: 'Describe this image',
96
+ model: DEFAULT_MODEL,
97
+ files: ['https://example.com/image.jpg'],
98
+ },
99
+ };
100
+
101
+ const mockResponse = {
102
+ data: {
103
+ id: 'chatcmpl-789',
104
+ model: DEFAULT_MODEL,
105
+ usage: {
106
+ prompt_tokens: 30,
107
+ completion_tokens: 20,
108
+ total_tokens: 50,
109
+ },
110
+ },
111
+ };
112
+
113
+ const mockRequest = jest.fn().mockResolvedValue(mockResponse);
114
+ const z = { request: mockRequest };
115
+
116
+ const result = await chatCompletion.operation.perform(z, bundle);
117
+
118
+ expect(mockRequest).toHaveBeenCalledTimes(1);
119
+ expect(mockRequest).toHaveBeenCalledWith({
120
+ url: expect.stringContaining('/chat/completions'),
121
+ method: 'POST',
122
+ body: expect.stringMatching(/image_url.*example.com/),
123
+ });
124
+
125
+ expect(result).toEqual(mockResponse.data);
126
+ });
127
+
128
+ it('handles API errors', async () => {
129
+ const bundle = {
130
+ inputData: {
131
+ user_message: 'Hello',
132
+ model: DEFAULT_MODEL,
133
+ },
134
+ };
135
+
136
+ const mockRequest = jest
137
+ .fn()
138
+ .mockRejectedValue(new Error('Invalid request'));
139
+ const z = { request: mockRequest };
140
+
141
+ try {
142
+ await chatCompletion.operation.perform(z, bundle);
143
+ } catch (error) {
144
+ expect(mockRequest).toHaveBeenCalledTimes(1);
145
+ expect(error.message).toContain('Invalid request');
146
+ return;
147
+ }
148
+ throw new Error('Should have thrown an error');
149
+ });
150
+ });
@@ -0,0 +1,51 @@
1
+ /* globals describe, it, expect */
2
+ /* eslint-disable no-undef */
3
+
4
+ const listModels = require('../dynamic_dropdowns/list_models');
5
+
6
+ describe('list_models', () => {
7
+ beforeEach(() => {
8
+ jest.clearAllMocks();
9
+ });
10
+
11
+ it('returns formatted list of models', async () => {
12
+ const mockResponse = {
13
+ data: {
14
+ data: [{ id: 'gpt-4' }, { id: 'gpt-3.5-turbo' }],
15
+ },
16
+ };
17
+
18
+ const mockRequest = jest.fn().mockResolvedValue(mockResponse);
19
+ const z = { request: mockRequest };
20
+ const bundle = {};
21
+
22
+ const results = await listModels.operation.perform(z, bundle);
23
+
24
+ expect(mockRequest).toHaveBeenCalledTimes(1);
25
+ expect(mockRequest).toHaveBeenCalledWith({
26
+ url: expect.stringContaining('/models'),
27
+ });
28
+
29
+ expect(results).toEqual([
30
+ { id: 'gpt-4', name: 'gpt-4' },
31
+ { id: 'gpt-3.5-turbo', name: 'gpt-3.5-turbo' },
32
+ ]);
33
+ });
34
+
35
+ it('handles API errors', async () => {
36
+ const mockRequest = jest
37
+ .fn()
38
+ .mockRejectedValue(new Error('Failed to fetch models'));
39
+ const z = { request: mockRequest };
40
+ const bundle = {};
41
+
42
+ try {
43
+ await listModels.operation.perform(z, bundle);
44
+ } catch (error) {
45
+ expect(mockRequest).toHaveBeenCalledTimes(1);
46
+ expect(error.message).toContain('Failed to fetch models');
47
+ return;
48
+ }
49
+ throw new Error('Should have thrown an error');
50
+ });
51
+ });
@@ -259,7 +259,7 @@ const getAuthLabel = async (labelTemplate, authData, meta, zcacheTestObj) => {
259
259
  const testResult = await testAuth(authData, meta, zcacheTestObj);
260
260
  labelTemplate = labelTemplate.replace('__', '.');
261
261
  const tpl = _.template(labelTemplate, { interpolate: /{{([\s\S]+?)}}/g });
262
- return tpl(testResult);
262
+ return tpl({ ...testResult, bundle: { authData, inputData: testResult } });
263
263
  };
264
264
 
265
265
  const getLabelForDynamicDropdown = (obj, preferredKey, fallbackKey) => {