@erdoai/cli 0.74.0 → 0.76.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 +153 -17
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -482,6 +482,32 @@ var ErdoClient = class {
|
|
|
482
482
|
getVoiceCall(callID) {
|
|
483
483
|
return this.request("GET", `/v1/voice/calls/${encodeURIComponent(callID)}`);
|
|
484
484
|
}
|
|
485
|
+
// Website concierge widget conversations — the text/voice/video sessions
|
|
486
|
+
// visitors held on the customer's own pages. Covers every widget in the org
|
|
487
|
+
// unless narrowed by the widget handle (its name or public key, vw_...).
|
|
488
|
+
listWidgetConversations(params) {
|
|
489
|
+
const q = new URLSearchParams();
|
|
490
|
+
if (params?.widget) q.set("widget", params.widget);
|
|
491
|
+
if (params?.since) q.set("since", params.since);
|
|
492
|
+
if (params?.until) q.set("until", params.until);
|
|
493
|
+
if (params?.limit !== void 0) q.set("limit", String(params.limit));
|
|
494
|
+
if (params?.offset !== void 0) q.set("offset", String(params.offset));
|
|
495
|
+
if (params?.cursor) q.set("cursor", params.cursor);
|
|
496
|
+
const qs = q.toString();
|
|
497
|
+
return this.request(
|
|
498
|
+
"GET",
|
|
499
|
+
`/v1/voice/widget-conversations${qs ? `?${qs}` : ""}`
|
|
500
|
+
);
|
|
501
|
+
}
|
|
502
|
+
// The session id identifies the conversation on its own and another org's
|
|
503
|
+
// session is never returned, so the widget handle is optional.
|
|
504
|
+
getWidgetConversation(sessionID, widget) {
|
|
505
|
+
return this.request(
|
|
506
|
+
"POST",
|
|
507
|
+
`/v1/voice/widget-conversations/get`,
|
|
508
|
+
{ session_id: sessionID, ...widget ? { widget } : {} }
|
|
509
|
+
);
|
|
510
|
+
}
|
|
485
511
|
// Run a read-only HogQL query against the org's page-analytics events. Rows are
|
|
486
512
|
// positional per columns; enabled:false means page analytics is off for the org
|
|
487
513
|
// (not zero traffic). A rejected query surfaces PostHog's message as the error.
|
|
@@ -2246,20 +2272,27 @@ wsCmd.command("get <slug>").description("Show a workstream \u2014 status, phases
|
|
|
2246
2272
|
fail(e);
|
|
2247
2273
|
}
|
|
2248
2274
|
});
|
|
2249
|
-
wsCmd.command("create").description("Create a workstream in a project").requiredOption("--project <slug>", "project slug").requiredOption("--slug <slug>", "workstream slug (unique per org)").requiredOption("--title <title>", "human-readable title").option("--description <text>", "free-form description").
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2275
|
+
wsCmd.command("create").description("Create a workstream in a project").requiredOption("--project <slug>", "project slug").requiredOption("--slug <slug>", "workstream slug (unique per org)").requiredOption("--title <title>", "human-readable title").option("--description <text>", "free-form description").option(
|
|
2276
|
+
"--token-budget <millicents>",
|
|
2277
|
+
"spend ceiling in millicents \u2014 100,000 per dollar, so 10000000 = $100 and 25000000 = $250; omit for unlimited",
|
|
2278
|
+
(v) => parseInt(v, 10)
|
|
2279
|
+
).action(
|
|
2280
|
+
async (opts) => {
|
|
2281
|
+
try {
|
|
2282
|
+
print(
|
|
2283
|
+
await new ErdoClient().createWorkstream({
|
|
2284
|
+
project_slug: opts.project,
|
|
2285
|
+
slug: opts.slug,
|
|
2286
|
+
title: opts.title,
|
|
2287
|
+
description: opts.description,
|
|
2288
|
+
token_budget_millicents: opts.tokenBudget
|
|
2289
|
+
})
|
|
2290
|
+
);
|
|
2291
|
+
} catch (e) {
|
|
2292
|
+
fail(e);
|
|
2293
|
+
}
|
|
2261
2294
|
}
|
|
2262
|
-
|
|
2295
|
+
);
|
|
2263
2296
|
wsCmd.command("log <slug> <body...>").description("Append a line to the workstream's event log").action(async (slug, body) => {
|
|
2264
2297
|
try {
|
|
2265
2298
|
print(await new ErdoClient().appendWorkstreamEvent(slug, body.join(" ")));
|
|
@@ -2290,6 +2323,10 @@ wsCmd.command("phase-add <slug>").description("Append a new phase to a workstrea
|
|
|
2290
2323
|
}
|
|
2291
2324
|
);
|
|
2292
2325
|
wsCmd.command("set-state <slug>").description("Update a workstream's status / title / description / phase statuses").option("--status <status>", "active, awaiting_user, blocked, completed").option("--title <title>", "retitle").option("--description <text>", "describe current state / surface a blocker").option("--summary <text>", "one-line audit-log summary of this update").option(
|
|
2326
|
+
"--token-budget <millicents>",
|
|
2327
|
+
"spend ceiling in millicents \u2014 100,000 per dollar, so 25000000 = $250; this is how you raise a ceiling a loop has spent through",
|
|
2328
|
+
(v) => parseInt(v, 10)
|
|
2329
|
+
).option("--escalation-budget <n>", "max urgent attention items per rolling 24h", (v) => parseInt(v, 10)).option(
|
|
2293
2330
|
"--phase <slug=status>",
|
|
2294
2331
|
"set one phase's status (repeatable), e.g. --phase brand-brief=completed --phase landing-pages=running; statuses: pending, running, awaiting_user, blocked, completed, skipped, failed",
|
|
2295
2332
|
collect,
|
|
@@ -2312,6 +2349,8 @@ wsCmd.command("set-state <slug>").description("Update a workstream's status / ti
|
|
|
2312
2349
|
title: opts.title,
|
|
2313
2350
|
description: opts.description,
|
|
2314
2351
|
summary: opts.summary,
|
|
2352
|
+
token_budget_millicents: opts.tokenBudget,
|
|
2353
|
+
escalation_budget_per_day: opts.escalationBudget,
|
|
2315
2354
|
phase_updates: phaseUpdates.length > 0 ? phaseUpdates : void 0
|
|
2316
2355
|
})
|
|
2317
2356
|
);
|
|
@@ -2320,7 +2359,11 @@ wsCmd.command("set-state <slug>").description("Update a workstream's status / ti
|
|
|
2320
2359
|
}
|
|
2321
2360
|
}
|
|
2322
2361
|
);
|
|
2323
|
-
wsCmd.command("arm <slug>").description("Arm the recurring reconciliation loop (allocator pass) for a workstream").option("--interval <minutes>", "how often the loop runs", (v) => parseInt(v, 10)).option("--model <id>", "model the loop runs on").option("--timezone <tz>", "timezone for scheduling").option(
|
|
2362
|
+
wsCmd.command("arm <slug>").description("Arm the recurring reconciliation loop (allocator pass) for a workstream").option("--interval <minutes>", "how often the loop runs", (v) => parseInt(v, 10)).option("--model <id>", "model the loop runs on").option("--timezone <tz>", "timezone for scheduling").option(
|
|
2363
|
+
"--token-budget <millicents>",
|
|
2364
|
+
"total spend ceiling in millicents \u2014 100,000 per dollar, so 25000000 = $250; also how you raise a ceiling a loop has spent through",
|
|
2365
|
+
(v) => parseInt(v, 10)
|
|
2366
|
+
).option("--escalation-budget <n>", "max escalations to a human per day", (v) => parseInt(v, 10)).action(
|
|
2324
2367
|
async (slug, opts) => {
|
|
2325
2368
|
try {
|
|
2326
2369
|
const client = new ErdoClient();
|
|
@@ -4736,7 +4779,17 @@ sentEmailsCmd.command("get <emailID>").description("Read one sent email, includi
|
|
|
4736
4779
|
fail(e);
|
|
4737
4780
|
}
|
|
4738
4781
|
});
|
|
4739
|
-
var voiceCmd = program.command("voice").description("Read voice agent phone conversations");
|
|
4782
|
+
var voiceCmd = program.command("voice").description("Read voice agent phone calls and website widget conversations");
|
|
4783
|
+
function contactLabel(contact) {
|
|
4784
|
+
if (!contact) return "-";
|
|
4785
|
+
const name = [contact.first_name, contact.last_name].filter(Boolean).join(" ");
|
|
4786
|
+
const reach = contact.email || contact.phone;
|
|
4787
|
+
if (!name && !reach) return "none";
|
|
4788
|
+
return [name, reach].filter(Boolean).join(" \xB7 ");
|
|
4789
|
+
}
|
|
4790
|
+
function leadStatusLabel(outcome) {
|
|
4791
|
+
return outcome ? outcome : "pending";
|
|
4792
|
+
}
|
|
4740
4793
|
var voiceCallsCmd = voiceCmd.command("calls").description("Inbound and outbound phone call records");
|
|
4741
4794
|
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(
|
|
4742
4795
|
async (opts) => {
|
|
@@ -4758,7 +4811,19 @@ voiceCallsCmd.command("list").description("List the organization's phone calls,
|
|
|
4758
4811
|
return;
|
|
4759
4812
|
}
|
|
4760
4813
|
printAlignedTable(
|
|
4761
|
-
[
|
|
4814
|
+
[
|
|
4815
|
+
"call id",
|
|
4816
|
+
"direction",
|
|
4817
|
+
"from",
|
|
4818
|
+
"to",
|
|
4819
|
+
"status",
|
|
4820
|
+
"secs",
|
|
4821
|
+
"transcript",
|
|
4822
|
+
"contact",
|
|
4823
|
+
"lead",
|
|
4824
|
+
"started",
|
|
4825
|
+
"summary"
|
|
4826
|
+
],
|
|
4762
4827
|
calls.map((call) => [
|
|
4763
4828
|
call.call_id,
|
|
4764
4829
|
call.direction,
|
|
@@ -4767,6 +4832,8 @@ voiceCallsCmd.command("list").description("List the organization's phone calls,
|
|
|
4767
4832
|
call.status,
|
|
4768
4833
|
call.duration_seconds,
|
|
4769
4834
|
call.has_transcript ? "yes" : "no",
|
|
4835
|
+
contactLabel(call.contact),
|
|
4836
|
+
leadStatusLabel(call.lead_status),
|
|
4770
4837
|
call.created_at,
|
|
4771
4838
|
call.transcript_summary
|
|
4772
4839
|
])
|
|
@@ -4782,13 +4849,82 @@ voiceCallsCmd.command("list").description("List the organization's phone calls,
|
|
|
4782
4849
|
}
|
|
4783
4850
|
}
|
|
4784
4851
|
);
|
|
4785
|
-
voiceCallsCmd.command("get <callID>").description(
|
|
4852
|
+
voiceCallsCmd.command("get <callID>").description(
|
|
4853
|
+
"Read one phone call in full: transcript, summary, per-turn LLM metrics, and the lead it produced (contact, lead_status, lead_saved_at)"
|
|
4854
|
+
).action(async (callID) => {
|
|
4786
4855
|
try {
|
|
4787
4856
|
print(await new ErdoClient().getVoiceCall(callID));
|
|
4788
4857
|
} catch (e) {
|
|
4789
4858
|
fail(e);
|
|
4790
4859
|
}
|
|
4791
4860
|
});
|
|
4861
|
+
var widgetConversationsCmd = voiceCmd.command("widget-conversations").description("Website concierge widget conversations (text, voice and video sessions)");
|
|
4862
|
+
widgetConversationsCmd.command("list").description("List the organization's widget conversations, newest first").option("--widget <handle>", "only this widget's conversations \u2014 its name or public key (vw_...)").option("--since <rfc3339>", "only conversations that started at or after this time (inclusive)").option("--until <rfc3339>", "only conversations that started before this time (exclusive)").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(
|
|
4863
|
+
async (opts) => {
|
|
4864
|
+
try {
|
|
4865
|
+
const res = await new ErdoClient().listWidgetConversations({
|
|
4866
|
+
widget: opts.widget,
|
|
4867
|
+
since: opts.since,
|
|
4868
|
+
until: opts.until,
|
|
4869
|
+
limit: opts.limit ? Number(opts.limit) : void 0,
|
|
4870
|
+
offset: opts.offset ? Number(opts.offset) : void 0,
|
|
4871
|
+
cursor: opts.cursor
|
|
4872
|
+
});
|
|
4873
|
+
if (opts.json) {
|
|
4874
|
+
print(res);
|
|
4875
|
+
return;
|
|
4876
|
+
}
|
|
4877
|
+
const conversations = res.conversations ?? [];
|
|
4878
|
+
if (conversations.length === 0) {
|
|
4879
|
+
console.log("No conversations match those filters.");
|
|
4880
|
+
return;
|
|
4881
|
+
}
|
|
4882
|
+
printAlignedTable(
|
|
4883
|
+
[
|
|
4884
|
+
"session id",
|
|
4885
|
+
"widget",
|
|
4886
|
+
"modality",
|
|
4887
|
+
"secs",
|
|
4888
|
+
"transcript",
|
|
4889
|
+
"contact",
|
|
4890
|
+
"lead",
|
|
4891
|
+
"started",
|
|
4892
|
+
"summary"
|
|
4893
|
+
],
|
|
4894
|
+
conversations.map((c) => [
|
|
4895
|
+
c.session_id,
|
|
4896
|
+
c.widget_name || c.widget,
|
|
4897
|
+
c.modality,
|
|
4898
|
+
c.duration_seconds,
|
|
4899
|
+
c.has_transcript ? "yes" : "no",
|
|
4900
|
+
contactLabel(c.contact),
|
|
4901
|
+
leadStatusLabel(c.lead_status),
|
|
4902
|
+
c.created_at,
|
|
4903
|
+
c.transcript_summary
|
|
4904
|
+
])
|
|
4905
|
+
);
|
|
4906
|
+
process.stderr.write(
|
|
4907
|
+
`showing ${conversations.length} of ${res.total} conversation(s)
|
|
4908
|
+
`
|
|
4909
|
+
);
|
|
4910
|
+
if (res.next_cursor) {
|
|
4911
|
+
process.stderr.write(`more available \u2014 re-run with --cursor ${res.next_cursor}
|
|
4912
|
+
`);
|
|
4913
|
+
}
|
|
4914
|
+
} catch (e) {
|
|
4915
|
+
fail(e);
|
|
4916
|
+
}
|
|
4917
|
+
}
|
|
4918
|
+
);
|
|
4919
|
+
widgetConversationsCmd.command("get <sessionID>").description(
|
|
4920
|
+
"Read one widget conversation in full: transcript, summary, topics, per-turn LLM metrics, and the lead it produced (contact, lead_status, lead_saved_at)"
|
|
4921
|
+
).option("--widget <handle>", "the widget it belongs to \u2014 optional, the session id identifies it").action(async (sessionID, opts) => {
|
|
4922
|
+
try {
|
|
4923
|
+
print(await new ErdoClient().getWidgetConversation(sessionID, opts.widget));
|
|
4924
|
+
} catch (e) {
|
|
4925
|
+
fail(e);
|
|
4926
|
+
}
|
|
4927
|
+
});
|
|
4792
4928
|
var datasetsCmd = program.command("datasets").description("Datasets");
|
|
4793
4929
|
datasetsCmd.command("list").description("List datasets").option(
|
|
4794
4930
|
"--class <class>",
|