@erdoai/cli 0.117.1 → 0.119.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 +1 -1
- package/dist/index.js +115 -4
- package/package.json +8 -8
package/README.md
CHANGED
|
@@ -11,7 +11,7 @@ npx @erdoai/cli --help # or run without installing
|
|
|
11
11
|
erdo update # self-update to the latest published version
|
|
12
12
|
```
|
|
13
13
|
|
|
14
|
-
Requires Node.js
|
|
14
|
+
Requires Node.js 22.13+. User-facing install docs: https://docs.erdo.ai/cli
|
|
15
15
|
|
|
16
16
|
When a newer version is published, every command prints one line on **stderr**
|
|
17
17
|
naming the installed version and `erdo update`. It is on stderr so stdout stays
|
package/dist/index.js
CHANGED
|
@@ -1075,6 +1075,25 @@ var ErdoClient = class {
|
|
|
1075
1075
|
throw err;
|
|
1076
1076
|
}
|
|
1077
1077
|
}
|
|
1078
|
+
// The thread owns the current invocation identity. Approval resumes create a
|
|
1079
|
+
// new invocation while deliberately reusing the original agent run, so the
|
|
1080
|
+
// run list is not an authoritative source for cancellation.
|
|
1081
|
+
getThreadCompletionStatus(threadID) {
|
|
1082
|
+
return this.request(
|
|
1083
|
+
"GET",
|
|
1084
|
+
`/v1/threads/${encodeURIComponent(threadID)}/completion-status`
|
|
1085
|
+
);
|
|
1086
|
+
}
|
|
1087
|
+
// cancelThreadInvocation names the invocation being stopped. The thread
|
|
1088
|
+
// service refuses (HTTP 400, "the run already changed") when the thread has
|
|
1089
|
+
// moved on to another invocation, so a replacement run is never cancelled.
|
|
1090
|
+
cancelThreadInvocation(threadID, invocationID) {
|
|
1091
|
+
return this.request(
|
|
1092
|
+
"POST",
|
|
1093
|
+
`/v1/threads/${encodeURIComponent(threadID)}/cancel-invocation`,
|
|
1094
|
+
{ invocation_id: invocationID }
|
|
1095
|
+
);
|
|
1096
|
+
}
|
|
1078
1097
|
sendMessage(threadID, input) {
|
|
1079
1098
|
return this.request(
|
|
1080
1099
|
"POST",
|
|
@@ -1149,6 +1168,13 @@ var ErdoClient = class {
|
|
|
1149
1168
|
const qs = q.toString();
|
|
1150
1169
|
return this.request("GET", `/v1/datasets-purposes${qs ? `?${qs}` : ""}`);
|
|
1151
1170
|
}
|
|
1171
|
+
// Reads the declared write contract through the slug-addressed fetch route's
|
|
1172
|
+
// declared_schema view. That route is the one a dataset-scoped API key can
|
|
1173
|
+
// reach (and is confined to its own datasets there); the dataset-details
|
|
1174
|
+
// route is not, because its response also names other datasets.
|
|
1175
|
+
getDatasetDeclaredSchema(slug) {
|
|
1176
|
+
return this.request("POST", `/v1/datasets/${encodeURIComponent(slug)}/fetch`, { view: "declared_schema" });
|
|
1177
|
+
}
|
|
1152
1178
|
// Create an empty dataset with its declared columns. The server refuses a
|
|
1153
1179
|
// create without a schema unless the purpose's columns are platform-defined.
|
|
1154
1180
|
createDataset(input) {
|
|
@@ -1583,6 +1609,15 @@ var ErdoClient = class {
|
|
|
1583
1609
|
);
|
|
1584
1610
|
}
|
|
1585
1611
|
// --- knowledge ---
|
|
1612
|
+
getKnowledge(id) {
|
|
1613
|
+
return this.request("GET", `/v1/knowledge/${encodeURIComponent(id)}`);
|
|
1614
|
+
}
|
|
1615
|
+
updateKnowledge(id, input) {
|
|
1616
|
+
return this.request("POST", `/v1/knowledge/${encodeURIComponent(id)}/actions`, {
|
|
1617
|
+
action: "update",
|
|
1618
|
+
...input
|
|
1619
|
+
});
|
|
1620
|
+
}
|
|
1586
1621
|
listKnowledge(visibility, options) {
|
|
1587
1622
|
const params = new URLSearchParams();
|
|
1588
1623
|
if (visibility) params.set("visibility", visibility);
|
|
@@ -1719,6 +1754,7 @@ var ErdoClient = class {
|
|
|
1719
1754
|
// what the evidence said afterwards. Referenced by slug, never UUID.
|
|
1720
1755
|
listDecisions(opts) {
|
|
1721
1756
|
const params = new URLSearchParams();
|
|
1757
|
+
if (opts.organization_slug) params.set("organization_slug", opts.organization_slug);
|
|
1722
1758
|
if (opts.workstream_slug) params.set("workstream_slug", opts.workstream_slug);
|
|
1723
1759
|
if (opts.source) params.set("source", opts.source);
|
|
1724
1760
|
if (opts.decision_class) params.set("decision_class", opts.decision_class);
|
|
@@ -3974,6 +4010,24 @@ agentCmd.command("send <threadId> <message>").description("Send a message to a t
|
|
|
3974
4010
|
fail(e);
|
|
3975
4011
|
}
|
|
3976
4012
|
});
|
|
4013
|
+
agentCmd.command("cancel <threadId>").description("Request cancellation of a thread's active invocation; read the run to confirm it has stopped").option("--invocation <id>", "invocation to cancel (default: the thread's current invocation)").action(async (threadId, opts) => {
|
|
4014
|
+
try {
|
|
4015
|
+
const client = new ErdoClient();
|
|
4016
|
+
let invocationID = opts.invocation;
|
|
4017
|
+
if (!invocationID) {
|
|
4018
|
+
const current = await client.getThreadCompletionStatus(threadId);
|
|
4019
|
+
invocationID = current.invocation_id;
|
|
4020
|
+
if (!invocationID) throw new Error(`thread ${threadId} has no invocation to cancel`);
|
|
4021
|
+
}
|
|
4022
|
+
const result = await client.cancelThreadInvocation(threadId, invocationID);
|
|
4023
|
+
if (!result.success) {
|
|
4024
|
+
throw new Error("cancellation could not be confirmed; inspect the thread's latest run and retry");
|
|
4025
|
+
}
|
|
4026
|
+
console.log(JSON.stringify(result, null, 2));
|
|
4027
|
+
} catch (e) {
|
|
4028
|
+
fail(e);
|
|
4029
|
+
}
|
|
4030
|
+
});
|
|
3977
4031
|
agentCmd.command("wait <threadId>").description("Re-attach to a thread's latest run and wait for it to finish; prints the answer").action(async (threadId) => {
|
|
3978
4032
|
try {
|
|
3979
4033
|
const client = new ErdoClient();
|
|
@@ -4254,7 +4308,7 @@ function renderReportEffect(e) {
|
|
|
4254
4308
|
console.log(` outcome: ${e.outcome_label}`);
|
|
4255
4309
|
}
|
|
4256
4310
|
}
|
|
4257
|
-
decisionsCmd.command("list").description("List decisions, newest first").option("--workstream <slug>", "only decisions filed under this workstream/Strategy").option(
|
|
4311
|
+
decisionsCmd.command("list").description("List decisions, newest first").option("--organization <slug>", "read an authorized client organization (default: active organization)").option("--workstream <slug>", "only decisions filed under this workstream/Strategy").option(
|
|
4258
4312
|
"--source <source>",
|
|
4259
4313
|
"producer family: approval | escalation | engine_gate | allocator | experiment | workstream_commitment"
|
|
4260
4314
|
).option("--class <class>", "decision class, e.g. paid_media.ad_group.pause").option("--subject-kind <kind>", "only decisions about this kind of subject").option("--subject-ref <id>", "only decisions about this exact subject").option(
|
|
@@ -4267,6 +4321,7 @@ decisionsCmd.command("list").description("List decisions, newest first").option(
|
|
|
4267
4321
|
async (opts) => {
|
|
4268
4322
|
try {
|
|
4269
4323
|
const res = await new ErdoClient().listDecisions({
|
|
4324
|
+
organization_slug: opts.organization,
|
|
4270
4325
|
workstream_slug: opts.workstream,
|
|
4271
4326
|
source: opts.source,
|
|
4272
4327
|
decision_class: opts.class,
|
|
@@ -6747,10 +6802,36 @@ Read one with: erdo datasets fetch ${res.dataset_slug} --revision <id> --sql "SE
|
|
|
6747
6802
|
}
|
|
6748
6803
|
});
|
|
6749
6804
|
datasetsCmd.command("schema <slug>").alias("describe").description(
|
|
6750
|
-
"Show
|
|
6751
|
-
).option("--json", "print the raw JSON result instead of a table").action(async (slug, opts) => {
|
|
6805
|
+
"Show physical columns by default, or the declared write contract with --declared. Physical columns are 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>'`."
|
|
6806
|
+
).option("--json", "print the raw JSON result instead of a table").option("--declared", "read the declared write contract (column names, type hints, required flags, semantic roles) instead of the physical columns").action(async (slug, opts) => {
|
|
6752
6807
|
try {
|
|
6753
|
-
const
|
|
6808
|
+
const client = new ErdoClient();
|
|
6809
|
+
if (opts.declared) {
|
|
6810
|
+
const res2 = await client.getDatasetDeclaredSchema(slug);
|
|
6811
|
+
if (!res2 || typeof res2 !== "object" || res2.view !== "declared_schema")
|
|
6812
|
+
throw new Error("Invalid dataset schema response: the server did not answer the declared_schema view");
|
|
6813
|
+
const schema = res2.declared_schema;
|
|
6814
|
+
if (schema != null && (!Array.isArray(schema.columns) || schema.columns.some((column) => !column || typeof column.name !== "string" || !column.name.trim())))
|
|
6815
|
+
throw new Error("Invalid dataset schema response: declared columns are malformed");
|
|
6816
|
+
if (opts.json) {
|
|
6817
|
+
print(res2);
|
|
6818
|
+
return;
|
|
6819
|
+
}
|
|
6820
|
+
if (schema == null) {
|
|
6821
|
+
console.log(`${slug} has no declared column contract. Use datasets schema ${slug} to inspect physical columns.`);
|
|
6822
|
+
return;
|
|
6823
|
+
}
|
|
6824
|
+
if (!schema.columns.length) {
|
|
6825
|
+
console.log(`${slug}'s declared column contract contains no columns.`);
|
|
6826
|
+
return;
|
|
6827
|
+
}
|
|
6828
|
+
printAlignedTable(
|
|
6829
|
+
["column", "type", "required", "semantic role", "description"],
|
|
6830
|
+
schema.columns.map((column) => [column.name, column.type || "", column.required ? "yes" : "no", column.semantic_role || "", column.description || ""])
|
|
6831
|
+
);
|
|
6832
|
+
return;
|
|
6833
|
+
}
|
|
6834
|
+
const res = await client.fetchDatasetContents(slug, { sql_query: "DESCRIBE data" });
|
|
6754
6835
|
if (opts.json) {
|
|
6755
6836
|
print(res);
|
|
6756
6837
|
return;
|
|
@@ -8142,6 +8223,36 @@ pipelinesCmd.command("executions <slug>").description("Show an event pipeline's
|
|
|
8142
8223
|
}
|
|
8143
8224
|
});
|
|
8144
8225
|
var knowledgeCmd = program.command("knowledge").description("Knowledge objects");
|
|
8226
|
+
knowledgeCmd.command("get <id>").description("Get one full typed Knowledge object, including links and related objects").action(async (id) => {
|
|
8227
|
+
try {
|
|
8228
|
+
print(await new ErdoClient().getKnowledge(id));
|
|
8229
|
+
} catch (e) {
|
|
8230
|
+
fail(e);
|
|
8231
|
+
}
|
|
8232
|
+
});
|
|
8233
|
+
knowledgeCmd.command("update <id>").description("Update a Knowledge object while preserving fields omitted from the command").option("--body <value>", "new body text, or @file; pass an empty value to clear the body").option("--config <file>", "JSON object file containing the complete replacement config, or @file").option("--title <title>", "new title").action(async (id, opts) => {
|
|
8234
|
+
try {
|
|
8235
|
+
if (opts.title === void 0 && opts.body === void 0 && opts.config === void 0) {
|
|
8236
|
+
throw new Error("provide at least one of --title, --body, or --config");
|
|
8237
|
+
}
|
|
8238
|
+
if (opts.title !== void 0 && !opts.title.trim()) {
|
|
8239
|
+
throw new Error("title must not be empty");
|
|
8240
|
+
}
|
|
8241
|
+
const replacementConfig = opts.config === void 0 ? void 0 : readJSONObject(opts.config.startsWith("@") ? opts.config.slice(1) : opts.config, "--config");
|
|
8242
|
+
const replacementBody = opts.body === void 0 ? void 0 : readMaybeFile(opts.body) ?? "";
|
|
8243
|
+
const client = new ErdoClient();
|
|
8244
|
+
const current = await client.getKnowledge(id);
|
|
8245
|
+
const object = current.object;
|
|
8246
|
+
print(await client.updateKnowledge(id, {
|
|
8247
|
+
title: opts.title ?? object.title,
|
|
8248
|
+
body_markdown: replacementBody ?? object.body_markdown,
|
|
8249
|
+
config_json: replacementConfig ?? object.config_json,
|
|
8250
|
+
expected_updated_at: object.updated_at
|
|
8251
|
+
}));
|
|
8252
|
+
} catch (e) {
|
|
8253
|
+
fail(e);
|
|
8254
|
+
}
|
|
8255
|
+
});
|
|
8145
8256
|
knowledgeCmd.command("list").description("List knowledge objects").option(
|
|
8146
8257
|
"--public",
|
|
8147
8258
|
"only entries opted into anonymous external surfaces (website widgets)"
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@erdoai/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.119.0",
|
|
4
4
|
"description": "Erdo CLI — drive datasets, pages, and evals from the terminal or CI",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
"scripts/postinstall.mjs"
|
|
12
12
|
],
|
|
13
13
|
"engines": {
|
|
14
|
-
"node": ">=
|
|
14
|
+
"node": "^22.13.0 || >=23.5.0"
|
|
15
15
|
},
|
|
16
16
|
"publishConfig": {
|
|
17
17
|
"access": "public",
|
|
@@ -41,14 +41,14 @@
|
|
|
41
41
|
"prepublishOnly": "npm run build"
|
|
42
42
|
},
|
|
43
43
|
"dependencies": {
|
|
44
|
-
"@inquirer/prompts": "^7.
|
|
45
|
-
"commander": "^
|
|
44
|
+
"@inquirer/prompts": "^8.7.3",
|
|
45
|
+
"commander": "^15.0.0"
|
|
46
46
|
},
|
|
47
47
|
"devDependencies": {
|
|
48
|
-
"@types/node": "^
|
|
49
|
-
"tsup": "^8.
|
|
50
|
-
"tsx": "^4.
|
|
51
|
-
"typescript": "^
|
|
48
|
+
"@types/node": "^26.6.4",
|
|
49
|
+
"tsup": "^8.5.1",
|
|
50
|
+
"tsx": "^4.23.15",
|
|
51
|
+
"typescript": "^7.0.2"
|
|
52
52
|
},
|
|
53
53
|
"overrides": {
|
|
54
54
|
"esbuild": "^0.28.1"
|