api-to-cli 0.1.2 → 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.
Files changed (62) hide show
  1. package/README.md +126 -4
  2. package/examples/openapi/sample-openapi-agent/README.md +12 -0
  3. package/examples/openapi/sample-openapi-agent/agentbridge.manifest.json +85 -0
  4. package/examples/openapi/sample-openapi-agent/cli/README.md +18 -0
  5. package/examples/openapi/sample-openapi-agent/cli/bin/sample-crm-api.js +64 -0
  6. package/examples/openapi/sample-openapi-agent/cli/commands/create-contact.js +59 -0
  7. package/examples/openapi/sample-openapi-agent/cli/commands/delete-contacts-by-contactid.js +45 -0
  8. package/examples/openapi/sample-openapi-agent/cli/commands/get-contacts-by-contactid.js +45 -0
  9. package/examples/openapi/sample-openapi-agent/cli/commands/list-contacts.js +45 -0
  10. package/examples/openapi/sample-openapi-agent/cli/commands/patch-contacts-by-contactid.js +60 -0
  11. package/examples/openapi/sample-openapi-agent/cli/lib/client.js +244 -0
  12. package/examples/openapi/sample-openapi-agent/cli/lib/output.js +21 -0
  13. package/examples/openapi/sample-openapi-agent/cli/package.json +16 -0
  14. package/examples/openapi/sample-openapi-agent/skill/SKILL.md +50 -0
  15. package/examples/openapi/sample-openapi-cli/README.md +18 -0
  16. package/examples/openapi/sample-openapi-cli/bin/sample-crm-api.js +64 -0
  17. package/examples/openapi/sample-openapi-cli/commands/create-contact.js +59 -0
  18. package/examples/openapi/sample-openapi-cli/commands/delete-contacts-by-contactid.js +45 -0
  19. package/examples/openapi/sample-openapi-cli/commands/get-contacts-by-contactid.js +45 -0
  20. package/examples/openapi/sample-openapi-cli/commands/list-contacts.js +45 -0
  21. package/examples/openapi/sample-openapi-cli/commands/patch-contacts-by-contactid.js +60 -0
  22. package/examples/openapi/sample-openapi-cli/lib/client.js +244 -0
  23. package/examples/openapi/sample-openapi-cli/lib/output.js +21 -0
  24. package/examples/openapi/sample-openapi-cli/node_modules/.package-lock.json +15 -0
  25. package/examples/openapi/sample-openapi-cli/node_modules/commander/LICENSE +22 -0
  26. package/examples/openapi/sample-openapi-cli/node_modules/commander/Readme.md +1157 -0
  27. package/examples/openapi/sample-openapi-cli/node_modules/commander/esm.mjs +16 -0
  28. package/examples/openapi/sample-openapi-cli/node_modules/commander/index.js +24 -0
  29. package/examples/openapi/sample-openapi-cli/node_modules/commander/lib/argument.js +149 -0
  30. package/examples/openapi/sample-openapi-cli/node_modules/commander/lib/command.js +2509 -0
  31. package/examples/openapi/sample-openapi-cli/node_modules/commander/lib/error.js +39 -0
  32. package/examples/openapi/sample-openapi-cli/node_modules/commander/lib/help.js +520 -0
  33. package/examples/openapi/sample-openapi-cli/node_modules/commander/lib/option.js +330 -0
  34. package/examples/openapi/sample-openapi-cli/node_modules/commander/lib/suggestSimilar.js +101 -0
  35. package/examples/openapi/sample-openapi-cli/node_modules/commander/package-support.json +16 -0
  36. package/examples/openapi/sample-openapi-cli/node_modules/commander/package.json +84 -0
  37. package/examples/openapi/sample-openapi-cli/node_modules/commander/typings/esm.d.mts +3 -0
  38. package/examples/openapi/sample-openapi-cli/node_modules/commander/typings/index.d.ts +969 -0
  39. package/examples/openapi/sample-openapi-cli/package.json +16 -0
  40. package/examples/openapi/sample-openapi.yaml +67 -0
  41. package/examples/trello/trelloapi-agent/README.md +1 -0
  42. package/examples/trello/trelloapi-agent/agentbridge.manifest.json +1 -1
  43. package/examples/trello/trelloapi-agent/cli/commands/get-board.js +4 -0
  44. package/examples/trello/trelloapi-agent/cli/commands/list-board-lists.js +4 -0
  45. package/examples/trello/trelloapi-agent/cli/commands/list-list-cards.js +4 -0
  46. package/examples/trello/trelloapi-agent/cli/lib/client.js +174 -9
  47. package/examples/trello/trelloapi-cli/commands/get-board.js +4 -0
  48. package/examples/trello/trelloapi-cli/commands/list-board-lists.js +4 -0
  49. package/examples/trello/trelloapi-cli/commands/list-list-cards.js +4 -0
  50. package/examples/trello/trelloapi-cli/lib/client.js +174 -9
  51. package/package.json +8 -2
  52. package/src/commands/doctor.js +234 -0
  53. package/src/commands/generate.js +4 -8
  54. package/src/commands/init.js +154 -0
  55. package/src/commands/scaffold.js +9 -9
  56. package/src/commands/validate.js +6 -10
  57. package/src/index.js +21 -5
  58. package/src/lib/generate-cli.js +208 -15
  59. package/src/lib/generate-skill.js +24 -2
  60. package/src/lib/load-config.js +39 -3
  61. package/src/lib/openapi-to-config.js +314 -0
  62. package/src/lib/resolve-config-input.js +50 -0
