@cargo-ai/cli 1.0.73 → 1.0.75
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/README.md +21 -0
- package/build/commands/ai/agent.js +2 -2
- package/build/commands/ai/chat.js +1 -1
- package/build/commands/ai/document.js +1 -1
- package/build/commands/ai/memory.js +3 -3
- package/build/commands/ai/message.js +2 -2
- package/build/commands/ai/release.d.ts.map +1 -1
- package/build/commands/ai/release.js +3 -3
- package/build/commands/ai/vote.d.ts.map +1 -1
- package/build/commands/ai/vote.js +1 -1
- package/build/commands/auth/index.js +2 -2
- package/build/commands/billing/subscription.d.ts.map +1 -1
- package/build/commands/billing/subscription.js +3 -3
- package/build/commands/billing/usage.d.ts.map +1 -1
- package/build/commands/billing/usage.js +1 -1
- package/build/commands/connection/action.d.ts.map +1 -1
- package/build/commands/connection/action.js +1 -1
- package/build/commands/connection/connector.js +1 -1
- package/build/commands/connection/integration.d.ts.map +1 -1
- package/build/commands/connection/integration.js +14 -0
- package/build/commands/content/library.js +1 -1
- package/build/commands/context/runtime.js +1 -1
- package/build/commands/expression/recipe.d.ts.map +1 -1
- package/build/commands/expression/recipe.js +9 -9
- package/build/commands/mailboxManagement/event.js +1 -1
- package/build/commands/mailboxManagement/mailbox.d.ts.map +1 -1
- package/build/commands/mailboxManagement/mailbox.js +3 -3
- package/build/commands/mailboxManagement/message.js +1 -1
- package/build/commands/mailboxManagement/suppression.js +1 -1
- package/build/commands/mailboxManagement/thread.js +1 -1
- package/build/commands/observability/alert.js +2 -2
- package/build/commands/orchestration/action.d.ts.map +1 -1
- package/build/commands/orchestration/action.js +18 -63
- package/build/commands/orchestration/batch.js +7 -7
- package/build/commands/orchestration/node.js +2 -2
- package/build/commands/orchestration/play.d.ts.map +1 -1
- package/build/commands/orchestration/play.js +9 -9
- package/build/commands/orchestration/record.d.ts.map +1 -1
- package/build/commands/orchestration/record.js +10 -10
- package/build/commands/orchestration/release.js +1 -1
- package/build/commands/orchestration/run.d.ts.map +1 -1
- package/build/commands/orchestration/run.js +14 -14
- package/build/commands/orchestration/span.d.ts.map +1 -1
- package/build/commands/orchestration/span.js +4 -4
- package/build/commands/orchestration/tool.js +2 -2
- package/build/commands/orchestration/trace.d.ts.map +1 -1
- package/build/commands/orchestration/trace.js +2 -2
- package/build/commands/revenueOrganization/capacity.d.ts.map +1 -1
- package/build/commands/revenueOrganization/capacity.js +2 -2
- package/build/commands/revenueOrganization/territory.js +2 -2
- package/build/commands/segmentation/change.d.ts.map +1 -1
- package/build/commands/segmentation/change.js +4 -4
- package/build/commands/segmentation/record.js +1 -1
- package/build/commands/segmentation/segment.js +2 -2
- package/build/commands/start/index.js +1 -1
- package/build/commands/storage/query.js +1 -1
- package/build/commands/storage/record.js +2 -2
- package/build/commands/storage/run.js +2 -2
- package/build/commands/systemOfRecordIntegration/log.js +1 -1
- package/build/commands/systemOfRecordIntegration/systemOfRecord.js +1 -1
- package/build/commands/version.js +1 -1
- package/build/commands/workspaceManagement/folder.d.ts.map +1 -1
- package/build/commands/workspaceManagement/folder.js +1 -1
- package/build/commands/workspaceManagement/role.d.ts.map +1 -1
- package/build/commands/workspaceManagement/role.js +2 -2
- package/build/commands/workspaceManagement/session.js +1 -1
- package/build/commands/workspaceManagement/user.d.ts.map +1 -1
- package/build/commands/workspaceManagement/user.js +2 -2
- package/build/commands/workspaceManagement/workspaces.js +1 -1
- package/build/index.js +52 -9
- package/build/paletteLayout.d.ts +16 -0
- package/build/paletteLayout.d.ts.map +1 -1
- package/build/paletteLayout.js +70 -27
- package/build/utils/booleanOption.d.ts.map +1 -1
- package/build/utils/booleanOption.js +5 -4
- package/package.json +3 -3
- package/build/commands/orchestration/costTable.d.ts +0 -121
- package/build/commands/orchestration/costTable.d.ts.map +0 -1
- package/build/commands/orchestration/costTable.js +0 -363
|
@@ -1,363 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Renders every credits-based action as a pricing table.
|
|
3
|
-
*
|
|
4
|
-
* The catalog is the only place pricing lives, and it moves — providers get
|
|
5
|
-
* added, actions get repriced, tiers get split. Anything that copies those
|
|
6
|
-
* numbers by hand goes stale silently, so the table is generated rather than
|
|
7
|
-
* maintained.
|
|
8
|
-
*
|
|
9
|
-
* Sourced from `orchestration action list` (without a limit), which spans
|
|
10
|
-
* connector and native actions in a single call. Natives are a small set but
|
|
11
|
-
* not a free one — `sendEmail` is 0.1, `modelAsk` 0.5, `fileSearch` 0.02/1k
|
|
12
|
-
* tokens — and a table built from connectors alone silently omits them while
|
|
13
|
-
* claiming to be complete.
|
|
14
|
-
*/
|
|
15
|
-
const DESCRIPTION_MAX_LENGTH = 120;
|
|
16
|
-
/**
|
|
17
|
-
* Natives have no integration of their own. `native` cannot collide with a real
|
|
18
|
-
* integration slug, and it is what `kind` an action object needs to run one:
|
|
19
|
-
* `{"kind":"native","actionSlug":"sendEmail"}`.
|
|
20
|
-
*/
|
|
21
|
-
export const NATIVE_PROVIDER_SLUG = "native";
|
|
22
|
-
export function asNativeCatalogIntegration(actions) {
|
|
23
|
-
return {
|
|
24
|
-
slug: NATIVE_PROVIDER_SLUG,
|
|
25
|
-
name: "Cargo native",
|
|
26
|
-
category: "platform",
|
|
27
|
-
actions: actions ?? {},
|
|
28
|
-
};
|
|
29
|
-
}
|
|
30
|
-
/**
|
|
31
|
-
* Groups the flat action list returned by `orchestration action list` into the
|
|
32
|
-
* integration-grouped shape that `buildCostRows` and `summarize` expect.
|
|
33
|
-
*/
|
|
34
|
-
export function groupActionsByIntegration(actions) {
|
|
35
|
-
const integrationMap = new Map();
|
|
36
|
-
for (const match of actions) {
|
|
37
|
-
const actionKind = match.action.kind;
|
|
38
|
-
const actionSlug = match.action.actionSlug;
|
|
39
|
-
if (actionSlug === undefined) {
|
|
40
|
-
continue;
|
|
41
|
-
}
|
|
42
|
-
let slug;
|
|
43
|
-
let name;
|
|
44
|
-
let category;
|
|
45
|
-
if (actionKind === "native") {
|
|
46
|
-
slug = NATIVE_PROVIDER_SLUG;
|
|
47
|
-
name = "Cargo native";
|
|
48
|
-
category = "platform";
|
|
49
|
-
}
|
|
50
|
-
else if (actionKind === "connector") {
|
|
51
|
-
slug =
|
|
52
|
-
match.action.integrationSlug !== undefined
|
|
53
|
-
? match.action.integrationSlug
|
|
54
|
-
: "";
|
|
55
|
-
name = match.integrationName !== undefined ? match.integrationName : slug;
|
|
56
|
-
category = match.category;
|
|
57
|
-
}
|
|
58
|
-
else {
|
|
59
|
-
continue;
|
|
60
|
-
}
|
|
61
|
-
let integration = integrationMap.get(slug);
|
|
62
|
-
if (integration === undefined) {
|
|
63
|
-
integration = { slug, name, category, actions: {} };
|
|
64
|
-
integrationMap.set(slug, integration);
|
|
65
|
-
}
|
|
66
|
-
if (integration.actions === undefined) {
|
|
67
|
-
integration.actions = {};
|
|
68
|
-
}
|
|
69
|
-
integration.actions[actionSlug] = {
|
|
70
|
-
name: match.name,
|
|
71
|
-
description: match.description,
|
|
72
|
-
credits: match.credits !== undefined ? { costs: match.credits } : undefined,
|
|
73
|
-
};
|
|
74
|
-
}
|
|
75
|
-
return Array.from(integrationMap.values());
|
|
76
|
-
}
|
|
77
|
-
export function buildCostRows(integrations) {
|
|
78
|
-
const rows = [];
|
|
79
|
-
for (const integration of integrations) {
|
|
80
|
-
for (const [actionSlug, action] of Object.entries(integration.actions ?? {})) {
|
|
81
|
-
const costs = action.credits?.costs;
|
|
82
|
-
const [first] = costs ?? [];
|
|
83
|
-
if (costs === undefined || first === undefined) {
|
|
84
|
-
continue;
|
|
85
|
-
}
|
|
86
|
-
const amounts = costs.map((cost) => cost.cost ?? 0);
|
|
87
|
-
const effective = costs.map((cost) => (cost.cost ?? 0) + (cost.fixedCost ?? 0));
|
|
88
|
-
const variants = costs.map((cost) => ({
|
|
89
|
-
when: describeSelector(cost.config?.jsonSchema),
|
|
90
|
-
price: renderPrice(cost),
|
|
91
|
-
}));
|
|
92
|
-
const minCost = Math.min(...amounts);
|
|
93
|
-
const maxCost = Math.max(...amounts);
|
|
94
|
-
rows.push({
|
|
95
|
-
integrationSlug: integration.slug,
|
|
96
|
-
integrationName: integration.name,
|
|
97
|
-
category: integration.category,
|
|
98
|
-
actionSlug,
|
|
99
|
-
name: action.name,
|
|
100
|
-
description: truncate(action.description),
|
|
101
|
-
minCost,
|
|
102
|
-
maxCost,
|
|
103
|
-
sortCost: Math.min(...effective),
|
|
104
|
-
price: costs.length === 1
|
|
105
|
-
? renderPrice(first)
|
|
106
|
-
: renderRange(costs, minCost, maxCost),
|
|
107
|
-
variesBy: costs.length === 1 ? [] : selectorFields(costs),
|
|
108
|
-
variants: costs.length === 1 ? [] : variants,
|
|
109
|
-
});
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
return rows.sort((a, b) => a.sortCost - b.sortCost ||
|
|
113
|
-
a.maxCost - b.maxCost ||
|
|
114
|
-
a.integrationSlug.localeCompare(b.integrationSlug) ||
|
|
115
|
-
a.actionSlug.localeCompare(b.actionSlug));
|
|
116
|
-
}
|
|
117
|
-
export function summarize(integrations) {
|
|
118
|
-
let actions = 0;
|
|
119
|
-
let creditsBasedActions = 0;
|
|
120
|
-
let integrationsWithActions = 0;
|
|
121
|
-
for (const integration of integrations) {
|
|
122
|
-
const entries = Object.values(integration.actions ?? {});
|
|
123
|
-
// The native pseudo-integration is a carrier for platform actions, not a
|
|
124
|
-
// 139th integration. Its actions count; it does not.
|
|
125
|
-
const isNative = integration.slug === NATIVE_PROVIDER_SLUG;
|
|
126
|
-
if (entries.length > 0 && isNative === false) {
|
|
127
|
-
integrationsWithActions += 1;
|
|
128
|
-
}
|
|
129
|
-
actions += entries.length;
|
|
130
|
-
for (const action of entries) {
|
|
131
|
-
if ((action.credits?.costs ?? []).length > 0) {
|
|
132
|
-
creditsBasedActions += 1;
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
return {
|
|
137
|
-
integrations: integrations.filter((integration) => {
|
|
138
|
-
return integration.slug !== NATIVE_PROVIDER_SLUG;
|
|
139
|
-
}).length,
|
|
140
|
-
integrationsWithActions,
|
|
141
|
-
actions,
|
|
142
|
-
creditsBasedActions,
|
|
143
|
-
freeActions: actions - creditsBasedActions,
|
|
144
|
-
};
|
|
145
|
-
}
|
|
146
|
-
export function renderCostTable(rows, summary, opts) {
|
|
147
|
-
const lines = [];
|
|
148
|
-
const filter = opts.filter;
|
|
149
|
-
const isFiltered = filter !== undefined &&
|
|
150
|
-
(filter.category !== undefined || filter.integration !== undefined);
|
|
151
|
-
lines.push("# Credits cost table");
|
|
152
|
-
lines.push("");
|
|
153
|
-
if (isFiltered) {
|
|
154
|
-
const filterDesc = filter.category !== undefined && filter.integration !== undefined
|
|
155
|
-
? `category \`${filter.category}\` and integration \`${filter.integration}\``
|
|
156
|
-
: filter.category !== undefined
|
|
157
|
-
? `category \`${filter.category}\``
|
|
158
|
-
: `integration \`${filter.integration}\``;
|
|
159
|
-
lines.push(`Filtered by ${filterDesc}: ${String(summary.creditsBasedActions)} credits-based actions ` +
|
|
160
|
-
`out of ${String(summary.actions)} total across ${String(summary.integrationsWithActions)} of ` +
|
|
161
|
-
`${String(summary.integrations)} integrations, sorted by cost. ` +
|
|
162
|
-
`The other ${String(summary.freeActions)} actions in this subset cost no credits. ` +
|
|
163
|
-
"Run without filters for the full catalog, including Cargo's native actions.");
|
|
164
|
-
}
|
|
165
|
-
else {
|
|
166
|
-
lines.push(`Every credits-based action Cargo can run — ${String(summary.creditsBasedActions)} of the ` +
|
|
167
|
-
`${String(summary.actions)} actions exposed by ${String(summary.integrationsWithActions)} of the catalog's ` +
|
|
168
|
-
`${String(summary.integrations)} integrations, plus Cargo's own native actions — sorted by cost. The other ${String(summary.freeActions)} actions ` +
|
|
169
|
-
"(CRM writes, sequencer pushes, Slack sends, …) cost no credits; list them with " +
|
|
170
|
-
"`cargo-ai connection integration get <slug>`.");
|
|
171
|
-
}
|
|
172
|
-
lines.push("");
|
|
173
|
-
if (isFiltered === false) {
|
|
174
|
-
lines.push("Rows whose provider is `native` are Cargo's own platform actions, run as " +
|
|
175
|
-
'`{"kind":"native","actionSlug":"<action>"}` with no integration; every other row ' +
|
|
176
|
-
'runs as `{"kind":"connector","integrationSlug":"<provider>","actionSlug":"<action>"}`.');
|
|
177
|
-
lines.push("");
|
|
178
|
-
}
|
|
179
|
-
lines.push("**Auto-generated. Do not edit by hand** — regenerate with " +
|
|
180
|
-
"`cargo-ai orchestration action cost-table --raw > cargo-gtm/references/credits-cost-table.md`.");
|
|
181
|
-
lines.push("");
|
|
182
|
-
lines.push(`Generated: ${opts.generatedAt}`);
|
|
183
|
-
lines.push("");
|
|
184
|
-
lines.push("| Cost | Provider | Category | Action | Description |");
|
|
185
|
-
lines.push("|---|---|---|---|---|");
|
|
186
|
-
for (const row of rows) {
|
|
187
|
-
lines.push(`| ${row.price} | \`${row.integrationSlug}\` | ${row.category ?? "—"} | ` +
|
|
188
|
-
`\`${row.actionSlug}\` | ${escapeCell(row.description ?? "")} |`);
|
|
189
|
-
}
|
|
190
|
-
const variable = rows.filter((row) => row.variants.length > 0);
|
|
191
|
-
if (variable.length > 0) {
|
|
192
|
-
lines.push("");
|
|
193
|
-
lines.push("## Actions whose price depends on config");
|
|
194
|
-
lines.push("");
|
|
195
|
-
lines.push("These bill differently depending on how the node is configured, so the range above is not a quote. " +
|
|
196
|
-
"Pick the row that matches the config you are about to run.");
|
|
197
|
-
lines.push("");
|
|
198
|
-
for (const row of variable) {
|
|
199
|
-
lines.push(`### \`${row.integrationSlug}.${row.actionSlug}\` — varies by ${row.variesBy
|
|
200
|
-
.map((field) => `\`${field}\``)
|
|
201
|
-
.join(", ")}`);
|
|
202
|
-
lines.push("");
|
|
203
|
-
lines.push("| Config | Cost |");
|
|
204
|
-
lines.push("|---|---|");
|
|
205
|
-
for (const variant of dedupeVariants(row.variants)) {
|
|
206
|
-
lines.push(`| ${escapeCell(variant.when)} | ${variant.price} |`);
|
|
207
|
-
}
|
|
208
|
-
lines.push("");
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
return `${lines.join("\n").trimEnd()}\n`;
|
|
212
|
-
}
|
|
213
|
-
/**
|
|
214
|
-
* One cost entry as a price expression.
|
|
215
|
-
*
|
|
216
|
-
* `fixed` bills per call, `unit` bills per unit consumed, and `package` bills
|
|
217
|
-
* per block of units on top of a flat call cost — an LLM step that charges a
|
|
218
|
-
* base plus a rate per 1k tokens is the common case.
|
|
219
|
-
*/
|
|
220
|
-
function renderPrice(cost) {
|
|
221
|
-
const amount = formatAmount(cost.cost ?? 0);
|
|
222
|
-
if (cost.type === "unit") {
|
|
223
|
-
const metered = cost.unit === undefined ? amount : `${amount} / ${cost.unit}`;
|
|
224
|
-
// A per-item rate can sit on top of a flat charge per call — dropping that
|
|
225
|
-
// base is how a 0.175 + 0.025/item search gets quoted as 0.025.
|
|
226
|
-
return cost.fixedCost === undefined || cost.fixedCost === 0
|
|
227
|
-
? metered
|
|
228
|
-
: `${formatAmount(cost.fixedCost)} + ${metered}`;
|
|
229
|
-
}
|
|
230
|
-
if (cost.type === "package") {
|
|
231
|
-
const block = formatUnits(cost.unitsCount, cost.unit);
|
|
232
|
-
const metered = block === undefined ? amount : `${amount} / ${block}`;
|
|
233
|
-
return cost.fixedCost === undefined || cost.fixedCost === 0
|
|
234
|
-
? metered
|
|
235
|
-
: `${formatAmount(cost.fixedCost)} + ${metered}`;
|
|
236
|
-
}
|
|
237
|
-
return amount;
|
|
238
|
-
}
|
|
239
|
-
function renderRange(costs, minCost, maxCost) {
|
|
240
|
-
const range = minCost === maxCost
|
|
241
|
-
? formatAmount(minCost)
|
|
242
|
-
: `${formatAmount(minCost)}–${formatAmount(maxCost)}`;
|
|
243
|
-
const first = costs[0];
|
|
244
|
-
const unit = first !== undefined ? first.unit : undefined;
|
|
245
|
-
const unitsCount = first !== undefined ? first.unitsCount : undefined;
|
|
246
|
-
const packageBlock = formatUnits(unitsCount, unit);
|
|
247
|
-
const metered = costs.some((cost) => cost.type === "package")
|
|
248
|
-
? packageBlock !== undefined
|
|
249
|
-
? `${range} / ${packageBlock}`
|
|
250
|
-
: range
|
|
251
|
-
: costs.every((cost) => cost.type === "unit") && unit !== undefined
|
|
252
|
-
? `${range} / ${unit}`
|
|
253
|
-
: range;
|
|
254
|
-
const hasBase = costs.some((cost) => cost.fixedCost !== undefined && cost.fixedCost !== 0);
|
|
255
|
-
return hasBase ? `${metered} + base` : metered;
|
|
256
|
-
}
|
|
257
|
-
/**
|
|
258
|
-
* The config fields a cost entry keys off, as dotted paths — the answer to
|
|
259
|
-
* "what do I change to land on a different price".
|
|
260
|
-
*/
|
|
261
|
-
function selectorPaths(schema, prefix = "") {
|
|
262
|
-
if (schema?.properties === undefined) {
|
|
263
|
-
return [];
|
|
264
|
-
}
|
|
265
|
-
const paths = [];
|
|
266
|
-
for (const [key, property] of Object.entries(schema.properties)) {
|
|
267
|
-
const path = prefix === "" ? key : `${prefix}.${key}`;
|
|
268
|
-
const nested = selectorPaths(property, path);
|
|
269
|
-
if (nested.length > 0) {
|
|
270
|
-
paths.push(...nested);
|
|
271
|
-
}
|
|
272
|
-
else {
|
|
273
|
-
paths.push(path);
|
|
274
|
-
}
|
|
275
|
-
}
|
|
276
|
-
return paths;
|
|
277
|
-
}
|
|
278
|
-
function selectorFields(costs) {
|
|
279
|
-
const fields = new Set();
|
|
280
|
-
for (const cost of costs) {
|
|
281
|
-
for (const path of selectorPaths(cost.config?.jsonSchema)) {
|
|
282
|
-
fields.add(path);
|
|
283
|
-
}
|
|
284
|
-
}
|
|
285
|
-
return [...fields];
|
|
286
|
-
}
|
|
287
|
-
/** A cost entry's selector as `field=value` pairs, e.g. `model=gpt-5-nano`. */
|
|
288
|
-
function describeSelector(schema) {
|
|
289
|
-
const pairs = selectorPairs(schema);
|
|
290
|
-
return pairs.length === 0 ? "any config" : pairs.join(", ");
|
|
291
|
-
}
|
|
292
|
-
function selectorPairs(schema, prefix = "") {
|
|
293
|
-
if (schema?.properties === undefined) {
|
|
294
|
-
return [];
|
|
295
|
-
}
|
|
296
|
-
const pairs = [];
|
|
297
|
-
for (const [key, property] of Object.entries(schema.properties)) {
|
|
298
|
-
const path = prefix === "" ? key : `${prefix}.${key}`;
|
|
299
|
-
const nested = selectorPairs(property, path);
|
|
300
|
-
if (nested.length > 0) {
|
|
301
|
-
pairs.push(...nested);
|
|
302
|
-
continue;
|
|
303
|
-
}
|
|
304
|
-
pairs.push(`${path}${describeConstraint(property)}`);
|
|
305
|
-
}
|
|
306
|
-
return pairs;
|
|
307
|
-
}
|
|
308
|
-
function describeConstraint(schema) {
|
|
309
|
-
if (schema.const !== undefined) {
|
|
310
|
-
return `=${String(schema.const)}`;
|
|
311
|
-
}
|
|
312
|
-
if (schema.enum !== undefined) {
|
|
313
|
-
return `∈ ${schema.enum.map((value) => String(value)).join("|")}`;
|
|
314
|
-
}
|
|
315
|
-
if (schema.maxItems === 0) {
|
|
316
|
-
return " empty";
|
|
317
|
-
}
|
|
318
|
-
if (schema.minItems !== undefined && schema.minItems > 0) {
|
|
319
|
-
return " set";
|
|
320
|
-
}
|
|
321
|
-
return " (any)";
|
|
322
|
-
}
|
|
323
|
-
/**
|
|
324
|
-
* Catalogs sometimes carry the same price twice under selectors that differ
|
|
325
|
-
* only in a field the reader cannot act on; collapsing them keeps the variant
|
|
326
|
-
* table readable.
|
|
327
|
-
*/
|
|
328
|
-
function dedupeVariants(variants) {
|
|
329
|
-
const seen = new Set();
|
|
330
|
-
const kept = [];
|
|
331
|
-
for (const variant of variants) {
|
|
332
|
-
const key = `${variant.when}|${variant.price}`;
|
|
333
|
-
if (seen.has(key) === true) {
|
|
334
|
-
continue;
|
|
335
|
-
}
|
|
336
|
-
seen.add(key);
|
|
337
|
-
kept.push(variant);
|
|
338
|
-
}
|
|
339
|
-
return kept;
|
|
340
|
-
}
|
|
341
|
-
function formatUnits(unitsCount, unit) {
|
|
342
|
-
if (unit === undefined) {
|
|
343
|
-
return undefined;
|
|
344
|
-
}
|
|
345
|
-
if (unitsCount === undefined || unitsCount === 1) {
|
|
346
|
-
return unit;
|
|
347
|
-
}
|
|
348
|
-
return unitsCount % 1000 === 0
|
|
349
|
-
? `${String(unitsCount / 1000)}k ${unit}`
|
|
350
|
-
: `${String(unitsCount)} ${unit}`;
|
|
351
|
-
}
|
|
352
|
-
function formatAmount(value) {
|
|
353
|
-
return String(Number(value.toFixed(6)));
|
|
354
|
-
}
|
|
355
|
-
function truncate(value) {
|
|
356
|
-
if (value === undefined || value.length <= DESCRIPTION_MAX_LENGTH) {
|
|
357
|
-
return value;
|
|
358
|
-
}
|
|
359
|
-
return `${value.slice(0, DESCRIPTION_MAX_LENGTH).trimEnd()}…`;
|
|
360
|
-
}
|
|
361
|
-
function escapeCell(value) {
|
|
362
|
-
return value.replace(/\r?\n/g, " ").replace(/\|/g, "\\|");
|
|
363
|
-
}
|