@runtypelabs/cli 2.59.1 → 2.59.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunk-0-T9NWjH.js +3899 -0
- package/dist/chunk-0x7UKC1U.js +17 -0
- package/dist/chunk-2ySxXtcg.js +418 -0
- package/dist/chunk-3An_BRHv.js +501 -0
- package/dist/chunk-6uTxgdiY.js +16218 -0
- package/dist/chunk-87nRnNRG.js +1363 -0
- package/dist/chunk-B3wo8uRx.js +322 -0
- package/dist/chunk-BGp3ZdBy.js +560 -0
- package/dist/chunk-BJQv9Elp.js +107 -0
- package/dist/chunk-BOpO6xE-.js +6053 -0
- package/dist/chunk-BQXXCL8B.js +102 -0
- package/dist/chunk-BV-8isOd.js +34 -0
- package/dist/chunk-BVMhjGVe.js +826 -0
- package/dist/chunk-BW2fg46T.js +141 -0
- package/dist/chunk-BWJdY5Ag.js +36 -0
- package/dist/chunk-BYJOrLhh.js +302 -0
- package/dist/chunk-BYab8EVL.js +127 -0
- package/dist/chunk-BfEKvp7h.js +82 -0
- package/dist/chunk-BhgLSfH6.js +1123 -0
- package/dist/chunk-BirUxTtH.js +296 -0
- package/dist/chunk-BmB6UVZ9.js +1559 -0
- package/dist/chunk-BnWhIqW1.js +109 -0
- package/dist/chunk-Bpfnfk0t.js +123 -0
- package/dist/chunk-BrhgdnM3.js +108 -0
- package/dist/chunk-Bt0XAgX5.js +327 -0
- package/dist/chunk-Bu0FJr6S.js +84 -0
- package/dist/chunk-BvEoKpN3.js +255 -0
- package/dist/chunk-BxNJgYbE.js +28 -0
- package/dist/chunk-C1OFhd7F.js +6131 -0
- package/dist/chunk-C7e7WwbU.js +331 -0
- package/dist/chunk-CDTYP6bN.js +558 -0
- package/dist/chunk-CGDQoqDC.js +24 -0
- package/dist/chunk-CJraEBVd.js +127 -0
- package/dist/chunk-CQrNhOtv.js +40 -0
- package/dist/chunk-CW6AzBH6.js +128 -0
- package/dist/chunk-CXgL4w-9.js +317 -0
- package/dist/chunk-CZrqoPyf.js +233 -0
- package/dist/chunk-CcFCyqbB.js +83 -0
- package/dist/chunk-CdaIvO-y.js +485 -0
- package/dist/chunk-CehSXtgK.js +624 -0
- package/dist/chunk-Ciqkop65.js +218 -0
- package/dist/chunk-CoAWdSiE.js +43 -0
- package/dist/chunk-Crb4y7t4.js +830 -0
- package/dist/chunk-CrlqIfmb.js +337 -0
- package/dist/chunk-Cs4pwCQ-.js +324 -0
- package/dist/chunk-CwM_9y01.js +519 -0
- package/dist/chunk-CyRbIJg8.js +440 -0
- package/dist/chunk-D0OHsyMy.js +212 -0
- package/dist/chunk-D3y2bURs.js +1347 -0
- package/dist/chunk-D4_mJk03.js +2338 -0
- package/dist/chunk-D6C8rned.js +578 -0
- package/dist/chunk-D9ZbUnks.js +11 -0
- package/dist/chunk-DEPmLshX.js +834 -0
- package/dist/chunk-DFj1UPB2.js +330 -0
- package/dist/chunk-DGdFdZVf.js +197 -0
- package/dist/chunk-DGv7X9S_.js +63 -0
- package/dist/chunk-DJQ47WYZ.js +664 -0
- package/dist/chunk-DJw3O61j.js +1974 -0
- package/dist/chunk-DK4FbWVK.js +100 -0
- package/dist/chunk-DSRYhUus.js +993 -0
- package/dist/chunk-DajZ9Eg4.js +299 -0
- package/dist/chunk-Df4dunvU.js +195 -0
- package/dist/chunk-DjNTPhfO.js +481 -0
- package/dist/chunk-Dn8eaJuu.js +1042 -0
- package/dist/chunk-DwDWqSxM.js +637 -0
- package/dist/chunk-Dz7cEtxl.js +84 -0
- package/dist/chunk-E_0bgjrS.js +359 -0
- package/dist/chunk-KPYN2vUp.js +776 -0
- package/dist/chunk-KcqPQJBi.js +86 -0
- package/dist/chunk-N9H0JnoG.js +349 -0
- package/dist/chunk-NONFcXkn.js +728 -0
- package/dist/chunk-PrFjcpmH.js +256 -0
- package/dist/chunk-QSfNYiw9.js +11 -0
- package/dist/chunk-R265bzj9.js +226 -0
- package/dist/chunk-X9V_r6jM.js +166 -0
- package/dist/chunk-ZBtPUtyA.js +55 -0
- package/dist/chunk-ZCTfmOs7.js +4939 -0
- package/dist/chunk-h6oavj0t.js +97 -0
- package/dist/chunk-i7DwkvvD.js +42 -0
- package/dist/chunk-mG-4eZ6S.js +203 -0
- package/dist/chunk-o0ygUbCf.js +313 -0
- package/dist/chunk-ucu5lrDZ.js +5715 -0
- package/dist/index.js +11367 -73789
- package/package.json +4 -4
|
@@ -0,0 +1,776 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { t as isTTY } from "./index.js";
|
|
3
|
+
import { ConfirmPrompt, DataList, EntityCard, MutationResult, React, render, useEffect, useState } from "./chunk-CoAWdSiE.js";
|
|
4
|
+
import { y as neutralizeCsvFormula } from "./chunk-ucu5lrDZ.js";
|
|
5
|
+
import { r as createCliClient } from "./chunk-BW2fg46T.js";
|
|
6
|
+
import { n as ensureAuth } from "./chunk-CJraEBVd.js";
|
|
7
|
+
import { a as printJson, r as getTotalCount, s as printNextCursorHint } from "./chunk-CcFCyqbB.js";
|
|
8
|
+
import { t as readJsonFile } from "./chunk-DGv7X9S_.js";
|
|
9
|
+
import { n as renderMessageContentWithImages, r as detectImageProtocol, t as renderMessageContent } from "./chunk-CW6AzBH6.js";
|
|
10
|
+
import { Command } from "commander";
|
|
11
|
+
import chalk from "chalk";
|
|
12
|
+
import { mkdirSync, writeFileSync } from "fs";
|
|
13
|
+
import { dirname } from "path";
|
|
14
|
+
// src/commands/records.ts
|
|
15
|
+
/**
|
|
16
|
+
* Resolve the owner-key intent of a `records update` invocation into the body field the API expects.
|
|
17
|
+
* `--owner-id` reassigns the owner; `--clear-owner` sends an explicit `null`; omitting both leaves the stored owner untouched.
|
|
18
|
+
* The two are mutually exclusive; an empty `--owner-id` is rejected here rather than sent as a 400.
|
|
19
|
+
* Returns `undefined` when neither flag was passed.
|
|
20
|
+
*/
|
|
21
|
+
function resolveOwnerIdUpdate(options) {
|
|
22
|
+
if (options.clearOwner && options.ownerId !== void 0) throw new Error("--owner-id and --clear-owner are mutually exclusive");
|
|
23
|
+
if (options.clearOwner) return { ownerId: null };
|
|
24
|
+
if (options.ownerId === void 0) return void 0;
|
|
25
|
+
if (options.ownerId.trim() === "") throw new Error("--owner-id cannot be empty; use --clear-owner to remove the owner");
|
|
26
|
+
return { ownerId: options.ownerId };
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Print a record's detail + any messages to stdout (the non-Ink path). When
|
|
30
|
+
* `imageProtocol` is set (interactive, image-capable terminal), image parts in
|
|
31
|
+
* message content are rendered inline; otherwise every part collapses to a text
|
|
32
|
+
* marker. Records with no `messages` print exactly as before (id/name/type/meta).
|
|
33
|
+
*/
|
|
34
|
+
function printRecordDetail(data, imageProtocol) {
|
|
35
|
+
console.log(` ID: ${data.id}`);
|
|
36
|
+
console.log(` Name: ${data.name}`);
|
|
37
|
+
console.log(` Type: ${data.type}`);
|
|
38
|
+
if (data.ownerId) console.log(` Owner: ${data.ownerId}`);
|
|
39
|
+
if (data.metadata) console.log(` Metadata: ${JSON.stringify(data.metadata, null, 2)}`);
|
|
40
|
+
const messages = data.messages ?? [];
|
|
41
|
+
if (messages.length === 0) return;
|
|
42
|
+
console.log(chalk.cyan(`\nMessages (${messages.length}):`));
|
|
43
|
+
for (const msg of messages) {
|
|
44
|
+
const role = typeof msg.role === "string" ? msg.role : "message";
|
|
45
|
+
const rolePrefix = ` ${(role === "user" ? chalk.blue : role === "assistant" ? chalk.green : chalk.gray)(role)}: `;
|
|
46
|
+
if (imageProtocol) {
|
|
47
|
+
const { text, images } = renderMessageContentWithImages(msg.content, imageProtocol);
|
|
48
|
+
process.stdout.write(`${rolePrefix}${text}\n`);
|
|
49
|
+
for (const seq of images) process.stdout.write(`${seq}\n`);
|
|
50
|
+
} else console.log(`${rolePrefix}${renderMessageContent(msg.content)}`);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
const recordsCommand = new Command("records").description("Manage records");
|
|
54
|
+
recordsCommand.command("list").description("List all records").option("--type <type>", "Filter by record type").option("--owner-id <id>", "Filter by owner key (the indexed owner_id column)").option("--limit <n>", "Limit number of results", "20").option("--cursor <cursor>", "Pagination cursor for the next page").option("--json", "Output as JSON").option("--tty", "Force TTY mode").option("--no-tty", "Force non-TTY mode").action(async (options) => {
|
|
55
|
+
const apiKey = await ensureAuth();
|
|
56
|
+
if (!apiKey) return;
|
|
57
|
+
const client = createCliClient(apiKey);
|
|
58
|
+
const params = { limit: options.limit };
|
|
59
|
+
if (options.type) params.type = options.type;
|
|
60
|
+
if (options.ownerId) params.ownerId = options.ownerId;
|
|
61
|
+
if (options.cursor) params.cursor = options.cursor;
|
|
62
|
+
if (!isTTY(options) || options.json) {
|
|
63
|
+
try {
|
|
64
|
+
const data = await client.get("/records", params);
|
|
65
|
+
if (options.json) {
|
|
66
|
+
printJson(data);
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
const records = data.data ?? [];
|
|
70
|
+
if (records.length === 0) {
|
|
71
|
+
console.log("No records found");
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
console.log("Your Records:");
|
|
75
|
+
for (const record of records) {
|
|
76
|
+
console.log(` ${record.id} - ${record.name} (${record.type})`);
|
|
77
|
+
if (record.metadata && Object.keys(record.metadata).length > 0) console.log(` ${JSON.stringify(record.metadata)}`);
|
|
78
|
+
}
|
|
79
|
+
const total = getTotalCount(data.pagination);
|
|
80
|
+
if (total !== void 0) console.log(`\n Total: ${total} records`);
|
|
81
|
+
printNextCursorHint("runtype records list", data.pagination);
|
|
82
|
+
} catch (error) {
|
|
83
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
84
|
+
console.error(chalk.red(`Failed to fetch records: ${message}`));
|
|
85
|
+
process.exit(1);
|
|
86
|
+
}
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
const App = () => {
|
|
90
|
+
const [items, setItems] = useState(null);
|
|
91
|
+
const [error, setError] = useState(null);
|
|
92
|
+
const [total, setTotal] = useState();
|
|
93
|
+
useEffect(() => {
|
|
94
|
+
client.get("/records", params).then((res) => {
|
|
95
|
+
setItems(res.data ?? []);
|
|
96
|
+
setTotal(getTotalCount(res.pagination));
|
|
97
|
+
}).catch((err) => setError(err instanceof Error ? err : new Error(String(err))));
|
|
98
|
+
}, []);
|
|
99
|
+
return React.createElement(DataList, {
|
|
100
|
+
title: "Your Records",
|
|
101
|
+
items,
|
|
102
|
+
error,
|
|
103
|
+
loading: items === null && error === null,
|
|
104
|
+
total,
|
|
105
|
+
renderCard: (item) => {
|
|
106
|
+
const record = item;
|
|
107
|
+
return React.createElement(EntityCard, { fields: [
|
|
108
|
+
{
|
|
109
|
+
label: "ID",
|
|
110
|
+
value: record.id,
|
|
111
|
+
color: "green"
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
label: "Name",
|
|
115
|
+
value: record.name
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
label: "Type",
|
|
119
|
+
value: record.type
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
label: "Metadata",
|
|
123
|
+
value: record.metadata && Object.keys(record.metadata).length > 0 ? JSON.stringify(record.metadata) : null,
|
|
124
|
+
color: "gray"
|
|
125
|
+
}
|
|
126
|
+
] });
|
|
127
|
+
},
|
|
128
|
+
emptyMessage: "No records found"
|
|
129
|
+
});
|
|
130
|
+
};
|
|
131
|
+
const { waitUntilExit } = render(React.createElement(App));
|
|
132
|
+
await waitUntilExit();
|
|
133
|
+
});
|
|
134
|
+
recordsCommand.command("get <id>").description("Get record details").option("--json", "Output as JSON").option("--tty", "Force TTY mode").option("--no-tty", "Force non-TTY mode").option("--no-images", "Disable inline image rendering even in supported terminals").action(async (id, options) => {
|
|
135
|
+
const apiKey = await ensureAuth();
|
|
136
|
+
if (!apiKey) return;
|
|
137
|
+
const client = createCliClient(apiKey);
|
|
138
|
+
const interactive = isTTY(options) && !options.json;
|
|
139
|
+
const imageProtocol = interactive && options.images !== false ? detectImageProtocol() : null;
|
|
140
|
+
if (!interactive || imageProtocol) {
|
|
141
|
+
try {
|
|
142
|
+
const data = await client.get(`/records/${id}`);
|
|
143
|
+
if (options.json) {
|
|
144
|
+
printJson(data);
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
printRecordDetail(data, imageProtocol);
|
|
148
|
+
} catch (error) {
|
|
149
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
150
|
+
console.error(chalk.red(`Failed to fetch record: ${message}`));
|
|
151
|
+
process.exit(1);
|
|
152
|
+
}
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
const App = () => {
|
|
156
|
+
const [items, setItems] = useState(null);
|
|
157
|
+
const [error, setError] = useState(null);
|
|
158
|
+
useEffect(() => {
|
|
159
|
+
client.get(`/records/${id}`).then((res) => setItems([res])).catch((err) => setError(err instanceof Error ? err : new Error(String(err))));
|
|
160
|
+
}, []);
|
|
161
|
+
return React.createElement(DataList, {
|
|
162
|
+
title: "Record",
|
|
163
|
+
items,
|
|
164
|
+
error,
|
|
165
|
+
loading: items === null && error === null,
|
|
166
|
+
renderCard: (item) => {
|
|
167
|
+
const record = item;
|
|
168
|
+
const messages = record.messages ?? [];
|
|
169
|
+
const fields = [
|
|
170
|
+
{
|
|
171
|
+
label: "ID",
|
|
172
|
+
value: record.id,
|
|
173
|
+
color: "green"
|
|
174
|
+
},
|
|
175
|
+
{
|
|
176
|
+
label: "Name",
|
|
177
|
+
value: record.name
|
|
178
|
+
},
|
|
179
|
+
{
|
|
180
|
+
label: "Type",
|
|
181
|
+
value: record.type
|
|
182
|
+
},
|
|
183
|
+
{
|
|
184
|
+
label: "Owner",
|
|
185
|
+
value: record.ownerId ?? null
|
|
186
|
+
},
|
|
187
|
+
{
|
|
188
|
+
label: "Metadata",
|
|
189
|
+
value: record.metadata ? JSON.stringify(record.metadata) : null
|
|
190
|
+
}
|
|
191
|
+
];
|
|
192
|
+
if (messages.length > 0) fields.push({
|
|
193
|
+
label: `Messages (${messages.length})`,
|
|
194
|
+
value: messages.map((m) => `${m.role ?? "message"}: ${renderMessageContent(m.content)}`).join("\n")
|
|
195
|
+
});
|
|
196
|
+
return React.createElement(EntityCard, { fields });
|
|
197
|
+
}
|
|
198
|
+
});
|
|
199
|
+
};
|
|
200
|
+
const { waitUntilExit } = render(React.createElement(App));
|
|
201
|
+
await waitUntilExit();
|
|
202
|
+
});
|
|
203
|
+
recordsCommand.command("results <id>").description("Get step-level execution results for a record").option("--flow-id <flowId>", "Filter by flow ID").option("--batch-id <batchId>", "Filter by batch ID").option("--status <status>", "Filter by status").option("--limit <n>", "Limit number of results").option("--offset <n>", "Offset for pagination").option("--json", "Output as JSON").option("--tty", "Force TTY mode").option("--no-tty", "Force non-TTY mode").action(async (id, options) => {
|
|
204
|
+
const apiKey = await ensureAuth();
|
|
205
|
+
if (!apiKey) return;
|
|
206
|
+
const client = createCliClient(apiKey);
|
|
207
|
+
const params = {
|
|
208
|
+
...options.flowId ? { flowId: options.flowId } : {},
|
|
209
|
+
...options.batchId ? { batchId: options.batchId } : {},
|
|
210
|
+
...options.status ? { status: options.status } : {},
|
|
211
|
+
...options.limit ? { limit: parseInt(options.limit, 10) } : {},
|
|
212
|
+
...options.offset ? { offset: parseInt(options.offset, 10) } : {}
|
|
213
|
+
};
|
|
214
|
+
if (!isTTY(options) || options.json) {
|
|
215
|
+
try {
|
|
216
|
+
const data = await client.records.getStepResults(id, params);
|
|
217
|
+
if (options.json) {
|
|
218
|
+
printJson(data);
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
const results = data.data ?? [];
|
|
222
|
+
if (results.length === 0) {
|
|
223
|
+
console.log(chalk.yellow("No step results found"));
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
for (const r of results) console.log(` ${chalk.green(r.id)} ${r.stepName} (${r.stepType}) ${r.status}` + (r.modelUsed ? ` ${r.modelUsed}` : ""));
|
|
227
|
+
} catch (error) {
|
|
228
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
229
|
+
console.error(chalk.red(`Failed to fetch step results: ${message}`));
|
|
230
|
+
process.exit(1);
|
|
231
|
+
}
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
const App = () => {
|
|
235
|
+
const [items, setItems] = useState(null);
|
|
236
|
+
const [error, setError] = useState(null);
|
|
237
|
+
useEffect(() => {
|
|
238
|
+
client.records.getStepResults(id, params).then((res) => setItems(res.data ?? [])).catch((err) => setError(err instanceof Error ? err : new Error(String(err))));
|
|
239
|
+
}, []);
|
|
240
|
+
return React.createElement(DataList, {
|
|
241
|
+
title: "Step results",
|
|
242
|
+
items,
|
|
243
|
+
error,
|
|
244
|
+
loading: items === null && error === null,
|
|
245
|
+
renderCard: (item) => {
|
|
246
|
+
const r = item;
|
|
247
|
+
return React.createElement(EntityCard, { fields: [
|
|
248
|
+
{
|
|
249
|
+
label: "ID",
|
|
250
|
+
value: r.id,
|
|
251
|
+
color: "green"
|
|
252
|
+
},
|
|
253
|
+
{
|
|
254
|
+
label: "Step",
|
|
255
|
+
value: `${r.stepName} (${r.stepType})`
|
|
256
|
+
},
|
|
257
|
+
{
|
|
258
|
+
label: "Status",
|
|
259
|
+
value: r.status
|
|
260
|
+
},
|
|
261
|
+
{
|
|
262
|
+
label: "Model",
|
|
263
|
+
value: r.modelUsed ?? null
|
|
264
|
+
},
|
|
265
|
+
{
|
|
266
|
+
label: "Duration",
|
|
267
|
+
value: r.durationMs !== null ? `${r.durationMs}ms` : null
|
|
268
|
+
}
|
|
269
|
+
] });
|
|
270
|
+
}
|
|
271
|
+
});
|
|
272
|
+
};
|
|
273
|
+
const { waitUntilExit } = render(React.createElement(App));
|
|
274
|
+
await waitUntilExit();
|
|
275
|
+
});
|
|
276
|
+
recordsCommand.command("costs <id>").description("Get aggregated cost breakdown for a record").option("--json", "Output as JSON").option("--tty", "Force TTY mode").option("--no-tty", "Force non-TTY mode").action(async (id, options) => {
|
|
277
|
+
const apiKey = await ensureAuth();
|
|
278
|
+
if (!apiKey) return;
|
|
279
|
+
const client = createCliClient(apiKey);
|
|
280
|
+
if (!isTTY(options) || options.json) {
|
|
281
|
+
try {
|
|
282
|
+
const data = await client.records.getCosts(id);
|
|
283
|
+
if (options.json) {
|
|
284
|
+
printJson(data);
|
|
285
|
+
return;
|
|
286
|
+
}
|
|
287
|
+
console.log(` Total cost: $${data.totalCost}`);
|
|
288
|
+
console.log(` Total tokens: ${data.totalTokens}`);
|
|
289
|
+
console.log(` Executions: ${data.executionCount}`);
|
|
290
|
+
if (data.modelBreakdown.length > 0) {
|
|
291
|
+
console.log(" By model:");
|
|
292
|
+
for (const m of data.modelBreakdown) console.log(` ${m.model}: $${m.cost} ${m.tokens} tokens ${m.count} calls`);
|
|
293
|
+
}
|
|
294
|
+
} catch (error) {
|
|
295
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
296
|
+
console.error(chalk.red(`Failed to fetch record costs: ${message}`));
|
|
297
|
+
process.exit(1);
|
|
298
|
+
}
|
|
299
|
+
return;
|
|
300
|
+
}
|
|
301
|
+
const App = () => {
|
|
302
|
+
const [items, setItems] = useState(null);
|
|
303
|
+
const [error, setError] = useState(null);
|
|
304
|
+
useEffect(() => {
|
|
305
|
+
client.records.getCosts(id).then((res) => setItems([res])).catch((err) => setError(err instanceof Error ? err : new Error(String(err))));
|
|
306
|
+
}, []);
|
|
307
|
+
return React.createElement(DataList, {
|
|
308
|
+
title: "Record costs",
|
|
309
|
+
items,
|
|
310
|
+
error,
|
|
311
|
+
loading: items === null && error === null,
|
|
312
|
+
renderCard: (item) => {
|
|
313
|
+
const c = item;
|
|
314
|
+
return React.createElement(EntityCard, { fields: [
|
|
315
|
+
{
|
|
316
|
+
label: "Total cost",
|
|
317
|
+
value: `$${c.totalCost}`,
|
|
318
|
+
color: "green"
|
|
319
|
+
},
|
|
320
|
+
{
|
|
321
|
+
label: "Total tokens",
|
|
322
|
+
value: String(c.totalTokens)
|
|
323
|
+
},
|
|
324
|
+
{
|
|
325
|
+
label: "Executions",
|
|
326
|
+
value: String(c.executionCount)
|
|
327
|
+
},
|
|
328
|
+
{
|
|
329
|
+
label: "Models",
|
|
330
|
+
value: c.modelBreakdown.map((m) => `${m.model} ($${m.cost})`).join(", ") || null
|
|
331
|
+
}
|
|
332
|
+
] });
|
|
333
|
+
}
|
|
334
|
+
});
|
|
335
|
+
};
|
|
336
|
+
const { waitUntilExit } = render(React.createElement(App));
|
|
337
|
+
await waitUntilExit();
|
|
338
|
+
});
|
|
339
|
+
recordsCommand.command("create").description("Create a new record").requiredOption("-n, --name <name>", "Record name").requiredOption("-t, --type <type>", "Record type").option("-m, --metadata <json>", "Metadata as JSON string").option("--owner-id <id>", "Owner key for the record (the indexed owner_id column)").option("--json", "Output as JSON").option("--tty", "Force TTY mode").option("--no-tty", "Force non-TTY mode").action(async (options) => {
|
|
340
|
+
const apiKey = await ensureAuth();
|
|
341
|
+
if (!apiKey) return;
|
|
342
|
+
const client = createCliClient(apiKey);
|
|
343
|
+
let metadata = {};
|
|
344
|
+
if (options.metadata) try {
|
|
345
|
+
metadata = JSON.parse(options.metadata);
|
|
346
|
+
} catch {
|
|
347
|
+
console.error(chalk.red("Invalid JSON in metadata"));
|
|
348
|
+
process.exit(1);
|
|
349
|
+
}
|
|
350
|
+
const ownerFields = options.ownerId !== void 0 ? { ownerId: options.ownerId } : {};
|
|
351
|
+
if (!isTTY(options) || options.json) {
|
|
352
|
+
try {
|
|
353
|
+
const data = await client.post("/records", {
|
|
354
|
+
name: options.name,
|
|
355
|
+
type: options.type,
|
|
356
|
+
...ownerFields,
|
|
357
|
+
metadata
|
|
358
|
+
});
|
|
359
|
+
if (options.json) {
|
|
360
|
+
printJson(data);
|
|
361
|
+
return;
|
|
362
|
+
}
|
|
363
|
+
console.log(` ID: ${data.id}`);
|
|
364
|
+
console.log(` Name: ${data.name}`);
|
|
365
|
+
console.log(` Type: ${data.type}`);
|
|
366
|
+
} catch (error) {
|
|
367
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
368
|
+
console.error(chalk.red(`Failed to create record: ${message}`));
|
|
369
|
+
process.exit(1);
|
|
370
|
+
}
|
|
371
|
+
return;
|
|
372
|
+
}
|
|
373
|
+
const App = () => {
|
|
374
|
+
const [loading, setLoading] = useState(true);
|
|
375
|
+
const [success, setSuccess] = useState(null);
|
|
376
|
+
const [error, setError] = useState(null);
|
|
377
|
+
const [result, setResult] = useState(null);
|
|
378
|
+
useEffect(() => {
|
|
379
|
+
client.post("/records", {
|
|
380
|
+
name: options.name,
|
|
381
|
+
type: options.type,
|
|
382
|
+
...ownerFields,
|
|
383
|
+
metadata
|
|
384
|
+
}).then((data) => {
|
|
385
|
+
setResult(data);
|
|
386
|
+
setSuccess(true);
|
|
387
|
+
setLoading(false);
|
|
388
|
+
}).catch((err) => {
|
|
389
|
+
setError(err instanceof Error ? err : new Error(String(err)));
|
|
390
|
+
setSuccess(false);
|
|
391
|
+
setLoading(false);
|
|
392
|
+
});
|
|
393
|
+
}, []);
|
|
394
|
+
return React.createElement(MutationResult, {
|
|
395
|
+
loading,
|
|
396
|
+
loadingLabel: "Creating record...",
|
|
397
|
+
success,
|
|
398
|
+
successMessage: "Record created",
|
|
399
|
+
error,
|
|
400
|
+
result: result ? React.createElement(EntityCard, { fields: [
|
|
401
|
+
{
|
|
402
|
+
label: "ID",
|
|
403
|
+
value: result.id,
|
|
404
|
+
color: "green"
|
|
405
|
+
},
|
|
406
|
+
{
|
|
407
|
+
label: "Name",
|
|
408
|
+
value: result.name
|
|
409
|
+
},
|
|
410
|
+
{
|
|
411
|
+
label: "Type",
|
|
412
|
+
value: result.type
|
|
413
|
+
}
|
|
414
|
+
] }) : void 0
|
|
415
|
+
});
|
|
416
|
+
};
|
|
417
|
+
const { waitUntilExit } = render(React.createElement(App));
|
|
418
|
+
await waitUntilExit();
|
|
419
|
+
});
|
|
420
|
+
recordsCommand.command("delete <id>").description("Delete a record").option("--tty", "Force TTY mode").option("--no-tty", "Force non-TTY mode").action(async (id, options) => {
|
|
421
|
+
const apiKey = await ensureAuth();
|
|
422
|
+
if (!apiKey) return;
|
|
423
|
+
const client = createCliClient(apiKey);
|
|
424
|
+
if (!isTTY(options)) {
|
|
425
|
+
try {
|
|
426
|
+
await client.records.delete(id);
|
|
427
|
+
console.log(JSON.stringify({
|
|
428
|
+
status: "deleted",
|
|
429
|
+
id
|
|
430
|
+
}, null, 2));
|
|
431
|
+
} catch (error) {
|
|
432
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
433
|
+
console.error(chalk.red(`Failed to delete record: ${message}`));
|
|
434
|
+
process.exit(1);
|
|
435
|
+
}
|
|
436
|
+
return;
|
|
437
|
+
}
|
|
438
|
+
const App = () => {
|
|
439
|
+
const [confirmed, setConfirmed] = useState(null);
|
|
440
|
+
const [loading, setLoading] = useState(false);
|
|
441
|
+
const [success, setSuccess] = useState(null);
|
|
442
|
+
const [error, setError] = useState(null);
|
|
443
|
+
useEffect(() => {
|
|
444
|
+
if (confirmed !== true) return void 0;
|
|
445
|
+
setLoading(true);
|
|
446
|
+
client.delete(`/records/${id}`).then(() => {
|
|
447
|
+
setSuccess(true);
|
|
448
|
+
setLoading(false);
|
|
449
|
+
}).catch((err) => {
|
|
450
|
+
setError(err instanceof Error ? err : new Error(String(err)));
|
|
451
|
+
setSuccess(false);
|
|
452
|
+
setLoading(false);
|
|
453
|
+
});
|
|
454
|
+
}, [confirmed]);
|
|
455
|
+
if (confirmed === null) return React.createElement(ConfirmPrompt, {
|
|
456
|
+
message: `Delete record ${id}?`,
|
|
457
|
+
onConfirm: (val) => setConfirmed(val)
|
|
458
|
+
});
|
|
459
|
+
if (confirmed === false) return React.createElement(MutationResult, {
|
|
460
|
+
loading: false,
|
|
461
|
+
success: true,
|
|
462
|
+
successMessage: "Cancelled",
|
|
463
|
+
error: null
|
|
464
|
+
});
|
|
465
|
+
return React.createElement(MutationResult, {
|
|
466
|
+
loading,
|
|
467
|
+
loadingLabel: "Deleting record...",
|
|
468
|
+
success,
|
|
469
|
+
successMessage: "Record deleted",
|
|
470
|
+
error
|
|
471
|
+
});
|
|
472
|
+
};
|
|
473
|
+
const { waitUntilExit } = render(React.createElement(App));
|
|
474
|
+
await waitUntilExit();
|
|
475
|
+
});
|
|
476
|
+
recordsCommand.command("update <id>").description("Update a record (name, type, and/or metadata)").option("-n, --name <name>", "New record name").option("-t, --type <type>", "New record type").option("-m, --metadata <json>", "Metadata as JSON string").option("--owner-id <id>", "Reassign the owner key (the indexed owner_id column)").option("--clear-owner", "Clear the owner key (sends an explicit null)").option("--from-file <path>", "Read the full update payload from a JSON file").option("--json", "Output as JSON").option("--tty", "Force TTY mode").option("--no-tty", "Force non-TTY mode").action(async (id, options) => {
|
|
477
|
+
const apiKey = await ensureAuth();
|
|
478
|
+
if (!apiKey) return;
|
|
479
|
+
const client = createCliClient(apiKey);
|
|
480
|
+
let body = {};
|
|
481
|
+
if (options.fromFile) body = readJsonFile(options.fromFile);
|
|
482
|
+
if (options.name !== void 0) body.name = options.name;
|
|
483
|
+
if (options.type !== void 0) body.type = options.type;
|
|
484
|
+
if (options.metadata !== void 0) try {
|
|
485
|
+
body.metadata = JSON.parse(options.metadata);
|
|
486
|
+
} catch {
|
|
487
|
+
console.error(chalk.red("Invalid JSON in metadata"));
|
|
488
|
+
process.exit(1);
|
|
489
|
+
}
|
|
490
|
+
try {
|
|
491
|
+
const ownerUpdate = resolveOwnerIdUpdate(options);
|
|
492
|
+
if (ownerUpdate) body.ownerId = ownerUpdate.ownerId;
|
|
493
|
+
} catch (error) {
|
|
494
|
+
console.error(chalk.red(error instanceof Error ? error.message : String(error)));
|
|
495
|
+
process.exit(1);
|
|
496
|
+
}
|
|
497
|
+
if (Object.keys(body).length === 0) {
|
|
498
|
+
console.error(chalk.red("Error: provide at least one of --name, --type, --metadata, --owner-id, --clear-owner, or --from-file"));
|
|
499
|
+
process.exit(1);
|
|
500
|
+
}
|
|
501
|
+
if (!isTTY(options) || options.json) {
|
|
502
|
+
try {
|
|
503
|
+
const data = await client.put(`/records/${id}`, body);
|
|
504
|
+
if (options.json) {
|
|
505
|
+
printJson(data);
|
|
506
|
+
return;
|
|
507
|
+
}
|
|
508
|
+
console.log(chalk.green("Record updated"));
|
|
509
|
+
console.log(` ID: ${chalk.green(data.id)}`);
|
|
510
|
+
console.log(` Name: ${data.name}`);
|
|
511
|
+
console.log(` Type: ${data.type}`);
|
|
512
|
+
console.log(` Owner: ${data.ownerId ?? "(none)"}`);
|
|
513
|
+
if (data.metadata) console.log(` Metadata: ${JSON.stringify(data.metadata)}`);
|
|
514
|
+
} catch (error) {
|
|
515
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
516
|
+
console.error(chalk.red(`Failed to update record: ${message}`));
|
|
517
|
+
process.exit(1);
|
|
518
|
+
}
|
|
519
|
+
return;
|
|
520
|
+
}
|
|
521
|
+
const App = () => {
|
|
522
|
+
const [loading, setLoading] = useState(true);
|
|
523
|
+
const [success, setSuccess] = useState(null);
|
|
524
|
+
const [error, setError] = useState(null);
|
|
525
|
+
const [resultNode, setResultNode] = useState(void 0);
|
|
526
|
+
useEffect(() => {
|
|
527
|
+
const run = async () => {
|
|
528
|
+
try {
|
|
529
|
+
const data = await client.put(`/records/${id}`, body);
|
|
530
|
+
setResultNode(React.createElement(EntityCard, { fields: [
|
|
531
|
+
{
|
|
532
|
+
label: "ID",
|
|
533
|
+
value: data.id,
|
|
534
|
+
color: "green"
|
|
535
|
+
},
|
|
536
|
+
{
|
|
537
|
+
label: "Name",
|
|
538
|
+
value: data.name
|
|
539
|
+
},
|
|
540
|
+
{
|
|
541
|
+
label: "Type",
|
|
542
|
+
value: data.type
|
|
543
|
+
},
|
|
544
|
+
{
|
|
545
|
+
label: "Owner",
|
|
546
|
+
value: data.ownerId ?? null
|
|
547
|
+
},
|
|
548
|
+
{
|
|
549
|
+
label: "Metadata",
|
|
550
|
+
value: data.metadata ? JSON.stringify(data.metadata) : null,
|
|
551
|
+
color: "gray"
|
|
552
|
+
}
|
|
553
|
+
] }));
|
|
554
|
+
setSuccess(true);
|
|
555
|
+
setLoading(false);
|
|
556
|
+
} catch (err) {
|
|
557
|
+
setError(err instanceof Error ? err : new Error(String(err)));
|
|
558
|
+
setSuccess(false);
|
|
559
|
+
setLoading(false);
|
|
560
|
+
}
|
|
561
|
+
};
|
|
562
|
+
run();
|
|
563
|
+
}, []);
|
|
564
|
+
return React.createElement(MutationResult, {
|
|
565
|
+
loading,
|
|
566
|
+
loadingLabel: "Updating record...",
|
|
567
|
+
success,
|
|
568
|
+
successMessage: "Record updated",
|
|
569
|
+
error,
|
|
570
|
+
result: resultNode
|
|
571
|
+
});
|
|
572
|
+
};
|
|
573
|
+
const { waitUntilExit } = render(React.createElement(App));
|
|
574
|
+
await waitUntilExit();
|
|
575
|
+
});
|
|
576
|
+
recordsCommand.command("bulk-delete").description("Bulk delete records (payload via --from-file)").requiredOption("--from-file <path>", "JSON file with the bulk-delete payload (e.g. { \"ids\": [...] })").option("--json", "Output as JSON").option("--tty", "Force TTY mode").option("--no-tty", "Force non-TTY mode").action(async (options) => {
|
|
577
|
+
const apiKey = await ensureAuth();
|
|
578
|
+
if (!apiKey) return;
|
|
579
|
+
const client = createCliClient(apiKey);
|
|
580
|
+
const body = readJsonFile(options.fromFile);
|
|
581
|
+
if (!isTTY(options) || options.json) {
|
|
582
|
+
try {
|
|
583
|
+
const data = await client.post("/records/bulk-delete", body);
|
|
584
|
+
if (options.json) {
|
|
585
|
+
printJson(data);
|
|
586
|
+
return;
|
|
587
|
+
}
|
|
588
|
+
console.log(chalk.green(`Deleted ${data.deleted} record(s)`));
|
|
589
|
+
if (data.deletedIds.length > 0) console.log(` Deleted IDs: ${data.deletedIds.join(", ")}`);
|
|
590
|
+
if (data.notFoundIds && data.notFoundIds.length > 0) console.log(chalk.yellow(` Not found: ${data.notFoundIds.join(", ")}`));
|
|
591
|
+
} catch (error) {
|
|
592
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
593
|
+
console.error(chalk.red(`Failed to bulk delete records: ${message}`));
|
|
594
|
+
process.exit(1);
|
|
595
|
+
}
|
|
596
|
+
return;
|
|
597
|
+
}
|
|
598
|
+
const App = () => {
|
|
599
|
+
const [loading, setLoading] = useState(true);
|
|
600
|
+
const [success, setSuccess] = useState(null);
|
|
601
|
+
const [error, setError] = useState(null);
|
|
602
|
+
const [resultNode, setResultNode] = useState(void 0);
|
|
603
|
+
useEffect(() => {
|
|
604
|
+
const run = async () => {
|
|
605
|
+
try {
|
|
606
|
+
const data = await client.post("/records/bulk-delete", body);
|
|
607
|
+
setResultNode(React.createElement(EntityCard, { fields: [
|
|
608
|
+
{
|
|
609
|
+
label: "Deleted",
|
|
610
|
+
value: String(data.deleted),
|
|
611
|
+
color: "green"
|
|
612
|
+
},
|
|
613
|
+
{
|
|
614
|
+
label: "Deleted IDs",
|
|
615
|
+
value: data.deletedIds.join(", ") || "(none)"
|
|
616
|
+
},
|
|
617
|
+
{
|
|
618
|
+
label: "Not Found",
|
|
619
|
+
value: data.notFoundIds && data.notFoundIds.length > 0 ? data.notFoundIds.join(", ") : null,
|
|
620
|
+
color: "yellow"
|
|
621
|
+
}
|
|
622
|
+
] }));
|
|
623
|
+
setSuccess(true);
|
|
624
|
+
setLoading(false);
|
|
625
|
+
} catch (err) {
|
|
626
|
+
setError(err instanceof Error ? err : new Error(String(err)));
|
|
627
|
+
setSuccess(false);
|
|
628
|
+
setLoading(false);
|
|
629
|
+
}
|
|
630
|
+
};
|
|
631
|
+
run();
|
|
632
|
+
}, []);
|
|
633
|
+
return React.createElement(MutationResult, {
|
|
634
|
+
loading,
|
|
635
|
+
loadingLabel: "Deleting records...",
|
|
636
|
+
success,
|
|
637
|
+
successMessage: "Records deleted",
|
|
638
|
+
error,
|
|
639
|
+
result: resultNode
|
|
640
|
+
});
|
|
641
|
+
};
|
|
642
|
+
const { waitUntilExit } = render(React.createElement(App));
|
|
643
|
+
await waitUntilExit();
|
|
644
|
+
});
|
|
645
|
+
recordsCommand.command("bulk-edit").description("Bulk edit record metadata (payload via --from-file)").requiredOption("--from-file <path>", "JSON file with the bulk-edit payload (recordIds/recordFilter, updates, deleteFields, dryRun, applyToAll)").option("--json", "Output as JSON").option("--tty", "Force TTY mode").option("--no-tty", "Force non-TTY mode").action(async (options) => {
|
|
646
|
+
const apiKey = await ensureAuth();
|
|
647
|
+
if (!apiKey) return;
|
|
648
|
+
const client = createCliClient(apiKey);
|
|
649
|
+
const body = readJsonFile(options.fromFile);
|
|
650
|
+
if (!isTTY(options) || options.json) {
|
|
651
|
+
try {
|
|
652
|
+
const data = await client.post("/records/bulk-edit", body);
|
|
653
|
+
if (options.json) {
|
|
654
|
+
printJson(data);
|
|
655
|
+
return;
|
|
656
|
+
}
|
|
657
|
+
console.log(chalk.green(`Edited ${data.recordIds.length} record(s)`));
|
|
658
|
+
for (const diff of data.data) {
|
|
659
|
+
console.log(` ${chalk.green(diff.id)}`);
|
|
660
|
+
console.log(` before: ${JSON.stringify(diff.before)}`);
|
|
661
|
+
console.log(` after: ${JSON.stringify(diff.after)}`);
|
|
662
|
+
}
|
|
663
|
+
} catch (error) {
|
|
664
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
665
|
+
console.error(chalk.red(`Failed to bulk edit records: ${message}`));
|
|
666
|
+
process.exit(1);
|
|
667
|
+
}
|
|
668
|
+
return;
|
|
669
|
+
}
|
|
670
|
+
const App = () => {
|
|
671
|
+
const [loading, setLoading] = useState(true);
|
|
672
|
+
const [success, setSuccess] = useState(null);
|
|
673
|
+
const [error, setError] = useState(null);
|
|
674
|
+
const [resultNode, setResultNode] = useState(void 0);
|
|
675
|
+
useEffect(() => {
|
|
676
|
+
const run = async () => {
|
|
677
|
+
try {
|
|
678
|
+
const data = await client.post("/records/bulk-edit", body);
|
|
679
|
+
setResultNode(React.createElement(EntityCard, { fields: [{
|
|
680
|
+
label: "Edited",
|
|
681
|
+
value: String(data.recordIds.length),
|
|
682
|
+
color: "green"
|
|
683
|
+
}, {
|
|
684
|
+
label: "Record IDs",
|
|
685
|
+
value: data.recordIds.join(", ") || "(none)"
|
|
686
|
+
}] }));
|
|
687
|
+
setSuccess(true);
|
|
688
|
+
setLoading(false);
|
|
689
|
+
} catch (err) {
|
|
690
|
+
setError(err instanceof Error ? err : new Error(String(err)));
|
|
691
|
+
setSuccess(false);
|
|
692
|
+
setLoading(false);
|
|
693
|
+
}
|
|
694
|
+
};
|
|
695
|
+
run();
|
|
696
|
+
}, []);
|
|
697
|
+
return React.createElement(MutationResult, {
|
|
698
|
+
loading,
|
|
699
|
+
loadingLabel: "Editing records...",
|
|
700
|
+
success,
|
|
701
|
+
successMessage: "Records edited",
|
|
702
|
+
error,
|
|
703
|
+
result: resultNode
|
|
704
|
+
});
|
|
705
|
+
};
|
|
706
|
+
const { waitUntilExit } = render(React.createElement(App));
|
|
707
|
+
await waitUntilExit();
|
|
708
|
+
});
|
|
709
|
+
recordsCommand.command("export").description("Export records to a file").option("--type <type>", "Filter by record type").option("--owner-id <id>", "Filter by owner key (the indexed owner_id column)").option("--limit <n>", "Maximum records to export", "100").option("-o, --output <file>", "Output file path").option("--format <format>", "Export format (json, csv)", "json").action(async (options) => {
|
|
710
|
+
const apiKey = await ensureAuth();
|
|
711
|
+
if (!apiKey) return;
|
|
712
|
+
console.log(chalk.gray("Exporting records..."));
|
|
713
|
+
try {
|
|
714
|
+
const client = createCliClient(apiKey);
|
|
715
|
+
const params = { limit: options.limit };
|
|
716
|
+
if (options.type) params.type = options.type;
|
|
717
|
+
if (options.ownerId) params.ownerId = options.ownerId;
|
|
718
|
+
const records = (await client.get("/records", params)).data ?? [];
|
|
719
|
+
if (records.length === 0) {
|
|
720
|
+
console.log(chalk.yellow("No records to export"));
|
|
721
|
+
return;
|
|
722
|
+
}
|
|
723
|
+
let content;
|
|
724
|
+
if (options.format === "csv") {
|
|
725
|
+
const headers = [
|
|
726
|
+
"id",
|
|
727
|
+
"name",
|
|
728
|
+
"type",
|
|
729
|
+
"ownerId",
|
|
730
|
+
"metadata"
|
|
731
|
+
];
|
|
732
|
+
const rows = records.map((r) => [
|
|
733
|
+
r.id,
|
|
734
|
+
r.name,
|
|
735
|
+
r.type,
|
|
736
|
+
r.ownerId ?? "",
|
|
737
|
+
r.metadata ? JSON.stringify(r.metadata) : ""
|
|
738
|
+
]);
|
|
739
|
+
content = [headers.join(","), ...rows.map((r) => r.map(csvEscape).join(","))].join("\n");
|
|
740
|
+
} else content = JSON.stringify(records, null, 2);
|
|
741
|
+
const filename = options.output || `records-export-${Date.now()}.${options.format === "csv" ? "csv" : "json"}`;
|
|
742
|
+
writeFileSync(filename, content);
|
|
743
|
+
console.log(chalk.green(`Exported ${records.length} records to ${filename}`));
|
|
744
|
+
} catch (error) {
|
|
745
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
746
|
+
console.error(chalk.red(`Failed to export records: ${message}`));
|
|
747
|
+
process.exit(1);
|
|
748
|
+
}
|
|
749
|
+
});
|
|
750
|
+
recordsCommand.command("typegen").description("Generate TypeScript types for your schematized record collections. Writes a .d.ts that augments the @runtypelabs/sdk RecordCollections map so client.records.from(slug) types record metadata. Commit the file and re-run this in CI, diffing against the committed copy (e.g. `runtype records typegen -o runtype-records.d.ts && git diff --exit-code runtype-records.d.ts`), to catch collection-schema drift.").option("-o, --output <file>", "Write the declarations to a file (default: print to stdout)").action(async (options) => {
|
|
751
|
+
const apiKey = await ensureAuth();
|
|
752
|
+
if (!apiKey) return;
|
|
753
|
+
const client = createCliClient(apiKey);
|
|
754
|
+
try {
|
|
755
|
+
const dts = await client.collections.typegen();
|
|
756
|
+
if (options.output) {
|
|
757
|
+
const dir = dirname(options.output);
|
|
758
|
+
if (dir && dir !== ".") mkdirSync(dir, { recursive: true });
|
|
759
|
+
writeFileSync(options.output, dts);
|
|
760
|
+
console.log(chalk.green(`Wrote record collection types to ${options.output}`));
|
|
761
|
+
return;
|
|
762
|
+
}
|
|
763
|
+
process.stdout.write(dts);
|
|
764
|
+
} catch (error) {
|
|
765
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
766
|
+
console.error(chalk.red(`Failed to generate record types: ${message}`));
|
|
767
|
+
process.exit(1);
|
|
768
|
+
}
|
|
769
|
+
});
|
|
770
|
+
function csvEscape(value) {
|
|
771
|
+
const text = neutralizeCsvFormula(value);
|
|
772
|
+
if (text.includes(",") || text.includes("\"") || text.includes("\n")) return `"${text.replace(/"/g, "\"\"")}"`;
|
|
773
|
+
return text;
|
|
774
|
+
}
|
|
775
|
+
export { recordsCommand };
|
|
776
|
+
|