@rulebricks/cli 2.0.1 → 2.0.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.
@@ -0,0 +1,298 @@
1
+ {
2
+ "root_group": {
3
+ "name": "",
4
+ "path": "",
5
+ "id": "d41d8cd98f00b204e9800998ecf8427e",
6
+ "groups": [],
7
+ "checks": [
8
+ {
9
+ "name": "status is 200",
10
+ "path": "::status is 200",
11
+ "id": "6210a8cd14cd70477eba5c5e4cb3fb5f",
12
+ "passes": 24000,
13
+ "fails": 0
14
+ },
15
+ {
16
+ "name": "valid response",
17
+ "path": "::valid response",
18
+ "id": "74e6940490a7b557672e61f02e36f4e8",
19
+ "passes": 24000,
20
+ "fails": 0
21
+ },
22
+ {
23
+ "name": "no error in response",
24
+ "path": "::no error in response",
25
+ "id": "04d657588d6c66446d4937ecba2292a2",
26
+ "passes": 24000,
27
+ "fails": 0
28
+ }
29
+ ]
30
+ },
31
+ "options": {
32
+ "summaryTrendStats": [
33
+ "avg",
34
+ "min",
35
+ "med",
36
+ "max",
37
+ "p(90)",
38
+ "p(95)"
39
+ ],
40
+ "summaryTimeUnit": "",
41
+ "noColor": false
42
+ },
43
+ "state": {
44
+ "isStdOutTTY": true,
45
+ "isStdErrTTY": true,
46
+ "testRunDurationMs": 300227.96
47
+ },
48
+ "metrics": {
49
+ "request_duration": {
50
+ "type": "trend",
51
+ "contains": "default",
52
+ "values": {
53
+ "p(95)": 318,
54
+ "avg": 207.51141666666666,
55
+ "min": 98,
56
+ "med": 195,
57
+ "max": 1305,
58
+ "p(90)": 285
59
+ }
60
+ },
61
+ "http_req_sending": {
62
+ "values": {
63
+ "p(90)": 0.443,
64
+ "p(95)": 0.575,
65
+ "avg": 0.3505282157261422,
66
+ "min": 0.029,
67
+ "med": 0.265,
68
+ "max": 197.681
69
+ },
70
+ "type": "trend",
71
+ "contains": "time"
72
+ },
73
+ "http_req_failed": {
74
+ "type": "rate",
75
+ "contains": "default",
76
+ "values": {
77
+ "rate": 0,
78
+ "passes": 0,
79
+ "fails": 30001
80
+ }
81
+ },
82
+ "checks": {
83
+ "type": "rate",
84
+ "contains": "default",
85
+ "values": {
86
+ "rate": 1,
87
+ "passes": 72000,
88
+ "fails": 0
89
+ }
90
+ },
91
+ "successes": {
92
+ "type": "rate",
93
+ "contains": "default",
94
+ "values": {
95
+ "rate": 1,
96
+ "passes": 24000,
97
+ "fails": 0
98
+ }
99
+ },
100
+ "http_req_blocked": {
101
+ "contains": "time",
102
+ "values": {
103
+ "med": 0.001,
104
+ "max": 446.733,
105
+ "p(90)": 0.001,
106
+ "p(95)": 0.002,
107
+ "avg": 1.6850438318703274,
108
+ "min": 0
109
+ },
110
+ "type": "trend"
111
+ },
112
+ "http_req_receiving": {
113
+ "type": "trend",
114
+ "contains": "time",
115
+ "values": {
116
+ "avg": 2.110890670310989,
117
+ "min": 0.019,
118
+ "med": 0.203,
119
+ "max": 206.634,
120
+ "p(90)": 3.99,
121
+ "p(95)": 5.81
122
+ }
123
+ },
124
+ "http_req_duration": {
125
+ "values": {
126
+ "avg": 204.29118289390323,
127
+ "min": 97.82,
128
+ "med": 193.159,
129
+ "max": 990.433,
130
+ "p(90)": 283.411,
131
+ "p(95)": 316.351
132
+ },
133
+ "type": "trend",
134
+ "contains": "time"
135
+ },
136
+ "vus_max": {
137
+ "type": "gauge",
138
+ "contains": "default",
139
+ "values": {
140
+ "value": 200,
141
+ "min": 200,
142
+ "max": 200
143
+ }
144
+ },
145
+ "errors{phase:measurement}": {
146
+ "values": {
147
+ "rate": 0,
148
+ "passes": 0,
149
+ "fails": 24000
150
+ },
151
+ "thresholds": {
152
+ "rate<0.05": {
153
+ "ok": true
154
+ }
155
+ },
156
+ "type": "rate",
157
+ "contains": "default"
158
+ },
159
+ "http_reqs": {
160
+ "contains": "default",
161
+ "values": {
162
+ "count": 30001,
163
+ "rate": 99.9274018315949
164
+ },
165
+ "type": "counter"
166
+ },
167
+ "data_received": {
168
+ "type": "counter",
169
+ "contains": "data",
170
+ "values": {
171
+ "count": 409778192,
172
+ "rate": 1364890.172121211
173
+ }
174
+ },
175
+ "iterations": {
176
+ "type": "counter",
177
+ "contains": "default",
178
+ "values": {
179
+ "count": 30001,
180
+ "rate": 99.9274018315949
181
+ }
182
+ },
183
+ "data_sent": {
184
+ "type": "counter",
185
+ "contains": "data",
186
+ "values": {
187
+ "count": 104185619,
188
+ "rate": 347021.7064393336
189
+ }
190
+ },
191
+ "http_req_tls_handshaking": {
192
+ "type": "trend",
193
+ "contains": "time",
194
+ "values": {
195
+ "avg": 0.9106618112729572,
196
+ "min": 0,
197
+ "med": 0,
198
+ "max": 306.889,
199
+ "p(90)": 0,
200
+ "p(95)": 0
201
+ }
202
+ },
203
+ "http_req_waiting": {
204
+ "contains": "time",
205
+ "values": {
206
+ "p(90)": 280.835,
207
+ "p(95)": 313.283,
208
+ "avg": 201.82976400786623,
209
+ "min": 94.379,
210
+ "med": 190.745,
211
+ "max": 933.167
212
+ },
213
+ "type": "trend"
214
+ },
215
+ "iteration_duration": {
216
+ "type": "trend",
217
+ "contains": "time",
218
+ "values": {
219
+ "p(95)": 324.982625,
220
+ "avg": 209.38244802586436,
221
+ "min": 100.226958,
222
+ "med": 196.689375,
223
+ "max": 1306.521125,
224
+ "p(90)": 288.073125
225
+ }
226
+ },
227
+ "errors": {
228
+ "type": "rate",
229
+ "contains": "default",
230
+ "values": {
231
+ "rate": 0,
232
+ "passes": 0,
233
+ "fails": 24000
234
+ }
235
+ },
236
+ "http_req_duration{phase:measurement}": {
237
+ "type": "trend",
238
+ "contains": "time",
239
+ "values": {
240
+ "p(95)": 316.0782,
241
+ "avg": 205.77155337500048,
242
+ "min": 97.82,
243
+ "med": 194.721,
244
+ "max": 990.433,
245
+ "p(90)": 283.5602
246
+ },
247
+ "thresholds": {
248
+ "p(95)<2000": {
249
+ "ok": true
250
+ },
251
+ "p(99)<5000": {
252
+ "ok": true
253
+ }
254
+ }
255
+ },
256
+ "http_req_connecting": {
257
+ "type": "trend",
258
+ "contains": "time",
259
+ "values": {
260
+ "avg": 0.7645731142295258,
261
+ "min": 0,
262
+ "med": 0,
263
+ "max": 257.371,
264
+ "p(90)": 0,
265
+ "p(95)": 0
266
+ }
267
+ },
268
+ "http_req_duration{expected_response:true}": {
269
+ "contains": "time",
270
+ "values": {
271
+ "avg": 204.29118289390323,
272
+ "min": 97.82,
273
+ "med": 193.159,
274
+ "max": 990.433,
275
+ "p(90)": 283.411,
276
+ "p(95)": 316.351
277
+ },
278
+ "type": "trend"
279
+ },
280
+ "vus": {
281
+ "type": "gauge",
282
+ "contains": "default",
283
+ "values": {
284
+ "max": 71,
285
+ "value": 21,
286
+ "min": 13
287
+ }
288
+ },
289
+ "total_payloads": {
290
+ "contains": "default",
291
+ "values": {
292
+ "count": 1200000,
293
+ "rate": 3996.9628411690906
294
+ },
295
+ "type": "counter"
296
+ }
297
+ }
298
+ }
@@ -0,0 +1,159 @@
1
+ /**
2
+ * Throughput (Solutions Per Second) Benchmark Test
3
+ *
4
+ * Measures rule engine capacity by sending bulk payload requests at a constant rate.
5
+ * This test helps you understand:
6
+ * - How many rule evaluations your deployment can process per second
7
+ * - Maximum throughput for batch workloads
8
+ * - Engine performance under sustained load
9
+ *
10
+ * Test Structure:
11
+ * - 1 minute warm-up phase (allows cluster to scale, excluded from results)
12
+ * - 4 minutes measurement phase (steady-state performance)
13
+ *
14
+ * Usage:
15
+ * k6 run -e API_URL=https://your-instance.com/api/v1/flows/flow_id \
16
+ * -e API_KEY=your-api-key \
17
+ * throughput-test.js
18
+ *
19
+ * Optional environment variables:
20
+ * TEST_DURATION - Measurement duration after warm-up (default: 4m)
21
+ * TARGET_RPS - Target bulk requests per second (default: 100)
22
+ * BULK_SIZE - Number of payloads per request (default: 50)
23
+ */
24
+
25
+ import { check } from "k6";
26
+ import http from "k6/http";
27
+ import { Counter, Rate, Trend } from "k6/metrics";
28
+ import {
29
+ generateBulkPayload,
30
+ getConfig,
31
+ createRequestParams,
32
+ } from "./lib/payload.js";
33
+ import {
34
+ generateThroughputReport,
35
+ generateThroughputConsoleSummary,
36
+ } from "./lib/report.js";
37
+
38
+ // Load configuration
39
+ const config = getConfig({
40
+ testDuration: "4m",
41
+ targetRps: 100,
42
+ bulkSize: 50,
43
+ });
44
+
45
+ // Custom metrics (only for measurement phase)
46
+ const errorRate = new Rate("errors");
47
+ const successRate = new Rate("successes");
48
+ const requestDuration = new Trend("request_duration");
49
+ const droppedRequests = new Counter("dropped_requests");
50
+ const totalPayloads = new Counter("total_payloads");
51
+ const failedPayloads = new Counter("failed_payloads");
52
+
53
+ // k6 options with warm-up and measurement phases
54
+ export const options = {
55
+ scenarios: {
56
+ // Warm-up phase: 1 minute to let cluster scale
57
+ warm_up: {
58
+ executor: "constant-arrival-rate",
59
+ rate: config.targetRps,
60
+ timeUnit: "1s",
61
+ duration: "1m",
62
+ preAllocatedVUs: Math.min(config.targetRps, 100),
63
+ maxVUs: Math.min(config.targetRps * 3, 500),
64
+ exec: "warmUp",
65
+ tags: { phase: "warmup" },
66
+ },
67
+ // Measurement phase: actual test after warm-up
68
+ throughput_test: {
69
+ executor: "constant-arrival-rate",
70
+ rate: config.targetRps,
71
+ timeUnit: "1s",
72
+ duration: config.testDuration,
73
+ preAllocatedVUs: Math.min(config.targetRps, 100),
74
+ maxVUs: Math.min(config.targetRps * 3, 500),
75
+ startTime: "1m", // Start after warm-up
76
+ exec: "measureTest",
77
+ tags: { phase: "measurement" },
78
+ },
79
+ },
80
+ thresholds: {
81
+ // Only apply thresholds to measurement phase
82
+ "http_req_duration{phase:measurement}": ["p(95)<2000", "p(99)<5000"],
83
+ "errors{phase:measurement}": ["rate<0.05"],
84
+ },
85
+ };
86
+
87
+ // Request parameters (longer timeout for bulk requests)
88
+ const params = createRequestParams(config.apiKey, "30s");
89
+
90
+ /**
91
+ * Warm-up function - same as test but metrics tagged differently
92
+ */
93
+ export function warmUp() {
94
+ const bulkPayload = generateBulkPayload(config.bulkSize);
95
+
96
+ try {
97
+ http.post(config.apiUrl, JSON.stringify(bulkPayload), params);
98
+ } catch (error) {
99
+ // Ignore errors during warm-up
100
+ }
101
+ }
102
+
103
+ /**
104
+ * Measurement test function - sends bulk requests
105
+ */
106
+ export function measureTest() {
107
+ const bulkPayload = generateBulkPayload(config.bulkSize);
108
+ const start = Date.now();
109
+
110
+ try {
111
+ const response = http.post(
112
+ config.apiUrl,
113
+ JSON.stringify(bulkPayload),
114
+ params
115
+ );
116
+
117
+ const duration = Date.now() - start;
118
+ requestDuration.add(duration);
119
+
120
+ const success = check(response, {
121
+ "status is 200": (r) => r.status === 200,
122
+ "valid response": (r) => r.body && r.body.length > 0,
123
+ "no error in response": (r) => {
124
+ try {
125
+ const body = JSON.parse(r.body);
126
+ return !body.error;
127
+ } catch (e) {
128
+ return false;
129
+ }
130
+ },
131
+ });
132
+
133
+ errorRate.add(!success);
134
+ successRate.add(success);
135
+ totalPayloads.add(config.bulkSize);
136
+
137
+ if (!success) {
138
+ droppedRequests.add(1);
139
+ failedPayloads.add(config.bulkSize);
140
+ }
141
+ } catch (error) {
142
+ errorRate.add(1);
143
+ successRate.add(0);
144
+ droppedRequests.add(1);
145
+ totalPayloads.add(config.bulkSize);
146
+ failedPayloads.add(config.bulkSize);
147
+ }
148
+ }
149
+
150
+ /**
151
+ * Generate summary report
152
+ */
153
+ export function handleSummary(data) {
154
+ return {
155
+ stdout: generateThroughputConsoleSummary(data, config),
156
+ "throughput-report.html": generateThroughputReport(data, config),
157
+ "throughput-results.json": JSON.stringify(data, null, 2),
158
+ };
159
+ }
@@ -1,6 +1,7 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  import { useState, useEffect, useCallback, useRef } from "react";
3
3
  import { Box, Text, useApp, useInput } from "ink";
