@rglabs/butterfly 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (117) hide show
  1. package/CLAUDE.md +201 -0
  2. package/README.md +371 -0
  3. package/dist/commands/add.d.ts +23 -0
  4. package/dist/commands/add.js +303 -0
  5. package/dist/commands/code.d.ts +11 -0
  6. package/dist/commands/code.js +72 -0
  7. package/dist/commands/create-object.d.ts +6 -0
  8. package/dist/commands/create-object.js +293 -0
  9. package/dist/commands/create-report.d.ts +6 -0
  10. package/dist/commands/create-report.js +154 -0
  11. package/dist/commands/diff.d.ts +4 -0
  12. package/dist/commands/diff.js +238 -0
  13. package/dist/commands/download.d.ts +4 -0
  14. package/dist/commands/download.js +374 -0
  15. package/dist/commands/layout.d.ts +12 -0
  16. package/dist/commands/layout.js +83 -0
  17. package/dist/commands/record.d.ts +21 -0
  18. package/dist/commands/record.js +483 -0
  19. package/dist/commands/run-poc.d.ts +3 -0
  20. package/dist/commands/run-poc.js +18 -0
  21. package/dist/commands/setup.d.ts +3 -0
  22. package/dist/commands/setup.js +66 -0
  23. package/dist/commands/start-poc.d.ts +3 -0
  24. package/dist/commands/start-poc.js +55 -0
  25. package/dist/commands/sync-docs.d.ts +3 -0
  26. package/dist/commands/sync-docs.js +27 -0
  27. package/dist/commands/translate.d.ts +13 -0
  28. package/dist/commands/translate.js +401 -0
  29. package/dist/commands/upload.d.ts +3 -0
  30. package/dist/commands/upload.js +150 -0
  31. package/dist/commands/workflow-info.d.ts +13 -0
  32. package/dist/commands/workflow-info.js +161 -0
  33. package/dist/components/ConflictResolver.d.ts +12 -0
  34. package/dist/components/ConflictResolver.js +77 -0
  35. package/dist/components/DiffView.d.ts +11 -0
  36. package/dist/components/DiffView.js +101 -0
  37. package/dist/components/DownloadProgress.d.ts +11 -0
  38. package/dist/components/DownloadProgress.js +29 -0
  39. package/dist/components/RecordPreview.d.ts +11 -0
  40. package/dist/components/RecordPreview.js +91 -0
  41. package/dist/components/SetupForm.d.ts +8 -0
  42. package/dist/components/SetupForm.js +56 -0
  43. package/dist/components/UploadProgress.d.ts +13 -0
  44. package/dist/components/UploadProgress.js +42 -0
  45. package/dist/diff/adapters/index.d.ts +8 -0
  46. package/dist/diff/adapters/index.js +18 -0
  47. package/dist/diff/adapters/objectsAdapter.d.ts +13 -0
  48. package/dist/diff/adapters/objectsAdapter.js +177 -0
  49. package/dist/diff/adapters/reportsAdapter.d.ts +14 -0
  50. package/dist/diff/adapters/reportsAdapter.js +212 -0
  51. package/dist/diff/adapters/types.d.ts +19 -0
  52. package/dist/diff/adapters/types.js +2 -0
  53. package/dist/diff/engine.d.ts +19 -0
  54. package/dist/diff/engine.js +57 -0
  55. package/dist/diff/types.d.ts +34 -0
  56. package/dist/diff/types.js +110 -0
  57. package/dist/index.d.ts +3 -0
  58. package/dist/index.js +117 -0
  59. package/dist/types/index.d.ts +18 -0
  60. package/dist/types/index.js +2 -0
  61. package/dist/utils/api.d.ts +85 -0
  62. package/dist/utils/api.js +1031 -0
  63. package/dist/utils/auth.d.ts +4 -0
  64. package/dist/utils/auth.js +22 -0
  65. package/dist/utils/bfySplitter.d.ts +12 -0
  66. package/dist/utils/bfySplitter.js +151 -0
  67. package/dist/utils/docs.d.ts +16 -0
  68. package/dist/utils/docs.js +186 -0
  69. package/dist/utils/errorLogger.d.ts +6 -0
  70. package/dist/utils/errorLogger.js +29 -0
  71. package/dist/utils/files.d.ts +14 -0
  72. package/dist/utils/files.js +772 -0
  73. package/dist/utils/lockManager.d.ts +15 -0
  74. package/dist/utils/lockManager.js +126 -0
  75. package/dist/utils/resourceHandlers.d.ts +50 -0
  76. package/dist/utils/resourceHandlers.js +684 -0
  77. package/dist/utils/resourceMapping.d.ts +32 -0
  78. package/dist/utils/resourceMapping.js +210 -0
  79. package/dist/utils/singleResourceDownload.d.ts +14 -0
  80. package/dist/utils/singleResourceDownload.js +261 -0
  81. package/dist/utils/summaryGenerator.d.ts +2 -0
  82. package/dist/utils/summaryGenerator.js +183 -0
  83. package/dist/utils/uploadHandler.d.ts +31 -0
  84. package/dist/utils/uploadHandler.js +263 -0
  85. package/docs/AI_API.md +93 -0
  86. package/docs/CLAUDE.md +216 -0
  87. package/docs/PROJECT_SPECIFIC.md +1 -0
  88. package/docs/RECORD_COMMAND.md +262 -0
  89. package/docs/WORKFLOW_API.md +480 -0
  90. package/docs/bfy-splitting.md +126 -0
  91. package/docs/cli-commands.md +333 -0
  92. package/docs/examples/README.md +95 -0
  93. package/docs/examples/order-system.md +147 -0
  94. package/docs/examples/product-catalog.md +195 -0
  95. package/docs/examples/reports.md +187 -0
  96. package/docs/excel-export.md +216 -0
  97. package/docs/field-types/README.md +29 -0
  98. package/docs/field-types/calculated.md +147 -0
  99. package/docs/field-types/code-mappings.md +84 -0
  100. package/docs/field-types/custom.md +340 -0
  101. package/docs/object-specs/README.md +136 -0
  102. package/docs/object-specs/code-parameters.md +151 -0
  103. package/docs/object-specs/creating.md +203 -0
  104. package/docs/object-specs/js-code-examples.md +208 -0
  105. package/docs/object-specs/js-field-updates.md +168 -0
  106. package/docs/objects/README.md +89 -0
  107. package/docs/objects/creating.md +127 -0
  108. package/docs/page-layout.md +361 -0
  109. package/docs/permissions.md +260 -0
  110. package/docs/reports.md +197 -0
  111. package/docs/state-machines.md +544 -0
  112. package/docs/tasks/create-object.md +81 -0
  113. package/docs/translations.md +346 -0
  114. package/docs/twig-helpers.md +283 -0
  115. package/docs/webservices.md +159 -0
  116. package/docs/workspaces.md +176 -0
  117. package/package.json +59 -0
