@wayai/cli 0.3.158 → 0.3.159
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 +356 -27
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -7376,9 +7376,26 @@ var init_contracts = __esm({
|
|
|
7376
7376
|
listConversationsQuery = external_exports.object({
|
|
7377
7377
|
hub_id: external_exports.string().uuid(),
|
|
7378
7378
|
status: external_exports.enum(["agent", "team", "ended"]).optional(),
|
|
7379
|
+
/**
|
|
7380
|
+
* Last-activity window, ISO-8601 UTC, both bounds inclusive. Filters the hub
|
|
7381
|
+
* conversation index on its last activity (`last_message_at`, falling back to
|
|
7382
|
+
* `created_at` for a row that has no message yet) — NOT on conversation start.
|
|
7383
|
+
*
|
|
7384
|
+
* Activity is the axis a support-queue check needs and the one this listing
|
|
7385
|
+
* already sorts by and returns as `updated_at`: `activity_to` alone answers
|
|
7386
|
+
* "team conversations with nothing since X" (idle), which a start-time window
|
|
7387
|
+
* cannot express at all. The ClickHouse-backed analytics list
|
|
7388
|
+
* (`POST /api/analytics/conversations`) windows `created_at` instead and only
|
|
7389
|
+
* ever holds ENDED conversations, so it can never answer this for `agent`/`team`.
|
|
7390
|
+
*/
|
|
7391
|
+
activity_from: external_exports.string().datetime().optional(),
|
|
7392
|
+
activity_to: external_exports.string().datetime().optional(),
|
|
7379
7393
|
limit: external_exports.coerce.number().int().min(1).max(100).default(20),
|
|
7380
7394
|
offset: external_exports.coerce.number().int().min(0).default(0)
|
|
7381
|
-
})
|
|
7395
|
+
}).refine(
|
|
7396
|
+
(q) => q.activity_from === void 0 || q.activity_to === void 0 || Date.parse(q.activity_from) <= Date.parse(q.activity_to),
|
|
7397
|
+
{ message: "activity_from must not be after activity_to", path: ["activity_from"] }
|
|
7398
|
+
);
|
|
7382
7399
|
flagSourceSchema = external_exports.enum(["evaluator", "monitor", "both"]).nullable();
|
|
7383
7400
|
getConversationsResponse = external_exports.object({
|
|
7384
7401
|
conversations: external_exports.array(
|
|
@@ -9164,6 +9181,8 @@ var init_api_client = __esm({
|
|
|
9164
9181
|
if (opts?.status) params.set("status", opts.status);
|
|
9165
9182
|
if (opts?.limit) params.set("limit", String(opts.limit));
|
|
9166
9183
|
if (opts?.offset) params.set("offset", String(opts.offset));
|
|
9184
|
+
if (opts?.activityFrom) params.set("activity_from", opts.activityFrom);
|
|
9185
|
+
if (opts?.activityTo) params.set("activity_to", opts.activityTo);
|
|
9167
9186
|
return getConversationsResponse.parse(
|
|
9168
9187
|
await this.request("GET", `/api/conversations?${params.toString()}`)
|
|
9169
9188
|
);
|
|
@@ -10148,6 +10167,12 @@ function refineToolConfig2(value, ctx) {
|
|
|
10148
10167
|
}
|
|
10149
10168
|
}
|
|
10150
10169
|
}
|
|
10170
|
+
function toIso(epochMs) {
|
|
10171
|
+
return new Date(epochMs).toISOString();
|
|
10172
|
+
}
|
|
10173
|
+
function fromIso(iso) {
|
|
10174
|
+
return new Date(iso).getTime();
|
|
10175
|
+
}
|
|
10151
10176
|
function isIsoTimestamp2(value) {
|
|
10152
10177
|
return typeof value === "string" && ISO_UTC_RE2.test(value);
|
|
10153
10178
|
}
|
|
@@ -13089,9 +13114,26 @@ var init_dist = __esm({
|
|
|
13089
13114
|
listConversationsQuery2 = external_exports.object({
|
|
13090
13115
|
hub_id: external_exports.string().uuid(),
|
|
13091
13116
|
status: external_exports.enum(["agent", "team", "ended"]).optional(),
|
|
13117
|
+
/**
|
|
13118
|
+
* Last-activity window, ISO-8601 UTC, both bounds inclusive. Filters the hub
|
|
13119
|
+
* conversation index on its last activity (`last_message_at`, falling back to
|
|
13120
|
+
* `created_at` for a row that has no message yet) — NOT on conversation start.
|
|
13121
|
+
*
|
|
13122
|
+
* Activity is the axis a support-queue check needs and the one this listing
|
|
13123
|
+
* already sorts by and returns as `updated_at`: `activity_to` alone answers
|
|
13124
|
+
* "team conversations with nothing since X" (idle), which a start-time window
|
|
13125
|
+
* cannot express at all. The ClickHouse-backed analytics list
|
|
13126
|
+
* (`POST /api/analytics/conversations`) windows `created_at` instead and only
|
|
13127
|
+
* ever holds ENDED conversations, so it can never answer this for `agent`/`team`.
|
|
13128
|
+
*/
|
|
13129
|
+
activity_from: external_exports.string().datetime().optional(),
|
|
13130
|
+
activity_to: external_exports.string().datetime().optional(),
|
|
13092
13131
|
limit: external_exports.coerce.number().int().min(1).max(100).default(20),
|
|
13093
13132
|
offset: external_exports.coerce.number().int().min(0).default(0)
|
|
13094
|
-
})
|
|
13133
|
+
}).refine(
|
|
13134
|
+
(q) => q.activity_from === void 0 || q.activity_to === void 0 || Date.parse(q.activity_from) <= Date.parse(q.activity_to),
|
|
13135
|
+
{ message: "activity_from must not be after activity_to", path: ["activity_from"] }
|
|
13136
|
+
);
|
|
13095
13137
|
flagSourceSchema2 = external_exports.enum(["evaluator", "monitor", "both"]).nullable();
|
|
13096
13138
|
getConversationsResponse2 = external_exports.object({
|
|
13097
13139
|
conversations: external_exports.array(
|
|
@@ -21773,6 +21815,140 @@ var init_eval_format = __esm({
|
|
|
21773
21815
|
}
|
|
21774
21816
|
});
|
|
21775
21817
|
|
|
21818
|
+
// src/lib/cross-hub-conversations.ts
|
|
21819
|
+
async function mapWithConcurrency(items, concurrency, worker) {
|
|
21820
|
+
const results = new Array(items.length);
|
|
21821
|
+
let next = 0;
|
|
21822
|
+
const runners = Array.from({ length: Math.min(concurrency, items.length) }, async () => {
|
|
21823
|
+
for (; ; ) {
|
|
21824
|
+
const index = next++;
|
|
21825
|
+
if (index >= items.length) return;
|
|
21826
|
+
results[index] = await worker(items[index]);
|
|
21827
|
+
}
|
|
21828
|
+
});
|
|
21829
|
+
await Promise.all(runners);
|
|
21830
|
+
return results;
|
|
21831
|
+
}
|
|
21832
|
+
async function resolveTargets(client, orgs, errors) {
|
|
21833
|
+
const perOrg = await mapWithConcurrency(orgs, CROSS_HUB_CONCURRENCY, async (org) => {
|
|
21834
|
+
try {
|
|
21835
|
+
const { hubs } = await client.listHubs(org.id);
|
|
21836
|
+
const targets = [];
|
|
21837
|
+
for (const hub of hubs) {
|
|
21838
|
+
targets.push({
|
|
21839
|
+
hub_id: hub.hub_id,
|
|
21840
|
+
hub_name: hub.hub_name,
|
|
21841
|
+
hub_environment: "preview",
|
|
21842
|
+
organization_id: org.id,
|
|
21843
|
+
organization_name: org.name,
|
|
21844
|
+
derived: false
|
|
21845
|
+
});
|
|
21846
|
+
if (hub.production_hub_id) {
|
|
21847
|
+
targets.push({
|
|
21848
|
+
hub_id: hub.production_hub_id,
|
|
21849
|
+
hub_name: hub.hub_name,
|
|
21850
|
+
hub_environment: "production",
|
|
21851
|
+
organization_id: org.id,
|
|
21852
|
+
organization_name: org.name,
|
|
21853
|
+
derived: true
|
|
21854
|
+
});
|
|
21855
|
+
}
|
|
21856
|
+
}
|
|
21857
|
+
return targets;
|
|
21858
|
+
} catch (err) {
|
|
21859
|
+
errors.push({
|
|
21860
|
+
scope_kind: "org",
|
|
21861
|
+
hub_id: null,
|
|
21862
|
+
hub_name: null,
|
|
21863
|
+
organization_id: org.id,
|
|
21864
|
+
error: extractApiMessage(err)
|
|
21865
|
+
});
|
|
21866
|
+
return [];
|
|
21867
|
+
}
|
|
21868
|
+
});
|
|
21869
|
+
const seen = /* @__PURE__ */ new Set();
|
|
21870
|
+
return perOrg.flat().filter((target) => {
|
|
21871
|
+
if (seen.has(target.hub_id)) return false;
|
|
21872
|
+
seen.add(target.hub_id);
|
|
21873
|
+
return true;
|
|
21874
|
+
});
|
|
21875
|
+
}
|
|
21876
|
+
async function listConversationsAcrossHubs(client, scope, opts) {
|
|
21877
|
+
const errors = [];
|
|
21878
|
+
const orgs = scope.mode === "all" ? (await client.organizations()).organizations : [{ id: scope.orgId, name: null }];
|
|
21879
|
+
const targets = await resolveTargets(client, orgs, errors);
|
|
21880
|
+
if (targets.length > MAX_CROSS_HUB_HUBS) {
|
|
21881
|
+
throw expected(
|
|
21882
|
+
`${targets.length} hubs are in scope, above the ${MAX_CROSS_HUB_HUBS}-hub cap for one cross-hub listing. Narrow the scope with --org <organization_id>.`
|
|
21883
|
+
);
|
|
21884
|
+
}
|
|
21885
|
+
const outOfScope = [];
|
|
21886
|
+
let hubsRead = 0;
|
|
21887
|
+
const perHub = await mapWithConcurrency(targets, CROSS_HUB_CONCURRENCY, async (target) => {
|
|
21888
|
+
const { derived, ...row } = target;
|
|
21889
|
+
try {
|
|
21890
|
+
const result2 = await client.getConversations(target.hub_id, opts);
|
|
21891
|
+
hubsRead++;
|
|
21892
|
+
return result2.conversations.map((conv) => ({ ...conv, ...row }));
|
|
21893
|
+
} catch (err) {
|
|
21894
|
+
if (derived && isAuthorizationRefusal(err)) {
|
|
21895
|
+
outOfScope.push(target.hub_id);
|
|
21896
|
+
return [];
|
|
21897
|
+
}
|
|
21898
|
+
errors.push({
|
|
21899
|
+
scope_kind: "hub",
|
|
21900
|
+
hub_id: target.hub_id,
|
|
21901
|
+
hub_name: target.hub_name,
|
|
21902
|
+
organization_id: target.organization_id,
|
|
21903
|
+
error: extractApiMessage(err)
|
|
21904
|
+
});
|
|
21905
|
+
return [];
|
|
21906
|
+
}
|
|
21907
|
+
});
|
|
21908
|
+
const conversations = perHub.flat().map((row) => ({ row, key: Date.parse(row.updated_at ?? row.created_at) })).sort((a, b) => b.key - a.key).map(({ row }) => row);
|
|
21909
|
+
const scopesTotal = targets.length - outOfScope.length + errors.filter((f) => f.scope_kind === "org").length;
|
|
21910
|
+
const result = {
|
|
21911
|
+
scope,
|
|
21912
|
+
organizations_queried: orgs.length,
|
|
21913
|
+
hubs_queried: targets.length,
|
|
21914
|
+
hubs_read: hubsRead,
|
|
21915
|
+
hubs_out_of_scope: outOfScope,
|
|
21916
|
+
scopes_total: scopesTotal,
|
|
21917
|
+
scopes_failed: errors.length,
|
|
21918
|
+
conversations,
|
|
21919
|
+
partial: errors.length > 0,
|
|
21920
|
+
errors
|
|
21921
|
+
};
|
|
21922
|
+
assertScopeAccounting(result);
|
|
21923
|
+
return result;
|
|
21924
|
+
}
|
|
21925
|
+
function isAuthorizationRefusal(err) {
|
|
21926
|
+
return err instanceof ApiError && err.status === 403;
|
|
21927
|
+
}
|
|
21928
|
+
function assertScopeAccounting(result) {
|
|
21929
|
+
const hubFailures = result.errors.filter((f) => f.scope_kind === "hub").length;
|
|
21930
|
+
const accountedFor = result.hubs_read + result.hubs_out_of_scope.length + hubFailures;
|
|
21931
|
+
if (accountedFor !== result.hubs_queried) {
|
|
21932
|
+
throw new Error(
|
|
21933
|
+
`cross-hub accounting: ${result.hubs_queried} hubs attempted but ${accountedFor} accounted for (read ${result.hubs_read}, out of scope ${result.hubs_out_of_scope.length}, failed ${hubFailures})`
|
|
21934
|
+
);
|
|
21935
|
+
}
|
|
21936
|
+
if (result.partial !== result.scopes_failed > 0) {
|
|
21937
|
+
throw new Error(`cross-hub accounting: partial=${result.partial} disagrees with scopes_failed=${result.scopes_failed}`);
|
|
21938
|
+
}
|
|
21939
|
+
}
|
|
21940
|
+
var MAX_CROSS_HUB_HUBS, CROSS_HUB_CONCURRENCY;
|
|
21941
|
+
var init_cross_hub_conversations = __esm({
|
|
21942
|
+
"src/lib/cross-hub-conversations.ts"() {
|
|
21943
|
+
"use strict";
|
|
21944
|
+
init_api_client();
|
|
21945
|
+
init_errors2();
|
|
21946
|
+
init_expected();
|
|
21947
|
+
MAX_CROSS_HUB_HUBS = 100;
|
|
21948
|
+
CROSS_HUB_CONCURRENCY = 6;
|
|
21949
|
+
}
|
|
21950
|
+
});
|
|
21951
|
+
|
|
21776
21952
|
// src/commands/observability.ts
|
|
21777
21953
|
async function runConversationObservability(client, hubId, conversationId, opts) {
|
|
21778
21954
|
if (opts.messageId) {
|
|
@@ -21895,8 +22071,36 @@ var init_observability = __esm({
|
|
|
21895
22071
|
// src/commands/conversations.ts
|
|
21896
22072
|
var conversations_exports = {};
|
|
21897
22073
|
__export(conversations_exports, {
|
|
21898
|
-
conversationsCommand: () => conversationsCommand
|
|
22074
|
+
conversationsCommand: () => conversationsCommand,
|
|
22075
|
+
detectCrossHubScope: () => detectCrossHubScope
|
|
21899
22076
|
});
|
|
22077
|
+
function detectCrossHubScope(args2) {
|
|
22078
|
+
const all = args2.includes("--all");
|
|
22079
|
+
const orgIdx = args2.findIndex((a) => a === "--org" || a.startsWith("--org="));
|
|
22080
|
+
const inlineOrg = orgIdx !== -1 && args2[orgIdx].startsWith("--org=");
|
|
22081
|
+
const orgValue = orgIdx === -1 ? void 0 : inlineOrg ? args2[orgIdx].slice("--org=".length) : args2[orgIdx + 1];
|
|
22082
|
+
if (orgIdx !== -1) {
|
|
22083
|
+
if (!orgValue || orgValue.startsWith("-")) {
|
|
22084
|
+
console.error("--org requires an organization id (uuid). Use --all to list every organization.");
|
|
22085
|
+
process.exit(1);
|
|
22086
|
+
}
|
|
22087
|
+
if (!UUID_RE2.test(orgValue)) {
|
|
22088
|
+
console.error(`Invalid --org value: ${orgValue}. Expected an organization id (uuid).`);
|
|
22089
|
+
process.exit(1);
|
|
22090
|
+
}
|
|
22091
|
+
}
|
|
22092
|
+
if (all && orgIdx !== -1) {
|
|
22093
|
+
console.error("--all and --org are mutually exclusive: --all already spans every organization.");
|
|
22094
|
+
process.exit(1);
|
|
22095
|
+
}
|
|
22096
|
+
if ((all || orgIdx !== -1) && args2.includes("--hub")) {
|
|
22097
|
+
console.error("--hub cannot be combined with --all/--org: a cross-hub listing spans every accessible hub.");
|
|
22098
|
+
process.exit(1);
|
|
22099
|
+
}
|
|
22100
|
+
if (all) return { mode: "all" };
|
|
22101
|
+
if (orgIdx !== -1) return { mode: "org", orgId: orgValue };
|
|
22102
|
+
return null;
|
|
22103
|
+
}
|
|
21900
22104
|
function printConversationsHelp() {
|
|
21901
22105
|
console.error(`
|
|
21902
22106
|
wayai conversations \u2014 list conversations or inspect what an agent received
|
|
@@ -21904,8 +22108,11 @@ wayai conversations \u2014 list conversations or inspect what an agent received
|
|
|
21904
22108
|
Usage:
|
|
21905
22109
|
wayai conversations List conversations
|
|
21906
22110
|
wayai conversations --status <agent|team|ended> Filter by status
|
|
22111
|
+
wayai conversations --status team --period 7d Status + a last-activity window
|
|
21907
22112
|
wayai conversations --period 7d Analytics-powered list
|
|
21908
22113
|
wayai conversations --from <date> --to <date> List within a date range
|
|
22114
|
+
wayai conversations --org <organization_id> Every hub in one organization
|
|
22115
|
+
wayai conversations --all Every hub this login can access
|
|
21909
22116
|
wayai conversations <conversation_id> Show detail + messages
|
|
21910
22117
|
wayai conversations <conversation_id> observability List the LLM turns (per-message id, latency, tool_calls)
|
|
21911
22118
|
wayai conversations <conversation_id> observability --message-id <id>
|
|
@@ -21923,10 +22130,32 @@ Usage:
|
|
|
21923
22130
|
|
|
21924
22131
|
Options:
|
|
21925
22132
|
--message-id <id> Expand one turn (only with the \`observability\` subcommand)
|
|
21926
|
-
--limit <n> Max rows (list view)
|
|
21927
|
-
--offset <n> Skip rows (list view)
|
|
22133
|
+
--limit <n> Max rows (list view; PER HUB with --all/--org)
|
|
22134
|
+
--offset <n> Skip rows (list view; per hub with --all/--org)
|
|
21928
22135
|
--json Raw JSON output
|
|
21929
22136
|
|
|
22137
|
+
Time windows:
|
|
22138
|
+
With \`--status\`, or with \`--all\`/\`--org\`, the listing comes from the live hub
|
|
22139
|
+
index and \`--period\`/\`--from\`/\`--to\` bound LAST ACTIVITY. So
|
|
22140
|
+
\`--status team --to 2026-08-22\` is "team conversations with nothing since the
|
|
22141
|
+
22nd" \u2014 the idle queue. Without \`--status\` AND without \`--all\`/\`--org\`, a bare
|
|
22142
|
+
\`--period\`/\`--from\`/\`--to\` still runs the ClickHouse-backed analytics list,
|
|
22143
|
+
which windows conversation START and only ever holds ended conversations.
|
|
22144
|
+
|
|
22145
|
+
Cross-hub:
|
|
22146
|
+
\`--all\` / \`--org <id>\` list across the hubs \`wayai list\` enumerates PLUS the
|
|
22147
|
+
production hubs those name as their parents. Two different cases end in a hub
|
|
22148
|
+
going unread, and they are reported differently:
|
|
22149
|
+
- A production PARENT this login cannot read is NAMED as skipped and does not
|
|
22150
|
+
make the run fail \u2014 it is a standing property of the login, not an outage.
|
|
22151
|
+
- A production hub whose PREVIEW this login cannot see is not enumerable
|
|
22152
|
+
anywhere, so it is not covered and NOT reported at all. Reach it with
|
|
22153
|
+
\`--hub <id>\`.
|
|
22154
|
+
Each hub is read with the caller's own grants; a hub IN SCOPE that refuses is
|
|
22155
|
+
reported and the listing exits non-zero as partial rather than looking like a
|
|
22156
|
+
smaller queue, and an empty scope is reported as such, never as an empty queue.
|
|
22157
|
+
\`--org\` accepts \`--org <id>\` and \`--org=<id>\`.
|
|
22158
|
+
|
|
21930
22159
|
The default text list omits message ids \u2014 use \`--json\` or the \`observability\`
|
|
21931
22160
|
subcommand to discover them. \`observability\` is the answer to "the agent did X \u2014
|
|
21932
22161
|
what did it ACTUALLY see?".
|
|
@@ -21937,9 +22166,10 @@ async function conversationsCommand(args2) {
|
|
|
21937
22166
|
printConversationsHelp();
|
|
21938
22167
|
return;
|
|
21939
22168
|
}
|
|
21940
|
-
const
|
|
22169
|
+
const crossHubScope = detectCrossHubScope(args2);
|
|
22170
|
+
const hubId = crossHubScope ? "" : resolveActiveHubId(args2);
|
|
21941
22171
|
const { config, accessToken } = await requireAuth();
|
|
21942
|
-
requireRepoConfig();
|
|
22172
|
+
if (!crossHubScope) requireRepoConfig();
|
|
21943
22173
|
let status;
|
|
21944
22174
|
let conversationId;
|
|
21945
22175
|
let subcommand;
|
|
@@ -22012,6 +22242,9 @@ async function conversationsCommand(args2) {
|
|
|
22012
22242
|
}
|
|
22013
22243
|
} else if (arg === "--json") {
|
|
22014
22244
|
jsonOutput = true;
|
|
22245
|
+
} else if (arg === "--all" || arg.startsWith("--org=")) {
|
|
22246
|
+
} else if (arg === "--org") {
|
|
22247
|
+
i++;
|
|
22015
22248
|
} else if (!arg.startsWith("-")) {
|
|
22016
22249
|
if (conversationId === void 0 && arg === "observability") {
|
|
22017
22250
|
subcommand = arg;
|
|
@@ -22027,6 +22260,10 @@ async function conversationsCommand(args2) {
|
|
|
22027
22260
|
}
|
|
22028
22261
|
const client = new ApiClient({ apiUrl: config.api_url, accessToken });
|
|
22029
22262
|
const hasAnalyticsFlags = !!(period || from || to);
|
|
22263
|
+
if (crossHubScope && (conversationId || subcommand)) {
|
|
22264
|
+
console.error("--all/--org apply to the conversation LIST only. Drop them and pass --hub <id> to open one conversation.");
|
|
22265
|
+
process.exit(1);
|
|
22266
|
+
}
|
|
22030
22267
|
if (subcommand === "observability") {
|
|
22031
22268
|
if (!conversationId) {
|
|
22032
22269
|
console.error("Usage: wayai conversations <conversation_id> observability [--message-id X] [--json]");
|
|
@@ -22152,11 +22389,65 @@ async function conversationsCommand(args2) {
|
|
|
22152
22389
|
}
|
|
22153
22390
|
console.log("Diagnosing agent behavior? The transcript shows what was sent \u2014 not what the model received.");
|
|
22154
22391
|
console.log(`Drill in: wayai conversations ${conversationId} observability (per-turn resolved prompt, tool calls, latency)`);
|
|
22155
|
-
} else if (
|
|
22156
|
-
|
|
22157
|
-
|
|
22392
|
+
} else if (crossHubScope) {
|
|
22393
|
+
const window = resolveActivityWindow(period, from, to);
|
|
22394
|
+
const result = await listConversationsAcrossHubs(client, crossHubScope, {
|
|
22395
|
+
status,
|
|
22396
|
+
limit,
|
|
22397
|
+
offset,
|
|
22398
|
+
activityFrom: window.start,
|
|
22399
|
+
activityTo: window.end
|
|
22400
|
+
});
|
|
22401
|
+
if (jsonOutput) {
|
|
22402
|
+
console.log(JSON.stringify(result, null, 2));
|
|
22403
|
+
if (result.partial || result.hubs_queried === 0) process.exit(1);
|
|
22404
|
+
return;
|
|
22405
|
+
}
|
|
22406
|
+
for (const failure of result.errors) {
|
|
22407
|
+
const where = failure.scope_kind === "hub" ? `hub ${sanitizeTerminalText(failure.hub_name || failure.hub_id || "")} (${failure.hub_id})` : `organization ${failure.organization_id}`;
|
|
22408
|
+
console.error(`Could not read ${where}: ${sanitizeTerminalText(failure.error)}`);
|
|
22409
|
+
}
|
|
22410
|
+
if (result.hubs_queried === 0) {
|
|
22411
|
+
console.error(
|
|
22412
|
+
`No hubs were in scope across ${result.organizations_queried} organization(s), so no queue was read. \`wayai list\` shows the hubs this login can enumerate; use \`--hub <id>\` for one hub outside that set.`
|
|
22413
|
+
);
|
|
22158
22414
|
process.exit(1);
|
|
22159
22415
|
}
|
|
22416
|
+
if (result.hubs_out_of_scope.length > 0) {
|
|
22417
|
+
const ids = result.hubs_out_of_scope.map(sanitizeTerminalText).join(", ");
|
|
22418
|
+
console.error(
|
|
22419
|
+
`Note: ${result.hubs_out_of_scope.length} production hub(s) are outside this login's grants and were not read: ${ids}. Reach one with --hub <id>.`
|
|
22420
|
+
);
|
|
22421
|
+
}
|
|
22422
|
+
if (result.conversations.length === 0) {
|
|
22423
|
+
console.log(`No conversations found across ${result.hubs_read} hub(s).`);
|
|
22424
|
+
} else {
|
|
22425
|
+
console.log(`Conversations (${result.conversations.length} across ${result.hubs_read} hubs):
|
|
22426
|
+
`);
|
|
22427
|
+
for (const conv of result.conversations) {
|
|
22428
|
+
const user = sanitizeTerminalText(conv.user_name || conv.user_email || "unknown");
|
|
22429
|
+
const updated = conv.updated_at ? new Date(conv.updated_at).toLocaleString() : "-";
|
|
22430
|
+
const env = conv.hub_environment === "production" ? "prod" : "prev";
|
|
22431
|
+
const hub = truncate(sanitizeTerminalText(conv.hub_name || conv.hub_id), HUB_COL_WIDTH);
|
|
22432
|
+
console.log(` ${hub.padEnd(HUB_COL_WIDTH)} ${env} ${conv.conversation_id} ${conv.conversation_status.padEnd(7)} ${user.padEnd(20)} ${updated}`);
|
|
22433
|
+
}
|
|
22434
|
+
}
|
|
22435
|
+
if (result.partial) {
|
|
22436
|
+
console.error(`
|
|
22437
|
+
Partial listing: ${result.scopes_failed} of ${result.scopes_total} scope(s) could not be read.`);
|
|
22438
|
+
process.exit(1);
|
|
22439
|
+
}
|
|
22440
|
+
} else if (status && hasAnalyticsFlags) {
|
|
22441
|
+
const window = resolveActivityWindow(period, from, to);
|
|
22442
|
+
const result = await client.getConversations(hubId, {
|
|
22443
|
+
status,
|
|
22444
|
+
limit,
|
|
22445
|
+
offset,
|
|
22446
|
+
activityFrom: window.start,
|
|
22447
|
+
activityTo: window.end
|
|
22448
|
+
});
|
|
22449
|
+
printIndexList(result, jsonOutput);
|
|
22450
|
+
} else if (hasAnalyticsFlags) {
|
|
22160
22451
|
if (from && !to || !from && to) {
|
|
22161
22452
|
console.error("--from and --to must both be provided together.");
|
|
22162
22453
|
process.exit(1);
|
|
@@ -22211,28 +22502,62 @@ async function conversationsCommand(args2) {
|
|
|
22211
22502
|
}
|
|
22212
22503
|
} else {
|
|
22213
22504
|
const result = await client.getConversations(hubId, { status, limit, offset });
|
|
22214
|
-
|
|
22215
|
-
|
|
22216
|
-
|
|
22505
|
+
printIndexList(result, jsonOutput);
|
|
22506
|
+
}
|
|
22507
|
+
}
|
|
22508
|
+
function printIndexList(result, jsonOutput) {
|
|
22509
|
+
if (jsonOutput) {
|
|
22510
|
+
console.log(JSON.stringify(result, null, 2));
|
|
22511
|
+
return;
|
|
22512
|
+
}
|
|
22513
|
+
if (result.conversations.length === 0) {
|
|
22514
|
+
console.log("No conversations found.");
|
|
22515
|
+
return;
|
|
22516
|
+
}
|
|
22517
|
+
console.log(`Conversations (${result.conversations.length} of ${result.total}):
|
|
22518
|
+
`);
|
|
22519
|
+
for (const conv of result.conversations) {
|
|
22520
|
+
const user = conv.user_name || conv.user_email || "unknown";
|
|
22521
|
+
const updated = conv.updated_at ? new Date(conv.updated_at).toLocaleString() : "-";
|
|
22522
|
+
const kanban = truncate(
|
|
22523
|
+
conv.outcome ? `${conv.kanban_status ?? "-"}/${conv.outcome}` : conv.kanban_status ?? "-",
|
|
22524
|
+
KANBAN_COL_WIDTH
|
|
22525
|
+
);
|
|
22526
|
+
console.log(` ${conv.conversation_id} ${conv.conversation_status.padEnd(7)} ${kanban.padEnd(KANBAN_COL_WIDTH)} ${user.padEnd(20)} ${updated}`);
|
|
22527
|
+
}
|
|
22528
|
+
}
|
|
22529
|
+
function resolveActivityWindow(period, from, to) {
|
|
22530
|
+
let start;
|
|
22531
|
+
let end;
|
|
22532
|
+
if (from || to) {
|
|
22533
|
+
try {
|
|
22534
|
+
start = from ? canonicalIso(parseDateArg(from, "from")) : void 0;
|
|
22535
|
+
end = to ? canonicalIso(parseDateArg(to, "to")) : void 0;
|
|
22536
|
+
} catch (err) {
|
|
22537
|
+
console.error(err instanceof Error ? err.message : String(err));
|
|
22538
|
+
process.exit(1);
|
|
22217
22539
|
}
|
|
22218
|
-
if (
|
|
22219
|
-
console.
|
|
22220
|
-
|
|
22540
|
+
if (start && end && Date.parse(start) > Date.parse(end)) {
|
|
22541
|
+
console.error("--from must be before --to.");
|
|
22542
|
+
process.exit(1);
|
|
22221
22543
|
}
|
|
22222
|
-
|
|
22223
|
-
|
|
22224
|
-
|
|
22225
|
-
|
|
22226
|
-
const
|
|
22227
|
-
|
|
22228
|
-
|
|
22229
|
-
|
|
22230
|
-
);
|
|
22231
|
-
console.log(` ${conv.conversation_id} ${conv.conversation_status.padEnd(7)} ${kanban.padEnd(KANBAN_COL_WIDTH)} ${user.padEnd(20)} ${updated}`);
|
|
22544
|
+
return { start, end };
|
|
22545
|
+
}
|
|
22546
|
+
if (period) {
|
|
22547
|
+
try {
|
|
22548
|
+
const parsed = parsePeriod(period);
|
|
22549
|
+
return { start: parsed.start, end: parsed.end };
|
|
22550
|
+
} catch (err) {
|
|
22551
|
+
console.error(err instanceof Error ? err.message : String(err));
|
|
22552
|
+
process.exit(1);
|
|
22232
22553
|
}
|
|
22233
22554
|
}
|
|
22555
|
+
return {};
|
|
22556
|
+
}
|
|
22557
|
+
function canonicalIso(value) {
|
|
22558
|
+
return toIso(fromIso(value));
|
|
22234
22559
|
}
|
|
22235
|
-
var KANBAN_COL_WIDTH;
|
|
22560
|
+
var KANBAN_COL_WIDTH, HUB_COL_WIDTH;
|
|
22236
22561
|
var init_conversations = __esm({
|
|
22237
22562
|
"src/commands/conversations.ts"() {
|
|
22238
22563
|
"use strict";
|
|
@@ -22242,8 +22567,12 @@ var init_conversations = __esm({
|
|
|
22242
22567
|
init_api_client();
|
|
22243
22568
|
init_utils();
|
|
22244
22569
|
init_eval_format();
|
|
22570
|
+
init_terminal_output();
|
|
22571
|
+
init_cross_hub_conversations();
|
|
22245
22572
|
init_observability();
|
|
22573
|
+
init_dist();
|
|
22246
22574
|
KANBAN_COL_WIDTH = 30;
|
|
22575
|
+
HUB_COL_WIDTH = 24;
|
|
22247
22576
|
}
|
|
22248
22577
|
});
|
|
22249
22578
|
|