4
+ import { platform } from "os";
4
5
  import { BorderBox, Spinner, StatusLine, ThemeProvider, useTheme, Logo, } from "../components/common/index.js";
5
6
  import { DNSWaitScreen } from "../components/DNSWaitScreen.js";
6
7
  import { loadDeploymentConfig, loadDeploymentState, saveDeploymentState, updateDeploymentStatus, } from "../lib/config.js";
@@ -360,7 +361,7 @@ function DeployCommandInner({ name, skipInfra, skipDns, version, }) {
360
361
  // Complete screen
361
362
  if (step === "complete") {
362
363
  const tlsSkipped = status.helmUpgradeTls === "skipped" && !useExternalDns;
363
- return (_jsx(BorderBox, { title: "Deployment Complete", children: _jsxs(Box, { flexDirection: "column", marginY: 1, children: [_jsx(Text, { color: colors.success, bold: true, children: "\u2713 Rulebricks deployed successfully!" }), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsxs(Text, { children: ["URL:", " ", _jsxs(Text, { color: colors.accent, children: ["https://", config?.domain, "/auth/signup"] })] }), useExternalDns && (_jsx(Text, { color: colors.muted, children: "DNS records will be created automatically by external-dns" })), tlsSkipped && (_jsx(Box, { marginTop: 1, children: _jsxs(Text, { color: colors.warning, children: ["\u26A0 TLS not configured. Run `rulebricks deploy ", name, "` again after DNS setup."] }) }))] }), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsx(Text, { bold: true, children: "Next steps:" }), _jsxs(Text, { color: colors.muted, children: [" ", "\u2022 Visit the URL to complete initial setup"] }), _jsxs(Text, { color: colors.muted, children: [" ", "\u2022 Run `rulebricks status ", name, "` to check deployment health"] }), tlsSkipped && (_jsxs(Text, { color: colors.muted, children: [" ", "\u2022 Configure DNS and re-run deploy for TLS"] }))] })] }) }));
364
+ return (_jsx(BorderBox, { title: "Deployment Complete", children: _jsxs(Box, { flexDirection: "column", marginY: 1, children: [_jsx(Text, { color: colors.success, bold: true, children: "\u2713 Rulebricks deployed successfully!" }), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsxs(Text, { children: ["URL:", " ", _jsxs(Text, { color: colors.accent, children: ["https://", config?.domain, "/auth/signup"] })] }), useExternalDns && (_jsx(Text, { color: colors.muted, children: "DNS records will be created automatically by external-dns" })), tlsSkipped && (_jsx(Box, { marginTop: 1, children: _jsxs(Text, { color: colors.warning, children: ["\u26A0 TLS not configured. Run `rulebricks deploy ", name, "` again after DNS setup."] }) }))] }), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsx(Text, { bold: true, children: "Next steps:" }), _jsxs(Text, { color: colors.muted, children: [" ", "\u2022 Visit the URL to complete initial setup"] }), _jsxs(Text, { color: colors.muted, children: [" ", "\u2022 Run `rulebricks status ", name, "` to check deployment health"] }), tlsSkipped && (_jsxs(Text, { color: colors.muted, children: [" ", "\u2022 Configure DNS and re-run deploy for TLS"] }))] }), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsx(Text, { color: colors.muted, dimColor: true, children: "Tip: If the URL isn't accessible yet, your local DNS may need time to propagate." }), _jsxs(Text, { color: colors.muted, dimColor: true, children: ["Flush DNS cache: ", getDnsFlushCommand()] })] })] }) }));
364
365
  }
