@erdoai/cli 0.52.0 → 0.54.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -0
- package/dist/index.js +42 -12
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -61,6 +61,15 @@ erdo datasets fetch acme.leads --filter <name> # opt into a saved filter
|
|
|
61
61
|
The table is named **`data`** — for file datasets (CSV, Excel, and anything
|
|
62
62
|
written by an event pipeline) that is the name regardless of the dataset's slug
|
|
63
63
|
or resource key, and it is not guessable, so it is the first thing to get right.
|
|
64
|
+
The columns aren't guessable either, so look before you write SQL:
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
erdo datasets schema acme.leads # column names and types
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
`datasets schema` reads the stored table itself, so it shows the columns that
|
|
71
|
+
are actually there — the ones `fetch --sql` can reference — rather than a
|
|
72
|
+
declared schema that may lag what recent writes added.
|
|
64
73
|
Database and warehouse datasets are queried through their real table names, which
|
|
65
74
|
come from the dataset's schema. The SQL dialect is DuckDB. A dataset's default
|
|
66
75
|
filters apply on every read; `--filter <name>` adds a saved filter on top, and
|
package/dist/index.js
CHANGED
|
@@ -1339,6 +1339,14 @@ function timedOutMessage(threadID) {
|
|
|
1339
1339
|
function print(value) {
|
|
1340
1340
|
console.log(JSON.stringify(value, null, 2));
|
|
1341
1341
|
}
|
|
1342
|
+
function printAlignedTable(columns, rows) {
|
|
1343
|
+
const cell = (v) => v === null || v === void 0 ? "" : typeof v === "object" ? JSON.stringify(v) : String(v);
|
|
1344
|
+
const widths = columns.map((c, i) => Math.max(c.length, ...rows.map((r) => cell(r[i]).length), 0));
|
|
1345
|
+
const line = (cells) => cells.map((c, i) => c.padEnd(widths[i])).join(" ").trimEnd();
|
|
1346
|
+
console.log(line(columns));
|
|
1347
|
+
console.log(widths.map((w) => "-".repeat(w)).join(" "));
|
|
1348
|
+
for (const r of rows) console.log(line(columns.map((_, i) => cell(r[i]))));
|
|
1349
|
+
}
|
|
1342
1350
|
var sleep2 = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
1343
1351
|
var collect = (v, acc) => {
|
|
1344
1352
|
acc.push(v);
|
|
@@ -2463,15 +2471,22 @@ agentCmd.command("threads").description("List your threads").action(async () =>
|
|
|
2463
2471
|
fail(e);
|
|
2464
2472
|
}
|
|
2465
2473
|
});
|
|
2466
|
-
agentCmd.command("messages <threadId>").description("Show a thread's messages (role + text)").action(async (threadId) => {
|
|
2474
|
+
agentCmd.command("messages <threadId>").description("Show a thread's messages (role + text)").option("-v, --verbose", "print full content for non-text entries (tool calls, results, etc.) instead of a placeholder").action(async (threadId, opts) => {
|
|
2467
2475
|
try {
|
|
2468
2476
|
const { messages } = await new ErdoClient().getThreadMessages(threadId);
|
|
2477
|
+
if (messages.length === 0) {
|
|
2478
|
+
console.log("No visible messages in this thread.");
|
|
2479
|
+
return;
|
|
2480
|
+
}
|
|
2469
2481
|
for (const m of messages) {
|
|
2470
2482
|
const role = m.message.author_entity_type === "app" ? "agent" : m.message.author_name || m.message.author_id || m.message.author_entity_type;
|
|
2471
2483
|
console.log(`\u2014 ${role} \xB7 ${m.message.created_at} \u2014`);
|
|
2472
2484
|
for (const c of m.contents) {
|
|
2473
2485
|
if (c.content_type === "text") {
|
|
2474
2486
|
console.log(c.content);
|
|
2487
|
+
} else if (opts.verbose) {
|
|
2488
|
+
console.log(`\x1B[2m[${c.content_type}]\x1B[0m`);
|
|
2489
|
+
console.log(typeof c.content === "string" ? c.content : JSON.stringify(c.content, null, 2));
|
|
2475
2490
|
} else {
|
|
2476
2491
|
console.log(`\x1B[2m[${c.content_type}]\x1B[0m`);
|
|
2477
2492
|
}
|
|
@@ -3437,7 +3452,7 @@ datasetsCmd.command("fetch <slug>").description(
|
|
|
3437
3452
|
"Read rows from a dataset \u2014 the deterministic read, and the one to use for anything mechanical or scripted. Returns {columns, rows, row_count}."
|
|
3438
3453
|
).option(
|
|
3439
3454
|
"-q, --sql <query>",
|
|
3440
|
-
"DuckDB SQL shaping the rows. File datasets (including anything an event pipeline writes) are queried as a table named `data`, whatever the dataset's slug; database and warehouse datasets use their real table names from the schema."
|
|
3455
|
+
"DuckDB SQL shaping the rows. File datasets (including anything an event pipeline writes) are queried as a table named `data`, whatever the dataset's slug; database and warehouse datasets use their real table names from the schema. `erdo datasets schema <slug>` lists the columns you can reference."
|
|
3441
3456
|
).option("-l, --limit <n>", "max rows to return", (v) => parseInt(v, 10)).option(
|
|
3442
3457
|
"-f, --filter <name>",
|
|
3443
3458
|
"opt into a saved filter by name (repeatable); additive on top of the dataset's default filters (see: erdo datasets filter list <slug>)",
|
|
@@ -3456,6 +3471,29 @@ datasetsCmd.command("fetch <slug>").description(
|
|
|
3456
3471
|
fail(e);
|
|
3457
3472
|
}
|
|
3458
3473
|
});
|
|
3474
|
+
datasetsCmd.command("schema <slug>").alias("describe").description(
|
|
3475
|
+
"Show a dataset's physical columns \u2014 the names and types you can reference in `datasets fetch --sql`, read from the stored `data` table itself (DuckDB DESCRIBE) so they are what is actually there, not what was declared. For a database or warehouse dataset, DESCRIBE its real table names instead: `datasets fetch <slug> --sql 'DESCRIBE <table>'`."
|
|
3476
|
+
).option("--json", "print the raw JSON result instead of a table").action(async (slug, opts) => {
|
|
3477
|
+
try {
|
|
3478
|
+
const res = await new ErdoClient().fetchDatasetContents(slug, { sql_query: "DESCRIBE data" });
|
|
3479
|
+
if (opts.json) {
|
|
3480
|
+
print(res);
|
|
3481
|
+
return;
|
|
3482
|
+
}
|
|
3483
|
+
const nameIdx = res.columns.indexOf("column_name");
|
|
3484
|
+
const typeIdx = res.columns.indexOf("column_type");
|
|
3485
|
+
if (nameIdx === -1 || typeIdx === -1) {
|
|
3486
|
+
printAlignedTable(res.columns, res.rows);
|
|
3487
|
+
return;
|
|
3488
|
+
}
|
|
3489
|
+
printAlignedTable(
|
|
3490
|
+
["column", "type"],
|
|
3491
|
+
res.rows.map((r) => [r[nameIdx], r[typeIdx]])
|
|
3492
|
+
);
|
|
3493
|
+
} catch (e) {
|
|
3494
|
+
fail(e);
|
|
3495
|
+
}
|
|
3496
|
+
});
|
|
3459
3497
|
var MAX_UPLOAD_BYTES = 20 * 1024 * 1024;
|
|
3460
3498
|
datasetsCmd.command("upload <file>").description("Upload a file (CSV, Excel, JSON, ...) and create a dataset from it").option("-n, --name <name>", "display name for the dataset (defaults to the filename)").option("-d, --description <text>", "description shown to agents analyzing the dataset").action(async (file, opts) => {
|
|
3461
3499
|
try {
|
|
@@ -3512,14 +3550,6 @@ datasetsCmd.command("configure-integration <dataset-id>").description("Set an in
|
|
|
3512
3550
|
fail(e);
|
|
3513
3551
|
}
|
|
3514
3552
|
});
|
|
3515
|
-
function printAnalyticsTable(columns, rows) {
|
|
3516
|
-
const cell = (v) => v === null || v === void 0 ? "" : typeof v === "object" ? JSON.stringify(v) : String(v);
|
|
3517
|
-
const widths = columns.map((c, i) => Math.max(c.length, ...rows.map((r) => cell(r[i]).length), 0));
|
|
3518
|
-
const line = (cells) => cells.map((c, i) => c.padEnd(widths[i])).join(" ").trimEnd();
|
|
3519
|
-
console.log(line(columns));
|
|
3520
|
-
console.log(widths.map((w) => "-".repeat(w)).join(" "));
|
|
3521
|
-
for (const r of rows) console.log(line(columns.map((_, i) => cell(r[i]))));
|
|
3522
|
-
}
|
|
3523
3553
|
var analytics = program.command("analytics").description("Page analytics \u2014 what is tracking your published pages, and how they perform with real visitors");
|
|
3524
3554
|
analytics.command("query <hogql>").description("Run a read-only HogQL query against this org's page-analytics events").option("--json", "print the raw JSON result instead of a table").action(async (hogql, opts) => {
|
|
3525
3555
|
try {
|
|
@@ -3532,7 +3562,7 @@ analytics.command("query <hogql>").description("Run a read-only HogQL query agai
|
|
|
3532
3562
|
console.log("Page analytics is not enabled for this organization \u2014 turn it on in the Erdo app, then query real traffic here.");
|
|
3533
3563
|
return;
|
|
3534
3564
|
}
|
|
3535
|
-
|
|
3565
|
+
printAlignedTable(res.columns, res.rows);
|
|
3536
3566
|
if (res.truncated) console.log(`
|
|
3537
3567
|
(rows truncated at the cap \u2014 aggregate further or add a tighter filter/LIMIT)`);
|
|
3538
3568
|
} catch (e) {
|
|
@@ -3563,7 +3593,7 @@ analytics.command("tracking").description("Show which analytics destinations thi
|
|
|
3563
3593
|
rows.push([d.kind, d.enabled ? "on" : "configured, off", d.public_id ?? "", d.provider ?? "", ""]);
|
|
3564
3594
|
}
|
|
3565
3595
|
}
|
|
3566
|
-
|
|
3596
|
+
printAlignedTable(["kind", "status", "public id", "provider", "what it does"], rows);
|
|
3567
3597
|
console.log(`
|
|
3568
3598
|
session replay input masking: ${res.mask_inputs ? "on" : "OFF \u2014 form values are recorded"}`);
|
|
3569
3599
|
console.log(`consent: ${res.consent}${res.consent === "required" ? " (recording waits on a consent banner, so thin volume may be the gate, not the traffic)" : ""}`);
|