@gravity-rail/cli 0.7.3 → 0.8.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 +27 -0
- package/dist/{account-verify-phone-TDQNZ5XN.js → account-verify-phone-TC73QIIE.js} +2 -2
- package/dist/{chunk-KTTHOL3B.js → chunk-L5YCPUAM.js} +6 -3
- package/dist/{chunk-KTTHOL3B.js.map → chunk-L5YCPUAM.js.map} +1 -1
- package/dist/gr.js +989 -216
- package/dist/gr.js.map +1 -1
- package/dist/lib.d.ts +1 -1
- package/dist/lib.js +787 -158
- package/dist/lib.js.map +1 -1
- package/package.json +4 -3
- /package/dist/{account-verify-phone-TDQNZ5XN.js.map → account-verify-phone-TC73QIIE.js.map} +0 -0
package/dist/lib.js
CHANGED
|
@@ -29,7 +29,7 @@ import {
|
|
|
29
29
|
startAccountPhoneVerify,
|
|
30
30
|
stderr,
|
|
31
31
|
unwrapItems
|
|
32
|
-
} from "./chunk-
|
|
32
|
+
} from "./chunk-L5YCPUAM.js";
|
|
33
33
|
import {
|
|
34
34
|
AGUIClient,
|
|
35
35
|
formatTimestamp,
|
|
@@ -959,8 +959,8 @@ function requireNumberFrom(args, ...keys) {
|
|
|
959
959
|
if (!isNaN(num)) return num;
|
|
960
960
|
}
|
|
961
961
|
}
|
|
962
|
-
const
|
|
963
|
-
throw new Error(`Missing required argument: ${
|
|
962
|
+
const hint2 = keys.map((k) => `--${k}`).join(" or ");
|
|
963
|
+
throw new Error(`Missing required argument: ${hint2}`);
|
|
964
964
|
}
|
|
965
965
|
|
|
966
966
|
// src/domains/agent-tasks.ts
|
|
@@ -1102,6 +1102,29 @@ var TOOL_COLUMNS = [
|
|
|
1102
1102
|
COL.name(),
|
|
1103
1103
|
{ header: "DESCRIPTION", getValue: (item) => (item.description ?? "").slice(0, 60) }
|
|
1104
1104
|
];
|
|
1105
|
+
var ABILITY_COLUMNS = [
|
|
1106
|
+
COL.uuid(),
|
|
1107
|
+
{ header: "ABILITY-TYPE", getValue: (item) => item.abilityType ?? "" },
|
|
1108
|
+
COL.enabled(),
|
|
1109
|
+
{ header: "CEL-CONDITION", getValue: (item) => item.celCondition ?? "" }
|
|
1110
|
+
];
|
|
1111
|
+
function requireAbilityScope(args) {
|
|
1112
|
+
const hasWorkflow = typeof args["workflow-id"] === "string";
|
|
1113
|
+
const hasTask = typeof args["task-id"] === "string";
|
|
1114
|
+
if (hasWorkflow === hasTask) {
|
|
1115
|
+
throw new CLIError(
|
|
1116
|
+
"Specify exactly one of --workflow-id or --task-id. Abilities are scoped to a workflow or task, not to the agent row itself."
|
|
1117
|
+
);
|
|
1118
|
+
}
|
|
1119
|
+
if (hasTask) {
|
|
1120
|
+
const taskId = Number(args["task-id"]);
|
|
1121
|
+
if (isNaN(taskId)) throw new CLIError("--task-id must be a number");
|
|
1122
|
+
return { taskId };
|
|
1123
|
+
}
|
|
1124
|
+
const workflowId = Number(args["workflow-id"]);
|
|
1125
|
+
if (isNaN(workflowId)) throw new CLIError("--workflow-id must be a number");
|
|
1126
|
+
return { workflowId };
|
|
1127
|
+
}
|
|
1105
1128
|
function normalizeChannel(channel) {
|
|
1106
1129
|
const aliases = {
|
|
1107
1130
|
phone: "phone-voice",
|
|
@@ -1126,11 +1149,16 @@ var agentsDomain = {
|
|
|
1126
1149
|
archiveMethod: "archiveAgent",
|
|
1127
1150
|
unarchiveMethod: "unarchiveAgent",
|
|
1128
1151
|
columns: COLUMNS,
|
|
1129
|
-
paginationSupport: "full"
|
|
1152
|
+
paginationSupport: "full",
|
|
1153
|
+
updateHelp: "Update an agent. Body schema (JSON via --data/--file/stdin), all fields optional:\n name string display name\n description string short summary shown in listings\n systemPrompt string base instructions for the agent\n model string AI model slug (see `gr ai-models list`)\n temperature number sampling temperature (0-2)\n avatarFileId number workspace file ID for the avatar\n config object AgentConfig overrides (advanced)\nAbilities are NOT set here \u2014 they live on a workflow/task scope; use `gr agents abilities` to manage them.",
|
|
1154
|
+
updateExamples: [
|
|
1155
|
+
`gr agents update --id 42 --data '{"name":"Scheduler","temperature":0.4}'`,
|
|
1156
|
+
"gr agents update --id 42 --file agent.json"
|
|
1157
|
+
]
|
|
1130
1158
|
}),
|
|
1131
1159
|
{
|
|
1132
1160
|
path: ["agents", "resolve-tools"],
|
|
1133
|
-
description: "Show resolved runtime tools for an agent in a channel context. Channel accepts canonical values (phone-voice, web-voice, phone-sms, web-chat) or shorthand aliases: phone -> phone-voice, voice -> phone-voice, sms -> phone-sms, web -> web-chat. Unknown values are passed through unchanged.",
|
|
1161
|
+
description: "Show resolved runtime tools for an agent in a channel context. Channel accepts canonical values (phone-voice, web-voice, phone-sms, web-chat) or shorthand aliases: phone -> phone-voice, voice -> phone-voice, sms -> phone-sms, web -> web-chat. Unknown values are passed through unchanged. When neither --task-id nor --task-uuid is supplied the agent's workflow start task is used.",
|
|
1134
1162
|
requiresWorkspace: true,
|
|
1135
1163
|
args: [
|
|
1136
1164
|
{ name: "id", description: "Agent ID", required: true },
|
|
@@ -1141,22 +1169,142 @@ var agentsDomain = {
|
|
|
1141
1169
|
},
|
|
1142
1170
|
{
|
|
1143
1171
|
name: "task-id",
|
|
1144
|
-
description: "
|
|
1145
|
-
required:
|
|
1172
|
+
description: "Numeric task ID whose workflow abilities to resolve. Omit to use the workflow start task (or supply --task-uuid instead).",
|
|
1173
|
+
required: false
|
|
1174
|
+
},
|
|
1175
|
+
{
|
|
1176
|
+
name: "task-uuid",
|
|
1177
|
+
description: "Task UUID as an alternative to --task-id. Omit to use the workflow start task.",
|
|
1178
|
+
required: false
|
|
1146
1179
|
}
|
|
1147
1180
|
],
|
|
1148
1181
|
handler: async ({ ctx, workspace, args }) => {
|
|
1182
|
+
const rawTaskId = args["task-id"];
|
|
1183
|
+
const rawTaskUuid = args["task-uuid"];
|
|
1184
|
+
if (rawTaskId !== void 0 && rawTaskUuid !== void 0) {
|
|
1185
|
+
throw new Error("Provide at most one of --task-id or --task-uuid, not both");
|
|
1186
|
+
}
|
|
1187
|
+
const options = {
|
|
1188
|
+
channel: normalizeChannel(String(args.channel))
|
|
1189
|
+
};
|
|
1190
|
+
if (typeof rawTaskId === "string") {
|
|
1191
|
+
const n = Number(rawTaskId);
|
|
1192
|
+
if (isNaN(n)) throw new Error("Argument --task-id must be a number");
|
|
1193
|
+
options.taskId = n;
|
|
1194
|
+
}
|
|
1195
|
+
if (typeof rawTaskUuid === "string") {
|
|
1196
|
+
options.taskUuid = rawTaskUuid;
|
|
1197
|
+
}
|
|
1149
1198
|
const result2 = await requireAuth(ctx).apiClient.resolveAgentTools(
|
|
1150
1199
|
workspace,
|
|
1151
1200
|
requireNumber(args, "id"),
|
|
1152
|
-
|
|
1153
|
-
channel: normalizeChannel(String(args.channel)),
|
|
1154
|
-
taskId: requireNumber(args, "task-id")
|
|
1155
|
-
}
|
|
1201
|
+
options
|
|
1156
1202
|
);
|
|
1157
1203
|
const tools = result2.tools ?? result2;
|
|
1158
1204
|
outputData(Array.isArray(tools) ? tools : [tools], ctx, TOOL_COLUMNS);
|
|
1159
1205
|
}
|
|
1206
|
+
},
|
|
1207
|
+
{
|
|
1208
|
+
path: ["agents", "abilities", "list"],
|
|
1209
|
+
description: "Show the abilities effective for an agent on a task (read-only). Requires --task-id because abilities are resolved through the task's workflow, not stored on the agent row.",
|
|
1210
|
+
requiresWorkspace: true,
|
|
1211
|
+
columns: ABILITY_COLUMNS,
|
|
1212
|
+
args: [
|
|
1213
|
+
{ name: "id", description: "Agent ID", required: true },
|
|
1214
|
+
{
|
|
1215
|
+
name: "task-id",
|
|
1216
|
+
description: "Task ID whose workflow abilities to list",
|
|
1217
|
+
required: true
|
|
1218
|
+
}
|
|
1219
|
+
],
|
|
1220
|
+
handler: async ({ ctx, workspace, args }) => {
|
|
1221
|
+
const result2 = await requireAuth(ctx).apiClient.listAgentAbilities(
|
|
1222
|
+
workspace,
|
|
1223
|
+
requireNumber(args, "id"),
|
|
1224
|
+
{ taskId: requireNumber(args, "task-id") }
|
|
1225
|
+
);
|
|
1226
|
+
outputData(result2, ctx, ABILITY_COLUMNS);
|
|
1227
|
+
}
|
|
1228
|
+
},
|
|
1229
|
+
{
|
|
1230
|
+
path: ["agents", "abilities", "enable"],
|
|
1231
|
+
description: "Enable (create) an ability on a workflow or task scope. Requires exactly one of --workflow-id or --task-id \u2014 abilities are scoped to a workflow/task, never to the agent row. Provide initial --config JSON inline or via --data/--file.",
|
|
1232
|
+
mutates: true,
|
|
1233
|
+
requiresWorkspace: true,
|
|
1234
|
+
args: [
|
|
1235
|
+
{ name: "ability-type", description: "Ability type slug (e.g. 'communication:send_sms')", required: true },
|
|
1236
|
+
{ name: "workflow-id", description: "Workflow scope (mutually exclusive with --task-id)" },
|
|
1237
|
+
{ name: "task-id", description: "Task scope (mutually exclusive with --workflow-id)" }
|
|
1238
|
+
],
|
|
1239
|
+
examples: [
|
|
1240
|
+
"gr agents abilities enable --workflow-id 7 --ability-type tools:web_search",
|
|
1241
|
+
`gr agents abilities enable --task-id 12 --ability-type integration:data_access --data '{"dataTypeUuid":"..."}'`
|
|
1242
|
+
],
|
|
1243
|
+
handler: async ({ ctx, workspace, args, data }) => {
|
|
1244
|
+
const abilityType = args["ability-type"];
|
|
1245
|
+
if (typeof abilityType !== "string") {
|
|
1246
|
+
throw new CLIError("--ability-type is required");
|
|
1247
|
+
}
|
|
1248
|
+
const scope = requireAbilityScope(args);
|
|
1249
|
+
const config = data && typeof data === "object" ? data : {};
|
|
1250
|
+
const body = {
|
|
1251
|
+
abilityType,
|
|
1252
|
+
config,
|
|
1253
|
+
...scope
|
|
1254
|
+
};
|
|
1255
|
+
const result2 = await requireAuth(ctx).apiClient.createAbility(workspace, body);
|
|
1256
|
+
outputSingle(result2, ctx, ABILITY_COLUMNS);
|
|
1257
|
+
}
|
|
1258
|
+
},
|
|
1259
|
+
{
|
|
1260
|
+
path: ["agents", "abilities", "disable"],
|
|
1261
|
+
description: "Disable an ability instance by UUID (sets enabled=false, keeps config). Use `gr agents abilities list` to find the UUID.",
|
|
1262
|
+
mutates: true,
|
|
1263
|
+
requiresWorkspace: true,
|
|
1264
|
+
args: [{ name: "uuid", description: "Ability instance UUID", required: true }],
|
|
1265
|
+
handler: async ({ ctx, workspace, args }) => {
|
|
1266
|
+
const uuid = args.uuid;
|
|
1267
|
+
if (typeof uuid !== "string") throw new CLIError("--uuid is required");
|
|
1268
|
+
const result2 = await requireAuth(ctx).apiClient.updateAbility(workspace, uuid, {
|
|
1269
|
+
enabled: false
|
|
1270
|
+
});
|
|
1271
|
+
outputSingle(result2, ctx, ABILITY_COLUMNS);
|
|
1272
|
+
}
|
|
1273
|
+
},
|
|
1274
|
+
{
|
|
1275
|
+
path: ["agents", "abilities", "configure"],
|
|
1276
|
+
description: "Update an ability instance by UUID. Pass --data/--file JSON to merge into config, and/or --enabled true|false to toggle. config is merged with the existing config server-side.",
|
|
1277
|
+
mutates: true,
|
|
1278
|
+
requiresWorkspace: true,
|
|
1279
|
+
args: [
|
|
1280
|
+
{ name: "uuid", description: "Ability instance UUID", required: true },
|
|
1281
|
+
{ name: "enabled", description: "Set enabled state (true|false)" }
|
|
1282
|
+
],
|
|
1283
|
+
examples: [
|
|
1284
|
+
`gr agents abilities configure --uuid <uuid> --data '{"canForward":true}'`,
|
|
1285
|
+
"gr agents abilities configure --uuid <uuid> --enabled true"
|
|
1286
|
+
],
|
|
1287
|
+
handler: async ({ ctx, workspace, args, data }) => {
|
|
1288
|
+
const uuid = args.uuid;
|
|
1289
|
+
if (typeof uuid !== "string") throw new CLIError("--uuid is required");
|
|
1290
|
+
const body = {};
|
|
1291
|
+
if (data && typeof data === "object") {
|
|
1292
|
+
body.config = data;
|
|
1293
|
+
}
|
|
1294
|
+
if (typeof args.enabled === "string") {
|
|
1295
|
+
if (args.enabled !== "true" && args.enabled !== "false") {
|
|
1296
|
+
throw new CLIError("--enabled must be true or false");
|
|
1297
|
+
}
|
|
1298
|
+
body.enabled = args.enabled === "true";
|
|
1299
|
+
}
|
|
1300
|
+
if (Object.keys(body).length === 0) {
|
|
1301
|
+
throw new CLIError(
|
|
1302
|
+
"Nothing to update: provide --data config and/or --enabled"
|
|
1303
|
+
);
|
|
1304
|
+
}
|
|
1305
|
+
const result2 = await requireAuth(ctx).apiClient.updateAbility(workspace, uuid, body);
|
|
1306
|
+
outputSingle(result2, ctx, ABILITY_COLUMNS);
|
|
1307
|
+
}
|
|
1160
1308
|
}
|
|
1161
1309
|
]
|
|
1162
1310
|
};
|
|
@@ -1382,21 +1530,47 @@ var chatsDomain = {
|
|
|
1382
1530
|
// Inspect (aggregated debug view)
|
|
1383
1531
|
{
|
|
1384
1532
|
path: ["chats", "inspect"],
|
|
1385
|
-
description: "Inspect a chat with messages, assignment state, and data records",
|
|
1386
|
-
requiresWorkspace:
|
|
1533
|
+
description: "Inspect a chat with messages, assignment state, and data records. Omit -w for concierge (account-level) chats.",
|
|
1534
|
+
requiresWorkspace: false,
|
|
1387
1535
|
args: [{ name: "id", description: "Chat ID", required: true }],
|
|
1388
1536
|
handler: async ({ ctx, workspace, args }) => {
|
|
1389
1537
|
const chatId = requireNumber(args, "id");
|
|
1390
|
-
const
|
|
1391
|
-
requireAuth(ctx).apiClient.getChat(workspace, chatId),
|
|
1392
|
-
requireAuth(ctx).apiClient.getChatMessages(workspace, chatId),
|
|
1393
|
-
requireAuth(ctx).apiClient.getWorkspace(workspace)
|
|
1394
|
-
]);
|
|
1395
|
-
const isHipaa = ws.features?.includes("hipaa_compliance");
|
|
1538
|
+
const authed = requireAuth(ctx);
|
|
1396
1539
|
const truncate2 = (content) => {
|
|
1397
1540
|
const text = typeof content === "string" ? content : JSON.stringify(content);
|
|
1398
1541
|
return text.slice(0, 200);
|
|
1399
1542
|
};
|
|
1543
|
+
if (!workspace) {
|
|
1544
|
+
const [chat2, messages2] = await Promise.all([
|
|
1545
|
+
getConciergeChat(authed, chatId),
|
|
1546
|
+
getConciergeChatMessages(authed, chatId)
|
|
1547
|
+
]);
|
|
1548
|
+
outputSingle(
|
|
1549
|
+
{
|
|
1550
|
+
chat: {
|
|
1551
|
+
id: chat2.id,
|
|
1552
|
+
uuid: chat2.uuid,
|
|
1553
|
+
title: chat2.title,
|
|
1554
|
+
chatType: chat2.chatType,
|
|
1555
|
+
threadId: chat2.threadId,
|
|
1556
|
+
createdAt: chat2.createdAt
|
|
1557
|
+
},
|
|
1558
|
+
messages: messages2.map((m) => ({
|
|
1559
|
+
id: m.id,
|
|
1560
|
+
role: m.role,
|
|
1561
|
+
content: truncate2(m.content)
|
|
1562
|
+
}))
|
|
1563
|
+
},
|
|
1564
|
+
ctx
|
|
1565
|
+
);
|
|
1566
|
+
return;
|
|
1567
|
+
}
|
|
1568
|
+
const [chat, messages, ws] = await Promise.all([
|
|
1569
|
+
authed.apiClient.getChat(workspace, chatId),
|
|
1570
|
+
authed.apiClient.getChatMessages(workspace, chatId),
|
|
1571
|
+
authed.apiClient.getWorkspace(workspace)
|
|
1572
|
+
]);
|
|
1573
|
+
const isHipaa = ws.features?.includes("hipaa_compliance");
|
|
1400
1574
|
const result2 = {
|
|
1401
1575
|
chat: {
|
|
1402
1576
|
id: chat.id,
|
|
@@ -1416,10 +1590,7 @@ var chatsDomain = {
|
|
|
1416
1590
|
};
|
|
1417
1591
|
const assignmentId = chat.assignmentId;
|
|
1418
1592
|
if (assignmentId) {
|
|
1419
|
-
const assignment = await
|
|
1420
|
-
workspace,
|
|
1421
|
-
assignmentId
|
|
1422
|
-
);
|
|
1593
|
+
const assignment = await authed.apiClient.getAssignment(workspace, assignmentId);
|
|
1423
1594
|
result2.assignment = {
|
|
1424
1595
|
id: assignment.id,
|
|
1425
1596
|
status: assignment.status,
|
|
@@ -1674,10 +1845,7 @@ var memberRolesDomain = {
|
|
|
1674
1845
|
updateMethod: "updateMemberRole",
|
|
1675
1846
|
deleteMethod: "deleteMemberRole",
|
|
1676
1847
|
columns: ROLE_COLUMNS2,
|
|
1677
|
-
createExamples: [
|
|
1678
|
-
"gr member-roles list -w $WID",
|
|
1679
|
-
"gr member-roles get -w $WID --id 2"
|
|
1680
|
-
]
|
|
1848
|
+
createExamples: ["gr member-roles list -w $WID", "gr member-roles get -w $WID --id 2"]
|
|
1681
1849
|
})
|
|
1682
1850
|
};
|
|
1683
1851
|
|
|
@@ -1686,10 +1854,13 @@ var COLUMNS4 = [
|
|
|
1686
1854
|
COL.id(),
|
|
1687
1855
|
COL.uuid(),
|
|
1688
1856
|
COL.name(),
|
|
1689
|
-
|
|
1857
|
+
// TaskResponse has no `status` field — it has `archived`. Surface that
|
|
1858
|
+
// directly so active vs archived is visible in the default table (relevant
|
|
1859
|
+
// once --include-archived mixes both into the listing).
|
|
1860
|
+
{ header: "STATUS", getValue: (item) => item.archived ? "archived" : "active" },
|
|
1690
1861
|
{
|
|
1691
1862
|
header: "WORKFLOW",
|
|
1692
|
-
getValue: (item) =>
|
|
1863
|
+
getValue: (item) => String(item.workflowId ?? "")
|
|
1693
1864
|
},
|
|
1694
1865
|
COL.created()
|
|
1695
1866
|
];
|
|
@@ -1707,7 +1878,21 @@ var tasksDomain = {
|
|
|
1707
1878
|
deleteMethod: "deleteTask",
|
|
1708
1879
|
archiveMethod: "archiveTask",
|
|
1709
1880
|
unarchiveMethod: "unarchiveTask",
|
|
1710
|
-
columns: COLUMNS4
|
|
1881
|
+
columns: COLUMNS4,
|
|
1882
|
+
// `tasks list` is workspace-wide by default. Filter to one workflow
|
|
1883
|
+
// with --workflow-id, and opt into archived tasks with
|
|
1884
|
+
// --include-archived (active-only by default, matching the backend).
|
|
1885
|
+
listArgs: [
|
|
1886
|
+
{
|
|
1887
|
+
name: "workflow-id",
|
|
1888
|
+
description: "Filter tasks to a single workflow by ID"
|
|
1889
|
+
},
|
|
1890
|
+
{
|
|
1891
|
+
name: "include-archived",
|
|
1892
|
+
description: "Include archived tasks in the listing",
|
|
1893
|
+
isFlag: true
|
|
1894
|
+
}
|
|
1895
|
+
]
|
|
1711
1896
|
}),
|
|
1712
1897
|
{
|
|
1713
1898
|
path: ["tasks", "clone"],
|
|
@@ -1762,9 +1947,18 @@ var COLUMNS5 = [
|
|
|
1762
1947
|
COL.id(),
|
|
1763
1948
|
COL.uuid(),
|
|
1764
1949
|
COL.name(),
|
|
1765
|
-
|
|
1950
|
+
// taskCount is the total (active + archived). Show the active count as the
|
|
1951
|
+
// headline number — matching `tasks list` — and call out archived tasks
|
|
1952
|
+
// separately so the table never overstates how many tasks actually run.
|
|
1953
|
+
{ header: "TASKS", getValue: (item) => formatTaskCount(item) },
|
|
1766
1954
|
COL.created()
|
|
1767
1955
|
];
|
|
1956
|
+
function formatTaskCount(item) {
|
|
1957
|
+
const total = Number(item.taskCount ?? 0);
|
|
1958
|
+
const archived = Number(item.archivedTaskCount ?? 0);
|
|
1959
|
+
const active = total - archived;
|
|
1960
|
+
return archived > 0 ? `${active} (+${archived} archived)` : String(active);
|
|
1961
|
+
}
|
|
1768
1962
|
var NOTE_COLUMNS = [
|
|
1769
1963
|
COL.id(),
|
|
1770
1964
|
{ header: "TITLE", getValue: (item) => item.title ?? "" },
|
|
@@ -1865,13 +2059,21 @@ var workflowsDomain = {
|
|
|
1865
2059
|
// Diagram
|
|
1866
2060
|
{
|
|
1867
2061
|
path: ["workflows", "diagram"],
|
|
1868
|
-
description: "Get workflow diagram",
|
|
2062
|
+
description: "Get workflow diagram (active tasks only by default)",
|
|
1869
2063
|
requiresWorkspace: true,
|
|
1870
|
-
args: [
|
|
2064
|
+
args: [
|
|
2065
|
+
{ name: "id", description: "Workflow ID", required: true },
|
|
2066
|
+
{
|
|
2067
|
+
name: "include-archived",
|
|
2068
|
+
description: "Render archived tasks (distinctly styled) too",
|
|
2069
|
+
isFlag: true
|
|
2070
|
+
}
|
|
2071
|
+
],
|
|
1871
2072
|
handler: async ({ ctx, workspace, args }) => {
|
|
1872
2073
|
const result2 = await requireAuth(ctx).apiClient.getWorkflowDiagram(
|
|
1873
2074
|
workspace,
|
|
1874
|
-
requireNumber(args, "id")
|
|
2075
|
+
requireNumber(args, "id"),
|
|
2076
|
+
{ includeArchived: args["include-archived"] === true }
|
|
1875
2077
|
);
|
|
1876
2078
|
outputSingle(result2, ctx);
|
|
1877
2079
|
}
|
|
@@ -2388,28 +2590,64 @@ var workspacesDomain = {
|
|
|
2388
2590
|
outputSingle(result2, ctx, COLUMNS7);
|
|
2389
2591
|
}
|
|
2390
2592
|
},
|
|
2391
|
-
//
|
|
2593
|
+
// Provision a workspace under an organization's active subscription.
|
|
2392
2594
|
{
|
|
2393
2595
|
path: ["workspaces", "create"],
|
|
2394
|
-
description: "
|
|
2596
|
+
description: "Provision a workspace in an organization (requires an active subscription)",
|
|
2395
2597
|
mutates: true,
|
|
2396
2598
|
requiresWorkspace: false,
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
|
|
2400
|
-
|
|
2599
|
+
args: [
|
|
2600
|
+
{
|
|
2601
|
+
name: "org",
|
|
2602
|
+
aliases: ["org-uuid"],
|
|
2603
|
+
description: "Organization UUID",
|
|
2604
|
+
required: true
|
|
2605
|
+
},
|
|
2606
|
+
{ name: "product", description: "Workspace plan product ID", required: true },
|
|
2607
|
+
{ name: "name", description: "Workspace name", required: true },
|
|
2608
|
+
{ name: "period", description: "Billing period: 'month' or 'year'", default: "month" },
|
|
2609
|
+
{ name: "slug", description: "Workspace slug (optional)" },
|
|
2610
|
+
{ name: "template", description: "Template workspace UUID to clone (optional)" }
|
|
2611
|
+
],
|
|
2612
|
+
examples: ['gr workspaces create --org <org-uuid> --product 14 --name "Front Desk"'],
|
|
2613
|
+
handler: async ({ ctx, args }) => {
|
|
2614
|
+
const orgUuid = args.org ?? args["org-uuid"];
|
|
2615
|
+
const name = args.name;
|
|
2616
|
+
const productRaw = args.product;
|
|
2617
|
+
if (!orgUuid) throw new CLIError("Missing required argument: --org <org-uuid>");
|
|
2618
|
+
if (!name) throw new CLIError("Missing required argument: --name");
|
|
2619
|
+
if (!productRaw) {
|
|
2401
2620
|
throw new CLIError(
|
|
2402
|
-
|
|
2621
|
+
"Missing required argument: --product\nList workspace plans with: gr billing products --type workspace"
|
|
2403
2622
|
);
|
|
2404
2623
|
}
|
|
2624
|
+
const payload = {
|
|
2625
|
+
name,
|
|
2626
|
+
productId: Number(productRaw),
|
|
2627
|
+
planPeriod: args.period || "month"
|
|
2628
|
+
};
|
|
2629
|
+
if (args.slug) payload.slug = args.slug;
|
|
2630
|
+
if (args.template) payload.templateWorkspaceUuid = args.template;
|
|
2405
2631
|
try {
|
|
2406
|
-
const result2 = await requireAuth(ctx).apiClient.
|
|
2632
|
+
const result2 = await requireAuth(ctx).apiClient.createOrganizationWorkspace(
|
|
2633
|
+
orgUuid,
|
|
2634
|
+
payload
|
|
2635
|
+
);
|
|
2407
2636
|
outputSingle(result2, ctx, COLUMNS7);
|
|
2637
|
+
if ((ctx.outputFormat ?? "table") === "table") {
|
|
2638
|
+
const wsUuid = result2?.uuid ?? "<workspace-uuid>";
|
|
2639
|
+
process.stdout.write(
|
|
2640
|
+
`
|
|
2641
|
+
Provisioning started. Watch it come up with:
|
|
2642
|
+
gr workspaces get -w ${wsUuid}
|
|
2643
|
+
`
|
|
2644
|
+
);
|
|
2645
|
+
}
|
|
2408
2646
|
} catch (err) {
|
|
2409
2647
|
const statusCode = err?.statusCode;
|
|
2410
|
-
if (statusCode ===
|
|
2648
|
+
if (statusCode === 400) {
|
|
2411
2649
|
throw new CLIError(
|
|
2412
|
-
|
|
2650
|
+
"Could not create the workspace. The organization needs an active, paid subscription first.\n gr billing products --type org # pick an org plan\n gr billing checkout --product <id> # pay up front\n gr billing status # confirm it is active"
|
|
2413
2651
|
);
|
|
2414
2652
|
}
|
|
2415
2653
|
throw err;
|
|
@@ -3463,35 +3701,24 @@ Common optional
|
|
|
3463
3701
|
Scope (set exactly one when objectType requires it)
|
|
3464
3702
|
routineId, workflowId, taskId, dataTypeId, journeyStepId, calendarId, ...
|
|
3465
3703
|
|
|
3466
|
-
chat:create actionParams (outbound voice/SMS)
|
|
3467
|
-
{"
|
|
3468
|
-
"
|
|
3704
|
+
chat:create actionParams (outbound voice/SMS \u2014 starts a workflow chat)
|
|
3705
|
+
{"workflowUUID":"<workflow-uuid>","channel":"phone-voice"|"phone-sms",
|
|
3706
|
+
"phoneNumberUUID":"<uuid>","inboxUUID":"<optional-uuid>","assistantEnabled":<bool>}
|
|
3707
|
+
Keys are UUID strings: workflowUUID (not workflowId), phoneNumberUUID (not
|
|
3708
|
+
phoneNumberId), inboxUUID (not inboxId). Unknown keys are rejected.
|
|
3709
|
+
|
|
3710
|
+
sms:send actionParams (send an SMS, or start an SMS workflow chat)
|
|
3711
|
+
One-off text: {"phoneNumberUUID":"<from-number-uuid>","messageTemplate":"Hi {{name}} \u2026"}
|
|
3712
|
+
Workflow chat: {"phoneNumberUUID":"<from-number-uuid>","workflowUUID":"<workflow-uuid>",
|
|
3713
|
+
"initialUserMessage":"<optional seed>"}
|
|
3714
|
+
phoneNumberUUID (the from-number) is required. The message body is messageTemplate
|
|
3715
|
+
(supports {{record/member}} template vars) \u2014 there is no "body"/"message" key.
|
|
3716
|
+
Provide messageTemplate for a one-off text, OR workflowUUID to start a chat.
|
|
3469
3717
|
|
|
3470
3718
|
member:label actionParams
|
|
3471
3719
|
{"addLabelIds":["<uuid>"],"removeLabelIds":["<uuid>"]}
|
|
3472
3720
|
`;
|
|
3473
3721
|
|
|
3474
|
-
// src/lib/routines/field-help.ts
|
|
3475
|
-
var ROUTINE_FIELD_REFERENCE = `
|
|
3476
|
-
Field reference for --data JSON (create/update):
|
|
3477
|
-
|
|
3478
|
-
Core
|
|
3479
|
-
name (string) Display name
|
|
3480
|
-
workflowId (number) Workflow run when the routine fires
|
|
3481
|
-
targetMemberId (number) Member who receives the scheduled outreach
|
|
3482
|
-
|
|
3483
|
-
Schedule
|
|
3484
|
-
scheduleEnabled (bool) Master on/off for the cron trigger
|
|
3485
|
-
scheduleMode (string) "CRON" (only mode today)
|
|
3486
|
-
cronString (string) Standard cron, e.g. "0 7 * * *"
|
|
3487
|
-
timezone (string, optional) IANA name, e.g. "America/Los_Angeles"
|
|
3488
|
-
Defaults to workspace timezone, else UTC
|
|
3489
|
-
|
|
3490
|
-
Visibility / lifecycle
|
|
3491
|
-
archived (bool) Archive flag on update
|
|
3492
|
-
event rules Use gr events rules create \u2014 not inline here
|
|
3493
|
-
`;
|
|
3494
|
-
|
|
3495
3722
|
// src/domains/events.ts
|
|
3496
3723
|
var RULE_COLUMNS = [
|
|
3497
3724
|
COL.id(),
|
|
@@ -3499,15 +3726,6 @@ var RULE_COLUMNS = [
|
|
|
3499
3726
|
{ header: "EVENT", getValue: (item) => item.eventType ?? "" },
|
|
3500
3727
|
{ header: "ACTIVE", getValue: (item) => String(item.active ?? "") }
|
|
3501
3728
|
];
|
|
3502
|
-
var ROUTINE_COLUMNS = [
|
|
3503
|
-
COL.id(),
|
|
3504
|
-
COL.name(),
|
|
3505
|
-
{ header: "MODE", getValue: (item) => item.scheduleMode ?? "" },
|
|
3506
|
-
{ header: "CRON", getValue: (item) => item.cronString ?? "" },
|
|
3507
|
-
{ header: "TZ", getValue: (item) => item.timezone ?? "UTC" },
|
|
3508
|
-
{ header: "ENABLED", getValue: (item) => String(item.scheduleEnabled ?? "") },
|
|
3509
|
-
{ header: "NEXT RUN", getValue: (item) => item.nextRun ?? "" }
|
|
3510
|
-
];
|
|
3511
3729
|
var eventRulesCrudNoList = crudCommands({
|
|
3512
3730
|
prefix: ["events", "rules"],
|
|
3513
3731
|
entityName: "event rule",
|
|
@@ -3531,8 +3749,8 @@ ${EVENT_RULE_FIELD_REFERENCE}`,
|
|
|
3531
3749
|
`gr events rules create -w $WID --allow-write --data '{"name":"AM voice check-in",`,
|
|
3532
3750
|
' "objectType":"routine","eventType":"routine:triggered","actionType":"chat:create",',
|
|
3533
3751
|
' "routineId":<routine-id>,',
|
|
3534
|
-
' "actionParams":{"
|
|
3535
|
-
` "
|
|
3752
|
+
' "actionParams":{"workflowUUID":"<workflow-uuid>","channel":"phone-voice",',
|
|
3753
|
+
` "phoneNumberUUID":"<workspace-phone-uuid>"}}'`,
|
|
3536
3754
|
"",
|
|
3537
3755
|
' PM SMS variant: use "channel":"phone-sms" and a second rule on the same routineId.',
|
|
3538
3756
|
" Prereq: org phone assigned + gr phone-numbers link; workflow published with start task.",
|
|
@@ -3670,74 +3888,6 @@ var eventsDomain = {
|
|
|
3670
3888
|
const result2 = await requireAuth(ctx).apiClient.searchEventRules(workspace, opts);
|
|
3671
3889
|
outputData(result2, ctx, RULE_COLUMNS);
|
|
3672
3890
|
}
|
|
3673
|
-
},
|
|
3674
|
-
// Routines
|
|
3675
|
-
...crudCommands({
|
|
3676
|
-
prefix: ["routines"],
|
|
3677
|
-
entityName: "routine",
|
|
3678
|
-
listMethod: "getRoutines",
|
|
3679
|
-
getMethod: "getRoutine",
|
|
3680
|
-
createMethod: "createRoutine",
|
|
3681
|
-
updateMethod: "updateRoutine",
|
|
3682
|
-
deleteMethod: "deleteRoutine",
|
|
3683
|
-
paginationSupport: "full",
|
|
3684
|
-
columns: ROUTINE_COLUMNS,
|
|
3685
|
-
createHelp: `Create a routine
|
|
3686
|
-
|
|
3687
|
-
${ROUTINE_FIELD_REFERENCE}`,
|
|
3688
|
-
updateHelp: `Update a routine
|
|
3689
|
-
|
|
3690
|
-
${ROUTINE_FIELD_REFERENCE}`,
|
|
3691
|
-
createExamples: [
|
|
3692
|
-
"Daily 7am check-in in the member\u2019s timezone (cron is interpreted in `timezone`):",
|
|
3693
|
-
`gr routines create -w $WID --allow-write --data '{"name":"AM check-in",`,
|
|
3694
|
-
' "scheduleEnabled":true,"scheduleMode":"CRON","cronString":"0 7 * * *",',
|
|
3695
|
-
` "timezone":"America/Los_Angeles","targetMemberId":1,"workflowId":2}'`,
|
|
3696
|
-
" (timezone is an IANA name; omit it to default to the workspace timezone, else UTC.",
|
|
3697
|
-
' Without it, "0 7 * * *" fires at 07:00 UTC, not 7am local.)'
|
|
3698
|
-
]
|
|
3699
|
-
}),
|
|
3700
|
-
{
|
|
3701
|
-
path: ["routines", "run"],
|
|
3702
|
-
description: "Run a routine now",
|
|
3703
|
-
mutates: true,
|
|
3704
|
-
requiresWorkspace: true,
|
|
3705
|
-
args: [{ name: "id", description: "Routine ID", required: true }],
|
|
3706
|
-
handler: async ({ ctx, workspace, args, data }) => {
|
|
3707
|
-
const result2 = await requireAuth(ctx).apiClient.runRoutine(
|
|
3708
|
-
workspace,
|
|
3709
|
-
requireNumber(args, "id"),
|
|
3710
|
-
data ?? {}
|
|
3711
|
-
);
|
|
3712
|
-
outputSingle(result2, ctx);
|
|
3713
|
-
}
|
|
3714
|
-
},
|
|
3715
|
-
{
|
|
3716
|
-
path: ["routines", "runs", "list"],
|
|
3717
|
-
description: "List runs for a routine",
|
|
3718
|
-
requiresWorkspace: true,
|
|
3719
|
-
args: [
|
|
3720
|
-
{
|
|
3721
|
-
name: "id",
|
|
3722
|
-
description: "Routine ID (aligns with routines get/run/update/delete)"
|
|
3723
|
-
},
|
|
3724
|
-
{ name: "status", description: "Filter by run status" },
|
|
3725
|
-
{ name: "page", description: "Page number (1-indexed)" },
|
|
3726
|
-
{ name: "page-size", description: "Page size (max 100)" }
|
|
3727
|
-
],
|
|
3728
|
-
handler: async ({ ctx, workspace, args }) => {
|
|
3729
|
-
const routineId = args.id ? requireNumber(args, "id") : void 0;
|
|
3730
|
-
const page = args.page ? requireNumber(args, "page") : void 0;
|
|
3731
|
-
const pageSize = args["page-size"] ? requireNumber(args, "page-size") : void 0;
|
|
3732
|
-
const status = args.status ?? void 0;
|
|
3733
|
-
const result2 = await requireAuth(ctx).apiClient.getRoutineRuns(workspace, {
|
|
3734
|
-
...routineId !== void 0 ? { routineId } : {},
|
|
3735
|
-
...page !== void 0 ? { page } : {},
|
|
3736
|
-
...pageSize !== void 0 ? { pageSize } : {},
|
|
3737
|
-
...status ? { status } : {}
|
|
3738
|
-
});
|
|
3739
|
-
outputData(result2, ctx);
|
|
3740
|
-
}
|
|
3741
3891
|
}
|
|
3742
3892
|
]
|
|
3743
3893
|
};
|
|
@@ -5354,6 +5504,178 @@ var sitesDomain = {
|
|
|
5354
5504
|
]
|
|
5355
5505
|
};
|
|
5356
5506
|
|
|
5507
|
+
// src/domains/abilities.ts
|
|
5508
|
+
import { randomUUID } from "crypto";
|
|
5509
|
+
var ATTACHED_COLUMNS = [
|
|
5510
|
+
{ header: "UUID", getValue: (item) => item.uuid ?? "" },
|
|
5511
|
+
{ header: "TYPE", getValue: (item) => item.abilityType ?? "" },
|
|
5512
|
+
{ header: "ENABLED", getValue: (item) => item.enabled === false ? "" : "yes" },
|
|
5513
|
+
{ header: "CEL", getValue: (item) => item.celCondition ?? "" }
|
|
5514
|
+
];
|
|
5515
|
+
function toAbilityData(a) {
|
|
5516
|
+
return {
|
|
5517
|
+
uuid: a.uuid,
|
|
5518
|
+
abilityType: a.abilityType,
|
|
5519
|
+
config: a.config ?? {},
|
|
5520
|
+
enabled: a.enabled ?? true,
|
|
5521
|
+
...a.celCondition ? { celCondition: a.celCondition } : {}
|
|
5522
|
+
};
|
|
5523
|
+
}
|
|
5524
|
+
var TYPE_COLUMNS = [
|
|
5525
|
+
{ header: "SLUG", getValue: (item) => item.slug ?? "" },
|
|
5526
|
+
{ header: "TITLE", getValue: (item) => item.title ?? "" },
|
|
5527
|
+
{ header: "CATEGORY", getValue: (item) => item.category ?? "" },
|
|
5528
|
+
{
|
|
5529
|
+
header: "CHANNELS",
|
|
5530
|
+
// null channels means "all channels" — show that explicitly so an empty
|
|
5531
|
+
// cell never reads as "no channels".
|
|
5532
|
+
getValue: (item) => Array.isArray(item.channels) ? item.channels.join(",") : "all"
|
|
5533
|
+
},
|
|
5534
|
+
{ header: "SINGLETON", getValue: (item) => item.singleton ? "yes" : "" },
|
|
5535
|
+
{ header: "FEATURE-GATE", getValue: (item) => item.requiredFeature ?? "" }
|
|
5536
|
+
];
|
|
5537
|
+
var abilitiesDomain = {
|
|
5538
|
+
name: "abilities",
|
|
5539
|
+
description: "Inspect ability types and attach/detach them on a task",
|
|
5540
|
+
commands: [
|
|
5541
|
+
{
|
|
5542
|
+
path: ["abilities", "list"],
|
|
5543
|
+
description: "List builtin ability types with their channel constraints and param specs. Table shows SLUG, TITLE, CATEGORY, CHANNELS (all = every channel), SINGLETON, FEATURE-GATE; use -o json for full specs including the params JSON Schema.",
|
|
5544
|
+
requiresWorkspace: true,
|
|
5545
|
+
columns: TYPE_COLUMNS,
|
|
5546
|
+
args: [
|
|
5547
|
+
{
|
|
5548
|
+
name: "include-system",
|
|
5549
|
+
description: "Also list auto-injected system abilities (hidden by default)",
|
|
5550
|
+
isFlag: true
|
|
5551
|
+
}
|
|
5552
|
+
],
|
|
5553
|
+
handler: async (hctx) => {
|
|
5554
|
+
const includeSystem = hctx.args["include-system"] === true;
|
|
5555
|
+
const result2 = await requireAuth(hctx.ctx).apiClient.getAbilityTypes(
|
|
5556
|
+
hctx.workspace,
|
|
5557
|
+
includeSystem ? { includeSystem: true } : {}
|
|
5558
|
+
);
|
|
5559
|
+
outputData(result2.types ?? [], hctx.ctx, TYPE_COLUMNS);
|
|
5560
|
+
}
|
|
5561
|
+
},
|
|
5562
|
+
{
|
|
5563
|
+
path: ["abilities", "get"],
|
|
5564
|
+
description: "Show the full spec for one ability type: title, description, category, channels, singleton/system flags, required feature, and the JSON Schema of its params model.",
|
|
5565
|
+
requiresWorkspace: true,
|
|
5566
|
+
args: [
|
|
5567
|
+
{
|
|
5568
|
+
name: "name",
|
|
5569
|
+
aliases: ["slug"],
|
|
5570
|
+
description: "Ability type slug (e.g. 'communication:phone_call_tools')",
|
|
5571
|
+
required: true
|
|
5572
|
+
}
|
|
5573
|
+
],
|
|
5574
|
+
handler: async (hctx) => {
|
|
5575
|
+
const slug = hctx.args.name ?? hctx.args.slug;
|
|
5576
|
+
if (!slug) throw new CLIError("--name is required");
|
|
5577
|
+
const result2 = await requireAuth(hctx.ctx).apiClient.getAbilityType(
|
|
5578
|
+
hctx.workspace,
|
|
5579
|
+
slug
|
|
5580
|
+
);
|
|
5581
|
+
outputSingle(result2, hctx.ctx);
|
|
5582
|
+
}
|
|
5583
|
+
},
|
|
5584
|
+
{
|
|
5585
|
+
path: ["abilities", "attached"],
|
|
5586
|
+
description: "List a task's task-specific abilities (excludes workflow-inherited).",
|
|
5587
|
+
requiresWorkspace: true,
|
|
5588
|
+
columns: ATTACHED_COLUMNS,
|
|
5589
|
+
args: [{ name: "task", description: "Task ID", required: true }],
|
|
5590
|
+
handler: async (hctx) => {
|
|
5591
|
+
const task = await requireAuth(hctx.ctx).apiClient.getTask(
|
|
5592
|
+
hctx.workspace,
|
|
5593
|
+
requireNumber(hctx.args, "task")
|
|
5594
|
+
);
|
|
5595
|
+
outputData(task.abilities ?? [], hctx.ctx, ATTACHED_COLUMNS);
|
|
5596
|
+
}
|
|
5597
|
+
},
|
|
5598
|
+
{
|
|
5599
|
+
path: ["abilities", "attach"],
|
|
5600
|
+
description: "Attach an ability to a task. Reads the task, appends the new ability (generating its uuid), and saves \u2014 so existing abilities aren't lost. Find slugs via 'gr abilities list' and the config schema via 'gr abilities get'.",
|
|
5601
|
+
mutates: true,
|
|
5602
|
+
requiresWorkspace: true,
|
|
5603
|
+
args: [
|
|
5604
|
+
{ name: "task", description: "Task ID to attach to", required: true },
|
|
5605
|
+
{
|
|
5606
|
+
name: "type",
|
|
5607
|
+
aliases: ["ability-type", "slug"],
|
|
5608
|
+
description: "Ability type slug, e.g. 'integration:data_access'",
|
|
5609
|
+
required: true
|
|
5610
|
+
},
|
|
5611
|
+
{
|
|
5612
|
+
name: "config",
|
|
5613
|
+
description: `Ability config as a JSON object, e.g. '{"pauseOnEscalate":true}'`
|
|
5614
|
+
},
|
|
5615
|
+
{ name: "cel", description: "Optional CEL activation condition" },
|
|
5616
|
+
{
|
|
5617
|
+
name: "disabled",
|
|
5618
|
+
description: "Attach in a disabled state (default: enabled)",
|
|
5619
|
+
isFlag: true
|
|
5620
|
+
}
|
|
5621
|
+
],
|
|
5622
|
+
handler: async (hctx) => {
|
|
5623
|
+
const api = requireAuth(hctx.ctx).apiClient;
|
|
5624
|
+
const taskId = requireNumber(hctx.args, "task");
|
|
5625
|
+
const abilityType = hctx.args.type ?? hctx.args["ability-type"] ?? hctx.args.slug;
|
|
5626
|
+
if (!abilityType) throw new CLIError("--type is required (an ability type slug)");
|
|
5627
|
+
let config = {};
|
|
5628
|
+
if (hctx.args.config) {
|
|
5629
|
+
try {
|
|
5630
|
+
config = JSON.parse(hctx.args.config);
|
|
5631
|
+
} catch {
|
|
5632
|
+
throw new CLIError("--config must be a valid JSON object");
|
|
5633
|
+
}
|
|
5634
|
+
}
|
|
5635
|
+
const task = await api.getTask(hctx.workspace, taskId);
|
|
5636
|
+
const existing = (task.abilities ?? []).map(toAbilityData);
|
|
5637
|
+
const next = {
|
|
5638
|
+
uuid: randomUUID(),
|
|
5639
|
+
abilityType,
|
|
5640
|
+
config,
|
|
5641
|
+
enabled: hctx.args.disabled !== true,
|
|
5642
|
+
...hctx.args.cel ? { celCondition: hctx.args.cel } : {}
|
|
5643
|
+
};
|
|
5644
|
+
const updated = await api.updateTask(hctx.workspace, taskId, {
|
|
5645
|
+
abilities: [...existing, next]
|
|
5646
|
+
});
|
|
5647
|
+
outputData(updated.abilities ?? [], hctx.ctx, ATTACHED_COLUMNS);
|
|
5648
|
+
}
|
|
5649
|
+
},
|
|
5650
|
+
{
|
|
5651
|
+
path: ["abilities", "detach"],
|
|
5652
|
+
description: "Detach an ability from a task by its uuid (from `gr abilities attached`). Re-saves the remaining task-specific abilities so the rest are preserved.",
|
|
5653
|
+
mutates: true,
|
|
5654
|
+
requiresWorkspace: true,
|
|
5655
|
+
args: [
|
|
5656
|
+
{ name: "task", description: "Task ID", required: true },
|
|
5657
|
+
{ name: "uuid", description: "UUID of the attached ability to remove", required: true }
|
|
5658
|
+
],
|
|
5659
|
+
handler: async (hctx) => {
|
|
5660
|
+
const api = requireAuth(hctx.ctx).apiClient;
|
|
5661
|
+
const taskId = requireNumber(hctx.args, "task");
|
|
5662
|
+
const uuid = hctx.args.uuid;
|
|
5663
|
+
if (!uuid) throw new CLIError("--uuid is required");
|
|
5664
|
+
const task = await api.getTask(hctx.workspace, taskId);
|
|
5665
|
+
const existing = task.abilities ?? [];
|
|
5666
|
+
const remaining = existing.filter((a) => a.uuid !== uuid).map(toAbilityData);
|
|
5667
|
+
if (remaining.length === existing.length) {
|
|
5668
|
+
throw new CLIError(`No attached ability with uuid '${uuid}' on task ${taskId}`);
|
|
5669
|
+
}
|
|
5670
|
+
const updated = await api.updateTask(hctx.workspace, taskId, {
|
|
5671
|
+
abilities: remaining
|
|
5672
|
+
});
|
|
5673
|
+
outputData(updated.abilities ?? [], hctx.ctx, ATTACHED_COLUMNS);
|
|
5674
|
+
}
|
|
5675
|
+
}
|
|
5676
|
+
]
|
|
5677
|
+
};
|
|
5678
|
+
|
|
5357
5679
|
// src/domains/access-grants.ts
|
|
5358
5680
|
var COLUMNS14 = [
|
|
5359
5681
|
COL.id(),
|
|
@@ -5875,6 +6197,149 @@ var authManageDomain = {
|
|
|
5875
6197
|
]
|
|
5876
6198
|
};
|
|
5877
6199
|
|
|
6200
|
+
// src/domains/billing.ts
|
|
6201
|
+
function priceSummary(product) {
|
|
6202
|
+
const plans = product.plans ?? {};
|
|
6203
|
+
const abbr = { month: "mo", year: "yr", quarter: "qtr" };
|
|
6204
|
+
const parts = [];
|
|
6205
|
+
for (const [period, plan] of Object.entries(plans)) {
|
|
6206
|
+
const cents = plan?.costPerPeriod ?? 0;
|
|
6207
|
+
if (!plan?.stripePriceId) continue;
|
|
6208
|
+
parts.push(`$${(cents / 100).toFixed(0)}/${abbr[period] ?? period}`);
|
|
6209
|
+
}
|
|
6210
|
+
return parts.join(", ") || "\u2014";
|
|
6211
|
+
}
|
|
6212
|
+
var PRODUCT_COLUMNS2 = [
|
|
6213
|
+
{ header: "ID", getValue: (p) => String(p.id ?? "") },
|
|
6214
|
+
{ header: "NAME", getValue: (p) => p.name ?? "" },
|
|
6215
|
+
{ header: "TYPE", getValue: (p) => String(p.productType ?? "") },
|
|
6216
|
+
{ header: "PRICE", getValue: (p) => priceSummary(p) },
|
|
6217
|
+
{
|
|
6218
|
+
header: "TRIAL",
|
|
6219
|
+
getValue: (p) => {
|
|
6220
|
+
const days = p.trialPeriodDays ?? 0;
|
|
6221
|
+
return days > 0 ? `${days}d` : "\u2014";
|
|
6222
|
+
}
|
|
6223
|
+
}
|
|
6224
|
+
];
|
|
6225
|
+
var SUBSCRIPTION_COLUMNS = [
|
|
6226
|
+
COL.id(),
|
|
6227
|
+
COL.status(),
|
|
6228
|
+
{ header: "PLAN", getValue: (s) => String(s.plan_name ?? s.productName ?? s.plan ?? "") },
|
|
6229
|
+
{ header: "PERIOD", getValue: (s) => String(s.planPeriod ?? s.plan_period ?? "") },
|
|
6230
|
+
{
|
|
6231
|
+
header: "ORG",
|
|
6232
|
+
getValue: (s) => String(s.organizationUuid ?? s.customerOrganizationUuid ?? "")
|
|
6233
|
+
}
|
|
6234
|
+
];
|
|
6235
|
+
function isTable(ctx) {
|
|
6236
|
+
return (ctx.outputFormat ?? "table") === "table";
|
|
6237
|
+
}
|
|
6238
|
+
function hint(ctx, lines) {
|
|
6239
|
+
if (!isTable(ctx)) return;
|
|
6240
|
+
process.stdout.write("\n" + lines.join("\n") + "\n");
|
|
6241
|
+
}
|
|
6242
|
+
function productType(args) {
|
|
6243
|
+
const t = args.type ?? args["product-type"];
|
|
6244
|
+
if (t === "org" || t === "workspace") return t;
|
|
6245
|
+
return void 0;
|
|
6246
|
+
}
|
|
6247
|
+
var billingDomain = {
|
|
6248
|
+
name: "billing",
|
|
6249
|
+
description: "Browse plans, start checkout, and check subscription status",
|
|
6250
|
+
commands: [
|
|
6251
|
+
{
|
|
6252
|
+
path: ["billing", "products"],
|
|
6253
|
+
description: "List self-service plans you can purchase",
|
|
6254
|
+
requiresWorkspace: false,
|
|
6255
|
+
args: [
|
|
6256
|
+
{
|
|
6257
|
+
name: "type",
|
|
6258
|
+
description: "Filter by plan type: 'org' or 'workspace'"
|
|
6259
|
+
}
|
|
6260
|
+
],
|
|
6261
|
+
examples: ["gr billing products --type org"],
|
|
6262
|
+
handler: async ({ ctx, args }) => {
|
|
6263
|
+
const products = await requireAuth(ctx).apiClient.listSelfServeProducts(
|
|
6264
|
+
productType(args)
|
|
6265
|
+
);
|
|
6266
|
+
outputData(products, ctx, PRODUCT_COLUMNS2);
|
|
6267
|
+
if (products.length === 0) {
|
|
6268
|
+
hint(ctx, ["No purchasable plans found. (Free plans are not sold via the CLI.)"]);
|
|
6269
|
+
} else {
|
|
6270
|
+
hint(ctx, [
|
|
6271
|
+
"Next \u2014 buy an organization plan to get started:",
|
|
6272
|
+
" gr billing checkout --product <id> [--period month|year]"
|
|
6273
|
+
]);
|
|
6274
|
+
}
|
|
6275
|
+
}
|
|
6276
|
+
},
|
|
6277
|
+
{
|
|
6278
|
+
path: ["billing", "checkout"],
|
|
6279
|
+
description: "Get a Stripe payment link to purchase an organization plan",
|
|
6280
|
+
mutates: true,
|
|
6281
|
+
requiresWorkspace: false,
|
|
6282
|
+
args: [
|
|
6283
|
+
{ name: "product", description: "Product ID of the org plan to buy", required: true },
|
|
6284
|
+
{ name: "period", description: "Billing period: 'month' or 'year'", default: "month" }
|
|
6285
|
+
],
|
|
6286
|
+
examples: ["gr billing checkout --product 12 --period month"],
|
|
6287
|
+
handler: async ({ ctx, args }) => {
|
|
6288
|
+
const productId = Number(requireString(args, "product"));
|
|
6289
|
+
const period = args.period || "month";
|
|
6290
|
+
const result2 = await requireAuth(ctx).apiClient.createCheckoutSession(
|
|
6291
|
+
productId,
|
|
6292
|
+
period
|
|
6293
|
+
);
|
|
6294
|
+
if (isTable(ctx)) {
|
|
6295
|
+
process.stdout.write(
|
|
6296
|
+
`
|
|
6297
|
+
\u{1F4B3} Open this link in a browser to complete payment:
|
|
6298
|
+
|
|
6299
|
+
${result2.checkoutUrl}
|
|
6300
|
+
|
|
6301
|
+
Once payment succeeds, your organization and subscription are created automatically.
|
|
6302
|
+
Then check it landed:
|
|
6303
|
+
gr billing status
|
|
6304
|
+
`
|
|
6305
|
+
);
|
|
6306
|
+
} else {
|
|
6307
|
+
outputSingle(result2, ctx);
|
|
6308
|
+
}
|
|
6309
|
+
}
|
|
6310
|
+
},
|
|
6311
|
+
{
|
|
6312
|
+
path: ["billing", "status"],
|
|
6313
|
+
description: "Show your subscriptions and what to do next",
|
|
6314
|
+
requiresWorkspace: false,
|
|
6315
|
+
examples: ["gr billing status"],
|
|
6316
|
+
handler: async ({ ctx }) => {
|
|
6317
|
+
const subs = await requireAuth(ctx).apiClient.getSubscriptions();
|
|
6318
|
+
outputData(subs, ctx, SUBSCRIPTION_COLUMNS);
|
|
6319
|
+
const active = subs.find(
|
|
6320
|
+
(s) => ["active", "trialing"].includes(String(s.status ?? s.subscriptionStatus ?? ""))
|
|
6321
|
+
);
|
|
6322
|
+
if (!active) {
|
|
6323
|
+
hint(ctx, [
|
|
6324
|
+
"No active subscription yet. To get started:",
|
|
6325
|
+
" gr billing products --type org # pick a plan",
|
|
6326
|
+
" gr billing checkout --product <id> # pay up front"
|
|
6327
|
+
]);
|
|
6328
|
+
return;
|
|
6329
|
+
}
|
|
6330
|
+
const orgUuid = String(
|
|
6331
|
+
active.organizationUuid ?? active.customerOrganizationUuid ?? "<org-uuid>"
|
|
6332
|
+
);
|
|
6333
|
+
hint(ctx, [
|
|
6334
|
+
"You have an active subscription. Add a workspace:",
|
|
6335
|
+
" gr billing products --type workspace # pick a workspace plan",
|
|
6336
|
+
` gr workspaces create --org ${orgUuid} --product <id> --name "My Workspace"`
|
|
6337
|
+
]);
|
|
6338
|
+
}
|
|
6339
|
+
}
|
|
6340
|
+
]
|
|
6341
|
+
};
|
|
6342
|
+
|
|
5878
6343
|
// src/domains/billing-portal.ts
|
|
5879
6344
|
var INVOICE_COLUMNS = [
|
|
5880
6345
|
{ header: "ID", getValue: (i) => String(i.id) },
|
|
@@ -7575,9 +8040,22 @@ function formatPhoneNumberBehaviorMatrix(phone, outboundUsers, warnings = collec
|
|
|
7575
8040
|
lines.push(`UUID: ${phone.uuid} | Mapped: ${phone.isMapped ? "yes" : "no"}`);
|
|
7576
8041
|
lines.push("");
|
|
7577
8042
|
lines.push("## Channels");
|
|
7578
|
-
lines.push(
|
|
8043
|
+
lines.push(
|
|
8044
|
+
` Voice: ${phone.enableVoice ? "on" : "off"} SMS: ${phone.enableSms ? "on" : "off"} WhatsApp: ${phone.enableWhatsApp ? "on" : "off"}`
|
|
8045
|
+
);
|
|
7579
8046
|
lines.push(` Assistant: ${phone.assistantEnabled ? "on" : "off"}`);
|
|
7580
8047
|
lines.push("");
|
|
8048
|
+
if (phone.enableVoice) {
|
|
8049
|
+
lines.push("## Channel defaults (auto-injected on voice)");
|
|
8050
|
+
lines.push(
|
|
8051
|
+
" Voice channels auto-add the hang_up_call tool (system:voice_channel);"
|
|
8052
|
+
);
|
|
8053
|
+
lines.push(
|
|
8054
|
+
" it is not listed by `gr agents abilities list`. See `gr abilities get"
|
|
8055
|
+
);
|
|
8056
|
+
lines.push(" --name communication:phone_call_tools` for the voice ability spec.");
|
|
8057
|
+
lines.push("");
|
|
8058
|
+
}
|
|
7581
8059
|
lines.push("## Inbound route");
|
|
7582
8060
|
lines.push(
|
|
7583
8061
|
` defaultWorkflowId (inbound): ${phone.defaultWorkflowId ?? "null"} restartDelay: ${phone.restartDelay ?? "null"}s`
|
|
@@ -7658,9 +8136,7 @@ async function findOutboundPhoneUsers(client, workspace, phone) {
|
|
|
7658
8136
|
}
|
|
7659
8137
|
page += 1;
|
|
7660
8138
|
if (page > 50) {
|
|
7661
|
-
process.stderr.write(
|
|
7662
|
-
"Warning: event-rules lookup truncated at 5000 results (50 pages)\n"
|
|
7663
|
-
);
|
|
8139
|
+
process.stderr.write("Warning: event-rules lookup truncated at 5000 results (50 pages)\n");
|
|
7664
8140
|
break;
|
|
7665
8141
|
}
|
|
7666
8142
|
}
|
|
@@ -10775,6 +11251,115 @@ var reportsDomain = {
|
|
|
10775
11251
|
]
|
|
10776
11252
|
};
|
|
10777
11253
|
|
|
11254
|
+
// src/lib/routines/field-help.ts
|
|
11255
|
+
var ROUTINE_FIELD_REFERENCE = `
|
|
11256
|
+
Field reference for --data JSON (create/update):
|
|
11257
|
+
|
|
11258
|
+
Core
|
|
11259
|
+
name (string) Display name
|
|
11260
|
+
workflowId (number) Workflow run when the routine fires
|
|
11261
|
+
targetMemberId (number) Member who receives the scheduled outreach
|
|
11262
|
+
|
|
11263
|
+
Schedule
|
|
11264
|
+
scheduleEnabled (bool) Master on/off for the cron trigger
|
|
11265
|
+
scheduleMode (string) "CRON" (only mode today)
|
|
11266
|
+
cronString (string) Standard cron, e.g. "0 7 * * *"
|
|
11267
|
+
timezone (string, optional) IANA name, e.g. "America/Los_Angeles"
|
|
11268
|
+
Defaults to workspace timezone, else UTC
|
|
11269
|
+
|
|
11270
|
+
Visibility / lifecycle
|
|
11271
|
+
archived (bool) Archive flag on update
|
|
11272
|
+
event rules Use gr events rules create \u2014 not inline here
|
|
11273
|
+
`;
|
|
11274
|
+
|
|
11275
|
+
// src/domains/routines.ts
|
|
11276
|
+
var ROUTINE_COLUMNS = [
|
|
11277
|
+
COL.id(),
|
|
11278
|
+
COL.name(),
|
|
11279
|
+
{ header: "MODE", getValue: (item) => item.scheduleMode ?? "" },
|
|
11280
|
+
{ header: "CRON", getValue: (item) => item.cronString ?? "" },
|
|
11281
|
+
{ header: "TZ", getValue: (item) => item.timezone ?? "UTC" },
|
|
11282
|
+
{ header: "ENABLED", getValue: (item) => String(item.scheduleEnabled ?? "") },
|
|
11283
|
+
{ header: "NEXT RUN", getValue: (item) => item.nextRun ?? "" }
|
|
11284
|
+
];
|
|
11285
|
+
var routinesDomain = {
|
|
11286
|
+
name: "routines",
|
|
11287
|
+
description: "Schedule recurring outbound (e.g. a daily SMS/voice check-in via cron)",
|
|
11288
|
+
commands: [
|
|
11289
|
+
...crudCommands({
|
|
11290
|
+
prefix: ["routines"],
|
|
11291
|
+
entityName: "routine",
|
|
11292
|
+
listMethod: "getRoutines",
|
|
11293
|
+
getMethod: "getRoutine",
|
|
11294
|
+
createMethod: "createRoutine",
|
|
11295
|
+
updateMethod: "updateRoutine",
|
|
11296
|
+
deleteMethod: "deleteRoutine",
|
|
11297
|
+
paginationSupport: "full",
|
|
11298
|
+
columns: ROUTINE_COLUMNS,
|
|
11299
|
+
createHelp: `Create a routine
|
|
11300
|
+
|
|
11301
|
+
${ROUTINE_FIELD_REFERENCE}`,
|
|
11302
|
+
updateHelp: `Update a routine
|
|
11303
|
+
|
|
11304
|
+
${ROUTINE_FIELD_REFERENCE}`,
|
|
11305
|
+
createExamples: [
|
|
11306
|
+
"Daily 7am check-in in the member\u2019s timezone (cron is interpreted in `timezone`):",
|
|
11307
|
+
`gr routines create -w $WID --allow-write --data '{"name":"AM check-in",`,
|
|
11308
|
+
' "scheduleEnabled":true,"scheduleMode":"CRON","cronString":"0 7 * * *",',
|
|
11309
|
+
` "timezone":"America/Los_Angeles","targetMemberId":1,"workflowId":2}'`,
|
|
11310
|
+
" (timezone is an IANA name; omit it to default to the workspace timezone, else UTC.",
|
|
11311
|
+
' Without it, "0 7 * * *" fires at 07:00 UTC, not 7am local.)',
|
|
11312
|
+
"",
|
|
11313
|
+
"To actually send via SMS/voice, pair the routine with an event rule:",
|
|
11314
|
+
' gr events rules create \u2014 objectType "routine", eventType "routine:triggered",',
|
|
11315
|
+
' actionType "chat:create". See `gr events --help`.'
|
|
11316
|
+
]
|
|
11317
|
+
}),
|
|
11318
|
+
{
|
|
11319
|
+
path: ["routines", "run"],
|
|
11320
|
+
description: "Run a routine now",
|
|
11321
|
+
mutates: true,
|
|
11322
|
+
requiresWorkspace: true,
|
|
11323
|
+
args: [{ name: "id", description: "Routine ID", required: true }],
|
|
11324
|
+
handler: async ({ ctx, workspace, args, data }) => {
|
|
11325
|
+
const result2 = await requireAuth(ctx).apiClient.runRoutine(
|
|
11326
|
+
workspace,
|
|
11327
|
+
requireNumber(args, "id"),
|
|
11328
|
+
data ?? {}
|
|
11329
|
+
);
|
|
11330
|
+
outputSingle(result2, ctx);
|
|
11331
|
+
}
|
|
11332
|
+
},
|
|
11333
|
+
{
|
|
11334
|
+
path: ["routines", "runs", "list"],
|
|
11335
|
+
description: "List runs for a routine",
|
|
11336
|
+
requiresWorkspace: true,
|
|
11337
|
+
args: [
|
|
11338
|
+
{
|
|
11339
|
+
name: "id",
|
|
11340
|
+
description: "Routine ID (aligns with routines get/run/update/delete)"
|
|
11341
|
+
},
|
|
11342
|
+
{ name: "status", description: "Filter by run status" },
|
|
11343
|
+
{ name: "page", description: "Page number (1-indexed)" },
|
|
11344
|
+
{ name: "page-size", description: "Page size (max 100)" }
|
|
11345
|
+
],
|
|
11346
|
+
handler: async ({ ctx, workspace, args }) => {
|
|
11347
|
+
const routineId = args.id ? requireNumber(args, "id") : void 0;
|
|
11348
|
+
const page = args.page ? requireNumber(args, "page") : void 0;
|
|
11349
|
+
const pageSize = args["page-size"] ? requireNumber(args, "page-size") : void 0;
|
|
11350
|
+
const status = args.status ?? void 0;
|
|
11351
|
+
const result2 = await requireAuth(ctx).apiClient.getRoutineRuns(workspace, {
|
|
11352
|
+
...routineId !== void 0 ? { routineId } : {},
|
|
11353
|
+
...page !== void 0 ? { page } : {},
|
|
11354
|
+
...pageSize !== void 0 ? { pageSize } : {},
|
|
11355
|
+
...status ? { status } : {}
|
|
11356
|
+
});
|
|
11357
|
+
outputData(result2, ctx);
|
|
11358
|
+
}
|
|
11359
|
+
}
|
|
11360
|
+
]
|
|
11361
|
+
};
|
|
11362
|
+
|
|
10778
11363
|
// src/domains/subscriptions.ts
|
|
10779
11364
|
var COLUMNS27 = [
|
|
10780
11365
|
COL.id(),
|
|
@@ -12637,11 +13222,13 @@ function registerPublicDomains(registry) {
|
|
|
12637
13222
|
registry.register(sitesDomain);
|
|
12638
13223
|
registry.register(filesDomain);
|
|
12639
13224
|
registry.register(eventsDomain);
|
|
13225
|
+
registry.register(routinesDomain);
|
|
12640
13226
|
registry.register(experimentsDomain);
|
|
12641
13227
|
registry.register(calendarsDomain);
|
|
12642
13228
|
registry.register(qualificationsDomain);
|
|
12643
13229
|
registry.register(personasDomain);
|
|
12644
13230
|
registry.register(journeysDomain);
|
|
13231
|
+
registry.register(abilitiesDomain);
|
|
12645
13232
|
registry.register(phoneNumbersDomain);
|
|
12646
13233
|
registry.register(inboxesDomain);
|
|
12647
13234
|
registry.register(operatorGroupsDomain);
|
|
@@ -12654,6 +13241,7 @@ function registerPublicDomains(registry) {
|
|
|
12654
13241
|
registry.register(mcpServersDomain);
|
|
12655
13242
|
registry.register(apiKeysDomain);
|
|
12656
13243
|
registry.register(subscriptionsDomain);
|
|
13244
|
+
registry.register(billingDomain);
|
|
12657
13245
|
registry.register(supportDomain);
|
|
12658
13246
|
registry.register(reportsDomain);
|
|
12659
13247
|
registry.register(aiModelsDomain);
|
|
@@ -13069,13 +13657,25 @@ async function runConciergeAguiLoop(client, initialMessages, onEvent, opts) {
|
|
|
13069
13657
|
process.stderr.write(
|
|
13070
13658
|
" [HITL] Unsupported interrupt in CLI \u2014 open app.gravityrail.com/setup to continue.\n"
|
|
13071
13659
|
);
|
|
13072
|
-
|
|
13660
|
+
return {
|
|
13661
|
+
streamText,
|
|
13662
|
+
hasToolCalls,
|
|
13663
|
+
unsupportedHitl: normalizeUnsupportedHitl(hitlPayload)
|
|
13664
|
+
};
|
|
13073
13665
|
}
|
|
13074
13666
|
resume = next;
|
|
13075
13667
|
streamText = "";
|
|
13076
13668
|
}
|
|
13077
13669
|
return { streamText, hasToolCalls };
|
|
13078
13670
|
}
|
|
13671
|
+
function normalizeUnsupportedHitl(raw) {
|
|
13672
|
+
const payload = raw && typeof raw === "object" ? raw : {};
|
|
13673
|
+
return {
|
|
13674
|
+
type: typeof payload.type === "string" ? payload.type : void 0,
|
|
13675
|
+
interruptId: typeof payload.interrupt_id === "string" ? payload.interrupt_id : void 0,
|
|
13676
|
+
payload
|
|
13677
|
+
};
|
|
13678
|
+
}
|
|
13079
13679
|
|
|
13080
13680
|
// src/lib/realtime-client.ts
|
|
13081
13681
|
import { EventEmitter } from "events";
|
|
@@ -14147,8 +14747,8 @@ var ChatTUI = class {
|
|
|
14147
14747
|
const indent = " ".repeat(labelWidth);
|
|
14148
14748
|
if (!this.streamContent) {
|
|
14149
14749
|
if (this.runState === "thinking" || this.runState === "tool_running") {
|
|
14150
|
-
const
|
|
14151
|
-
lines.push(`${label}${YELLOW}${
|
|
14750
|
+
const hint2 = this.runState === "tool_running" && this.activeToolName ? `Running ${this.activeToolName}\u2026` : "Thinking\u2026";
|
|
14751
|
+
lines.push(`${label}${YELLOW}${hint2}${RESET}`);
|
|
14152
14752
|
} else {
|
|
14153
14753
|
lines.push(label);
|
|
14154
14754
|
}
|
|
@@ -14641,6 +15241,7 @@ var AudioPlayer = class extends EventEmitter2 {
|
|
|
14641
15241
|
};
|
|
14642
15242
|
|
|
14643
15243
|
// src/lib/chat-session.ts
|
|
15244
|
+
var HITL_INTERRUPT_EXIT_CODE = 3;
|
|
14644
15245
|
async function runChatSession(session, args) {
|
|
14645
15246
|
const debug = !!args.debug;
|
|
14646
15247
|
const isVoice = !!args.voice;
|
|
@@ -14670,6 +15271,14 @@ async function runChatSession(session, args) {
|
|
|
14670
15271
|
function vadMode(args) {
|
|
14671
15272
|
return typeof args.vad === "string" ? args.vad : "server";
|
|
14672
15273
|
}
|
|
15274
|
+
function sanitizeInterruptForStdout(result2) {
|
|
15275
|
+
const url = typeof result2.payload.url === "string" ? result2.payload.url : void 0;
|
|
15276
|
+
return {
|
|
15277
|
+
type: result2.type,
|
|
15278
|
+
interrupt_id: result2.interruptId,
|
|
15279
|
+
url
|
|
15280
|
+
};
|
|
15281
|
+
}
|
|
14673
15282
|
async function singleTurnChatSession(session, message, debug, autoApprove = false, allowWrite = false) {
|
|
14674
15283
|
const aguiClient = new AGUIClient({
|
|
14675
15284
|
baseUrl: session.ctx.envConfig.apiUrl,
|
|
@@ -14689,7 +15298,7 @@ async function singleTurnChatSession(session, message, debug, autoApprove = fals
|
|
|
14689
15298
|
`);
|
|
14690
15299
|
}
|
|
14691
15300
|
};
|
|
14692
|
-
const { streamText, hasToolCalls } = await runConciergeAguiLoop(
|
|
15301
|
+
const { streamText, hasToolCalls, unsupportedHitl } = await runConciergeAguiLoop(
|
|
14693
15302
|
aguiClient,
|
|
14694
15303
|
[{ role: "user", content: message }],
|
|
14695
15304
|
onEvent,
|
|
@@ -14702,6 +15311,21 @@ async function singleTurnChatSession(session, message, debug, autoApprove = fals
|
|
|
14702
15311
|
nonInteractive: true
|
|
14703
15312
|
}
|
|
14704
15313
|
);
|
|
15314
|
+
if (unsupportedHitl) {
|
|
15315
|
+
const jsonPayload = JSON.stringify({
|
|
15316
|
+
error: "hitl_interrupt",
|
|
15317
|
+
message: "A human-in-the-loop interrupt requires out-of-band action before the operation can continue.",
|
|
15318
|
+
resume: {
|
|
15319
|
+
chatId: session.chatId,
|
|
15320
|
+
chatUuid: session.chatUuid,
|
|
15321
|
+
interruptId: unsupportedHitl.interruptId
|
|
15322
|
+
},
|
|
15323
|
+
interrupt: sanitizeInterruptForStdout(unsupportedHitl)
|
|
15324
|
+
});
|
|
15325
|
+
process.stdout.write(`${jsonPayload}
|
|
15326
|
+
`);
|
|
15327
|
+
throw new CLIError("HITL interrupt requires out-of-band handling", HITL_INTERRUPT_EXIT_CODE);
|
|
15328
|
+
}
|
|
14705
15329
|
if (streamText) {
|
|
14706
15330
|
process.stdout.write(`\x1B[32mAssistant:\x1B[0m ${streamText}
|
|
14707
15331
|
`);
|
|
@@ -15209,11 +15833,12 @@ async function createMemberChat(apiClient, workspace, memberId) {
|
|
|
15209
15833
|
async function conciergeOutboundCommand(hctx) {
|
|
15210
15834
|
const ctx = requireAuth(hctx.ctx);
|
|
15211
15835
|
const { args } = hctx;
|
|
15836
|
+
const message = typeof args.message === "string" ? args.message : void 0;
|
|
15212
15837
|
if (args.call && args.sms) {
|
|
15213
15838
|
throw new CLIError("--call and --sms are mutually exclusive");
|
|
15214
15839
|
}
|
|
15215
15840
|
if (args.call) {
|
|
15216
|
-
const result2 = await originateConciergeCall(ctx);
|
|
15841
|
+
const result2 = await originateConciergeCall(ctx, "concierge_setup", message, "cli");
|
|
15217
15842
|
process.stderr.write(
|
|
15218
15843
|
`Concierge call started. callSid=${result2.callSid} startedAt=${result2.startedAt}
|
|
15219
15844
|
`
|
|
@@ -15647,7 +16272,7 @@ var topLevelCommands = [
|
|
|
15647
16272
|
mutates: true,
|
|
15648
16273
|
args: [{ name: "phone", description: "E.164 phone number (optional; prompts if omitted)" }],
|
|
15649
16274
|
handler: async (hctx) => {
|
|
15650
|
-
const { accountVerifyPhoneCommand } = await import("./account-verify-phone-
|
|
16275
|
+
const { accountVerifyPhoneCommand } = await import("./account-verify-phone-TC73QIIE.js");
|
|
15651
16276
|
await accountVerifyPhoneCommand(hctx);
|
|
15652
16277
|
}
|
|
15653
16278
|
},
|
|
@@ -15669,7 +16294,11 @@ var topLevelCommands = [
|
|
|
15669
16294
|
description: "SMS your verified account phone (server-side)",
|
|
15670
16295
|
isFlag: true
|
|
15671
16296
|
},
|
|
15672
|
-
{
|
|
16297
|
+
{
|
|
16298
|
+
name: "message",
|
|
16299
|
+
alias: "m",
|
|
16300
|
+
description: 'Single-turn: send one message and exit. With --call: the "why I am calling" anchor for the opening self-introduction.'
|
|
16301
|
+
},
|
|
15673
16302
|
{ name: "chat-id", description: "Resume an existing chat by numeric ID" },
|
|
15674
16303
|
{ name: "vad", description: "Voice activity detection: server (default) or client" },
|
|
15675
16304
|
{ name: "list-devices", description: "List audio devices and exit", isFlag: true },
|