@@ -0,0 +1,83 @@
1
+ import React, { useEffect, useState } from 'react';
2
+ import { render, Box, Text } from 'ink';
3
+ import { loadAuthConfig } from '../utils/auth.js';
4
+ import { ButterflyAPI } from '../utils/api.js';
5
+ import { promises as fs } from 'fs';
6
+ const LayoutCommand = ({ options, api }) => {
7
+ const [state, setState] = useState({ status: 'loading', message: 'Initializing...' });
8
+ useEffect(() => {
9
+ const run = async () => {
10
+ try {
11
+ let layoutData;
12
+ if (options.file) {
13
+ setState({ status: 'loading', message: `Reading layout from file: ${options.file}` });
14
+ const fileContent = await fs.readFile(options.file, 'utf-8');
15
+ layoutData = JSON.parse(fileContent);
16
+ }
17
+ else if (options.data) {
18
+ layoutData = JSON.parse(options.data);
19
+ }
20
+ else {
21
+ setState({ status: 'error', message: 'Either --file or --data is required' });
22
+ return;
23
+ }
24
+ if (!Array.isArray(layoutData)) {
25
+ setState({ status: 'error', message: 'Layout data must be an array' });
26
+ return;
27
+ }
28
+ setState({ status: 'updating', message: 'Updating layout order...' });
29
+ const response = await api.httpClient.post('/admin/ajax/cms_object_specs/edit_order', layoutData, {
30
+ headers: { 'Content-Type': 'application/json' }
31
+ });
32
+ if (response.data?.success === false) {
33
+ throw new Error(response.data?.error || response.data?.message || 'Failed to update layout');
34
+ }
35
+ let tabCount = 0;
36
+ let specCount = 0;
37
+ for (const position of layoutData) {
38
+ tabCount += position.tabs?.length || 0;
39
+ for (const tab of position.tabs || []) {
40
+ specCount += tab.specs?.length || 0;
41
+ }
42
+ }
43
+ setState({
44
+ status: 'complete',
45
+ message: `Layout updated successfully! (${tabCount} tabs, ${specCount} fields)`
46
+ });
47
+ }
48
+ catch (error) {
49
+ setState({
50
+ status: 'error',
51
+ message: error instanceof Error ? error.message : 'Unknown error occurred'
52
+ });
53
+ }
54
+ };
55
+ run();
56
+ }, []);
57
+ return (React.createElement(Box, { flexDirection: "column" },
58
+ state.status === 'loading' && React.createElement(Text, { color: "yellow" },
59
+ "\u23F3 ",
60
+ state.message),
61
+ state.status === 'updating' && React.createElement(Text, { color: "blue" },
62
+ "\uD83D\uDD04 ",
63
+ state.message),
64
+ state.status === 'complete' && React.createElement(Text, { color: "green" },
65
+ "\u2713 ",
66
+ state.message),
67
+ state.status === 'error' && React.createElement(Text, { color: "red" },
68
+ "\u2717 ",
69
+ state.message)));
70
+ };
71
+ export default async function layoutCommand(options) {
72
+ const config = await loadAuthConfig();
73
+ if (!config) {
74
+ console.error('No authentication configured. Run "butterfly-cli setup" first.');
75
+ process.exit(1);
76
+ }
77
+ const api = new ButterflyAPI(config);
78
+ await api.authenticate();
79
+ if (api.needs2FA)
80
+ await api.complete2FA();
81
+ render(React.createElement(LayoutCommand, { options: options, api: api }));
82
+ }
83
+ //# sourceMappingURL=layout.js.map
@@ -0,0 +1,21 @@
1
+ import { ButterflyAPI } from '../utils/api.js';
2
+ interface RecordCommandProps {
3
+ operation: string;
4
+ table: string;
5
+ api: ButterflyAPI;
6
+ options: {
7
+ id?: string;
8
+ column?: string;
9
+ value?: string;
10
+ columns?: string;
11
+ data?: string;
12
+ file?: string;
13
+ dbAlias?: string;
14
+ preview?: boolean;
15
+ yes?: boolean;
16
+ output?: string;
17
+ };
18
+ }
19
+ export default function recordCommand(operation: string, table: string, options: RecordCommandProps['options']): Promise<void>;
20
+ export {};
21
+ //# sourceMappingURL=record.d.ts.map
@@ -0,0 +1,483 @@
1
+ import React, { useEffect, useState } from 'react';
2
+ import { render, Box, Text, useInput } from 'ink';
3
+ import { promises as fs } from 'fs';
4
+ import { loadAuthConfig } from '../utils/auth.js';
5
+ import { ButterflyAPI } from '../utils/api.js';
6
+ import { RecordPreview } from '../components/RecordPreview.js';
7
+ import { downloadSingleResource, shouldTriggerDownload } from '../utils/singleResourceDownload.js';
8
+ const VALID_OPERATIONS = ['get', 'add', 'edit', 'delete'];
9
+ const RecordCommand = ({ operation, table, options, api: apiProp }) => {
10
+ const [state, setState] = useState({ status: 'loading', message: 'Initializing...' });
11
+ const api = apiProp;
12
+ useInput((input, key) => {
13
+ if (state.status === 'confirming') {
14
+ if (input.toLowerCase() === 'y' || key.return) {
15
+ executeOperation();
16
+ }
17
+ else if (input.toLowerCase() === 'n' || key.escape) {
18
+ setState({ status: 'cancelled', message: 'Operation cancelled by user.' });
19
+ setTimeout(() => process.exit(0), 500);
20
+ }
21
+ }
22
+ });
23
+ const executeOperation = async () => {
24
+ if (!api)
25
+ return;
26
+ const op = operation;
27
+ if (op === 'get') {
28
+ setState({ status: 'complete', message: 'Record retrieved successfully.' });
29
+ setTimeout(() => process.exit(0), 500);
30
+ return;
31
+ }
32
+ if (!state.data)
33
+ return;
34
+ setState({ ...state, status: 'executing', message: `Executing ${operation} on ${table}...` });
35
+ try {
36
+ const result = await api.performOperation(table, op, state.data, options.dbAlias);
37
+ if (result.xtra?.type === 'confirm') {
38
+ console.log('\n');
39
+ console.log('⚠️ CONFIRMATION REQUIRED');
40
+ console.log(`Message: ${result.xtra.message}`);
41
+ console.log(`\nTo confirm, re-run the command with the following parameter added to --data:`);
42
+ console.log(` "confirm_message_${result.xtra.alias}": 1`);
43
+ console.log('\nExample:');
44
+ console.log(` butterfly-cli record ${operation} ${table} --id ${state.data.id} --data '{..., "confirm_message_${result.xtra.alias}": 1}'`);
45
+ setState({
46
+ status: 'complete',
47
+ message: 'Operation requires confirmation. See details above.'
48
+ });
49
+ setTimeout(() => process.exit(0), 500);
50
+ return;
51
+ }
52
+ if (result.success) {
53
+ const recordId = result.id || state.data.id;
54
+ if (op === 'delete') {
55
+ setState({
56
+ status: 'complete',
57
+ message: `Successfully deleted record (ID: ${state.data.id})`,
58
+ resultId: state.data.id
59
+ });
60
+ setTimeout(() => process.exit(0), 1000);
61
+ return;
62
+ }
63
+ if (shouldTriggerDownload(table)) {
64
+ setState({
65
+ ...state,
66
+ status: 'downloading',
67
+ message: `Operation successful. Syncing resource...`,
68
+ resultId: recordId
69
+ });
70
+ const outputPath = './butterfly-resources';
71
+ const downloadResult = await downloadSingleResource(api, outputPath, table, recordId, { ...state.data, id: recordId });
72
+ if (downloadResult.success) {
73
+ setState({
74
+ status: 'complete',
75
+ message: `Successfully ${op === 'add' ? 'added' : 'updated'} and synced${recordId ? ` (ID: ${recordId})` : ''}`,
76
+ resultId: recordId
77
+ });
78
+ }
79
+ else {
80
+ setState({
81
+ status: 'complete',
82
+ message: `${op === 'add' ? 'Added' : 'Updated'} record${recordId ? ` (ID: ${recordId})` : ''} - sync warning: ${downloadResult.message}`,
83
+ resultId: recordId
84
+ });
85
+ }
86
+ }
87
+ else {
88
+ setState({
89
+ status: 'complete',
90
+ message: `Successfully ${op === 'add' ? 'added' : 'updated'} record${recordId ? ` (ID: ${recordId})` : ''}`,
91
+ resultId: recordId
92
+ });
93
+ }
94
+ setTimeout(() => process.exit(0), 1000);
95
+ }
96
+ else {
97
+ setState({
98
+ status: 'error',
99
+ message: result.error || result.message || 'Operation failed'
100
+ });
101
+ setTimeout(() => process.exit(1), 2000);
102
+ }
103
+ }
104
+ catch (error) {
105
+ setState({
106
+ status: 'error',
107
+ message: error instanceof Error ? error.message : 'Unknown error'
108
+ });
109
+ setTimeout(() => process.exit(1), 2000);
110
+ }
111
+ };
112
+ useEffect(() => {
113
+ const run = async () => {
114
+ try {
115
+ const op = operation;
116
+ if (!VALID_OPERATIONS.includes(op)) {
117
+ setState({
118
+ status: 'error',
119
+ message: `Invalid operation: ${operation}. Use: ${VALID_OPERATIONS.join(', ')}`
120
+ });
121
+ setTimeout(() => process.exit(1), 2000);
122
+ return;
123
+ }
124
+ if ((op === 'edit' || op === 'delete') && !options.id) {
125
+ setState({
126
+ status: 'error',
127
+ message: `--id is required for ${op} operation`
128
+ });
129
+ setTimeout(() => process.exit(1), 2000);
130
+ return;
131
+ }
132
+ if ((options.column && !options.value) || (!options.column && options.value)) {
133
+ setState({
134
+ status: 'error',
135
+ message: `--column and --value must be used together`
136
+ });
137
+ setTimeout(() => process.exit(1), 2000);
138
+ return;
139
+ }
140
+ if ((op === 'add' || op === 'edit') && !options.data && !options.file) {
141
+ setState({
142
+ status: 'error',
143
+ message: `--data or --file is required for ${op} operation`
144
+ });
145
+ setTimeout(() => process.exit(1), 2000);
146
+ return;
147
+ }
148
+ let operationData = {};
149
+ if (options.file) {
150
+ setState({ status: 'loading', message: `Reading data from ${options.file}...` });
151
+ const fileContent = await fs.readFile(options.file, 'utf-8');
152
+ operationData = JSON.parse(fileContent);
153
+ }
154
+ else if (options.data) {
155
+ operationData = JSON.parse(options.data);
156
+ }
157
+ if (options.id) {
158
+ operationData.id = parseInt(options.id, 10);
159
+ }
160
+ if (op === 'get') {
161
+ let records;
162
+ let fetchMessage;
163
+ if (options.id) {
164
+ fetchMessage = `Fetching record ${options.id} from ${table}...`;
165
+ setState({ status: 'loading', message: fetchMessage });
166
+ records = await apiProp.fetchTableAdvanced(table, {
167
+ column: 'id',
168
+ value: options.id,
169
+ columns: options.columns,
170
+ dbAlias: options.dbAlias
171
+ });
172
+ }
173
+ else if (options.column && options.value) {
174
+ fetchMessage = `Fetching records from ${table} where ${options.column}=${options.value}...`;
175
+ setState({ status: 'loading', message: fetchMessage });
176
+ records = await apiProp.fetchTableAdvanced(table, {
177
+ column: options.column,
178
+ value: options.value,
179
+ columns: options.columns,
180
+ dbAlias: options.dbAlias
181
+ });
182
+ }
183
+ else {
184
+ fetchMessage = `Fetching all records from ${table}...`;
185
+ setState({ status: 'loading', message: fetchMessage });
186
+ records = await apiProp.fetchTableAdvanced(table, {
187
+ columns: options.columns,
188
+ dbAlias: options.dbAlias
189
+ });
190
+ }
191
+ if (records.length === 0) {
192
+ const filterDesc = options.id
193
+ ? `ID ${options.id}`
194
+ : options.column
195
+ ? `${options.column}=${options.value}`
196
+ : 'no filter';
197
+ setState({
198
+ status: 'error',
199
+ message: `No records found in ${table} (${filterDesc})`
200
+ });
201
+ setTimeout(() => process.exit(1), 2000);
202
+ return;
203
+ }
204
+ const output = records.length === 1 ? records[0] : records;
205
+ const countMsg = records.length === 1 ? '1 record' : `${records.length} records`;
206
+ if (options.output) {
207
+ await fs.writeFile(options.output, JSON.stringify(output, null, 2), 'utf-8');
208
+ setState({
209
+ status: 'complete',
210
+ message: `${countMsg} saved to ${options.output}`,
211
+ data: output
212
+ });
213
+ setTimeout(() => process.exit(0), 1000);
214
+ }
215
+ else {
216
+ console.log('\n' + JSON.stringify(output, null, 2) + '\n');
217
+ setState({
218
+ status: 'complete',
219
+ message: `Retrieved ${countMsg}.`,
220
+ data: output
221
+ });
222
+ setTimeout(() => process.exit(0), 500);
223
+ }
224
+ return;
225
+ }
226
+ let existingRecord;
227
+ if ((op === 'edit' || op === 'delete') && options.id) {
228
+ setState({ status: 'loading', message: `Fetching existing record...` });
229
+ const record = await apiProp.fetchRecordById(table, parseInt(options.id, 10), options.dbAlias);
230
+ if (record) {
231
+ existingRecord = record;
232
+ }
233
+ else if (op === 'edit') {
234
+ setState({
235
+ status: 'error',
236
+ message: `Record not found: ${table} with ID ${options.id}`
237
+ });
238
+ setTimeout(() => process.exit(1), 2000);
239
+ return;
240
+ }
241
+ }
242
+ if (options.preview === false || options.yes) {
243
+ setState({
244
+ status: 'executing',
245
+ message: `Executing ${op} on ${table}...`,
246
+ data: operationData,
247
+ existingRecord
248
+ });
249
+ const result = await apiProp.performOperation(table, op, operationData, options.dbAlias);
250
+ if (result.xtra?.type === 'confirm') {
251
+ console.log('\n');
252
+ console.log('⚠️ CONFIRMATION REQUIRED');
253
+ console.log(`Message: ${result.xtra.message}`);
254
+ console.log(`\nTo confirm, re-run the command with the following parameter added to --data:`);
255
+ console.log(` "confirm_message_${result.xtra.alias}": 1`);
256
+ console.log('\nExample:');
257
+ console.log(` butterfly-cli record ${op} ${table} --id ${operationData.id} --data '{..., "confirm_message_${result.xtra.alias}": 1}'`);
258
+ setState({
259
+ status: 'complete',
260
+ message: 'Operation requires confirmation. See details above.'
261
+ });
262
+ setTimeout(() => process.exit(0), 1000);
263
+ return;
264
+ }
265
+ if (result.success) {
266
+ const recordId = result.id || operationData.id;
267
+ if (op !== 'delete' && shouldTriggerDownload(table) && recordId) {
268
+ setState({
269
+ status: 'downloading',
270
+ message: `Syncing resource...`,
271
+ resultId: recordId
272
+ });
273
+ const outputPath = './butterfly-resources';
274
+ const downloadResult = await downloadSingleResource(apiProp, outputPath, table, recordId, { ...operationData, id: recordId });
275
+ setState({
276
+ status: 'complete',
277
+ message: downloadResult.success
278
+ ? `Successfully ${op === 'add' ? 'added' : 'updated'} and synced (ID: ${recordId})`
279
+ : `${op === 'add' ? 'Added' : 'Updated'} record (ID: ${recordId}) - sync: ${downloadResult.message}`,
280
+ resultId: recordId
281
+ });
282
+ }
283
+ else {
284
+ setState({
285
+ status: 'complete',
286
+ message: `Successfully ${op === 'add' ? 'added' : op === 'edit' ? 'updated' : 'deleted'} record${recordId ? ` (ID: ${recordId})` : ''}`,
287
+ resultId: recordId
288
+ });
289
+ }
290
+ }
291
+ else {
292
+ setState({
293
+ status: 'error',
294
+ message: result.error || result.message || 'Operation failed'
295
+ });
296
+ }
297
+ setTimeout(() => process.exit(result.success ? 0 : 1), 1000);
298
+ return;
299
+ }
300
+ setState({
301
+ status: 'preview',
302
+ message: '',
303
+ data: operationData,
304
+ existingRecord
305
+ });
306
+ setTimeout(() => {
307
+ setState(prev => ({ ...prev, status: 'confirming' }));
308
+ }, 100);
309
+ }
310
+ catch (error) {
311
+ setState({
312
+ status: 'error',
313
+ message: error instanceof Error ? error.message : 'Unknown error'
314
+ });
315
+ setTimeout(() => process.exit(1), 2000);
316
+ }
317
+ };
318
+ run();
319
+ }, []);
320
+ return (React.createElement(Box, { flexDirection: "column", padding: 1 },
321
+ React.createElement(Text, { bold: true, color: "cyan" }, "Butterfly CLI - Record Operation"),
322
+ React.createElement(Text, null, " "),
323
+ state.status === 'loading' && (React.createElement(Text, { color: "yellow" },
324
+ "Loading... ",
325
+ state.message)),
326
+ (state.status === 'preview' || state.status === 'confirming') && state.data && (React.createElement(RecordPreview, { operation: operation, table: table, data: state.data, existingRecord: state.existingRecord })),
327
+ state.status === 'confirming' && (React.createElement(Box, { marginTop: 1 },
328
+ React.createElement(Text, { color: "yellow" }, "Proceed with this operation? (y/n): "))),
329
+ state.status === 'executing' && (React.createElement(Text, { color: "blue" },
330
+ "Executing... ",
331
+ state.message)),
332
+ state.status === 'downloading' && (React.createElement(Text, { color: "cyan" },
333
+ "Downloading... ",
334
+ state.message)),
335
+ state.status === 'complete' && (React.createElement(Box, { flexDirection: "column" },
336
+ React.createElement(Text, { color: "green" },
337
+ "SUCCESS: ",
338
+ state.message),
339
+ state.resultId && React.createElement(Text, { color: "gray" },
340
+ " Record ID: ",
341
+ state.resultId))),
342
+ state.status === 'error' && (React.createElement(Text, { color: "red" },
343
+ "ERROR: ",
344
+ state.message)),
345
+ state.status === 'cancelled' && (React.createElement(Text, { color: "gray" }, state.message))));
346
+ };
347
+ async function runNonInteractive(operation, table, options) {
348
+ const op = operation;
349
+ if (!VALID_OPERATIONS.includes(op)) {
350
+ console.error(`ERROR: Invalid operation: ${operation}. Use: ${VALID_OPERATIONS.join(', ')}`);
351
+ process.exit(1);
352
+ }
353
+ if ((op === 'edit' || op === 'delete') && !options.id) {
354
+ console.error(`ERROR: --id is required for ${op} operation`);
355
+ process.exit(1);
356
+ }
357
+ if ((options.column && !options.value) || (!options.column && options.value)) {
358
+ console.error(`ERROR: --column and --value must be used together`);
359
+ process.exit(1);
360
+ }
361
+ if ((op === 'add' || op === 'edit') && !options.data && !options.file) {
362
+ console.error(`ERROR: --data or --file is required for ${op} operation`);
363
+ process.exit(1);
364
+ }
365
+ try {
366
+ let operationData = {};
367
+ if (options.file) {
368
+ const fileContent = await fs.readFile(options.file, 'utf-8');
369
+ operationData = JSON.parse(fileContent);
370
+ }
371
+ else if (options.data) {
372
+ operationData = JSON.parse(options.data);
373
+ }
374
+ if (options.id) {
375
+ operationData.id = parseInt(options.id, 10);
376
+ }
377
+ const config = await loadAuthConfig();
378
+ if (!config) {
379
+ console.error('ERROR: No authentication configured. Run "butterfly-cli setup" first.');
380
+ process.exit(1);
381
+ }
382
+ const api = new ButterflyAPI(config);
383
+ await api.authenticate();
384
+ if (api.needs2FA)
385
+ await api.complete2FA();
386
+ if (op === 'get') {
387
+ let records;
388
+ if (options.id) {
389
+ records = await api.fetchTableAdvanced(table, {
390
+ column: 'id',
391
+ value: options.id,
392
+ columns: options.columns,
393
+ dbAlias: options.dbAlias
394
+ });
395
+ }
396
+ else if (options.column && options.value) {
397
+ records = await api.fetchTableAdvanced(table, {
398
+ column: options.column,
399
+ value: options.value,
400
+ columns: options.columns,
401
+ dbAlias: options.dbAlias
402
+ });
403
+ }
404
+ else {
405
+ records = await api.fetchTableAdvanced(table, {
406
+ columns: options.columns,
407
+ dbAlias: options.dbAlias
408
+ });
409
+ }
410
+ if (records.length === 0) {
411
+ console.error(`ERROR: No records found in ${table}`);
412
+ process.exit(1);
413
+ }
414
+ const output = records.length === 1 ? records[0] : records;
415
+ if (options.output) {
416
+ await fs.writeFile(options.output, JSON.stringify(output, null, 2), 'utf-8');
417
+ console.log(`SUCCESS: ${records.length} record(s) saved to ${options.output}`);
418
+ }
419
+ else {
420
+ console.log(JSON.stringify(output, null, 2));
421
+ }
422
+ process.exit(0);
423
+ }
424
+ const result = await api.performOperation(table, op, operationData, options.dbAlias);
425
+ if (result.xtra?.type === 'confirm') {
426
+ console.log('\n⚠️ CONFIRMATION REQUIRED');
427
+ console.log(`Message: ${result.xtra.message}`);
428
+ console.log(`\nTo confirm, re-run the command with the following parameter added to --data:`);
429
+ console.log(` "confirm_message_${result.xtra.alias}": 1`);
430
+ console.log('\nExample:');
431
+ console.log(` butterfly-cli record ${operation} ${table} --id ${operationData.id} --data '{..., "confirm_message_${result.xtra.alias}": 1}'`);
432
+ process.exit(0);
433
+ }
434
+ if (result.success) {
435
+ const recordId = result.id || operationData.id;
436
+ if (op !== 'delete' && shouldTriggerDownload(table) && recordId) {
437
+ const outputPath = './butterfly-resources';
438
+ const downloadResult = await downloadSingleResource(api, outputPath, table, recordId, { ...operationData, id: recordId });
439
+ if (downloadResult.success) {
440
+ console.log(`SUCCESS: ${op} operation completed and synced (ID: ${recordId})`);
441
+ if (downloadResult.downloadedResource) {
442
+ console.log(` Synced: ${downloadResult.downloadedResource.type}/${downloadResult.downloadedResource.name || downloadResult.downloadedResource.id}`);
443
+ }
444
+ }
445
+ else {
446
+ console.log(`SUCCESS: ${op} operation completed (ID: ${recordId})`);
447
+ console.log(` Sync warning: ${downloadResult.message}`);
448
+ }
449
+ }
450
+ else {
451
+ console.log(`SUCCESS: ${op} operation completed${recordId ? ` (ID: ${recordId})` : ''}`);
452
+ }
453
+ process.exit(0);
454
+ }
455
+ else {
456
+ console.error(`ERROR: ${result.error || result.message || 'Operation failed'}`);
457
+ process.exit(1);
458
+ }
459
+ }
460
+ catch (error) {
461
+ console.error(`ERROR: ${error instanceof Error ? error.message : 'Unknown error'}`);
462
+ process.exit(1);
463
+ }
464
+ }
465
+ export default async function recordCommand(operation, table, options) {
466
+ const isNonInteractive = !process.stdin.isTTY || options.yes || options.preview === false;
467
+ if (isNonInteractive) {
468
+ await runNonInteractive(operation, table, options);
469
+ }
470
+ else {
471
+ const config = await loadAuthConfig();
472
+ if (!config) {
473
+ console.error('No authentication configured. Run "butterfly-cli setup" first.');
474
+ process.exit(1);
475
+ }
476
+ const api = new ButterflyAPI(config);
477
+ await api.authenticate();
478
+ if (api.needs2FA)
479
+ await api.complete2FA();
480
+ render(React.createElement(RecordCommand, { operation: operation, table: table, options: options, api: api }));
481
+ }
482
+ }
483
+ //# sourceMappingURL=record.js.map
@@ -0,0 +1,3 @@
1
+ declare const _default: () => void;
2
+ export default _default;
3
+ //# sourceMappingURL=run-poc.d.ts.map
@@ -0,0 +1,18 @@
1
+ import { spawnSync } from 'child_process';
2
+ import { existsSync, readFileSync } from 'fs';
3
+ import path from 'path';
4
+ const PROMPT_FILE = 'POC-Generator/prompts/00-master.md';
5
+ export default () => {
6
+ const promptPath = path.join(process.cwd(), PROMPT_FILE);
7
+ if (!existsSync(promptPath)) {
8
+ console.error(`Prompt file not found: ${promptPath}`);
9
+ console.error(`Run 'butterfly-cli start-poc' first to clone the repository.`);
10
+ process.exit(1);
11
+ }
12
+ const promptContent = readFileSync(promptPath, 'utf-8');
13
+ spawnSync('claude', [promptContent], {
14
+ stdio: 'inherit',
15
+ cwd: process.cwd()
16
+ });
17
+ };
18
+ //# sourceMappingURL=run-poc.js.map
@@ -0,0 +1,3 @@
1
+ declare const _default: () => Promise<void>;
2
+ export default _default;
3
+ //# sourceMappingURL=setup.d.ts.map