@sinch/cli 0.1.27-beta.0 → 0.1.28-beta.0

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 (133) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +198 -198
  3. package/bin/sinch +2 -2
  4. package/dist/commands/conversations/apps.d.ts +2 -0
  5. package/dist/commands/conversations/apps.d.ts.map +1 -0
  6. package/dist/commands/conversations/apps.js +162 -0
  7. package/dist/commands/conversations/apps.js.map +1 -0
  8. package/dist/commands/conversations/contacts.d.ts +2 -0
  9. package/dist/commands/conversations/contacts.d.ts.map +1 -0
  10. package/dist/commands/conversations/contacts.js +245 -0
  11. package/dist/commands/conversations/contacts.js.map +1 -0
  12. package/dist/commands/conversations/conversation.d.ts +2 -0
  13. package/dist/commands/conversations/conversation.d.ts.map +1 -0
  14. package/dist/commands/conversations/conversation.js +19 -0
  15. package/dist/commands/conversations/conversation.js.map +1 -0
  16. package/dist/commands/conversations/conversations.d.ts +2 -0
  17. package/dist/commands/conversations/conversations.d.ts.map +1 -0
  18. package/dist/commands/conversations/conversations.js +104 -0
  19. package/dist/commands/conversations/conversations.js.map +1 -0
  20. package/dist/commands/conversations/messages.d.ts +2 -0
  21. package/dist/commands/conversations/messages.d.ts.map +1 -0
  22. package/dist/commands/conversations/messages.js +107 -0
  23. package/dist/commands/conversations/messages.js.map +1 -0
  24. package/dist/commands/conversations/send.d.ts +2 -0
  25. package/dist/commands/conversations/send.d.ts.map +1 -0
  26. package/dist/commands/conversations/send.js +185 -0
  27. package/dist/commands/conversations/send.js.map +1 -0
  28. package/dist/commands/conversations/utils.d.ts +4 -0
  29. package/dist/commands/conversations/utils.d.ts.map +1 -0
  30. package/dist/commands/conversations/utils.js +30 -0
  31. package/dist/commands/conversations/utils.js.map +1 -0
  32. package/dist/commands/conversations/webhooks.d.ts +2 -0
  33. package/dist/commands/conversations/webhooks.d.ts.map +1 -0
  34. package/dist/commands/conversations/webhooks.js +238 -0
  35. package/dist/commands/conversations/webhooks.js.map +1 -0
  36. package/dist/commands/delete.d.ts +2 -0
  37. package/dist/commands/delete.d.ts.map +1 -0
  38. package/dist/commands/delete.js +93 -0
  39. package/dist/commands/delete.js.map +1 -0
  40. package/dist/commands/deploy.d.ts +2 -0
  41. package/dist/commands/deploy.d.ts.map +1 -0
  42. package/dist/commands/deploy.js +687 -0
  43. package/dist/commands/deploy.js.map +1 -0
  44. package/dist/commands/dev.d.ts +2 -0
  45. package/dist/commands/dev.d.ts.map +1 -0
  46. package/dist/commands/dev.js +460 -0
  47. package/dist/commands/dev.js.map +1 -0
  48. package/dist/commands/docs.d.ts +2 -0
  49. package/dist/commands/docs.d.ts.map +1 -0
  50. package/dist/commands/docs.js +105 -0
  51. package/dist/commands/docs.js.map +1 -0
  52. package/dist/commands/download.d.ts +2 -0
  53. package/dist/commands/download.d.ts.map +1 -0
  54. package/dist/commands/download.js +207 -0
  55. package/dist/commands/download.js.map +1 -0
  56. package/dist/commands/functions.d.ts +2 -0
  57. package/dist/commands/functions.d.ts.map +1 -0
  58. package/dist/commands/functions.js +25 -0
  59. package/dist/commands/functions.js.map +1 -0
  60. package/dist/commands/init.d.ts +2 -0
  61. package/dist/commands/init.d.ts.map +1 -0
  62. package/dist/commands/init.js +552 -0
  63. package/dist/commands/init.js.map +1 -0
  64. package/dist/commands/list.d.ts +2 -0
  65. package/dist/commands/list.d.ts.map +1 -0
  66. package/dist/commands/list.js +180 -0
  67. package/dist/commands/list.js.map +1 -0
  68. package/dist/commands/logs.d.ts +2 -0
  69. package/dist/commands/logs.d.ts.map +1 -0
  70. package/dist/commands/logs.js +1001 -0
  71. package/dist/commands/logs.js.map +1 -0
  72. package/dist/commands/secrets.d.ts +2 -0
  73. package/dist/commands/secrets.d.ts.map +1 -0
  74. package/dist/commands/secrets.js +197 -0
  75. package/dist/commands/secrets.js.map +1 -0
  76. package/dist/commands/skills.d.ts +4 -0
  77. package/dist/commands/skills.d.ts.map +1 -0
  78. package/dist/commands/skills.js +309 -0
  79. package/dist/commands/skills.js.map +1 -0
  80. package/dist/commands/status.d.ts +2 -0
  81. package/dist/commands/status.d.ts.map +1 -0
  82. package/dist/commands/status.js +145 -0
  83. package/dist/commands/status.js.map +1 -0
  84. package/dist/commands/templates.d.ts +2 -0
  85. package/dist/commands/templates.d.ts.map +1 -0
  86. package/dist/commands/templates.js +168 -0
  87. package/dist/commands/templates.js.map +1 -0
  88. package/dist/commands/voice.d.ts +2 -0
  89. package/dist/commands/voice.d.ts.map +1 -0
  90. package/dist/commands/voice.js +259 -0
  91. package/dist/commands/voice.js.map +1 -0
  92. package/dist/index.d.ts +2 -0
  93. package/dist/index.d.ts.map +1 -0
  94. package/dist/index.js +780 -325
  95. package/dist/index.js.map +1 -0
  96. package/dist/lib/api.d.ts +73 -0
  97. package/dist/lib/api.d.ts.map +1 -0
  98. package/dist/lib/api.js +455 -0
  99. package/dist/lib/api.js.map +1 -0
  100. package/dist/lib/config.d.ts +68 -0
  101. package/dist/lib/config.d.ts.map +1 -0
  102. package/dist/lib/config.js +276 -0
  103. package/dist/lib/config.js.map +1 -0
  104. package/dist/lib/conversation.d.ts +8 -0
  105. package/dist/lib/conversation.d.ts.map +1 -0
  106. package/dist/lib/conversation.js +179 -0
  107. package/dist/lib/conversation.js.map +1 -0
  108. package/dist/lib/credentials.d.ts +43 -0
  109. package/dist/lib/credentials.d.ts.map +1 -0
  110. package/dist/lib/credentials.js +365 -0
  111. package/dist/lib/credentials.js.map +1 -0
  112. package/dist/lib/docs-utils.d.ts +13 -0
  113. package/dist/lib/docs-utils.d.ts.map +1 -0
  114. package/dist/lib/docs-utils.js +115 -0
  115. package/dist/lib/docs-utils.js.map +1 -0
  116. package/dist/lib/update-check.d.ts +5 -0
  117. package/dist/lib/update-check.d.ts.map +1 -0
  118. package/dist/lib/update-check.js +114 -0
  119. package/dist/lib/update-check.js.map +1 -0
  120. package/dist/lib/voice.d.ts +3 -0
  121. package/dist/lib/voice.d.ts.map +1 -0
  122. package/dist/lib/voice.js +49 -0
  123. package/dist/lib/voice.js.map +1 -0
  124. package/dist/package.json +71 -0
  125. package/dist/utils/logger.d.ts +28 -0
  126. package/dist/utils/logger.d.ts.map +1 -0
  127. package/dist/utils/logger.js +138 -0
  128. package/dist/utils/logger.js.map +1 -0
  129. package/dist/utils/spinner.d.ts +17 -0
  130. package/dist/utils/spinner.d.ts.map +1 -0
  131. package/dist/utils/spinner.js +75 -0
  132. package/dist/utils/spinner.js.map +1 -0
  133. package/package.json +71 -71