365
366
  // Progress screen
366
367
  const helmInstallLabel = useExternalDns
@@ -376,6 +377,16 @@ function DeployCommandInner({ name, skipInfra, skipDns, version, }) {
376
377
  ? "Applying infrastructure"
377
378
  : undefined }), _jsx(StatusLine, { status: status.kubeconfig, label: "Kubernetes configuration" }), _jsx(StatusLine, { status: status.helmInstall, label: helmInstallLabel }), !useExternalDns && (_jsxs(_Fragment, { children: [_jsx(StatusLine, { status: status.dnsConfig, label: "DNS configuration" }), _jsx(StatusLine, { status: status.helmUpgradeTls, label: "TLS configuration" })] })), step !== "dns-wait" && (_jsx(Box, { marginTop: 1, children: _jsx(Spinner, { label: getStepLabel(step, useExternalDns) }) }))] }) }));
378
379
  }
380
+ function getDnsFlushCommand() {
381
+ switch (platform()) {
382
+ case "darwin":
383
+ return "sudo dscacheutil -flushcache && sudo killall -HUP mDNSResponder";
384
+ case "win32":
385
+ return "ipconfig /flushdns";
386
+ default:
387
+ return "sudo systemd-resolve --flush-caches";
388
+ }
389
+ }
379
390
  function getStepLabel(step, useExternalDns) {
380
391
  switch (step) {
381
392
  case "loading":
@@ -175,7 +175,7 @@ export function TestModeStep({ onComplete, onBack, state }) {
175
175
  ...defaults,
176
176
  });
177
177
  };
