api-to-cli 0.1.1 → 0.1.3
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/README.md +153 -9
- package/examples/openapi/sample-openapi-agent/README.md +12 -0
- package/examples/openapi/sample-openapi-agent/agentbridge.manifest.json +85 -0
- package/examples/openapi/sample-openapi-agent/cli/README.md +18 -0
- package/examples/openapi/sample-openapi-agent/cli/bin/sample-crm-api.js +64 -0
- package/examples/openapi/sample-openapi-agent/cli/commands/create-contact.js +59 -0
- package/examples/openapi/sample-openapi-agent/cli/commands/delete-contacts-by-contactid.js +45 -0
- package/examples/openapi/sample-openapi-agent/cli/commands/get-contacts-by-contactid.js +45 -0
- package/examples/openapi/sample-openapi-agent/cli/commands/list-contacts.js +45 -0
- package/examples/openapi/sample-openapi-agent/cli/commands/patch-contacts-by-contactid.js +60 -0
- package/examples/openapi/sample-openapi-agent/cli/lib/client.js +244 -0
- package/examples/openapi/sample-openapi-agent/cli/lib/output.js +21 -0
- package/examples/openapi/sample-openapi-agent/cli/package.json +16 -0
- package/examples/openapi/sample-openapi-agent/skill/SKILL.md +50 -0
- package/examples/openapi/sample-openapi-cli/README.md +18 -0
- package/examples/openapi/sample-openapi-cli/bin/sample-crm-api.js +64 -0
- package/examples/openapi/sample-openapi-cli/commands/create-contact.js +59 -0
- package/examples/openapi/sample-openapi-cli/commands/delete-contacts-by-contactid.js +45 -0
- package/examples/openapi/sample-openapi-cli/commands/get-contacts-by-contactid.js +45 -0
- package/examples/openapi/sample-openapi-cli/commands/list-contacts.js +45 -0
- package/examples/openapi/sample-openapi-cli/commands/patch-contacts-by-contactid.js +60 -0
- package/examples/openapi/sample-openapi-cli/lib/client.js +244 -0
- package/examples/openapi/sample-openapi-cli/lib/output.js +21 -0
- package/examples/openapi/sample-openapi-cli/node_modules/.package-lock.json +15 -0
- package/examples/openapi/sample-openapi-cli/node_modules/commander/LICENSE +22 -0
- package/examples/openapi/sample-openapi-cli/node_modules/commander/Readme.md +1157 -0
- package/examples/openapi/sample-openapi-cli/node_modules/commander/esm.mjs +16 -0
- package/examples/openapi/sample-openapi-cli/node_modules/commander/index.js +24 -0
- package/examples/openapi/sample-openapi-cli/node_modules/commander/lib/argument.js +149 -0
- package/examples/openapi/sample-openapi-cli/node_modules/commander/lib/command.js +2509 -0
- package/examples/openapi/sample-openapi-cli/node_modules/commander/lib/error.js +39 -0
- package/examples/openapi/sample-openapi-cli/node_modules/commander/lib/help.js +520 -0
- package/examples/openapi/sample-openapi-cli/node_modules/commander/lib/option.js +330 -0
- package/examples/openapi/sample-openapi-cli/node_modules/commander/lib/suggestSimilar.js +101 -0
- package/examples/openapi/sample-openapi-cli/node_modules/commander/package-support.json +16 -0
- package/examples/openapi/sample-openapi-cli/node_modules/commander/package.json +84 -0
- package/examples/openapi/sample-openapi-cli/node_modules/commander/typings/esm.d.mts +3 -0
- package/examples/openapi/sample-openapi-cli/node_modules/commander/typings/index.d.ts +969 -0
- package/examples/openapi/sample-openapi-cli/package.json +16 -0
- package/examples/openapi/sample-openapi.yaml +67 -0
- package/examples/trello/trelloapi-agent/README.md +1 -0
- package/examples/trello/trelloapi-agent/agentbridge.manifest.json +1 -1
- package/examples/trello/trelloapi-agent/cli/commands/get-board.js +4 -0
- package/examples/trello/trelloapi-agent/cli/commands/list-board-lists.js +4 -0
- package/examples/trello/trelloapi-agent/cli/commands/list-list-cards.js +4 -0
- package/examples/trello/trelloapi-agent/cli/lib/client.js +174 -9
- package/examples/trello/trelloapi-cli/commands/get-board.js +4 -0
- package/examples/trello/trelloapi-cli/commands/list-board-lists.js +4 -0
- package/examples/trello/trelloapi-cli/commands/list-list-cards.js +4 -0
- package/examples/trello/trelloapi-cli/lib/client.js +174 -9
- package/package.json +9 -5
- package/src/commands/doctor.js +234 -0
- package/src/commands/generate.js +4 -8
- package/src/commands/init.js +154 -0
- package/src/commands/scaffold.js +9 -9
- package/src/commands/validate.js +6 -10
- package/src/index.js +21 -5
- package/src/lib/generate-cli.js +208 -15
- package/src/lib/generate-skill.js +24 -2
- package/src/lib/load-config.js +39 -3
- package/src/lib/openapi-to-config.js +314 -0
- package/src/lib/resolve-config-input.js +50 -0
- package/PROJECT_BRIEF.md +0 -65
- package/SPEC.md +0 -99
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
|
|
3
|
+
function toKebab(name) {
|
|
4
|
+
return String(name)
|
|
5
|
+
.trim()
|
|
6
|
+
.replace(/([a-z0-9])([A-Z])/g, '$1-$2')
|
|
7
|
+
.toLowerCase()
|
|
8
|
+
.replace(/[^a-z0-9]+/g, '-')
|
|
9
|
+
.replace(/^-+|-+$/g, '');
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function toCamelCase(name) {
|
|
13
|
+
return String(name).replace(/[-_]+([a-zA-Z0-9])/g, (_m, g1) => g1.toUpperCase());
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function readOption(options, name) {
|
|
17
|
+
if (Object.prototype.hasOwnProperty.call(options, name)) {
|
|
18
|
+
return options[name];
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const camel = toCamelCase(name);
|
|
22
|
+
if (Object.prototype.hasOwnProperty.call(options, camel)) {
|
|
23
|
+
return options[camel];
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return undefined;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function coerceValue(value, type) {
|
|
30
|
+
if (value === undefined || value === null || value === '') {
|
|
31
|
+
return value;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (type === 'number') {
|
|
35
|
+
const parsed = Number(value);
|
|
36
|
+
if (Number.isNaN(parsed)) {
|
|
37
|
+
throw new Error(`Expected number but received: ${value}`);
|
|
38
|
+
}
|
|
39
|
+
return parsed;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (type === 'boolean') {
|
|
43
|
+
if (typeof value === 'boolean') {
|
|
44
|
+
return value;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const normalized = String(value).toLowerCase();
|
|
48
|
+
if (normalized === 'true' || normalized === '1') {
|
|
49
|
+
return true;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (normalized === 'false' || normalized === '0') {
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
throw new Error(`Expected boolean but received: ${value}`);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return String(value);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function parseJsonText(raw, label) {
|
|
63
|
+
try {
|
|
64
|
+
return JSON.parse(raw);
|
|
65
|
+
} catch (_error) {
|
|
66
|
+
throw new Error(`Invalid JSON for ${label}`);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function parseJsonBody(rawBody) {
|
|
71
|
+
if (rawBody === undefined || rawBody === null || rawBody === '') {
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return parseJsonText(rawBody, '--body');
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function parseBodyFromStdin(enabled) {
|
|
79
|
+
if (!enabled) {
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const raw = fs.readFileSync(0, 'utf8').trim();
|
|
84
|
+
if (!raw) {
|
|
85
|
+
throw new Error('Expected JSON on stdin because --body-stdin was provided');
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return parseJsonText(raw, '--body-stdin');
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function buildRequestBody(command, options) {
|
|
92
|
+
const requestBody = command.requestBody || null;
|
|
93
|
+
if (!requestBody) {
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const direct = parseJsonBody(readOption(options, 'body'));
|
|
98
|
+
const stdin = parseBodyFromStdin(Boolean(readOption(options, 'body-stdin')));
|
|
99
|
+
|
|
100
|
+
if (direct !== null && stdin !== null) {
|
|
101
|
+
throw new Error('Use either --body or --body-stdin, not both');
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const properties = requestBody.properties || {};
|
|
105
|
+
const hasBodyProps = Object.keys(properties).length > 0;
|
|
106
|
+
|
|
107
|
+
let payload = direct !== null ? direct : stdin !== null ? stdin : hasBodyProps ? {} : null;
|
|
108
|
+
if (payload !== null && (typeof payload !== 'object' || Array.isArray(payload))) {
|
|
109
|
+
throw new Error('Request body must be a JSON object');
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (payload === null && requestBody.required) {
|
|
113
|
+
throw new Error('Missing required request body. Provide --body, --body-stdin, or body field flags.');
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
let hadBodyFlag = false;
|
|
117
|
+
Object.entries(properties).forEach(([propName, schema]) => {
|
|
118
|
+
const optionName = `body-${toKebab(propName)}`;
|
|
119
|
+
const raw = readOption(options, optionName);
|
|
120
|
+
if (raw === undefined || raw === null || raw === '') {
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if (payload === null) {
|
|
125
|
+
payload = {};
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
payload[propName] = coerceValue(raw, schema.type);
|
|
129
|
+
hadBodyFlag = true;
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
if (hasBodyProps) {
|
|
133
|
+
if (payload === null) {
|
|
134
|
+
payload = {};
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
Object.entries(properties).forEach(([propName, schema]) => {
|
|
138
|
+
if (!schema.required) {
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
if (!Object.prototype.hasOwnProperty.call(payload, propName) || payload[propName] === undefined || payload[propName] === null || payload[propName] === '') {
|
|
143
|
+
const optionName = `--body-${toKebab(propName)}`;
|
|
144
|
+
throw new Error(`Missing required request body field: ${optionName}`);
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
if (payload !== null) {
|
|
150
|
+
const isEmptyObject = typeof payload === 'object' && !Array.isArray(payload) && Object.keys(payload).length === 0;
|
|
151
|
+
if (isEmptyObject && !requestBody.required && !hadBodyFlag && direct === null && stdin === null) {
|
|
152
|
+
return null;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
return payload;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
async function request(command, options) {
|
|
160
|
+
const auth = {
|
|
161
|
+
"credentials": []
|
|
162
|
+
};
|
|
163
|
+
const params = new URLSearchParams();
|
|
164
|
+
const commandParams = command.params || {};
|
|
165
|
+
let resolvedPath = command.path;
|
|
166
|
+
const headers = {
|
|
167
|
+
accept: 'application/json'
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
(auth.credentials || []).forEach((credential) => {
|
|
171
|
+
const envValue = process.env[credential.envVar];
|
|
172
|
+
|
|
173
|
+
if (!envValue) {
|
|
174
|
+
throw new Error(`Missing required auth environment variable: ${credential.envVar}`);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
const authValue = credential.prefix ? `${credential.prefix}${envValue}` : envValue;
|
|
178
|
+
|
|
179
|
+
if (credential.in === 'header') {
|
|
180
|
+
headers[credential.name] = authValue;
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
params.append(credential.name, authValue);
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
Object.entries(commandParams).forEach(([name, schema]) => {
|
|
188
|
+
const raw = readOption(options, name);
|
|
189
|
+
|
|
190
|
+
if ((raw === undefined || raw === null || raw === '') && schema.required) {
|
|
191
|
+
throw new Error(`Missing required parameter: --${name}`);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
if (raw === undefined || raw === null || raw === '') {
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
const value = coerceValue(raw, schema.type);
|
|
199
|
+
const token = `{${name}}`;
|
|
200
|
+
|
|
201
|
+
if (resolvedPath.includes(token)) {
|
|
202
|
+
resolvedPath = resolvedPath.replaceAll(token, encodeURIComponent(String(value)));
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
params.append(name, String(value));
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
const query = params.toString();
|
|
210
|
+
const url = 'https://api.example-crm.com/v1' + resolvedPath + (query ? '?' + query : '');
|
|
211
|
+
const requestBody = buildRequestBody(command, options);
|
|
212
|
+
|
|
213
|
+
if (requestBody !== null) {
|
|
214
|
+
headers['content-type'] = 'application/json';
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
const response = await fetch(url, {
|
|
218
|
+
method: command.method,
|
|
219
|
+
headers,
|
|
220
|
+
body: requestBody !== null ? JSON.stringify(requestBody) : undefined
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
const text = await response.text();
|
|
224
|
+
let responseBody = text;
|
|
225
|
+
|
|
226
|
+
try {
|
|
227
|
+
responseBody = text ? JSON.parse(text) : null;
|
|
228
|
+
} catch (_err) {
|
|
229
|
+
responseBody = text;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
if (!response.ok) {
|
|
233
|
+
const error = new Error(`HTTP ${response.status}`);
|
|
234
|
+
error.statusCode = response.status;
|
|
235
|
+
error.responseBody = responseBody;
|
|
236
|
+
throw error;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
return responseBody;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
module.exports = {
|
|
243
|
+
request
|
|
244
|
+
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
function json(data, pretty) {
|
|
2
|
+
const text = pretty ? JSON.stringify(data, null, 2) : JSON.stringify(data);
|
|
3
|
+
process.stdout.write(text + '\n');
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
function error(payload, pretty) {
|
|
7
|
+
const envelope = {
|
|
8
|
+
error: true,
|
|
9
|
+
code: payload.code || 'REQUEST_FAILED',
|
|
10
|
+
message: payload.message || 'Request failed',
|
|
11
|
+
details: payload.details || {}
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
const text = pretty ? JSON.stringify(envelope, null, 2) : JSON.stringify(envelope);
|
|
15
|
+
process.stderr.write(text + '\n');
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
module.exports = {
|
|
19
|
+
json,
|
|
20
|
+
error
|
|
21
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "sample-crm-api-cli",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "sample-crm-api CLI generated by AgentBridge",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "commonjs",
|
|
7
|
+
"bin": {
|
|
8
|
+
"sample-crm-api": "./bin/sample-crm-api.js"
|
|
9
|
+
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"start": "node ./bin/sample-crm-api.js"
|
|
12
|
+
},
|
|
13
|
+
"dependencies": {
|
|
14
|
+
"commander": "^12.1.0"
|
|
15
|
+
}
|
|
16
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# sample-crm-api CLI Skill
|
|
2
|
+
|
|
3
|
+
## Purpose
|
|
4
|
+
Use the generated sample-crm-api CLI from AgentBridge. Always prefer JSON output for machine parsing.
|
|
5
|
+
|
|
6
|
+
## Location
|
|
7
|
+
- CLI project: ./cli
|
|
8
|
+
- Binary name: sample-crm-api
|
|
9
|
+
|
|
10
|
+
## Setup
|
|
11
|
+
1. cd ./cli
|
|
12
|
+
2. npm install
|
|
13
|
+
3. npm link
|
|
14
|
+
|
|
15
|
+
## Auth
|
|
16
|
+
- No auth env vars required
|
|
17
|
+
|
|
18
|
+
## Commands
|
|
19
|
+
- list-contacts: List contacts
|
|
20
|
+
- --limit <value> (optional)
|
|
21
|
+
- example: sample-crm-api list-contacts --limit <value>
|
|
22
|
+
- create-contact: Create contact
|
|
23
|
+
- --yes (required for non-GET operations)
|
|
24
|
+
- --body <json> (raw JSON body fallback)
|
|
25
|
+
- --body-stdin (read JSON body from stdin)
|
|
26
|
+
- --body-name <value> (required)
|
|
27
|
+
- --body-email <value> (optional)
|
|
28
|
+
- --body-subscribed <value> (optional)
|
|
29
|
+
- example: sample-crm-api create-contact --yes --body-name <value> --body-email <value> --body-subscribed <value> --body '{"key":"value"}'
|
|
30
|
+
- get-contacts-by-contactid: Get contact by ID
|
|
31
|
+
- --contact-id <value> (required)
|
|
32
|
+
- example: sample-crm-api get-contacts-by-contactid --contact-id <value>
|
|
33
|
+
- patch-contacts-by-contactid: Update contact fields
|
|
34
|
+
- --contact-id <value> (required)
|
|
35
|
+
- --yes (required for non-GET operations)
|
|
36
|
+
- --body <json> (raw JSON body fallback)
|
|
37
|
+
- --body-stdin (read JSON body from stdin)
|
|
38
|
+
- --body-name <value> (optional)
|
|
39
|
+
- --body-email <value> (optional)
|
|
40
|
+
- example: sample-crm-api patch-contacts-by-contactid --contact-id <value> --yes --body-name <value> --body-email <value> --body '{"key":"value"}'
|
|
41
|
+
- delete-contacts-by-contactid: Delete contact
|
|
42
|
+
- --contact-id <value> (required)
|
|
43
|
+
- --yes (required for non-GET operations)
|
|
44
|
+
- example: sample-crm-api delete-contacts-by-contactid --contact-id <value> --yes
|
|
45
|
+
|
|
46
|
+
## Rules
|
|
47
|
+
- Do not echo or log auth secrets.
|
|
48
|
+
- Do not pass credentials as command flags.
|
|
49
|
+
- Parse command stdout as JSON.
|
|
50
|
+
- Treat non-zero exits as failure and read stderr JSON envelope.
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# sample-crm-api CLI
|
|
2
|
+
|
|
3
|
+
Generated by AgentBridge (api-to-cli).
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install
|
|
9
|
+
npm link
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
## Commands
|
|
13
|
+
|
|
14
|
+
- `sample-crm-api list-contacts` - List contacts
|
|
15
|
+
- `sample-crm-api create-contact` - Create contact
|
|
16
|
+
- `sample-crm-api get-contacts-by-contactid` - Get contact by ID
|
|
17
|
+
- `sample-crm-api patch-contacts-by-contactid` - Update contact fields
|
|
18
|
+
- `sample-crm-api delete-contacts-by-contactid` - Delete contact
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const { Command } = require('commander');
|
|
4
|
+
|
|
5
|
+
const cmd0 = require('../commands/list-contacts');
|
|
6
|
+
const cmd1 = require('../commands/create-contact');
|
|
7
|
+
const cmd2 = require('../commands/get-contacts-by-contactid');
|
|
8
|
+
const cmd3 = require('../commands/patch-contacts-by-contactid');
|
|
9
|
+
const cmd4 = require('../commands/delete-contacts-by-contactid');
|
|
10
|
+
|
|
11
|
+
const program = new Command();
|
|
12
|
+
|
|
13
|
+
program
|
|
14
|
+
.name('sample-crm-api')
|
|
15
|
+
.description('sample-crm-api CLI generated by AgentBridge')
|
|
16
|
+
.version('1.0.0');
|
|
17
|
+
|
|
18
|
+
program
|
|
19
|
+
.command('list-contacts')
|
|
20
|
+
.description('List contacts')
|
|
21
|
+
.option('--limit <value>', 'Max contacts to return')
|
|
22
|
+
.option('--pretty', 'Pretty-print JSON')
|
|
23
|
+
.action((options) => cmd0.run(options));
|
|
24
|
+
|
|
25
|
+
program
|
|
26
|
+
.command('create-contact')
|
|
27
|
+
.description('Create contact')
|
|
28
|
+
.option('--yes', 'Confirm non-GET operation')
|
|
29
|
+
.option('--body <json>', 'Raw JSON request body')
|
|
30
|
+
.option('--body-stdin', 'Read JSON body from stdin')
|
|
31
|
+
.option('--body-name <value>', 'Contact full name (required)')
|
|
32
|
+
.option('--body-email <value>', 'Contact email (optional)')
|
|
33
|
+
.option('--body-subscribed <value>', 'Newsletter subscription status (optional)')
|
|
34
|
+
.option('--pretty', 'Pretty-print JSON')
|
|
35
|
+
.action((options) => cmd1.run(options));
|
|
36
|
+
|
|
37
|
+
program
|
|
38
|
+
.command('get-contacts-by-contactid')
|
|
39
|
+
.description('Get contact by ID')
|
|
40
|
+
.option('--contact-id <value>', 'Contact ID')
|
|
41
|
+
.option('--pretty', 'Pretty-print JSON')
|
|
42
|
+
.action((options) => cmd2.run(options));
|
|
43
|
+
|
|
44
|
+
program
|
|
45
|
+
.command('patch-contacts-by-contactid')
|
|
46
|
+
.description('Update contact fields')
|
|
47
|
+
.option('--contact-id <value>', 'Contact ID')
|
|
48
|
+
.option('--yes', 'Confirm non-GET operation')
|
|
49
|
+
.option('--body <json>', 'Raw JSON request body')
|
|
50
|
+
.option('--body-stdin', 'Read JSON body from stdin')
|
|
51
|
+
.option('--body-name <value>', 'Updated full name (optional)')
|
|
52
|
+
.option('--body-email <value>', 'Updated email (optional)')
|
|
53
|
+
.option('--pretty', 'Pretty-print JSON')
|
|
54
|
+
.action((options) => cmd3.run(options));
|
|
55
|
+
|
|
56
|
+
program
|
|
57
|
+
.command('delete-contacts-by-contactid')
|
|
58
|
+
.description('Delete contact')
|
|
59
|
+
.option('--contact-id <value>', 'Contact ID')
|
|
60
|
+
.option('--yes', 'Confirm non-GET operation')
|
|
61
|
+
.option('--pretty', 'Pretty-print JSON')
|
|
62
|
+
.action((options) => cmd4.run(options));
|
|
63
|
+
|
|
64
|
+
program.parse(process.argv);
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
const { request } = require('../lib/client');
|
|
2
|
+
const output = require('../lib/output');
|
|
3
|
+
|
|
4
|
+
const command = {
|
|
5
|
+
"name": "create-contact",
|
|
6
|
+
"description": "Create contact",
|
|
7
|
+
"method": "POST",
|
|
8
|
+
"path": "/contacts",
|
|
9
|
+
"params": {},
|
|
10
|
+
"requestBody": {
|
|
11
|
+
"required": true,
|
|
12
|
+
"properties": {
|
|
13
|
+
"name": {
|
|
14
|
+
"type": "string",
|
|
15
|
+
"required": true,
|
|
16
|
+
"description": "Contact full name"
|
|
17
|
+
},
|
|
18
|
+
"email": {
|
|
19
|
+
"type": "string",
|
|
20
|
+
"required": false,
|
|
21
|
+
"description": "Contact email"
|
|
22
|
+
},
|
|
23
|
+
"subscribed": {
|
|
24
|
+
"type": "boolean",
|
|
25
|
+
"required": false,
|
|
26
|
+
"description": "Newsletter subscription status"
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
async function createContact(options) {
|
|
33
|
+
try {
|
|
34
|
+
if (command.method !== 'GET' && !options.yes) {
|
|
35
|
+
throw new Error('This operation changes state. Re-run with --yes to confirm.');
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const data = await request(command, options);
|
|
39
|
+
output.json(data, Boolean(options.pretty));
|
|
40
|
+
} catch (error) {
|
|
41
|
+
output.error(
|
|
42
|
+
{
|
|
43
|
+
code: error.statusCode ? 'HTTP_ERROR' : 'REQUEST_FAILED',
|
|
44
|
+
message: error.message,
|
|
45
|
+
details: {
|
|
46
|
+
statusCode: error.statusCode || null,
|
|
47
|
+
command: command.name
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
Boolean(options.pretty)
|
|
51
|
+
);
|
|
52
|
+
process.exit(1);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
module.exports = {
|
|
57
|
+
run: createContact,
|
|
58
|
+
command
|
|
59
|
+
};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
const { request } = require('../lib/client');
|
|
2
|
+
const output = require('../lib/output');
|
|
3
|
+
|
|
4
|
+
const command = {
|
|
5
|
+
"name": "delete-contacts-by-contactid",
|
|
6
|
+
"description": "Delete contact",
|
|
7
|
+
"method": "DELETE",
|
|
8
|
+
"path": "/contacts/{contactId}",
|
|
9
|
+
"params": {
|
|
10
|
+
"contactId": {
|
|
11
|
+
"type": "string",
|
|
12
|
+
"required": true,
|
|
13
|
+
"description": "Contact ID"
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
async function deleteContactsByContactid(options) {
|
|
19
|
+
try {
|
|
20
|
+
if (command.method !== 'GET' && !options.yes) {
|
|
21
|
+
throw new Error('This operation changes state. Re-run with --yes to confirm.');
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const data = await request(command, options);
|
|
25
|
+
output.json(data, Boolean(options.pretty));
|
|
26
|
+
} catch (error) {
|
|
27
|
+
output.error(
|
|
28
|
+
{
|
|
29
|
+
code: error.statusCode ? 'HTTP_ERROR' : 'REQUEST_FAILED',
|
|
30
|
+
message: error.message,
|
|
31
|
+
details: {
|
|
32
|
+
statusCode: error.statusCode || null,
|
|
33
|
+
command: command.name
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
Boolean(options.pretty)
|
|
37
|
+
);
|
|
38
|
+
process.exit(1);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
module.exports = {
|
|
43
|
+
run: deleteContactsByContactid,
|
|
44
|
+
command
|
|
45
|
+
};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
const { request } = require('../lib/client');
|
|
2
|
+
const output = require('../lib/output');
|
|
3
|
+
|
|
4
|
+
const command = {
|
|
5
|
+
"name": "get-contacts-by-contactid",
|
|
6
|
+
"description": "Get contact by ID",
|
|
7
|
+
"method": "GET",
|
|
8
|
+
"path": "/contacts/{contactId}",
|
|
9
|
+
"params": {
|
|
10
|
+
"contactId": {
|
|
11
|
+
"type": "string",
|
|
12
|
+
"required": true,
|
|
13
|
+
"description": "Contact ID"
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
async function getContactsByContactid(options) {
|
|
19
|
+
try {
|
|
20
|
+
if (command.method !== 'GET' && !options.yes) {
|
|
21
|
+
throw new Error('This operation changes state. Re-run with --yes to confirm.');
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const data = await request(command, options);
|
|
25
|
+
output.json(data, Boolean(options.pretty));
|
|
26
|
+
} catch (error) {
|
|
27
|
+
output.error(
|
|
28
|
+
{
|
|
29
|
+
code: error.statusCode ? 'HTTP_ERROR' : 'REQUEST_FAILED',
|
|
30
|
+
message: error.message,
|
|
31
|
+
details: {
|
|
32
|
+
statusCode: error.statusCode || null,
|
|
33
|
+
command: command.name
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
Boolean(options.pretty)
|
|
37
|
+
);
|
|
38
|
+
process.exit(1);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
module.exports = {
|
|
43
|
+
run: getContactsByContactid,
|
|
44
|
+
command
|
|
45
|
+
};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
const { request } = require('../lib/client');
|
|
2
|
+
const output = require('../lib/output');
|
|
3
|
+
|
|
4
|
+
const command = {
|
|
5
|
+
"name": "list-contacts",
|
|
6
|
+
"description": "List contacts",
|
|
7
|
+
"method": "GET",
|
|
8
|
+
"path": "/contacts",
|
|
9
|
+
"params": {
|
|
10
|
+
"limit": {
|
|
11
|
+
"type": "number",
|
|
12
|
+
"required": false,
|
|
13
|
+
"description": "Max contacts to return"
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
async function listContacts(options) {
|
|
19
|
+
try {
|
|
20
|
+
if (command.method !== 'GET' && !options.yes) {
|
|
21
|
+
throw new Error('This operation changes state. Re-run with --yes to confirm.');
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const data = await request(command, options);
|
|
25
|
+
output.json(data, Boolean(options.pretty));
|
|
26
|
+
} catch (error) {
|
|
27
|
+
output.error(
|
|
28
|
+
{
|
|
29
|
+
code: error.statusCode ? 'HTTP_ERROR' : 'REQUEST_FAILED',
|
|
30
|
+
message: error.message,
|
|
31
|
+
details: {
|
|
32
|
+
statusCode: error.statusCode || null,
|
|
33
|
+
command: command.name
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
Boolean(options.pretty)
|
|
37
|
+
);
|
|
38
|
+
process.exit(1);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
module.exports = {
|
|
43
|
+
run: listContacts,
|
|
44
|
+
command
|
|
45
|
+
};
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
const { request } = require('../lib/client');
|
|
2
|
+
const output = require('../lib/output');
|
|
3
|
+
|
|
4
|
+
const command = {
|
|
5
|
+
"name": "patch-contacts-by-contactid",
|
|
6
|
+
"description": "Update contact fields",
|
|
7
|
+
"method": "PATCH",
|
|
8
|
+
"path": "/contacts/{contactId}",
|
|
9
|
+
"params": {
|
|
10
|
+
"contactId": {
|
|
11
|
+
"type": "string",
|
|
12
|
+
"required": true,
|
|
13
|
+
"description": "Contact ID"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"requestBody": {
|
|
17
|
+
"required": true,
|
|
18
|
+
"properties": {
|
|
19
|
+
"name": {
|
|
20
|
+
"type": "string",
|
|
21
|
+
"required": false,
|
|
22
|
+
"description": "Updated full name"
|
|
23
|
+
},
|
|
24
|
+
"email": {
|
|
25
|
+
"type": "string",
|
|
26
|
+
"required": false,
|
|
27
|
+
"description": "Updated email"
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
async function patchContactsByContactid(options) {
|
|
34
|
+
try {
|
|
35
|
+
if (command.method !== 'GET' && !options.yes) {
|
|
36
|
+
throw new Error('This operation changes state. Re-run with --yes to confirm.');
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const data = await request(command, options);
|
|
40
|
+
output.json(data, Boolean(options.pretty));
|
|
41
|
+
} catch (error) {
|
|
42
|
+
output.error(
|
|
43
|
+
{
|
|
44
|
+
code: error.statusCode ? 'HTTP_ERROR' : 'REQUEST_FAILED',
|
|
45
|
+
message: error.message,
|
|
46
|
+
details: {
|
|
47
|
+
statusCode: error.statusCode || null,
|
|
48
|
+
command: command.name
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
Boolean(options.pretty)
|
|
52
|
+
);
|
|
53
|
+
process.exit(1);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
module.exports = {
|
|
58
|
+
run: patchContactsByContactid,
|
|
59
|
+
command
|
|
60
|
+
};
|