@@ -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,67 @@
1
+ openapi: 3.0.3
2
+ info:
3
+ title: Sample CRM API
4
+ version: 1.0.0
5
+ servers:
6
+ - url: https://api.example-crm.com/v1
7
+ paths:
8
+ /contacts:
9
+ get:
10
+ summary: List contacts
11
+ operationId: listContacts
12
+ parameters:
13
+ - in: query
14
+ name: limit
15
+ required: false
16
+ schema:
17
+ type: integer
18
+ description: Max contacts to return
19
+ post:
20
+ summary: Create contact
21
+ operationId: createContact
22
+ requestBody:
23
+ required: true
24
+ content:
25
+ application/json:
26
+ schema:
27
+ type: object
28
+ required:
29
+ - name
30
+ properties:
31
+ name:
32
+ type: string
33
+ description: Contact full name
34
+ email:
35
+ type: string
36
+ description: Contact email
37
+ subscribed:
38
+ type: boolean
39
+ description: Newsletter subscription status
40
+
41
+ /contacts/{contactId}:
42
+ parameters:
43
+ - in: path
44
+ name: contactId
45
+ required: true
46
+ schema:
47
+ type: string
48
+ description: Contact ID
49
+ get:
50
+ summary: Get contact by ID
51
+ patch:
52
+ summary: Update contact fields
53
+ requestBody:
54
+ required: true
55
+ content:
56
+ application/json:
57
+ schema:
58
+ type: object
59
+ properties:
60
+ name:
61
+ type: string
62
+ description: Updated full name
63
+ email:
64
+ type: string
65
+ description: Updated email
66
+ delete:
67
+ summary: Delete contact
@@ -2,6 +2,7 @@
2
2
 
3
3
  ## Contents
4
4
  - CLI project: ./cli
5
+ - Input source: config (/Users/randall/Developer/Src/Repos/api-to-cli/examples/trello/api-to-cli.config.js)
5
6
  - Skill file: ./skill/SKILL.md
6
7
  - Manifest: ./agentbridge.manifest.json
7
8
 
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "schemaVersion": "1.0",
3
3
  "generatedBy": "AgentBridge",