178
- return (_jsxs(BorderBox, { title: "Test Mode", children: [_jsx(Box, { flexDirection: "column", marginY: 1, children: _jsx(Text, { children: "Select the type of benchmark to run:" }) }), _jsx(SelectInput, { items: items, onSelect: handleSelect, itemComponent: ({ isSelected, label }) => {
178
+ return (_jsxs(BorderBox, { title: "Test Mode", children: [_jsx(Box, { flexDirection: "column", marginY: 1, children: _jsx(Text, { children: "Select the type of benchmark to run:" }) }), _jsx(SelectInput, { items: items, onSelect: handleSelect, indicatorComponent: () => null, itemComponent: ({ isSelected, label }) => {
179
179
  const item = items.find((i) => i.label === label);
180
180
  return (_jsxs(Box, { flexDirection: "column", marginY: isSelected ? 1 : 0, children: [_jsxs(Text, { color: isSelected ? colors.accent : undefined, bold: isSelected, children: [isSelected ? "❯ " : " ", label] }), isSelected && item && (_jsxs(Text, { color: colors.muted, dimColor: true, children: [" ", item.description] }))] }));
181
181
  } }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { color: colors.muted, dimColor: true, children: "Esc to go back \u2022 Enter to select" }) })] }));
@@ -262,7 +262,7 @@ export function PresetsStep({ onComplete, onBack, state }) {
262
262
  if (customMode) {
263
263
  return (_jsxs(BorderBox, { title: "Custom Configuration", children: [_jsxs(Box, { flexDirection: "column", marginY: 1, children: [_jsx(Text, { children: "Configure your custom benchmark parameters:" }), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsxs(Box, { children: [_jsxs(Text, { color: activeField === "rps" ? colors.accent : undefined, children: [activeField === "rps" ? "❯ " : " ", "Target RPS:", " "] }), activeField === "rps" ? (_jsx(TextInput, { value: customRps, onChange: setCustomRps, onSubmit: handleCustomSubmit, placeholder: "e.g., 500" })) : (_jsx(Text, { children: customRps }))] }), _jsxs(Box, { children: [_jsxs(Text, { color: activeField === "duration" ? colors.accent : undefined, children: [activeField === "duration" ? "❯ " : " ", "Duration:", " "] }), activeField === "duration" ? (_jsx(TextInput, { value: customDuration, onChange: setCustomDuration, onSubmit: handleCustomSubmit, placeholder: "e.g., 4m" })) : (_jsx(Text, { children: customDuration }))] }), state.testMode === "throughput" && (_jsxs(Box, { children: [_jsxs(Text, { color: activeField === "bulkSize" ? colors.accent : undefined, children: [activeField === "bulkSize" ? "❯ " : " ", "Bulk Size:", " "] }), activeField === "bulkSize" ? (_jsx(TextInput, { value: customBulkSize, onChange: setCustomBulkSize, onSubmit: handleCustomSubmit, placeholder: "e.g., 50" })) : (_jsx(Text, { children: customBulkSize }))] }))] })] }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { color: colors.muted, dimColor: true, children: "Tab to switch fields \u2022 Esc to go back \u2022 Enter to continue" }) })] }));
264
264
  }
265
- return (_jsxs(BorderBox, { title: "Test Presets", children: [_jsx(Box, { flexDirection: "column", marginY: 1, children: _jsxs(Text, { children: ["Select a preset for your ", state.testMode.toUpperCase(), " test:"] }) }), _jsx(SelectInput, { items: items, onSelect: handleSelect, itemComponent: ({ isSelected, label }) => {
265
+ return (_jsxs(BorderBox, { title: "Test Presets", children: [_jsx(Box, { flexDirection: "column", marginY: 1, children: _jsxs(Text, { children: ["Select a preset for your ", state.testMode.toUpperCase(), " test:"] }) }), _jsx(SelectInput, { items: items, onSelect: handleSelect, indicatorComponent: () => null, itemComponent: ({ isSelected, label }) => {
266
266
  const item = items.find((i) => i.label === label);
267
267
  return (_jsxs(Box, { flexDirection: "column", marginY: isSelected ? 1 : 0, children: [_jsxs(Text, { color: isSelected ? colors.accent : undefined, bold: isSelected, children: [isSelected ? "❯ " : " ", label] }), isSelected && item && (_jsxs(Text, { color: colors.muted, dimColor: true, children: [" ", item.description] }))] }));
268
268
  } }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { color: colors.muted, dimColor: true, children: "Esc to go back \u2022 Enter to select" }) })] }));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rulebricks/cli",
3
- "version": "2.0.1",
3
+ "version": "2.0.3",
4
4
  "description": "CLI for deploying and managing private Rulebricks instances",
5
5
  "type": "module",
6
6
  "bin": {
@@ -8,6 +8,7 @@
8
8
  },
9
9
  "scripts": {
10
10
  "build": "tsc",
11
+ "bundle": "esbuild dist/index.js --bundle --platform=node --target=node20 --format=cjs --outfile=bundle/cli.cjs",
11
12
  "dev": "tsc --watch",
12
13
  "start": "node dist/index.js",
13
14
  "lint": "eslint src --ext .ts,.tsx",
@@ -51,15 +52,16 @@
51
52
  "devDependencies": {
52
53
  "@types/node": "^20.14.10",
53
54
  "@types/react": "^19.2.4",
55
+ "esbuild": "^0.20.0",
54
56
  "typescript": "^5.5.3"
55
57
  },
56
58
  "engines": {
57
59
  "node": ">=18"
58
60
  },
59
61
  "pkg": {
60
- "scripts": "dist/**/*.js",
61
62
  "assets": [
62
- "terraform/**/*"
63
+ "terraform/**/*",
64
+ "benchmarks/**/*"
63
65
  ],
64
66
  "targets": [
65
67
  "node20-linux-x64",
@@ -73,6 +75,7 @@
73
75
  "files": [
74
76
  "dist",
75
77
  "terraform",
76
- "templates"
78
+ "templates",
79
+ "benchmarks"
77
80
  ]
78
81
  }