@erdoai/cli 0.68.0 → 0.71.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/dist/index.js +296 -15
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -297,14 +297,19 @@ var ErdoClient = class {
|
|
|
297
297
|
createManagerKey() {
|
|
298
298
|
return this.request("POST", "/v1/manager-key");
|
|
299
299
|
}
|
|
300
|
-
// Brings an EXISTING org under your manager org
|
|
301
|
-
// token
|
|
302
|
-
//
|
|
303
|
-
//
|
|
304
|
-
//
|
|
305
|
-
// a
|
|
306
|
-
|
|
307
|
-
|
|
300
|
+
// Brings an EXISTING org under your manager org. Two consents redeem here: a
|
|
301
|
+
// one-time token minted by the target org's owner (or, for an ownerless managed
|
|
302
|
+
// org, its current manager) — a manager credential on its own can never adopt an
|
|
303
|
+
// arbitrary org — or, DIRECT adoption, the org's slug when the caller themselves
|
|
304
|
+
// is a seated owner of it (both halves of the consent in one person, so no token
|
|
305
|
+
// ferrying). The path is a sibling of /v1/managed-organizations rather than a
|
|
306
|
+
// nested `adopt` because Encore rejects a static segment alongside the
|
|
307
|
+
// parameterized /:orgSlug routes.
|
|
308
|
+
adoptManagedOrganization(input) {
|
|
309
|
+
return this.request("POST", "/v1/managed-organization-adoptions", {
|
|
310
|
+
token: input.token ?? "",
|
|
311
|
+
organization_slug: input.organizationSlug ?? ""
|
|
312
|
+
});
|
|
308
313
|
}
|
|
309
314
|
// Seats a person in a managed org. The role is capped at admin — ownership of a
|
|
310
315
|
// client org stays with the client — and an omitted role means the backend's
|
|
@@ -434,6 +439,22 @@ var ErdoClient = class {
|
|
|
434
439
|
getSentEmail(emailID) {
|
|
435
440
|
return this.request("GET", `/v1/emails/${encodeURIComponent(emailID)}`);
|
|
436
441
|
}
|
|
442
|
+
// Phone conversation records: inbound calls to a voice agent's own number and
|
|
443
|
+
// the outbound calls Erdo placed. Website chat widget conversations are a
|
|
444
|
+
// different resource — they live under /v1/voice/widget-conversations.
|
|
445
|
+
listVoiceCalls(params) {
|
|
446
|
+
const q = new URLSearchParams();
|
|
447
|
+
if (params?.agent) q.set("agent", params.agent);
|
|
448
|
+
if (params?.direction) q.set("direction", params.direction);
|
|
449
|
+
if (params?.limit !== void 0) q.set("limit", String(params.limit));
|
|
450
|
+
if (params?.offset !== void 0) q.set("offset", String(params.offset));
|
|
451
|
+
if (params?.cursor) q.set("cursor", params.cursor);
|
|
452
|
+
const qs = q.toString();
|
|
453
|
+
return this.request("GET", `/v1/voice/calls${qs ? `?${qs}` : ""}`);
|
|
454
|
+
}
|
|
455
|
+
getVoiceCall(callID) {
|
|
456
|
+
return this.request("GET", `/v1/voice/calls/${encodeURIComponent(callID)}`);
|
|
457
|
+
}
|
|
437
458
|
// Run a read-only HogQL query against the org's page-analytics events. Rows are
|
|
438
459
|
// positional per columns; enabled:false means page analytics is off for the org
|
|
439
460
|
// (not zero traffic). A rejected query surfaces PostHog's message as the error.
|
|
@@ -869,6 +890,14 @@ var ErdoClient = class {
|
|
|
869
890
|
listDatasetPurposes() {
|
|
870
891
|
return this.request("GET", `/v1/datasets-purposes`);
|
|
871
892
|
}
|
|
893
|
+
// Correct which dataset carries a purpose. Pass `purpose` to set one,
|
|
894
|
+
// `clear_purpose` to remove it; moving a purpose is clearing it from the old
|
|
895
|
+
// dataset and setting it on the right one. Setting a purpose the org already
|
|
896
|
+
// uses elsewhere succeeds and answers with a warning naming what it shadows,
|
|
897
|
+
// since one purpose is meant to map to one dataset.
|
|
898
|
+
setDatasetPurpose(slug, input) {
|
|
899
|
+
return this.request("POST", `/v1/datasets/${encodeURIComponent(slug)}/schema`, input);
|
|
900
|
+
}
|
|
872
901
|
// The endpoint's field is `question` (QueryDataNaturalLanguageInput). Sending // The endpoint's field is `question` (QueryDataNaturalLanguageInput). Sending
|
|
873
902
|
// `query` made every `erdo datasets query` fail with "question is required".
|
|
874
903
|
//
|
|
@@ -1165,17 +1194,36 @@ var ErdoClient = class {
|
|
|
1165
1194
|
`/v1/kv/${encodeURIComponent(slug)}/items/${encodeURIComponent(key)}`
|
|
1166
1195
|
);
|
|
1167
1196
|
}
|
|
1197
|
+
// --- page monitoring ---
|
|
1198
|
+
// These live here rather than being called through `request` from the command
|
|
1199
|
+
// layer: `request` is private, and reaching past it left the CLI's own
|
|
1200
|
+
// typecheck red.
|
|
1201
|
+
setPageMonitoring(id, body) {
|
|
1202
|
+
return this.request(
|
|
1203
|
+
"POST",
|
|
1204
|
+
`/v1/pages/monitoring/${encodeURIComponent(id)}`,
|
|
1205
|
+
body
|
|
1206
|
+
);
|
|
1207
|
+
}
|
|
1208
|
+
getPageMonitoring(id) {
|
|
1209
|
+
return this.request(
|
|
1210
|
+
"GET",
|
|
1211
|
+
`/v1/pages/monitoring/${encodeURIComponent(id)}`
|
|
1212
|
+
);
|
|
1213
|
+
}
|
|
1168
1214
|
// --- agent runs ---
|
|
1169
1215
|
listAgentRuns(opts) {
|
|
1170
1216
|
const params = new URLSearchParams();
|
|
1171
1217
|
if (opts.agent) params.set("agent_key", opts.agent);
|
|
1172
1218
|
if (opts.thread) params.set("thread_id", opts.thread);
|
|
1219
|
+
if (opts.status) params.set("status", opts.status);
|
|
1173
1220
|
if (opts.limit) params.set("limit", String(opts.limit));
|
|
1174
1221
|
const qs = params.toString();
|
|
1175
1222
|
return this.request("GET", `/v1/runs${qs ? `?${qs}` : ""}`);
|
|
1176
1223
|
}
|
|
1177
|
-
getAgentRun(id) {
|
|
1178
|
-
|
|
1224
|
+
getAgentRun(id, include = []) {
|
|
1225
|
+
const qs = include.length ? `?include=${encodeURIComponent(include.join(","))}` : "";
|
|
1226
|
+
return this.request("GET", `/v1/runs/${encodeURIComponent(id)}${qs}`);
|
|
1179
1227
|
}
|
|
1180
1228
|
// --- approvals ---
|
|
1181
1229
|
listApprovals(opts) {
|
|
@@ -1910,9 +1958,20 @@ id: ${o.id}`);
|
|
|
1910
1958
|
fail(e);
|
|
1911
1959
|
}
|
|
1912
1960
|
});
|
|
1913
|
-
managed.command("adopt
|
|
1961
|
+
managed.command("adopt [token]").description(
|
|
1962
|
+
"Take over an existing org: redeem a one-time consent token, or --from-org <slug> for an org you own yourself (no token needed)"
|
|
1963
|
+
).option("--from-org <slug>", "slug of an org you are an owner of, adopted directly").action(async (token, opts) => {
|
|
1914
1964
|
try {
|
|
1915
|
-
|
|
1965
|
+
if (!token && !opts.fromOrg) {
|
|
1966
|
+
fail(new Error("provide a consent token, or --from-org <slug> for an org you own"));
|
|
1967
|
+
}
|
|
1968
|
+
if (token && opts.fromOrg) {
|
|
1969
|
+
fail(new Error("provide a token or --from-org, not both"));
|
|
1970
|
+
}
|
|
1971
|
+
const o = await new ErdoClient().adoptManagedOrganization({
|
|
1972
|
+
token,
|
|
1973
|
+
organizationSlug: opts.fromOrg
|
|
1974
|
+
});
|
|
1916
1975
|
console.log(`Adopted managed org: ${o.name}`);
|
|
1917
1976
|
console.log(`slug: ${o.slug}
|
|
1918
1977
|
id: ${o.id}`);
|
|
@@ -3019,7 +3078,7 @@ agentCmd.command("messages <threadId>").description("Show a thread's messages (r
|
|
|
3019
3078
|
}
|
|
3020
3079
|
});
|
|
3021
3080
|
var runsCmd = program.command("runs").description("Inspect agent runs");
|
|
3022
|
-
runsCmd.command("list").description("List agent runs (filter by agent OR thread \u2014 mutually exclusive)").option("-a, --agent <key>", "filter by agent key").option("-t, --thread <id>", "filter to a thread").option("-l, --limit <n>", "max runs", (v) => parseInt(v, 10)).action(async (opts) => {
|
|
3081
|
+
runsCmd.command("list").description("List agent runs (filter by agent OR thread \u2014 mutually exclusive)").option("-a, --agent <key>", "filter by agent key").option("-t, --thread <id>", "filter to a thread").option("-s, --status <status>", "filter by status (running, completed, failed, cancelled, pending)").option("-l, --limit <n>", "max runs", (v) => parseInt(v, 10)).action(async (opts) => {
|
|
3023
3082
|
try {
|
|
3024
3083
|
const { runs } = await new ErdoClient().listAgentRuns(opts);
|
|
3025
3084
|
for (const r of runs) console.log(`${r.id} ${r.status} ${r.agent_key}`);
|
|
@@ -3027,13 +3086,130 @@ runsCmd.command("list").description("List agent runs (filter by agent OR thread
|
|
|
3027
3086
|
fail(e);
|
|
3028
3087
|
}
|
|
3029
3088
|
});
|
|
3030
|
-
runsCmd.command("get <id>").description("Show an agent run (status, output, trace metadata)").action(async (id) => {
|
|
3089
|
+
runsCmd.command("get <id>").description("Show an agent run (status, output, trace metadata)").option("--resources", "also show the skills, knowledge and entities the run used").option("--steps", "also show the run's iterations, tool calls and sub-agent invocations").action(async (id, opts) => {
|
|
3031
3090
|
try {
|
|
3032
|
-
|
|
3091
|
+
const include = [];
|
|
3092
|
+
if (opts.resources) include.push("resources");
|
|
3093
|
+
if (opts.steps) include.push("steps");
|
|
3094
|
+
const detail = await new ErdoClient().getAgentRun(id, include);
|
|
3095
|
+
if (include.length === 0) {
|
|
3096
|
+
print(detail);
|
|
3097
|
+
return;
|
|
3098
|
+
}
|
|
3099
|
+
printRunDetail(detail, opts.resources === true, opts.steps === true);
|
|
3033
3100
|
} catch (e) {
|
|
3034
3101
|
fail(e);
|
|
3035
3102
|
}
|
|
3036
3103
|
});
|
|
3104
|
+
var RUN_ARRIVAL_LABELS = {
|
|
3105
|
+
"knowledge.kind_pinned": "pinned for this kind",
|
|
3106
|
+
"knowledge.list": "pinned",
|
|
3107
|
+
"knowledge.search": "ambient search",
|
|
3108
|
+
"knowledge.tool_search": "fetched mid-run",
|
|
3109
|
+
"knowledge.tool_list": "listed mid-run",
|
|
3110
|
+
judge_lens: "review lens",
|
|
3111
|
+
persona_lens: "persona lens",
|
|
3112
|
+
read_knowledge_object: "read directly",
|
|
3113
|
+
definition: "definition",
|
|
3114
|
+
constraint_source: "constraint source"
|
|
3115
|
+
};
|
|
3116
|
+
function arrivalLabel(usageKind) {
|
|
3117
|
+
return RUN_ARRIVAL_LABELS[usageKind] || usageKind || "used";
|
|
3118
|
+
}
|
|
3119
|
+
function resourceKind(resource) {
|
|
3120
|
+
if (resource.resource_type === "query_constraint") return "query_constraint";
|
|
3121
|
+
return resource.type || resource.resource_type;
|
|
3122
|
+
}
|
|
3123
|
+
function printRunDetail(detail, wantResources, wantSteps) {
|
|
3124
|
+
const run = detail.run;
|
|
3125
|
+
console.log(`${run.id} ${run.status} ${run.agent_name || run.agent_key}`);
|
|
3126
|
+
if (wantResources) {
|
|
3127
|
+
const resources = detail.resources ?? [];
|
|
3128
|
+
console.log("");
|
|
3129
|
+
console.log("Resources used");
|
|
3130
|
+
if (resources.length === 0) {
|
|
3131
|
+
console.log(" (none recorded)");
|
|
3132
|
+
} else {
|
|
3133
|
+
const byKind = /* @__PURE__ */ new Map();
|
|
3134
|
+
for (const resource of resources) {
|
|
3135
|
+
const kind = resourceKind(resource);
|
|
3136
|
+
const key = resource.resource_id || resource.id;
|
|
3137
|
+
const group = byKind.get(kind) ?? /* @__PURE__ */ new Map();
|
|
3138
|
+
const entry = group.get(key) ?? {
|
|
3139
|
+
name: resource.display_name || resource.resource_id || resource.id,
|
|
3140
|
+
arrivals: []
|
|
3141
|
+
};
|
|
3142
|
+
const arrival = arrivalLabel(resource.usage_kind);
|
|
3143
|
+
if (!entry.arrivals.includes(arrival)) entry.arrivals.push(arrival);
|
|
3144
|
+
group.set(key, entry);
|
|
3145
|
+
byKind.set(kind, group);
|
|
3146
|
+
}
|
|
3147
|
+
const kinds = [...byKind.keys()].sort(
|
|
3148
|
+
(a, b) => a === "skill" ? -1 : b === "skill" ? 1 : a.localeCompare(b)
|
|
3149
|
+
);
|
|
3150
|
+
for (const kind of kinds) {
|
|
3151
|
+
console.log(` ${kind}`);
|
|
3152
|
+
for (const entry of byKind.get(kind).values()) {
|
|
3153
|
+
console.log(` ${entry.name} (${entry.arrivals.join(", ")})`);
|
|
3154
|
+
}
|
|
3155
|
+
}
|
|
3156
|
+
}
|
|
3157
|
+
}
|
|
3158
|
+
if (wantSteps) {
|
|
3159
|
+
const steps = detail.steps ?? [];
|
|
3160
|
+
console.log("");
|
|
3161
|
+
console.log("Steps");
|
|
3162
|
+
if (steps.length === 0) {
|
|
3163
|
+
console.log(" (none recorded)");
|
|
3164
|
+
} else {
|
|
3165
|
+
printStepTree(steps);
|
|
3166
|
+
}
|
|
3167
|
+
}
|
|
3168
|
+
}
|
|
3169
|
+
function printStepTree(steps) {
|
|
3170
|
+
const childrenOf = /* @__PURE__ */ new Map();
|
|
3171
|
+
const known = new Set(steps.map((s) => s.step_id));
|
|
3172
|
+
const roots = [];
|
|
3173
|
+
for (const step of steps) {
|
|
3174
|
+
if (step.parent_step_id && known.has(step.parent_step_id)) {
|
|
3175
|
+
const siblings = childrenOf.get(step.parent_step_id) ?? [];
|
|
3176
|
+
siblings.push(step);
|
|
3177
|
+
childrenOf.set(step.parent_step_id, siblings);
|
|
3178
|
+
} else {
|
|
3179
|
+
roots.push(step);
|
|
3180
|
+
}
|
|
3181
|
+
}
|
|
3182
|
+
const printed = /* @__PURE__ */ new Set();
|
|
3183
|
+
const walk = (step, depth) => {
|
|
3184
|
+
if (printed.has(step.step_id)) return;
|
|
3185
|
+
printed.add(step.step_id);
|
|
3186
|
+
console.log(`${" ".repeat(depth + 1)}${stepLine(step)}`);
|
|
3187
|
+
for (const child of childrenOf.get(step.step_id) ?? []) walk(child, depth + 1);
|
|
3188
|
+
};
|
|
3189
|
+
for (const root of roots) walk(root, 0);
|
|
3190
|
+
for (const step of steps) {
|
|
3191
|
+
if (!printed.has(step.step_id)) {
|
|
3192
|
+
printed.add(step.step_id);
|
|
3193
|
+
console.log(` ${stepLine(step)}`);
|
|
3194
|
+
}
|
|
3195
|
+
}
|
|
3196
|
+
}
|
|
3197
|
+
function stepLine(step) {
|
|
3198
|
+
const parts = [];
|
|
3199
|
+
if (step.kind === "agent") {
|
|
3200
|
+
parts.push(`agent ${step.agent_key || step.name}`);
|
|
3201
|
+
if (step.agent_run_id) parts.push(`run ${step.agent_run_id}`);
|
|
3202
|
+
} else if (step.kind === "tool_call") {
|
|
3203
|
+
parts.push(`tool ${step.name}`);
|
|
3204
|
+
} else {
|
|
3205
|
+
parts.push(step.iteration ? `iteration ${step.iteration}` : "iteration");
|
|
3206
|
+
if (step.name) parts.push(step.name);
|
|
3207
|
+
}
|
|
3208
|
+
if (step.duration_ms !== void 0) parts.push(`${step.duration_ms}ms`);
|
|
3209
|
+
if (step.status) parts.push(step.status);
|
|
3210
|
+
if (step.error) parts.push(`error: ${step.error}`);
|
|
3211
|
+
return parts.join(" ");
|
|
3212
|
+
}
|
|
3037
3213
|
var decisionsCmd = program.command("decisions").description(
|
|
3038
3214
|
"The decision record \u2014 what your organization committed to, whether the change actually happened, and what the evidence said afterwards"
|
|
3039
3215
|
);
|
|
@@ -3943,6 +4119,32 @@ pagesCmd.command("restore <id>").description("Restore a previously deleted page
|
|
|
3943
4119
|
fail(e);
|
|
3944
4120
|
}
|
|
3945
4121
|
});
|
|
4122
|
+
pagesCmd.command("monitor <id>").description(
|
|
4123
|
+
"Control health monitoring for a published page: --exclude stops all probing (dashboards, trackers, demos); --ack <sig> acknowledges a known failure class (mobile:overflow, desktop:resource) so it stops alerting and never auto-repairs while a NEW failure class still alerts"
|
|
4124
|
+
).option("--exclude [reason]", "exclude this page from monitoring entirely (optional reason)").option("--include", "re-include the page in monitoring").option("--ack <sigs>", "comma-separated failure signatures to acknowledge (e.g. mobile:overflow,desktop:resource)").option("--unack <sigs>", "comma-separated failure signatures to un-acknowledge").option("--reset", "clear all acknowledged failure signatures").action(async (id, opts) => {
|
|
4125
|
+
try {
|
|
4126
|
+
const api = new ErdoClient();
|
|
4127
|
+
const body = {};
|
|
4128
|
+
if (opts.include) body.excluded = false;
|
|
4129
|
+
else if (typeof opts.exclude === "string") {
|
|
4130
|
+
body.excluded = true;
|
|
4131
|
+
body.excluded_reason = opts.exclude;
|
|
4132
|
+
} else if (opts.exclude === true) body.excluded = true;
|
|
4133
|
+
if (opts.ack) body.ack_add = opts.ack.split(",").map((s) => s.trim()).filter(Boolean);
|
|
4134
|
+
if (opts.unack) body.ack_remove = opts.unack.split(",").map((s) => s.trim()).filter(Boolean);
|
|
4135
|
+
if (opts.reset) body.ack_reset = true;
|
|
4136
|
+
print(await api.setPageMonitoring(id, body));
|
|
4137
|
+
} catch (e) {
|
|
4138
|
+
fail(e);
|
|
4139
|
+
}
|
|
4140
|
+
});
|
|
4141
|
+
pagesCmd.command("monitor-get <id>").description("Read a page's monitoring controls (excluded flag + acknowledged failure signatures)").action(async (id) => {
|
|
4142
|
+
try {
|
|
4143
|
+
print(await new ErdoClient().getPageMonitoring(id));
|
|
4144
|
+
} catch (e) {
|
|
4145
|
+
fail(e);
|
|
4146
|
+
}
|
|
4147
|
+
});
|
|
3946
4148
|
pagesCmd.command("clone <id>").description(
|
|
3947
4149
|
"Copy a page. The copy is byte-identical and starts private (publish state is never inherited); the source's lead-form pipelines are duplicated onto it and the ids in its content rewritten, so it captures its own leads"
|
|
3948
4150
|
).option("--title <title>", 'title for the copy (default: "Copy of <source title>")').option("--json", "print the full JSON, including the cloned pipelines and id rewrites").action(async (id, opts) => {
|
|
@@ -4389,6 +4591,59 @@ sentEmailsCmd.command("get <emailID>").description("Read one sent email, includi
|
|
|
4389
4591
|
fail(e);
|
|
4390
4592
|
}
|
|
4391
4593
|
});
|
|
4594
|
+
var voiceCmd = program.command("voice").description("Read voice agent phone conversations");
|
|
4595
|
+
var voiceCallsCmd = voiceCmd.command("calls").description("Inbound and outbound phone call records");
|
|
4596
|
+
voiceCallsCmd.command("list").description("List the organization's phone calls, newest first").option("--agent <slug>", "only calls held by this voice agent, by slug").option("--direction <direction>", "only 'inbound' (calls to the agent's number) or 'outbound'").option("--limit <n>", "page size (default 25, max 100)").option("--offset <n>", "rows to skip").option("--cursor <cursor>", "next_cursor from the preceding page (stable paging)").option("--json", "print the raw JSON result instead of a table").action(
|
|
4597
|
+
async (opts) => {
|
|
4598
|
+
try {
|
|
4599
|
+
const res = await new ErdoClient().listVoiceCalls({
|
|
4600
|
+
agent: opts.agent,
|
|
4601
|
+
direction: opts.direction,
|
|
4602
|
+
limit: opts.limit ? Number(opts.limit) : void 0,
|
|
4603
|
+
offset: opts.offset ? Number(opts.offset) : void 0,
|
|
4604
|
+
cursor: opts.cursor
|
|
4605
|
+
});
|
|
4606
|
+
if (opts.json) {
|
|
4607
|
+
print(res);
|
|
4608
|
+
return;
|
|
4609
|
+
}
|
|
4610
|
+
const calls = res.conversations ?? [];
|
|
4611
|
+
if (calls.length === 0) {
|
|
4612
|
+
console.log("No calls match those filters.");
|
|
4613
|
+
return;
|
|
4614
|
+
}
|
|
4615
|
+
printAlignedTable(
|
|
4616
|
+
["call id", "direction", "from", "to", "status", "secs", "transcript", "started", "summary"],
|
|
4617
|
+
calls.map((call) => [
|
|
4618
|
+
call.call_id,
|
|
4619
|
+
call.direction,
|
|
4620
|
+
call.from_number ?? "",
|
|
4621
|
+
call.to_number,
|
|
4622
|
+
call.status,
|
|
4623
|
+
call.duration_seconds,
|
|
4624
|
+
call.has_transcript ? "yes" : "no",
|
|
4625
|
+
call.created_at,
|
|
4626
|
+
call.transcript_summary
|
|
4627
|
+
])
|
|
4628
|
+
);
|
|
4629
|
+
process.stderr.write(`showing ${calls.length} call(s)
|
|
4630
|
+
`);
|
|
4631
|
+
if (res.next_cursor) {
|
|
4632
|
+
process.stderr.write(`more available \u2014 re-run with --cursor ${res.next_cursor}
|
|
4633
|
+
`);
|
|
4634
|
+
}
|
|
4635
|
+
} catch (e) {
|
|
4636
|
+
fail(e);
|
|
4637
|
+
}
|
|
4638
|
+
}
|
|
4639
|
+
);
|
|
4640
|
+
voiceCallsCmd.command("get <callID>").description("Read one phone call in full: transcript, summary, and per-turn LLM metrics").action(async (callID) => {
|
|
4641
|
+
try {
|
|
4642
|
+
print(await new ErdoClient().getVoiceCall(callID));
|
|
4643
|
+
} catch (e) {
|
|
4644
|
+
fail(e);
|
|
4645
|
+
}
|
|
4646
|
+
});
|
|
4392
4647
|
var datasetsCmd = program.command("datasets").description("Datasets");
|
|
4393
4648
|
datasetsCmd.command("list").description("List datasets").option(
|
|
4394
4649
|
"--class <class>",
|
|
@@ -4433,6 +4688,32 @@ datasetsCmd.command("purposes").description(
|
|
|
4433
4688
|
fail(e);
|
|
4434
4689
|
}
|
|
4435
4690
|
});
|
|
4691
|
+
datasetsCmd.command("set-purpose <slug> [purpose]").description(
|
|
4692
|
+
"Set (or with --clear, remove) the dataset's purpose \u2014 the org-vocabulary role `datasets purposes` reports. Use it to CORRECT a purpose that names the wrong dataset: one purpose maps to one dataset per org, so a purpose left on the wrong one points everything that resolves through the vocabulary at the wrong rows. Moving a purpose is two calls \u2014 clear it from the old dataset, set it on the right one. Setting a purpose the org already uses elsewhere succeeds and prints a warning naming what it now shadows."
|
|
4693
|
+
).option("--clear", "remove the dataset's purpose instead of setting one").option(
|
|
4694
|
+
"--description <text>",
|
|
4695
|
+
"description for a purpose new to this org; ignored when the org already has an entry for it"
|
|
4696
|
+
).action(async (slug, purpose, opts) => {
|
|
4697
|
+
try {
|
|
4698
|
+
if (opts.clear && purpose) {
|
|
4699
|
+
fail(new Error("pass a purpose or --clear, not both"));
|
|
4700
|
+
return;
|
|
4701
|
+
}
|
|
4702
|
+
if (!opts.clear && !purpose) {
|
|
4703
|
+
fail(new Error("pass a purpose to set, or --clear to remove one"));
|
|
4704
|
+
return;
|
|
4705
|
+
}
|
|
4706
|
+
const res = await new ErdoClient().setDatasetPurpose(slug, {
|
|
4707
|
+
...purpose ? { purpose } : {},
|
|
4708
|
+
...opts.clear ? { clear_purpose: true } : {},
|
|
4709
|
+
...opts.description ? { purpose_description: opts.description } : {}
|
|
4710
|
+
});
|
|
4711
|
+
for (const w of res.warnings ?? []) console.error(`warning: ${w}`);
|
|
4712
|
+
console.log(res.purpose ? `${slug} ${res.purpose}` : `${slug} (no purpose)`);
|
|
4713
|
+
} catch (e) {
|
|
4714
|
+
fail(e);
|
|
4715
|
+
}
|
|
4716
|
+
});
|
|
4436
4717
|
datasetsCmd.command("query <slug> <question>").description(
|
|
4437
4718
|
"Ask a natural-language question of a dataset \u2014 Erdo writes and runs the SQL, and answers with that SQL alongside the values. It runs an agent, so it is slower and two identical questions can produce two different queries: for a deterministic or scripted read, write the SQL yourself with `datasets fetch --sql`."
|
|
4438
4719
|
).action(async (slug, question) => {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@erdoai/cli",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Erdo CLI
|
|
3
|
+
"version": "0.71.0",
|
|
4
|
+
"description": "Erdo CLI \u2014 drive datasets, pages, and evals from the terminal or CI",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"erdo": "dist/index.js"
|
|
@@ -53,4 +53,4 @@
|
|
|
53
53
|
"overrides": {
|
|
54
54
|
"esbuild": "^0.28.1"
|
|
55
55
|
}
|
|
56
|
-
}
|
|
56
|
+
}
|