@@ -0,0 +1,1001 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const chalk_1 = __importDefault(require("chalk"));
7
+ const commander_1 = require("commander");
8
+ const api_1 = require("../lib/api");
9
+ const config_1 = require("../lib/config");
10
+ const logger_1 = require("../utils/logger");
11
+ const spinner_1 = require("../utils/spinner");
12
+ const child_process_1 = require("child_process");
13
+ const fs_1 = __importDefault(require("fs"));
14
+ const os_1 = __importDefault(require("os"));
15
+ const path_1 = __importDefault(require("path"));
16
+ const logs = new commander_1.Command('logs');
17
+ logs
18
+ .description('View function logs')
19
+ .argument('[function-id]', 'Function ID to get logs for (optional if sinch.json exists)')
20
+ .option('-f, --follow', 'Follow log output in real-time')
21
+ .option('-n, --lines <number>', 'Number of historical lines to show', '50')
22
+ .option('--level <level>', 'Filter by log level (debug, info, warning, error)')
23
+ .option('--search <text>', 'Search for text in log messages')
24
+ .option('--since <duration>', 'Show logs since duration (e.g., 1h, 30m, 5s)')
25
+ .action(async (functionIdArg, options) => {
26
+ try {
27
+ await config_1.config.load();
28
+ const api = new api_1.SinchAPI(config_1.config.getApiConfig());
29
+ let functionId = functionIdArg;
30
+ if (!functionId) {
31
+ if (!config_1.config.isInProject()) {
32
+ throw new Error('Function ID required. Provide as argument or run from function directory with sinch.json');
33
+ }
34
+ const projectConfig = config_1.config.getProjectConfig();
35
+ if (!projectConfig) {
36
+ throw new Error('No project configuration found');
37
+ }
38
+ functionId = projectConfig.functionId;
39
+ if (!functionId) {
40
+ throw new Error('No function ID found in sinch.json. Deploy the function first or provide function ID as argument.');
41
+ }
42
+ }
43
+ spinner_1.spinner.start('Loading function details...');
44
+ const func = await api.getFunction(functionId);
45
+ spinner_1.spinner.stop();
46
+ logger_1.logger.info(`📋 Logs for function: ${chalk_1.default.cyan(func.name)} (${getStatusDisplay(func.status)})`);
47
+ logger_1.logger.newline();
48
+ if (options.follow) {
49
+ await streamFunctionLogs(api, functionId, func.name, options);
50
+ }
51
+ else {
52
+ await showHistoricalLogs(api, functionId, options);
53
+ }
54
+ }
55
+ catch (error) {
56
+ spinner_1.spinner.stop();
57
+ logger_1.logger.error(`Failed to get logs: ${error.message}`);
58
+ process.exit(1);
59
+ }
60
+ });
61
+ logs
62
+ .command('stream')
63
+ .description('Stream function logs in real-time')
64
+ .argument('[function-id]', 'Function ID to stream logs for (optional if sinch.json exists)')
65
+ .option('--level <level>', 'Filter by log level (debug, info, warning, error)')
66
+ .option('--search <text>', 'Search for text in log messages')
67
+ .action(async (functionIdArg, options) => {
68
+ try {
69
+ await config_1.config.load();
70
+ const api = new api_1.SinchAPI(config_1.config.getApiConfig());
71
+ let functionId = functionIdArg;
72
+ if (!functionId) {
73
+ if (!config_1.config.isInProject()) {
74
+ throw new Error('Function ID required. Provide as argument or run from function directory with sinch.json');
75
+ }
76
+ const projectConfig = config_1.config.getProjectConfig();
77
+ if (!projectConfig) {
78
+ throw new Error('No project configuration found');
79
+ }
80
+ functionId = projectConfig.functionId;
81
+ if (!functionId) {
82
+ throw new Error('No function ID found in sinch.json. Deploy the function first or provide function ID as argument.');
83
+ }
84
+ }
85
+ spinner_1.spinner.start('Connecting to log stream...');
86
+ const func = await setupStreamConnection(api, functionId);
87
+ spinner_1.spinner.stop();
88
+ await streamFunctionLogs(api, functionId, func.name, { ...options, follow: true });
89
+ }
90
+ catch (error) {
91
+ spinner_1.spinner.stop();
92
+ logger_1.logger.error(`Failed to stream logs: ${error.message}`);
93
+ process.exit(1);
94
+ }
95
+ });
96
+ async function setupStreamConnection(api, functionId) {
97
+ const func = await api.getFunction(functionId);
98
+ return func;
99
+ }
100
+ async function streamFunctionLogs(api, functionId, functionName, _options) {
101
+ const baseUrl = api.baseUrl.replace(/\/+$/, '');
102
+ const projectId = api.projectId;
103
+ const streamUrl = `${baseUrl}/v1/projects/${projectId}/functions/${functionId}/logs/stream`;
104
+ const credentials = api.credentials;
105
+ const requests = [];
106
+ const screen = createBlessedLogViewer(requests, 'STREAMING', functionName);
107
+ let headers = {
108
+ 'Accept': 'text/event-stream',
109
+ 'Cache-Control': 'no-cache'
110
+ };
111
+ if (credentials) {
112
+ try {
113
+ const creds = await credentials.retrieve();
114
+ if (creds && creds.keyId && creds.keySecret) {
115
+ const authString = Buffer.from(`${creds.keyId}:${creds.keySecret}`).toString('base64');
116
+ headers['Authorization'] = `Basic ${authString}`;
117
+ }
118
+ }
119
+ catch (error) {
120
+ logger_1.logger.debug('Failed to add authentication:', error);
121
+ }
122
+ }
123
+ const abortController = new AbortController();
124
+ try {
125
+ const response = await fetch(streamUrl, {
126
+ method: 'GET',
127
+ headers,
128
+ signal: abortController.signal
129
+ });
130
+ if (!response.ok) {
131
+ throw new Error(`SSE connection failed: ${response.status} ${response.statusText}`);
132
+ }
133
+ if (!response.body) {
134
+ throw new Error('Response body is null');
135
+ }
136
+ updateStatusBar(screen.statusBar, 'CONNECTED', functionName);
137
+ screen.screen.render();
138
+ const reader = response.body.getReader();
139
+ const decoder = new TextDecoder();
140
+ let buffer = '';
141
+ const processStream = async () => {
142
+ try {
143
+ while (true) {
144
+ const { done, value } = await reader.read();
145
+ if (done)
146
+ break;
147
+ buffer += decoder.decode(value, { stream: true });
148
+ const lines = buffer.split('\n');
149
+ buffer = lines.pop() || '';
150
+ for (const line of lines) {
151
+ if (line.startsWith('event:')) {
152
+ continue;
153
+ }
154
+ if (line.startsWith('data:')) {
155
+ const dataStr = line.substring(5).trim();
156
+ if (dataStr) {
157
+ try {
158
+ const logEvent = JSON.parse(dataStr);
159
+ if (logEvent.type === 'request' && logEvent.request) {
160
+ requests.push(logEvent.request);
161
+ updateTableWithRequest(screen.table, logEvent.request, requests);
162
+ screen.table.focus();
163
+ screen.screen.render();
164
+ }
165
+ }
166
+ catch (e) {
167
+ }
168
+ }
169
+ }
170
+ }
171
+ }
172
+ }
173
+ catch (error) {
174
+ if (error.name !== 'AbortError') {
175
+ updateStatusBar(screen.statusBar, 'RECONNECTING', functionName);
176
+ screen.screen.render();
177
+ }
178
+ }
179
+ };
180
+ processStream();
181
+ screen.screen.key(['q', 'C-c'], () => {
182
+ abortController.abort();
183
+ process.exit(0);
184
+ });
185
+ await new Promise(() => { });
186
+ }
187
+ catch (error) {
188
+ abortController.abort();
189
+ logger_1.logger.error(`❌ Streaming failed: ${error.message}`);
190
+ process.exit(1);
191
+ }
192
+ }
193
+ function getStatusDisplay(status) {
194
+ const statusColors = {
195
+ 'Running': chalk_1.default.green('✅ Running'),
196
+ 'Failed': chalk_1.default.red('❌ Failed'),
197
+ 'Pending': chalk_1.default.yellow('⏳ Pending'),
198
+ 'Building': chalk_1.default.blue('🔨 Building')
199
+ };
200
+ return statusColors[status] || chalk_1.default.gray(status);
201
+ }
202
+ let globalTableData = [];
203
+ function updateTableWithRequest(table, request, requestsArray) {
204
+ const date = new Date(request.startTime);
205
+ const timestamp = date.getFullYear() + '-' +
206
+ String(date.getMonth() + 1).padStart(2, '0') + '-' +
207
+ String(date.getDate()).padStart(2, '0') + ' ' +
208
+ String(date.getHours()).padStart(2, '0') + ':' +
209
+ String(date.getMinutes()).padStart(2, '0') + ':' +
210
+ String(date.getSeconds()).padStart(2, '0');
211
+ const callback = extractCallback(request);
212
+ const duration = `${request.executionTimeMs}ms`;
213
+ const status = request.statusCode.toString();
214
+ const terminalWidth = process.stdout.columns || 120;
215
+ const fixedColumnsWidth = 35;
216
+ const availablePathWidth = Math.max(30, terminalWidth - fixedColumnsWidth);
217
+ const pathDisplay = request.url.length > availablePathWidth ?
218
+ request.url.substring(0, availablePathWidth - 2) + '..' :
219
+ request.url;
220
+ globalTableData.push([
221
+ timestamp,
222
+ callback,
223
+ duration,
224
+ status,
225
+ pathDisplay
226
+ ]);
227
+ if (globalTableData.length > 100) {
228
+ globalTableData = globalTableData.slice(-100);
229
+ if (requestsArray && requestsArray.length > 100) {
230
+ requestsArray.splice(0, requestsArray.length - 100);
231
+ }
232
+ }
233
+ const tableData = [['Time', 'Callback', 'Duration', 'Status', 'Path'], ...globalTableData];
234
+ table.setData(tableData);
235
+ }
236
+ function showBlessedZoomedRequest(request, screen, table, _statusBar, _functionName) {
237
+ try {
238
+ const blessed = require('blessed');
239
+ logger_1.logger.debug(`Showing request details for ${request.httpMethod} ${request.url}`);
240
+ const modal = blessed.box({
241
+ parent: screen,
242
+ top: 0,
243
+ left: 0,
244
+ width: '100%',
245
+ height: '100%',
246
+ border: { type: 'line' },
247
+ style: {
248
+ border: { fg: 'cyan' },
249
+ bg: 'black',
250
+ fg: 'white'
251
+ },
252
+ scrollable: true,
253
+ alwaysScroll: true,
254
+ keys: true,
255
+ vi: true,
256
+ tags: true,
257
+ label: ' Request Details '
258
+ });
259
+ const modalStatus = blessed.box({
260
+ parent: modal,
261
+ top: 1,
262
+ left: 2,
263
+ width: '100%-4',
264
+ height: 1,
265
+ content: '{gray-fg}[Esc/Q] Close [↑↓] Scroll [J/1] JSON [C/2] cURL{/}',
266
+ tags: true,
267
+ style: { bg: 'blue', fg: 'white' }
268
+ });
269
+ blessed.box({
270
+ parent: modal,
271
+ top: 2,
272
+ left: 2,
273
+ width: '100%-4',
274
+ height: 1,
275
+ content: `{bold}{cyan-fg}${request.httpMethod}{/} {blue-fg}${request.url}{/} - {${getStatusCodeColorName(request.statusCode)}}${request.statusCode}{/} - {yellow-fg}${request.executionTimeMs}ms{/} - {magenta-fg}${request.memoryUsedMB || 0}MB{/}`,
276
+ tags: true,
277
+ style: { bg: 'black' }
278
+ });
279
+ const contentArea = blessed.box({
280
+ parent: modal,
281
+ top: 4,
282
+ left: 2,
283
+ width: '100%-4',
284
+ height: '100%-5',
285
+ scrollable: true,
286
+ alwaysScroll: true,
287
+ keys: true,
288
+ vi: true,
289
+ tags: true,
290
+ content: buildFullRequestResponseContent(request)
291
+ });
292
+ contentArea.focus();
293
+ screen.render();
294
+ let isModalOpen = true;
295
+ const closeModal = () => {
296
+ if (!isModalOpen)
297
+ return;
298
+ isModalOpen = false;
299
+ modal.destroy();
300
+ table.focus();
301
+ screen.render();
302
+ };
303
+ screen.key(['escape', 'q'], () => {
304
+ if (isModalOpen)
305
+ closeModal();
306
+ });
307
+ screen.key(['up', 'k'], () => {
308
+ if (isModalOpen) {
309
+ contentArea.scroll(-1);
310
+ screen.render();
311
+ }
312
+ });
313
+ screen.key(['down', 'j'], () => {
314
+ if (isModalOpen) {
315
+ contentArea.scroll(1);
316
+ screen.render();
317
+ }
318
+ });
319
+ screen.on('keypress', (ch, key) => {
320
+ if (isModalOpen) {
321
+ logger_1.logger.debug(`Key pressed: ch="${ch}" key=${JSON.stringify(key)}`);
322
+ }
323
+ });
324
+ screen.key(['J', 'j'], () => {
325
+ if (!isModalOpen)
326
+ return;
327
+ logger_1.logger.debug('J/j key pressed - attempting JSON copy');
328
+ modalStatus.setContent('{black-bg}{yellow-fg} J KEY PRESSED - COPYING JSON... {/}');
329
+ screen.render();
330
+ setTimeout(() => {
331
+ try {
332
+ const jsonContent = JSON.stringify(request, null, 2);
333
+ logger_1.logger.debug(`JSON content length: ${jsonContent.length}`);
334
+ logger_1.logger.debug('Attempting native clipboard copy');
335
+ copyToClipboard(jsonContent)
336
+ .then(() => {
337
+ modalStatus.setContent('{green-fg}✓ Request JSON copied to clipboard!{/}');
338
+ logger_1.logger.debug('Native clipboard copy succeeded');
339
+ screen.render();
340
+ setTimeout(() => {
341
+ if (isModalOpen) {
342
+ modalStatus.setContent('{gray-fg}[Esc/Q] Close [↑↓] Scroll [J] JSON [C] cURL{/}');
343
+ screen.render();
344
+ }
345
+ }, 3000);
346
+ })
347
+ .catch((_clipError) => {
348
+ logger_1.logger.debug(`Native clipboard failed: ${_clipError.message}`);
349
+ const tempFile = path_1.default.join(os_1.default.tmpdir(), `sinch-request-${Date.now()}.json`);
350
+ fs_1.default.writeFileSync(tempFile, jsonContent);
351
+ modalStatus.setContent(`{yellow-fg}✓ JSON saved to: ${tempFile}{/}`);
352
+ logger_1.logger.debug(`Saved to temp file: ${tempFile}`);
353
+ screen.render();
354
+ setTimeout(() => {
355
+ if (isModalOpen) {
356
+ modalStatus.setContent('{gray-fg}[Esc/Q] Close [↑↓] Scroll [J] JSON [C] cURL{/}');
357
+ screen.render();
358
+ }
359
+ }, 3000);
360
+ });
361
+ }
362
+ catch (error) {
363
+ logger_1.logger.debug(`JSON copy error: ${error.message}`);
364
+ modalStatus.setContent(`{red-fg}✗ Copy failed: ${error.message}{/}`);
365
+ screen.render();
366
+ setTimeout(() => {
367
+ if (isModalOpen) {
368
+ modalStatus.setContent('{gray-fg}[Esc/Q] Close [↑↓] Scroll [J] JSON [C] cURL{/}');
369
+ screen.render();
370
+ }
371
+ }, 3000);
372
+ }
373
+ }, 100);
374
+ });
375
+ screen.key(['C', 'c'], () => {
376
+ if (!isModalOpen)
377
+ return;
378
+ logger_1.logger.debug('C/c key pressed - attempting cURL copy');
379
+ modalStatus.setContent('{black-bg}{yellow-fg} C KEY PRESSED - COPYING CURL... {/}');
380
+ screen.render();
381
+ setTimeout(() => {
382
+ try {
383
+ const curlCommand = generateCurlCommand(request);
384
+ logger_1.logger.debug('Attempting native clipboard copy for cURL');
385
+ copyToClipboard(curlCommand)
386
+ .then(() => {
387
+ modalStatus.setContent('{green-fg}✓ cURL command copied to clipboard!{/}');
388
+ logger_1.logger.debug('Native clipboard copy succeeded for cURL');
389
+ screen.render();
390
+ setTimeout(() => {
391
+ if (isModalOpen) {
392
+ modalStatus.setContent('{gray-fg}[Esc/Q] Close [↑↓] Scroll [J] JSON [C] cURL{/}');
393
+ screen.render();
394
+ }
395
+ }, 3000);
396
+ })
397
+ .catch((_clipError) => {
398
+ logger_1.logger.debug(`Native clipboard failed for cURL: ${_clipError.message}`);
399
+ const tempFile = path_1.default.join(os_1.default.tmpdir(), `sinch-curl-${Date.now()}.sh`);
400
+ fs_1.default.writeFileSync(tempFile, curlCommand);
401
+ modalStatus.setContent(`{yellow-fg}✓ cURL saved to: ${tempFile}{/}`);
402
+ screen.render();
403
+ setTimeout(() => {
404
+ if (isModalOpen) {
405
+ modalStatus.setContent('{gray-fg}[Esc/Q] Close [↑↓] Scroll [J] JSON [C] cURL{/}');
406
+ screen.render();
407
+ }
408
+ }, 3000);
409
+ });
410
+ }
411
+ catch (error) {
412
+ logger_1.logger.debug(`cURL copy error: ${error.message}`);
413
+ modalStatus.setContent(`{red-fg}✗ Copy failed: ${error.message}{/}`);
414
+ screen.render();
415
+ setTimeout(() => {
416
+ if (isModalOpen) {
417
+ modalStatus.setContent('{gray-fg}[Esc/Q] Close [↑↓] Scroll [J] JSON [C] cURL{/}');
418
+ screen.render();
419
+ }
420
+ }, 3000);
421
+ }
422
+ }, 100);
423
+ });
424
+ screen.key(['1'], () => {
425
+ if (!isModalOpen)
426
+ return;
427
+ logger_1.logger.debug('1 key pressed - copying JSON (alternative binding)');
428
+ modalStatus.setContent('{black-bg}{yellow-fg} 1 KEY - COPYING JSON... {/}');
429
+ screen.render();
430
+ setTimeout(() => {
431
+ try {
432
+ const jsonContent = JSON.stringify(request, null, 2);
433
+ copyToClipboard(jsonContent)
434
+ .then(() => {
435
+ modalStatus.setContent('{green-fg}✓ Request JSON copied to clipboard!{/}');
436
+ screen.render();
437
+ setTimeout(() => {
438
+ if (isModalOpen) {
439
+ modalStatus.setContent('{gray-fg}[Esc/Q] Close [↑↓] Scroll [J] JSON [C] cURL{/}');
440
+ screen.render();
441
+ }
442
+ }, 3000);
443
+ })
444
+ .catch((_clipError) => {
445
+ const tempFile = path_1.default.join(os_1.default.tmpdir(), `sinch-request-${Date.now()}.json`);
446
+ fs_1.default.writeFileSync(tempFile, jsonContent);
447
+ modalStatus.setContent(`{yellow-fg}✓ JSON saved to: ${tempFile}{/}`);
448
+ screen.render();
449
+ setTimeout(() => {
450
+ if (isModalOpen) {
451
+ modalStatus.setContent('{gray-fg}[Esc/Q] Close [↑↓] Scroll [J] JSON [C] cURL{/}');
452
+ screen.render();
453
+ }
454
+ }, 3000);
455
+ });
456
+ }
457
+ catch (error) {
458
+ modalStatus.setContent(`{red-fg}✗ Copy failed: ${error.message}{/}`);
459
+ screen.render();
460
+ setTimeout(() => {
461
+ if (isModalOpen) {
462
+ modalStatus.setContent('{gray-fg}[Esc/Q] Close [↑↓] Scroll [J] JSON [C] cURL{/}');
463
+ screen.render();
464
+ }
465
+ }, 3000);
466
+ }
467
+ }, 100);
468
+ });
469
+ screen.key(['2'], () => {
470
+ if (!isModalOpen)
471
+ return;
472
+ logger_1.logger.debug('2 key pressed - copying cURL (alternative binding)');
473
+ modalStatus.setContent('{black-bg}{yellow-fg} 2 KEY - COPYING CURL... {/}');
474
+ screen.render();
475
+ setTimeout(() => {
476
+ try {
477
+ const curlCommand = generateCurlCommand(request);
478
+ copyToClipboard(curlCommand)
479
+ .then(() => {
480
+ modalStatus.setContent('{green-fg}✓ cURL command copied to clipboard!{/}');
481
+ screen.render();
482
+ setTimeout(() => {
483
+ if (isModalOpen) {
484
+ modalStatus.setContent('{gray-fg}[Esc/Q] Close [↑↓] Scroll [J] JSON [C] cURL{/}');
485
+ screen.render();
486
+ }
487
+ }, 3000);
488
+ })
489
+ .catch((_clipError) => {
490
+ const tempFile = path_1.default.join(os_1.default.tmpdir(), `sinch-curl-${Date.now()}.sh`);
491
+ fs_1.default.writeFileSync(tempFile, curlCommand);
492
+ modalStatus.setContent(`{yellow-fg}✓ cURL saved to: ${tempFile}{/}`);
493
+ screen.render();
494
+ setTimeout(() => {
495
+ if (isModalOpen) {
496
+ modalStatus.setContent('{gray-fg}[Esc/Q] Close [↑↓] Scroll [J] JSON [C] cURL{/}');
497
+ screen.render();
498
+ }
499
+ }, 3000);
500
+ });
501
+ }
502
+ catch (error) {
503
+ modalStatus.setContent(`{red-fg}✗ Copy failed: ${error.message}{/}`);
504
+ screen.render();
505
+ setTimeout(() => {
506
+ if (isModalOpen) {
507
+ modalStatus.setContent('{gray-fg}[Esc/Q] Close [↑↓] Scroll [J] JSON [C] cURL{/}');
508
+ screen.render();
509
+ }
510
+ }, 3000);
511
+ }
512
+ }, 100);
513
+ });
514
+ }
515
+ catch (error) {
516
+ logger_1.logger.debug(`Error in showBlessedZoomedRequest: ${error.message}`);
517
+ }
518
+ }
519
+ function buildFullRequestResponseContent(request) {
520
+ let content = '';
521
+ content += '\n';
522
+ content += buildRequestSection(request);
523
+ content += '\n';
524
+ content += buildResponseSection(request);
525
+ content += '\n';
526
+ content += buildLogsSection(request);
527
+ return content;
528
+ }
529
+ function buildRequestSection(request) {
530
+ let content = '';
531
+ const WIDTH = process.stdout.columns - 6;
532
+ content += '{bold}{green-fg}┌─ REQUEST ' + '─'.repeat(Math.max(0, WIDTH - 10)) + '┐{/}\n';
533
+ const callId = extractCallId(request);
534
+ if (callId) {
535
+ content += `{green-fg}│{/} {bold}Call ID:{/} {cyan-fg}${callId}{/}\n`;
536
+ }
537
+ if (request.headers) {
538
+ content += `{green-fg}│{/} {bold}Headers:{/}\n`;
539
+ try {
540
+ const headers = typeof request.headers === 'string' ?
541
+ JSON.parse(request.headers) : request.headers;
542
+ Object.entries(headers).forEach(([key, value]) => {
543
+ const headerLine = ` ${key}: ${value}`;
544
+ content += wrapText(headerLine, WIDTH - 2, '{green-fg}│{/} ');
545
+ });
546
+ }
547
+ catch (e) {
548
+ content += wrapText(` ${request.headers}`, WIDTH - 2, '{green-fg}│{/} ');
549
+ }
550
+ }
551
+ if (request.requestBody) {
552
+ content += `{green-fg}│{/} {bold}Body:{/}\n`;
553
+ try {
554
+ const parsed = JSON.parse(request.requestBody);
555
+ const formatted = JSON.stringify(parsed, null, 2);
556
+ formatted.split('\n').forEach(line => {
557
+ content += wrapText(line, WIDTH - 2, '{green-fg}│{/} ');
558
+ });
559
+ }
560
+ catch (e) {
561
+ content += wrapText(request.requestBody, WIDTH - 2, '{green-fg}│{/} ');
562
+ }
563
+ }
564
+ content += '{green-fg}└' + '─'.repeat(Math.max(0, WIDTH)) + '┘{/}\n';
565
+ return content;
566
+ }
567
+ function buildResponseSection(request) {
568
+ let content = '';
569
+ const WIDTH = process.stdout.columns - 6;
570
+ content += '{bold}{blue-fg}┌─ RESPONSE ' + '─'.repeat(Math.max(0, WIDTH - 11)) + '┐{/}\n';
571
+ if (request.responseHeaders) {
572
+ content += `{blue-fg}│{/} {bold}Headers:{/}\n`;
573
+ try {
574
+ const headers = typeof request.responseHeaders === 'string' ?
575
+ JSON.parse(request.responseHeaders) : request.responseHeaders;
576
+ Object.entries(headers).forEach(([key, value]) => {
577
+ const headerLine = ` ${key}: ${value}`;
578
+ content += wrapText(headerLine, WIDTH - 2, '{blue-fg}│{/} ');
579
+ });
580
+ }
581
+ catch (e) {
582
+ content += wrapText(` ${request.responseHeaders}`, WIDTH - 2, '{blue-fg}│{/} ');
583
+ }
584
+ }
585
+ if (request.responseBody) {
586
+ content += `{blue-fg}│{/} {bold}Body:{/}\n`;
587
+ try {
588
+ const parsed = JSON.parse(request.responseBody);
589
+ const formatted = JSON.stringify(parsed, null, 2);
590
+ formatted.split('\n').forEach(line => {
591
+ content += wrapText(line, WIDTH - 2, '{blue-fg}│{/} ');
592
+ });
593
+ }
594
+ catch (e) {
595
+ content += wrapText(request.responseBody, WIDTH - 2, '{blue-fg}│{/} ');
596
+ }
597
+ }
598
+ if (request.errorMessage) {
599
+ content += `{blue-fg}│{/} {bold}{red-fg}Error:{/}\n`;
600
+ content += wrapText(request.errorMessage, WIDTH - 2, '{blue-fg}│{/} ');
601
+ }
602
+ content += '{blue-fg}└' + '─'.repeat(Math.max(0, WIDTH)) + '┘{/}\n';
603
+ return content;
604
+ }
605
+ function buildLogsSection(request) {
606
+ let content = '';
607
+ const WIDTH = process.stdout.columns - 6;
608
+ content += '{bold}{magenta-fg}┌─ CONSOLE LOGS ' + '─'.repeat(Math.max(0, WIDTH - 15)) + '┐{/}\n';
609
+ if (request.logs && request.logs.length > 0) {
610
+ request.logs.forEach((log, _index) => {
611
+ const time = new Date(log.timestamp).toLocaleTimeString();
612
+ const level = log.level.toUpperCase().padEnd(5);
613
+ const color = getLogLevelColor(log.level);
614
+ const logLine = `${time} [${level}] ${log.message}`;
615
+ content += wrapText(logLine, WIDTH - 2, '{magenta-fg}│{/} ', color);
616
+ });
617
+ }
618
+ else {
619
+ content += `{magenta-fg}│{/} {gray-fg}No console logs for this request{/}\n`;
620
+ }
621
+ content += '{magenta-fg}└' + '─'.repeat(Math.max(0, WIDTH)) + '┘{/}\n';
622
+ return content;
623
+ }
624
+ function wrapText(text, maxWidth, prefix, color = '') {
625
+ if (!text)
626
+ return '';
627
+ let result = '';
628
+ const lines = text.split('\n');
629
+ lines.forEach(line => {
630
+ if (line.length <= maxWidth) {
631
+ result += `${prefix}${color}${line}{/}\n`;
632
+ }
633
+ else {
634
+ let currentLine = line;
635
+ let isFirst = true;
636
+ while (currentLine.length > maxWidth) {
637
+ const wrapPoint = maxWidth - (isFirst ? 0 : 4);
638
+ const part = currentLine.substring(0, wrapPoint);
639
+ const indent = isFirst ? '' : ' ';
640
+ result += `${prefix}${indent}${color}${part}{/}\n`;
641
+ currentLine = currentLine.substring(wrapPoint);
642
+ isFirst = false;
643
+ }
644
+ if (currentLine.length > 0) {
645
+ const indent = isFirst ? '' : ' ';
646
+ result += `${prefix}${indent}${color}${currentLine}{/}\n`;
647
+ }
648
+ }
649
+ });
650
+ return result;
651
+ }
652
+ function getLogLevelColor(level) {
653
+ switch (level.toLowerCase()) {
654
+ case 'debug': return 'gray-fg';
655
+ case 'info': return 'blue-fg';
656
+ case 'warning': return 'yellow-fg';
657
+ case 'error': return 'red-fg';
658
+ default: return 'white-fg';
659
+ }
660
+ }
661
+ function getStatusCodeColorName(statusCode) {
662
+ if (statusCode >= 200 && statusCode < 300)
663
+ return 'green-fg';
664
+ if (statusCode >= 300 && statusCode < 400)
665
+ return 'yellow-fg';
666
+ if (statusCode >= 400 && statusCode < 500)
667
+ return 'red-fg';
668
+ if (statusCode >= 500)
669
+ return 'red-fg}{bold';
670
+ return 'white-fg';
671
+ }
672
+ function extractCallId(request) {
673
+ try {
674
+ if (request.requestBody) {
675
+ const parsed = JSON.parse(request.requestBody);
676
+ return parsed.callId || parsed.callid || parsed.call_id || null;
677
+ }
678
+ }
679
+ catch (e) {
680
+ }
681
+ return null;
682
+ }
683
+ function extractCallback(request) {
684
+ try {
685
+ let action = '';
686
+ if (request.requestBody) {
687
+ const parsed = JSON.parse(request.requestBody);
688
+ if (parsed.event) {
689
+ action = parsed.event.toLowerCase();
690
+ }
691
+ else {
692
+ const pathSegments = request.url.split('/').filter(s => s);
693
+ action = pathSegments[pathSegments.length - 1] || '';
694
+ }
695
+ return action || '';
696
+ }
697
+ }
698
+ catch (e) {
699
+ }
700
+ return '';
701
+ }
702
+ async function showHistoricalLogs(api, functionId, options) {
703
+ try {
704
+ spinner_1.spinner.start(`Loading ${options.lines} recent log entries...`);
705
+ const logOptions = {
706
+ limit: parseInt(options.lines || '50'),
707
+ level: options.level,
708
+ search: options.search
709
+ };
710
+ if (options.since) {
711
+ const sinceTime = parseSinceDuration(options.since);
712
+ if (sinceTime) {
713
+ logOptions.startTime = sinceTime.toISOString();
714
+ }
715
+ }
716
+ const response = await api.getFunctionLogs(functionId, logOptions);
717
+ spinner_1.spinner.stop();
718
+ logger_1.logger.debug('Historical API response:', {
719
+ totalRequests: response.requests?.length || 0,
720
+ firstRequest: response.requests?.[0] || 'No requests',
721
+ responseKeys: Object.keys(response)
722
+ });
723
+ if (!response.requests || response.requests.length === 0) {
724
+ logger_1.logger.info('📝 No logs found');
725
+ logger_1.logger.info('💡 Logs will appear here after function receives requests');
726
+ return;
727
+ }
728
+ const requests = response.requests;
729
+ logger_1.logger.debug('Historical requests:', {
730
+ totalRequests: requests.length,
731
+ firstRequest: requests[0] || 'No requests',
732
+ requestTimestamps: requests.slice(0, 3).map(r => r.startTime)
733
+ });
734
+ await showBlessedLogViewer(requests, 'HISTORICAL', 'Function');
735
+ }
736
+ catch (error) {
737
+ spinner_1.spinner.stop();
738
+ throw error;
739
+ }
740
+ }
741
+ function parseSinceDuration(duration) {
742
+ if (!duration)
743
+ return null;
744
+ const match = duration.match(/^(\d+)([smhd])$/);
745
+ if (!match)
746
+ return null;
747
+ const value = parseInt(match[1]);
748
+ const unit = match[2];
749
+ const now = new Date();
750
+ switch (unit) {
751
+ case 's': return new Date(now.getTime() - value * 1000);
752
+ case 'm': return new Date(now.getTime() - value * 60 * 1000);
753
+ case 'h': return new Date(now.getTime() - value * 60 * 60 * 1000);
754
+ case 'd': return new Date(now.getTime() - value * 24 * 60 * 60 * 1000);
755
+ default: return null;
756
+ }
757
+ }
758
+ async function showBlessedLogViewer(requests, mode, functionName) {
759
+ const screen = createBlessedLogViewer(requests, mode, functionName);
760
+ requests.forEach((request) => {
761
+ updateTableWithRequest(screen.table, request, requests);
762
+ });
763
+ screen.screen.render();
764
+ await new Promise(() => { });
765
+ }
766
+ function createBlessedLogViewer(requests, mode, functionName) {
767
+ const blessed = require('blessed');
768
+ process.stdout.write('\x1Bc');
769
+ globalTableData = [];
770
+ const screen = blessed.screen({
771
+ smartCSR: true,
772
+ title: `Sinch Functions - ${functionName} Logs`,
773
+ fullUnicode: true
774
+ });
775
+ const statusBar = blessed.box({
776
+ parent: screen,
777
+ top: 0,
778
+ left: 0,
779
+ width: '100%',
780
+ height: 1,
781
+ content: getStatusBarContent(mode, functionName),
782
+ tags: true,
783
+ style: { bg: 'black' }
784
+ });
785
+ const table = blessed.listtable({
786
+ parent: screen,
787
+ top: 1,
788
+ left: 0,
789
+ width: '100%',
790
+ height: '100%-1',
791
+ border: { type: 'line', fg: 'blue' },
792
+ style: {
793
+ header: { fg: 'white', bg: 'blue', bold: true },
794
+ cell: { fg: 'white' },
795
+ selected: { bg: 'blue', fg: 'white', bold: true }
796
+ },
797
+ keys: true,
798
+ vi: true,
799
+ mouse: true,
800
+ interactive: true,
801
+ label: `${functionName} Logs (q to quit)`,
802
+ align: 'left',
803
+ pad: 1
804
+ });
805
+ const headers = ['Time', 'Callback', 'Duration', 'Status', 'Path'];
806
+ table.setData([headers]);
807
+ table.focus();
808
+ screen.key(['q', 'C-c'], () => {
809
+ process.exit(0);
810
+ });
811
+ const openSelectedRequest = () => {
812
+ try {
813
+ let selectedIndex = table.selected || 0;
814
+ const dataIndex = selectedIndex - 1;
815
+ if (dataIndex >= 0 && dataIndex < requests.length && requests[dataIndex]) {
816
+ logger_1.logger.debug(`Opening request details for index ${dataIndex}`);
817
+ showBlessedZoomedRequest(requests[dataIndex], screen, table, statusBar, functionName);
818
+ }
819
+ else {
820
+ const debugInfo = `Selected: ${selectedIndex}, DataIndex: ${dataIndex}, Requests: ${requests.length}, TableData: ${globalTableData.length}`;
821
+ logger_1.logger.debug(`Selection out of bounds: ${debugInfo}`);
822
+ }
823
+ }
824
+ catch (error) {
825
+ logger_1.logger.debug(`Error in request selection: ${error.message}`);
826
+ }
827
+ };
828
+ screen.key(['enter'], openSelectedRequest);
829
+ table.on('select', (_item, _index) => {
830
+ openSelectedRequest();
831
+ });
832
+ screen.on('wheelup', () => {
833
+ if (table.focused) {
834
+ table.scroll(-1);
835
+ screen.render();
836
+ }
837
+ });
838
+ screen.on('wheeldown', () => {
839
+ if (table.focused) {
840
+ table.scroll(1);
841
+ screen.render();
842
+ }
843
+ });
844
+ return { screen, table, statusBar };
845
+ }
846
+ function getStatusBarContent(mode, functionName) {
847
+ switch (mode) {
848
+ case 'STREAMING':
849
+ return '{blue-fg}🔴 STREAMING: ' + functionName + '{/} {gray-fg}| ↑↓/Wheel=scroll | Enter/Click=details | Q=quit{/}';
850
+ case 'CONNECTED':
851
+ return '{green-fg}✅ CONNECTED: ' + functionName + '{/} {gray-fg}| ↑↓/Wheel=scroll | Enter/Click=details | Q=quit{/}';
852
+ case 'RECONNECTING':
853
+ return '{yellow-fg}⚠️ RECONNECTING: ' + functionName + '{/} {gray-fg}| ↑↓/Wheel=scroll | Enter/Click=details | Q=quit{/}';
854
+ case 'HISTORICAL':
855
+ return '{cyan-fg}📜 HISTORICAL: ' + functionName + '{/} {gray-fg}| ↑↓/Wheel=scroll | Enter/Click=details | Q=quit{/}';
856
+ default:
857
+ return '{gray-fg}' + functionName + ' | ↑↓/Wheel=scroll | Enter/Click=details | Q=quit{/}';
858
+ }
859
+ }
860
+ function updateStatusBar(statusBar, mode, functionName) {
861
+ statusBar.setContent(getStatusBarContent(mode, functionName));
862
+ }
863
+ function copyToClipboard(text) {
864
+ return new Promise((resolve, reject) => {
865
+ let clipboardCmd;
866
+ let clipboardArgs;
867
+ if (process.platform === 'win32') {
868
+ clipboardCmd = 'clip';
869
+ clipboardArgs = [];
870
+ }
871
+ else if (process.platform === 'darwin') {
872
+ clipboardCmd = 'pbcopy';
873
+ clipboardArgs = [];
874
+ }
875
+ else {
876
+ clipboardCmd = 'xclip';
877
+ clipboardArgs = ['-selection', 'clipboard'];
878
+ }
879
+ logger_1.logger.debug(`Using clipboard command: ${clipboardCmd} with args:`, clipboardArgs);
880
+ const clipProcess = (0, child_process_1.spawn)(clipboardCmd, clipboardArgs, {
881
+ stdio: ['pipe', 'ignore', 'pipe']
882
+ });
883
+ let errorOutput = '';
884
+ clipProcess.stderr.on('data', (data) => {
885
+ errorOutput += data.toString();
886
+ });
887
+ clipProcess.on('error', (error) => {
888
+ logger_1.logger.debug(`Clipboard process error: ${error.message}`);
889
+ if (process.platform === 'linux' && clipboardCmd === 'xclip') {
890
+ logger_1.logger.debug('Trying xsel as fallback');
891
+ const xselProcess = (0, child_process_1.spawn)('xsel', ['--clipboard', '--input'], {
892
+ stdio: ['pipe', 'ignore', 'pipe']
893
+ });
894
+ xselProcess.on('error', (xselError) => {
895
+ reject(new Error(`No clipboard utility found: ${error.message} / ${xselError.message}`));
896
+ });
897
+ xselProcess.on('close', (code) => {
898
+ if (code === 0) {
899
+ resolve();
900
+ }
901
+ else {
902
+ reject(new Error(`xsel failed with code ${code}`));
903
+ }
904
+ });
905
+ if (xselProcess.stdin) {
906
+ xselProcess.stdin.write(text);
907
+ xselProcess.stdin.end();
908
+ }
909
+ return;
910
+ }
911
+ reject(error);
912
+ });
913
+ clipProcess.on('close', (code) => {
914
+ if (code === 0) {
915
+ logger_1.logger.debug('Clipboard copy successful');
916
+ resolve();
917
+ }
918
+ else {
919
+ logger_1.logger.debug(`Clipboard process failed with code ${code}, error: ${errorOutput}`);
920
+ reject(new Error(`Clipboard command failed with code ${code}: ${errorOutput}`));
921
+ }
922
+ });
923
+ if (clipProcess.stdin) {
924
+ clipProcess.stdin.write(text);
925
+ clipProcess.stdin.end();
926
+ }
927
+ });
928
+ }
929
+ function generateCurlCommand(request) {
930
+ const isWindows = process.platform === 'win32';
931
+ let curl = `curl -X ${request.httpMethod}`;
932
+ let headers = {};
933
+ if (request.headers) {
934
+ try {
935
+ headers = typeof request.headers === 'string' ?
936
+ JSON.parse(request.headers) : request.headers;
937
+ }
938
+ catch (e) {
939
+ }
940
+ }
941
+ let url = request.url;
942
+ if (url && !url.startsWith('http')) {
943
+ const host = headers.host || headers.Host;
944
+ if (host) {
945
+ const protocol = host.includes('localhost') || host.includes('127.0.0.1') ? 'http' : 'https';
946
+ url = `${protocol}://${host}${url.startsWith('/') ? url : '/' + url}`;
947
+ }
948
+ }
949
+ curl += ` "${url}"`;
950
+ Object.entries(headers).forEach(([key, value]) => {
951
+ const skipHeaders = ['content-length', 'connection', 'user-agent'];
952
+ if (!request.url.startsWith('http')) {
953
+ skipHeaders.push('host');
954
+ }
955
+ if (!skipHeaders.includes(key.toLowerCase())) {
956
+ if (isWindows) {
957
+ curl += ` -H "${key}: ${value}"`;
958
+ }
959
+ else {
960
+ curl += ` \\\n -H "${key}: ${value}"`;
961
+ }
962
+ }
963
+ });
964
+ if (request.requestBody && ['POST', 'PUT', 'PATCH'].includes(request.httpMethod)) {
965
+ try {
966
+ const parsed = JSON.parse(request.requestBody);
967
+ const formatted = JSON.stringify(parsed);
968
+ if (isWindows) {
969
+ curl += ` -d '${formatted.replace(/'/g, "\\'")}'`;
970
+ }
971
+ else {
972
+ curl += ` \\\n -d '${JSON.stringify(parsed, null, 2)}'`;
973
+ }
974
+ }
975
+ catch (e) {
976
+ if (isWindows) {
977
+ curl += ` -d '${request.requestBody.replace(/'/g, "\\'")}'`;
978
+ }
979
+ else {
980
+ curl += ` \\\n -d '${request.requestBody}'`;
981
+ }
982
+ }
983
+ if (!curl.toLowerCase().includes('content-type')) {
984
+ if (isWindows) {
985
+ curl += ` -H "Content-Type: application/json"`;
986
+ }
987
+ else {
988
+ curl += ` \\\n -H "Content-Type: application/json"`;
989
+ }
990
+ }
991
+ }
992
+ if (isWindows) {
993
+ curl += ` -v`;
994
+ }
995
+ else {
996
+ curl += ` \\\n -v`;
997
+ }
998
+ return curl;
999
+ }
1000
+ module.exports = logs;
1001
+ //# sourceMappingURL=logs.js.map