@erdoai/cli 0.79.0 → 0.81.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 +167 -4
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -508,6 +508,32 @@ var ErdoClient = class {
|
|
|
508
508
|
{ session_id: sessionID, ...widget ? { widget } : {} }
|
|
509
509
|
);
|
|
510
510
|
}
|
|
511
|
+
// Text conversations on those same numbers. A conversation is one Erdo number
|
|
512
|
+
// and one person, for as long as they keep texting — so it is addressed by its
|
|
513
|
+
// session id rather than by a message id, and narrowed by the two numbers
|
|
514
|
+
// rather than by an agent slug and a direction.
|
|
515
|
+
listSMSConversations(params) {
|
|
516
|
+
const q = new URLSearchParams();
|
|
517
|
+
if (params?.phone_number) q.set("phone_number", params.phone_number);
|
|
518
|
+
if (params?.address) q.set("address", params.address);
|
|
519
|
+
if (params?.limit !== void 0) q.set("limit", String(params.limit));
|
|
520
|
+
if (params?.cursor) q.set("cursor", params.cursor);
|
|
521
|
+
const qs = q.toString();
|
|
522
|
+
return this.request(
|
|
523
|
+
"GET",
|
|
524
|
+
`/v1/voice/sms-conversations${qs ? `?${qs}` : ""}`
|
|
525
|
+
);
|
|
526
|
+
}
|
|
527
|
+
getSMSConversation(sessionID, params) {
|
|
528
|
+
const q = new URLSearchParams();
|
|
529
|
+
if (params?.limit !== void 0) q.set("limit", String(params.limit));
|
|
530
|
+
if (params?.cursor) q.set("cursor", params.cursor);
|
|
531
|
+
const qs = q.toString();
|
|
532
|
+
return this.request(
|
|
533
|
+
"GET",
|
|
534
|
+
`/v1/voice/sms-conversations/${encodeURIComponent(sessionID)}${qs ? `?${qs}` : ""}`
|
|
535
|
+
);
|
|
536
|
+
}
|
|
511
537
|
// Run a read-only HogQL query against the org's page-analytics events. Rows are
|
|
512
538
|
// positional per columns; enabled:false means page analytics is off for the org
|
|
513
539
|
// (not zero traffic). A rejected query surfaces PostHog's message as the error.
|
|
@@ -1093,9 +1119,21 @@ var ErdoClient = class {
|
|
|
1093
1119
|
const qs = schemaName ? `?schema_name=${encodeURIComponent(schemaName)}` : "";
|
|
1094
1120
|
return this.request("GET", `/v1/integrations/${encodeURIComponent(integration)}/tables${qs}`);
|
|
1095
1121
|
}
|
|
1122
|
+
// `provided_by_organization_slug` marks a connection your manager
|
|
1123
|
+
// organization lends to you rather than one this org connected. It is usable
|
|
1124
|
+
// exactly like your own for as long as the management link lives, and is not
|
|
1125
|
+
// yours to rotate or disconnect.
|
|
1096
1126
|
listIntegrations() {
|
|
1097
1127
|
return this.request("GET", "/v1/integrations");
|
|
1098
1128
|
}
|
|
1129
|
+
// Start or stop lending a connection to the organizations this one manages.
|
|
1130
|
+
// `managed_organizations` names the orgs it now serves — empty when this org
|
|
1131
|
+
// manages nobody yet, which is a truthful answer rather than a failure.
|
|
1132
|
+
setIntegrationManagedAccess(integrationId, managedAccess) {
|
|
1133
|
+
return this.request("POST", `/v1/integrations/${encodeURIComponent(integrationId)}/managed-access`, {
|
|
1134
|
+
managed_access: managedAccess
|
|
1135
|
+
});
|
|
1136
|
+
}
|
|
1099
1137
|
// For a Pipedream-backed integration this is the full disconnect: the
|
|
1100
1138
|
// credential is released at the provider and the connection removed, not just
|
|
1101
1139
|
// the integration row deleted.
|
|
@@ -4834,7 +4872,7 @@ sentEmailsCmd.command("get <emailID>").description("Read one sent email, includi
|
|
|
4834
4872
|
fail(e);
|
|
4835
4873
|
}
|
|
4836
4874
|
});
|
|
4837
|
-
var voiceCmd = program.command("voice").description("Read voice agent phone calls and website widget conversations");
|
|
4875
|
+
var voiceCmd = program.command("voice").description("Read a voice agent's phone calls, text conversations and website widget conversations");
|
|
4838
4876
|
function contactLabel(contact) {
|
|
4839
4877
|
if (!contact) return "-";
|
|
4840
4878
|
const name = [contact.first_name, contact.last_name].filter(Boolean).join(" ");
|
|
@@ -4980,6 +5018,103 @@ widgetConversationsCmd.command("get <sessionID>").description(
|
|
|
4980
5018
|
fail(e);
|
|
4981
5019
|
}
|
|
4982
5020
|
});
|
|
5021
|
+
var voiceSMSCmd = voiceCmd.command("sms").description("Text conversations on a voice agent's own number");
|
|
5022
|
+
voiceSMSCmd.command("list").description("List the organization's SMS conversations, most recently active first").option("--phone-number <number>", "only conversations on this Erdo number (any format)").option("--address <number>", "only the conversation with this person (any format)").option("--limit <n>", "page size (default 25, max 100)").option("--cursor <cursor>", "next_cursor from the preceding page (stable paging)").option("--json", "print the raw JSON result instead of a table").action(
|
|
5023
|
+
async (opts) => {
|
|
5024
|
+
try {
|
|
5025
|
+
const res = await new ErdoClient().listSMSConversations({
|
|
5026
|
+
phone_number: opts.phoneNumber,
|
|
5027
|
+
address: opts.address,
|
|
5028
|
+
limit: opts.limit ? Number(opts.limit) : void 0,
|
|
5029
|
+
cursor: opts.cursor
|
|
5030
|
+
});
|
|
5031
|
+
if (opts.json) {
|
|
5032
|
+
print(res);
|
|
5033
|
+
return;
|
|
5034
|
+
}
|
|
5035
|
+
const conversations = res.conversations ?? [];
|
|
5036
|
+
if (conversations.length === 0) {
|
|
5037
|
+
console.log("No SMS conversations match those filters.");
|
|
5038
|
+
return;
|
|
5039
|
+
}
|
|
5040
|
+
printAlignedTable(
|
|
5041
|
+
["session id", "phone", "address", "msgs", "last inbound", "last outbound", "summary"],
|
|
5042
|
+
conversations.map((c) => [
|
|
5043
|
+
c.session_id,
|
|
5044
|
+
c.phone_number,
|
|
5045
|
+
// An opted-out conversation is marked on the person, since that is
|
|
5046
|
+
// what the flag governs: nothing may text THEM until they send START.
|
|
5047
|
+
c.suppressed ? `${c.address} (suppressed)` : c.address,
|
|
5048
|
+
c.message_count,
|
|
5049
|
+
c.last_inbound_at ?? "",
|
|
5050
|
+
c.last_outbound_at ?? "",
|
|
5051
|
+
c.summary
|
|
5052
|
+
])
|
|
5053
|
+
);
|
|
5054
|
+
process.stderr.write(`showing ${conversations.length} conversation(s)
|
|
5055
|
+
`);
|
|
5056
|
+
if (res.next_cursor) {
|
|
5057
|
+
process.stderr.write(`more available \u2014 re-run with --cursor ${res.next_cursor}
|
|
5058
|
+
`);
|
|
5059
|
+
}
|
|
5060
|
+
} catch (e) {
|
|
5061
|
+
fail(e);
|
|
5062
|
+
}
|
|
5063
|
+
}
|
|
5064
|
+
);
|
|
5065
|
+
voiceSMSCmd.command("get <sessionID>").description("Read one SMS conversation: its metadata, then every text oldest first").option("--limit <n>", "texts per page (default 50, max 200)").option("--cursor <cursor>", "next_cursor from the preceding page of texts").option("--json", "print the raw JSON result instead of a table").action(
|
|
5066
|
+
async (sessionID, opts) => {
|
|
5067
|
+
try {
|
|
5068
|
+
const res = await new ErdoClient().getSMSConversation(sessionID, {
|
|
5069
|
+
limit: opts.limit ? Number(opts.limit) : void 0,
|
|
5070
|
+
cursor: opts.cursor
|
|
5071
|
+
});
|
|
5072
|
+
if (opts.json) {
|
|
5073
|
+
print(res);
|
|
5074
|
+
return;
|
|
5075
|
+
}
|
|
5076
|
+
const c = res.conversation;
|
|
5077
|
+
printAlignedTable(
|
|
5078
|
+
["session id", "phone", "address", "msgs", "last inbound", "last outbound", "summary"],
|
|
5079
|
+
[
|
|
5080
|
+
[
|
|
5081
|
+
c.session_id,
|
|
5082
|
+
c.phone_number,
|
|
5083
|
+
c.suppressed ? `${c.address} (suppressed)` : c.address,
|
|
5084
|
+
c.message_count,
|
|
5085
|
+
c.last_inbound_at ?? "",
|
|
5086
|
+
c.last_outbound_at ?? "",
|
|
5087
|
+
c.summary
|
|
5088
|
+
]
|
|
5089
|
+
]
|
|
5090
|
+
);
|
|
5091
|
+
const messages = res.messages ?? [];
|
|
5092
|
+
if (messages.length === 0) {
|
|
5093
|
+
console.log("\nNo texts have been recorded in this conversation yet.");
|
|
5094
|
+
return;
|
|
5095
|
+
}
|
|
5096
|
+
console.log("");
|
|
5097
|
+
printAlignedTable(
|
|
5098
|
+
["at", "direction", "status", "media", "body"],
|
|
5099
|
+
messages.map((m) => [
|
|
5100
|
+
m.created_at,
|
|
5101
|
+
m.direction,
|
|
5102
|
+
m.status,
|
|
5103
|
+
m.num_media > 0 ? m.num_media : "",
|
|
5104
|
+
m.body
|
|
5105
|
+
])
|
|
5106
|
+
);
|
|
5107
|
+
process.stderr.write(`showing ${messages.length} text(s), oldest first
|
|
5108
|
+
`);
|
|
5109
|
+
if (res.next_cursor) {
|
|
5110
|
+
process.stderr.write(`more available \u2014 re-run with --cursor ${res.next_cursor}
|
|
5111
|
+
`);
|
|
5112
|
+
}
|
|
5113
|
+
} catch (e) {
|
|
5114
|
+
fail(e);
|
|
5115
|
+
}
|
|
5116
|
+
}
|
|
5117
|
+
);
|
|
4983
5118
|
var datasetsCmd = program.command("datasets").description("Datasets");
|
|
4984
5119
|
datasetsCmd.command("list").description("List datasets").option(
|
|
4985
5120
|
"--class <class>",
|
|
@@ -5296,7 +5431,10 @@ var integrationsCmd = program.command("integrations").description("Connect and i
|
|
|
5296
5431
|
integrationsCmd.command("list").description("List connected integrations").action(async () => {
|
|
5297
5432
|
try {
|
|
5298
5433
|
const { integrations } = await new ErdoClient().listIntegrations();
|
|
5299
|
-
for (const i of integrations)
|
|
5434
|
+
for (const i of integrations) {
|
|
5435
|
+
const provided = i.provided_by_organization_slug ? ` provided by ${i.provided_by_organization_slug}` : "";
|
|
5436
|
+
console.log(`${i.app} ${i.status} ${i.auth_type} ${i.name}${provided}`);
|
|
5437
|
+
}
|
|
5300
5438
|
} catch (e) {
|
|
5301
5439
|
fail(e);
|
|
5302
5440
|
}
|
|
@@ -5309,7 +5447,10 @@ integrationsCmd.command("apps [query]").description("Search connectable apps (na
|
|
|
5309
5447
|
fail(e);
|
|
5310
5448
|
}
|
|
5311
5449
|
});
|
|
5312
|
-
integrationsCmd.command("connect <app>").description("Connect an app \u2014 pass its API key or other credentials with -c, or omit them to get a browser connect URL for OAuth apps").option("-c, --credential <key=value...>", "credential field, e.g. -c api_key=\u2026 (repeatable); rejected by OAuth apps, which have no credential to pass", (v, acc) => acc.concat(v), []).option("-n, --name <name>", "display name for the connection").option("--rotate", "replace the credentials of the app's existing connection instead of adding a second one \u2014 for a key that was rotated at the provider; requires -c").option("--share", "share the connection with the whole organization instead of keeping it private to you \u2014 for org-level credentials teammates and agents should see").
|
|
5450
|
+
integrationsCmd.command("connect <app>").description("Connect an app \u2014 pass its API key or other credentials with -c, or omit them to get a browser connect URL for OAuth apps").option("-c, --credential <key=value...>", "credential field, e.g. -c api_key=\u2026 (repeatable); rejected by OAuth apps, which have no credential to pass", (v, acc) => acc.concat(v), []).option("-n, --name <name>", "display name for the connection").option("--rotate", "replace the credentials of the app's existing connection instead of adding a second one \u2014 for a key that was rotated at the provider; requires -c").option("--share", "share the connection with the whole organization instead of keeping it private to you \u2014 for org-level credentials teammates and agents should see").option(
|
|
5451
|
+
"--provide-to-managed",
|
|
5452
|
+
"let the organizations you manage use this connection as if it were their own \u2014 for a vendor key your agency pays for. Key-authenticated native integrations only, connected in a manager organization"
|
|
5453
|
+
).action(async (app, opts) => {
|
|
5313
5454
|
try {
|
|
5314
5455
|
let credentials;
|
|
5315
5456
|
if (opts.credential.length) {
|
|
@@ -5325,7 +5466,8 @@ integrationsCmd.command("connect <app>").description("Connect an app \u2014 pass
|
|
|
5325
5466
|
name: opts.name,
|
|
5326
5467
|
credentials,
|
|
5327
5468
|
rotate_credentials: opts.rotate || void 0,
|
|
5328
|
-
share_with_org: opts.share || void 0
|
|
5469
|
+
share_with_org: opts.share || void 0,
|
|
5470
|
+
managed_access: opts.provideToManaged ? "managed_organizations" : void 0
|
|
5329
5471
|
});
|
|
5330
5472
|
print(res);
|
|
5331
5473
|
const notes = [];
|
|
@@ -5340,6 +5482,27 @@ ${notes.join("\n")}`);
|
|
|
5340
5482
|
fail(e);
|
|
5341
5483
|
}
|
|
5342
5484
|
});
|
|
5485
|
+
integrationsCmd.command("managed-access <integration-id> <owner_only|managed_organizations>").description(
|
|
5486
|
+
"Start or stop providing a connection to the organizations you manage \u2014 a vendor key your agency pays for, spendable by every client for as long as you manage it. Key-authenticated connections only"
|
|
5487
|
+
).action(async (integrationId, managedAccess) => {
|
|
5488
|
+
try {
|
|
5489
|
+
const res = await new ErdoClient().setIntegrationManagedAccess(integrationId, managedAccess);
|
|
5490
|
+
print(res);
|
|
5491
|
+
if (res.managed_access !== "managed_organizations") return;
|
|
5492
|
+
const orgs = res.managed_organizations ?? [];
|
|
5493
|
+
if (!orgs.length) {
|
|
5494
|
+
console.error(
|
|
5495
|
+
"\nNo organizations are managed by this one yet, so nothing uses it today \u2014 a client you link later picks it up with nothing further to do here."
|
|
5496
|
+
);
|
|
5497
|
+
return;
|
|
5498
|
+
}
|
|
5499
|
+
console.error(`
|
|
5500
|
+
Provided to ${orgs.length} organization${orgs.length === 1 ? "" : "s"}:`);
|
|
5501
|
+
for (const o of orgs) console.error(` ${o.slug} ${o.name}`);
|
|
5502
|
+
} catch (e) {
|
|
5503
|
+
fail(e);
|
|
5504
|
+
}
|
|
5505
|
+
});
|
|
5343
5506
|
integrationsCmd.command("disconnect <app>").description("Disconnect an app \u2014 releases the stored credential and removes the connection; org admins can disconnect any of the org's connections").action(async (app) => {
|
|
5344
5507
|
try {
|
|
5345
5508
|
const client = new ErdoClient();
|