4
- "generatedAt": "2026-02-20T00:42:33.467Z",
4
+ "generatedAt": "2026-02-20T01:48:45.357Z",
5
5
  "project": {
6
6
  "name": "trelloapi",
7
7
  "version": "1.0.0"
@@ -17,6 +17,10 @@ const command = {
17
17
 
18
18
  async function getBoard(options) {
19
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
+
20
24
  const data = await request(command, options);
21
25
  output.json(data, Boolean(options.pretty));
22
26
  } catch (error) {
@@ -17,6 +17,10 @@ const command = {
17
17
 
18
18
  async function listBoardLists(options) {
19
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
+
20
24
  const data = await request(command, options);
21
25
  output.json(data, Boolean(options.pretty));
22
26
  } catch (error) {
@@ -17,6 +17,10 @@ const command = {
17
17
 
18
18
  async function listListCards(options) {
19
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
+
20
24
  const data = await request(command, options);
21
25
  output.json(data, Boolean(options.pretty));
22
26
  } catch (error) {
@@ -1,3 +1,161 @@
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
+
1
159
  async function request(command, options) {
2
160
  const auth = {
3
161
  "credentials": [
@@ -38,16 +196,17 @@ async function request(command, options) {
38
196
  });
39
197
 
40
198
  Object.entries(commandParams).forEach(([name, schema]) => {
41
- const value = options[name];
199
+ const raw = readOption(options, name);
42
200
 
43
- if ((value === undefined || value === null || value === '') && schema.required) {
201
+ if ((raw === undefined || raw === null || raw === '') && schema.required) {
44
202
  throw new Error(`Missing required parameter: --${name}`);
45
203
  }
46
204
 
47
- if (value === undefined || value === null || value === '') {
205
+ if (raw === undefined || raw === null || raw === '') {
48
206
  return;
49
207
  }
50
208
 
209
+ const value = coerceValue(raw, schema.type);
51
210
  const token = `{${name}}`;
52
211
 
53
212
  if (resolvedPath.includes(token)) {
@@ -60,29 +219,35 @@ async function request(command, options) {
60
219
 
61
220
  const query = params.toString();
62
221
  const url = 'https://api.trello.com/1' + resolvedPath + (query ? '?' + query : '');
222
+ const requestBody = buildRequestBody(command, options);
223
+
224
+ if (requestBody !== null) {
225
+ headers['content-type'] = 'application/json';
226
+ }
63
227
 
64
228
  const response = await fetch(url, {
65
229
  method: command.method,
66
- headers
230
+ headers,
231
+ body: requestBody !== null ? JSON.stringify(requestBody) : undefined
67
232
  });
68
233
 
69
234
  const text = await response.text();
70
- let body = text;
235
+ let responseBody = text;
71
236
 
72
237
  try {
73
- body = text ? JSON.parse(text) : null;
238
+ responseBody = text ? JSON.parse(text) : null;
74
239
  } catch (_err) {
75
- body = text;
240
+ responseBody = text;
76
241
  }
77
242
 
78
243
  if (!response.ok) {
79
244
  const error = new Error(`HTTP ${response.status}`);
80
245
  error.statusCode = response.status;
81
- error.responseBody = body;
246
+ error.responseBody = responseBody;
82
247
  throw error;
83
248
  }
84
249
 
85
- return body;
250
+ return responseBody;
86
251
  }
87
252
 
88
253
  module.exports = {
@@ -17,6 +17,10 @@ const command = {
17
17
 
18
18
  async function getBoard(options) {
19
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
+
20
24
  const data = await request(command, options);
21
25
  output.json(data, Boolean(options.pretty));
22
26
  } catch (error) {
@@ -17,6 +17,10 @@ const command = {
17
17
 
18
18
  async function listBoardLists(options) {
19
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
+
20
24
  const data = await request(command, options);
21
25
  output.json(data, Boolean(options.pretty));
22
26
  } catch (error) {
@@ -17,6 +17,10 @@ const command = {
17
17
 
18
18
  async function listListCards(options) {
19
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
+
20
24
  const data = await request(command, options);
21
25
  output.json(data, Boolean(options.pretty));
22
26
  } catch (error) {
@@ -1,3 +1,161 @@
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
+
1
159
  async function request(command, options) {
2
160
  const auth = {
3
161
  "credentials": [
@@ -38,16 +196,17 @@ async function request(command, options) {
38
196
  });
39
197
 
40
198
  Object.entries(commandParams).forEach(([name, schema]) => {
41
- const value = options[name];
199
+ const raw = readOption(options, name);
42
200
 
43
- if ((value === undefined || value === null || value === '') && schema.required) {
201
+ if ((raw === undefined || raw === null || raw === '') && schema.required) {
44
202
  throw new Error(`Missing required parameter: --${name}`);
45
203
  }
46
204
 
47
- if (value === undefined || value === null || value === '') {
205
+ if (raw === undefined || raw === null || raw === '') {
48
206
  return;
49
207
  }
50
208
 
209
+ const value = coerceValue(raw, schema.type);
51
210
  const token = `{${name}}`;
52
211
 
53
212
  if (resolvedPath.includes(token)) {
@@ -60,29 +219,35 @@ async function request(command, options) {
60
219
 
61
220
  const query = params.toString();
62
221
  const url = 'https://api.trello.com/1' + resolvedPath + (query ? '?' + query : '');
222
+ const requestBody = buildRequestBody(command, options);
223
+
224
+ if (requestBody !== null) {
225
+ headers['content-type'] = 'application/json';
226
+ }
63
227
 
64
228
  const response = await fetch(url, {
65
229
  method: command.method,
66
- headers
230
+ headers,
231
+ body: requestBody !== null ? JSON.stringify(requestBody) : undefined
67
232
  });
68
233
 
69
234
  const text = await response.text();
70
- let body = text;
235
+ let responseBody = text;
71
236
 
72
237
  try {
73
- body = text ? JSON.parse(text) : null;
238
+ responseBody = text ? JSON.parse(text) : null;
74
239
  } catch (_err) {
75
- body = text;
240
+ responseBody = text;
76
241
  }
77
242
 
78
243
  if (!response.ok) {
79
244
  const error = new Error(`HTTP ${response.status}`);
80
245
  error.statusCode = response.status;
81
- error.responseBody = body;
246
+ error.responseBody = responseBody;
82
247
  throw error;
83
248
  }
84
249
 
85
- return body;
250
+ return responseBody;
86
251
  }
87
252
 
88
253
  module.exports = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "api-to-cli",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "description": "Generate AI-agent-friendly CLIs, skills, and manifests from API configs",
5
5
  "license": "MIT",
6
6
  "type": "commonjs",
@@ -36,11 +36,17 @@
36
36
  "publishConfig": {
37
37
  "access": "public"
38
38
  },
39
+ "dependencies": {
40
+ "js-yaml": "^4.1.0"
41
+ },
39
42
  "scripts": {
40
43
  "validate:trello": "node ./bin/api-to-cli.js validate --config ./examples/trello/api-to-cli.config.js",
41
44
  "generate:trello": "node ./bin/api-to-cli.js generate --config ./examples/trello/api-to-cli.config.js --output ./examples/trello/trelloapi-cli",
42
45
  "scaffold:trello": "node ./bin/api-to-cli.js scaffold --config ./examples/trello/api-to-cli.config.js --output ./examples/trello/trelloapi-agent",
43
- "test:smoke": "npm run validate:trello && npm run generate:trello && npm run scaffold:trello",
46
+ "validate:openapi": "node ./bin/api-to-cli.js validate --spec ./examples/openapi/sample-openapi.yaml",
47
+ "generate:openapi": "node ./bin/api-to-cli.js generate --spec ./examples/openapi/sample-openapi.yaml --output ./examples/openapi/sample-openapi-cli",
48
+ "scaffold:openapi": "node ./bin/api-to-cli.js scaffold --spec ./examples/openapi/sample-openapi.yaml --output ./examples/openapi/sample-openapi-agent",
49
+ "test:smoke": "npm run validate:trello && npm run generate:trello && npm run scaffold:trello && npm run validate:openapi && npm run generate:openapi && npm run scaffold:openapi",
44
50
  "pack:check": "npm pack --dry-run"
45
51
  }
46
52
  }