@erdoai/cli 0.138.1 → 0.139.1
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/index.js +82 -5
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1223,6 +1223,9 @@ var ErdoClient = class {
|
|
|
1223
1223
|
getDatasetDeclaredSchema(slug) {
|
|
1224
1224
|
return this.request("POST", `/v1/datasets/${encodeURIComponent(slug)}/fetch`, { view: "declared_schema" });
|
|
1225
1225
|
}
|
|
1226
|
+
getDatasetResourceSchema(slug) {
|
|
1227
|
+
return this.request("POST", `/v1/datasets/${encodeURIComponent(slug)}/fetch`, { view: "resource_schema" });
|
|
1228
|
+
}
|
|
1226
1229
|
// Create an empty dataset with its declared columns. The server refuses a
|
|
1227
1230
|
// create without a schema unless the purpose's columns are platform-defined.
|
|
1228
1231
|
createDataset(input) {
|
|
@@ -1730,6 +1733,9 @@ var ErdoClient = class {
|
|
|
1730
1733
|
listHeartbeats() {
|
|
1731
1734
|
return this.request("GET", "/v1/heartbeats");
|
|
1732
1735
|
}
|
|
1736
|
+
createHeartbeat(body) {
|
|
1737
|
+
return this.request("POST", "/v1/heartbeats", body);
|
|
1738
|
+
}
|
|
1733
1739
|
runHeartbeat(id) {
|
|
1734
1740
|
return this.request("POST", `/v1/heartbeats/${encodeURIComponent(id)}/run`, {});
|
|
1735
1741
|
}
|
|
@@ -7278,7 +7284,7 @@ Read one with: erdo datasets fetch ${res.dataset_slug} --revision <id> --sql "SE
|
|
|
7278
7284
|
}
|
|
7279
7285
|
});
|
|
7280
7286
|
datasetsCmd.command("schema <slug>").alias("describe").description(
|
|
7281
|
-
"Show physical columns by default, or the declared write contract with --declared.
|
|
7287
|
+
"Show physical columns by default, or the declared write contract with --declared. File datasets read the stored `data` table; integration datasets show their discovered resource tables and columns. For an integration dataset, choose a resource/table explicitly when fetching: `datasets fetch <slug> --sql 'SELECT * FROM <table>'`."
|
|
7282
7288
|
).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) => {
|
|
7283
7289
|
try {
|
|
7284
7290
|
const client = new ErdoClient();
|
|
@@ -7307,7 +7313,35 @@ datasetsCmd.command("schema <slug>").alias("describe").description(
|
|
|
7307
7313
|
);
|
|
7308
7314
|
return;
|
|
7309
7315
|
}
|
|
7310
|
-
const res = await client.
|
|
7316
|
+
const res = await client.getDatasetResourceSchema(slug);
|
|
7317
|
+
if (!res || typeof res !== "object" || res.view !== "resource_schema")
|
|
7318
|
+
throw new Error("Invalid dataset schema response: the server did not answer the resource_schema view");
|
|
7319
|
+
if (res.schema_provenance === "resource_catalog") {
|
|
7320
|
+
const resources = res.resources ?? [];
|
|
7321
|
+
if (!Array.isArray(resources) || resources.some((resource) => !resource || !Array.isArray(resource.columns)))
|
|
7322
|
+
throw new Error("Invalid dataset schema response: malformed resource catalog");
|
|
7323
|
+
if (opts.json) {
|
|
7324
|
+
print(res);
|
|
7325
|
+
return;
|
|
7326
|
+
}
|
|
7327
|
+
if (!resources.length) {
|
|
7328
|
+
console.log(`${slug} has no discovered integration resources yet.`);
|
|
7329
|
+
return;
|
|
7330
|
+
}
|
|
7331
|
+
const rows = resources.flatMap(
|
|
7332
|
+
(resource) => resource.columns.length ? resource.columns.map((column) => [
|
|
7333
|
+
resource.table_name || resource.key || resource.name,
|
|
7334
|
+
column.name,
|
|
7335
|
+
column.type || ""
|
|
7336
|
+
]) : [[resource.table_name || resource.key || resource.name, "", "(columns unavailable)"]]
|
|
7337
|
+
);
|
|
7338
|
+
printAlignedTable(["table", "column", "type"], rows);
|
|
7339
|
+
console.log(`
|
|
7340
|
+
Fetch a selected table with: erdo datasets fetch ${slug} --sql "SELECT * FROM <table>"`);
|
|
7341
|
+
return;
|
|
7342
|
+
}
|
|
7343
|
+
if (res.schema_provenance !== "dataset_storage" || !Array.isArray(res.columns) || !Array.isArray(res.rows))
|
|
7344
|
+
throw new Error("Invalid dataset schema response: malformed physical schema");
|
|
7311
7345
|
if (opts.json) {
|
|
7312
7346
|
print(res);
|
|
7313
7347
|
return;
|
|
@@ -8887,10 +8921,51 @@ knowledgeAssetsCmd.command("list <objectId>").description("List files attached t
|
|
|
8887
8921
|
}
|
|
8888
8922
|
});
|
|
8889
8923
|
var autoCmd = program.command("automations").description("Scheduled automations (heartbeats)");
|
|
8924
|
+
function heartbeatBounds(h) {
|
|
8925
|
+
return `${h.run_at ? ` run_at:${h.run_at}` : ""}${h.ends_at ? ` ends_at:${h.ends_at}` : ""}`;
|
|
8926
|
+
}
|
|
8927
|
+
function scheduleBound(flag, v) {
|
|
8928
|
+
if (v === void 0) return void 0;
|
|
8929
|
+
const value = v.trim();
|
|
8930
|
+
if (!/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?(Z|[+-]\d{2}:\d{2})$/.test(value) || Number.isNaN(Date.parse(value))) {
|
|
8931
|
+
throw new Error(`${flag} must be an RFC3339 timestamp such as 2026-10-19T09:00:00Z \u2014 got ${JSON.stringify(v)}`);
|
|
8932
|
+
}
|
|
8933
|
+
return value;
|
|
8934
|
+
}
|
|
8935
|
+
autoCmd.command("create <name>").description(
|
|
8936
|
+
"Create an automation. Pass --run-at for one-off work at a date (it runs once, then stops for good) instead of a recurring automation that checks the date"
|
|
8937
|
+
).option("--description <text>", "What the automation does").option("--instructions <text>", "Instructions for each run (agent automation)").option("--script-js <js>", "Deterministic script body (scripted automation, no LLM)").option("--script-file <path>", "Read the script body from a file (scripted automation)").requiredOption("--interval <minutes>", "Interval in minutes (minimum 5)", (v) => parseInt(v, 10)).option("--timezone <tz>", "Scheduling timezone, e.g. America/New_York (default UTC)").option("--run-at <rfc3339>", "Run once at or after this instant, then stop permanently").option("--ends-at <rfc3339>", "Never run after this instant").addOption(new Option2("--agent-key <key>", "Executor for an agent automation").choices(["erdo.heartbeat-executor", "erdo.workflow-executor"])).addOption(new Option2("--conversation <target>", "Target 'new' (fresh thread) or 'direct' conversation").choices(["new", "direct"])).option("--thread-id <id>", "Post each run into an existing conversation UUID").option("--persona-id <id>", "Org-enabled persona UUID to speak as").action(
|
|
8938
|
+
async (name, opts) => {
|
|
8939
|
+
try {
|
|
8940
|
+
if (opts.scriptJs && opts.scriptFile) {
|
|
8941
|
+
fail(new Error("pass either --script-js or --script-file, not both"));
|
|
8942
|
+
return;
|
|
8943
|
+
}
|
|
8944
|
+
const scriptJs = opts.scriptFile ? readFileSync4(opts.scriptFile, "utf8") : opts.scriptJs;
|
|
8945
|
+
const h = await new ErdoClient().createHeartbeat({
|
|
8946
|
+
name,
|
|
8947
|
+
description: opts.description,
|
|
8948
|
+
instructions: opts.instructions,
|
|
8949
|
+
script_js: scriptJs,
|
|
8950
|
+
interval_minutes: opts.interval,
|
|
8951
|
+
timezone: opts.timezone,
|
|
8952
|
+
run_at: scheduleBound("--run-at", opts.runAt),
|
|
8953
|
+
ends_at: scheduleBound("--ends-at", opts.endsAt),
|
|
8954
|
+
agent_key: opts.agentKey,
|
|
8955
|
+
conversation: opts.conversation,
|
|
8956
|
+
thread_id: opts.threadId,
|
|
8957
|
+
persona_id: opts.personaId
|
|
8958
|
+
});
|
|
8959
|
+
console.log(`${h.id} ${h.state} ${h.name}${heartbeatBounds(h)}`);
|
|
8960
|
+
} catch (e) {
|
|
8961
|
+
fail(e);
|
|
8962
|
+
}
|
|
8963
|
+
}
|
|
8964
|
+
);
|
|
8890
8965
|
autoCmd.command("list").description("List automations").action(async () => {
|
|
8891
8966
|
try {
|
|
8892
8967
|
const { heartbeats } = await new ErdoClient().listHeartbeats();
|
|
8893
|
-
for (const h of heartbeats) console.log(`${h.id} ${h.state} ${h.name} ${h.kind === "script" ? "script" : h.thread_id ? `thread:${h.thread_id}` : h.conversation ?? "new"}${h.persona_id ? ` persona:${h.persona_id}` : ""}`);
|
|
8968
|
+
for (const h of heartbeats) console.log(`${h.id} ${h.state} ${h.name} ${h.kind === "script" ? "script" : h.thread_id ? `thread:${h.thread_id}` : h.conversation ?? "new"}${h.persona_id ? ` persona:${h.persona_id}` : ""}${heartbeatBounds(h)}`);
|
|
8894
8969
|
} catch (e) {
|
|
8895
8970
|
fail(e);
|
|
8896
8971
|
}
|
|
@@ -8916,7 +8991,7 @@ autoCmd.command("run <id>").description("Trigger an automation now").action(asyn
|
|
|
8916
8991
|
autoCmd.command("update <id>").description("Edit an automation \u2014 rename, reschedule, or replace its script/instructions").option("--name <name>", "New name").option("--description <text>", "New description").option("--instructions <text>", "Replacement instructions (agent automation)").addOption(new Option2("--agent-key <key>", "Executor for an agent automation; omit to keep current").choices(["erdo.heartbeat-executor", "erdo.workflow-executor"])).option("--script-js <js>", "Replacement script body (scripted automation)").option(
|
|
8917
8992
|
"--script-file <path>",
|
|
8918
8993
|
"Read the replacement script body from a file (scripted automation)"
|
|
8919
|
-
).addOption(new Option2("--conversation <target>", "Target 'new' (fresh thread) or 'direct' conversation").choices(["new", "direct"])).option("--thread-id <id>", "Post each run into an existing conversation UUID").option("--persona-id <id>", "Org-enabled persona UUID to speak as").option("--interval <minutes>", "New interval in minutes (minimum 5)", (v) => parseInt(v, 10)).option("--timezone <tz>", "New scheduling timezone, e.g. America/New_York").action(
|
|
8994
|
+
).addOption(new Option2("--conversation <target>", "Target 'new' (fresh thread) or 'direct' conversation").choices(["new", "direct"])).option("--thread-id <id>", "Post each run into an existing conversation UUID").option("--persona-id <id>", "Org-enabled persona UUID to speak as").option("--interval <minutes>", "New interval in minutes (minimum 5)", (v) => parseInt(v, 10)).option("--timezone <tz>", "New scheduling timezone, e.g. America/New_York").option("--run-at <rfc3339>", "New one-off fire time: runs once at or after it, then stops permanently (omit to keep)").option("--ends-at <rfc3339>", "New end time after which it never runs (omit to keep)").action(
|
|
8920
8995
|
async (id, opts) => {
|
|
8921
8996
|
try {
|
|
8922
8997
|
if (opts.scriptJs && opts.scriptFile) {
|
|
@@ -8932,6 +9007,8 @@ autoCmd.command("update <id>").description("Edit an automation \u2014 rename, re
|
|
|
8932
9007
|
script_js: scriptJs,
|
|
8933
9008
|
interval_minutes: opts.interval,
|
|
8934
9009
|
timezone: opts.timezone,
|
|
9010
|
+
run_at: scheduleBound("--run-at", opts.runAt),
|
|
9011
|
+
ends_at: scheduleBound("--ends-at", opts.endsAt),
|
|
8935
9012
|
conversation: opts.conversation,
|
|
8936
9013
|
thread_id: opts.threadId,
|
|
8937
9014
|
persona_id: opts.personaId
|
|
@@ -8941,7 +9018,7 @@ autoCmd.command("update <id>").description("Edit an automation \u2014 rename, re
|
|
|
8941
9018
|
return;
|
|
8942
9019
|
}
|
|
8943
9020
|
const h = await new ErdoClient().updateHeartbeat(id, body);
|
|
8944
|
-
console.log(`${h.id} ${h.state} ${h.name}`);
|
|
9021
|
+
console.log(`${h.id} ${h.state} ${h.name}${heartbeatBounds(h)}`);
|
|
8945
9022
|
} catch (e) {
|
|
8946
9023
|
fail(e);
|
|
8947
9024
|
}
|