@chatlychat/cli 0.1.4 → 0.1.6
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/commands/ai.d.ts +4 -0
- package/dist/commands/ai.d.ts.map +1 -0
- package/dist/commands/ai.js +149 -0
- package/dist/commands/ai.js.map +1 -0
- package/dist/commands/common.d.ts +79 -0
- package/dist/commands/common.d.ts.map +1 -0
- package/dist/commands/common.js +194 -0
- package/dist/commands/common.js.map +1 -0
- package/dist/commands/config.d.ts +40 -0
- package/dist/commands/config.d.ts.map +1 -0
- package/dist/commands/config.js +336 -0
- package/dist/commands/config.js.map +1 -0
- package/dist/commands/insights.d.ts +5 -0
- package/dist/commands/insights.d.ts.map +1 -0
- package/dist/commands/insights.js +277 -0
- package/dist/commands/insights.js.map +1 -0
- package/dist/commands/integrations.d.ts +15 -0
- package/dist/commands/integrations.d.ts.map +1 -0
- package/dist/commands/integrations.js +256 -0
- package/dist/commands/integrations.js.map +1 -0
- package/dist/commands/resources.d.ts +19 -7
- package/dist/commands/resources.d.ts.map +1 -1
- package/dist/commands/resources.js +92 -167
- package/dist/commands/resources.js.map +1 -1
- package/dist/commands/workspace-as-code.d.ts +27 -0
- package/dist/commands/workspace-as-code.d.ts.map +1 -0
- package/dist/commands/workspace-as-code.js +377 -0
- package/dist/commands/workspace-as-code.js.map +1 -0
- package/dist/help.d.ts +25 -1
- package/dist/help.d.ts.map +1 -1
- package/dist/help.js +104 -8
- package/dist/help.js.map +1 -1
- package/dist/run.d.ts +17 -0
- package/dist/run.d.ts.map +1 -1
- package/dist/run.js +88 -41
- package/dist/run.js.map +1 -1
- package/dist/wac/__tests__/wac-fixtures.d.ts +20 -0
- package/dist/wac/__tests__/wac-fixtures.d.ts.map +1 -0
- package/dist/wac/__tests__/wac-fixtures.js +38 -0
- package/dist/wac/__tests__/wac-fixtures.js.map +1 -0
- package/dist/wac/apply.d.ts +113 -0
- package/dist/wac/apply.d.ts.map +1 -0
- package/dist/wac/apply.js +265 -0
- package/dist/wac/apply.js.map +1 -0
- package/dist/wac/diff.d.ts +137 -0
- package/dist/wac/diff.d.ts.map +1 -0
- package/dist/wac/diff.js +413 -0
- package/dist/wac/diff.js.map +1 -0
- package/dist/wac/document.d.ts +110 -0
- package/dist/wac/document.d.ts.map +1 -0
- package/dist/wac/document.js +429 -0
- package/dist/wac/document.js.map +1 -0
- package/dist/wac/render.d.ts +52 -0
- package/dist/wac/render.d.ts.map +1 -0
- package/dist/wac/render.js +156 -0
- package/dist/wac/render.js.map +1 -0
- package/dist/wac/resource-def.d.ts +116 -0
- package/dist/wac/resource-def.d.ts.map +1 -0
- package/dist/wac/resource-def.js +44 -0
- package/dist/wac/resource-def.js.map +1 -0
- package/dist/wac/resources.d.ts +104 -0
- package/dist/wac/resources.d.ts.map +1 -0
- package/dist/wac/resources.js +541 -0
- package/dist/wac/resources.js.map +1 -0
- package/dist/wac/values.d.ts +16 -0
- package/dist/wac/values.d.ts.map +1 -0
- package/dist/wac/values.js +53 -0
- package/dist/wac/values.js.map +1 -0
- package/package.json +2 -1
- package/dist/cli.spec.d.ts +0 -2
- package/dist/cli.spec.d.ts.map +0 -1
- package/dist/cli.spec.js +0 -226
- package/dist/cli.spec.js.map +0 -1
|
@@ -0,0 +1,336 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Config-as-code: the surface an integrator keeps in a repository.
|
|
3
|
+
*
|
|
4
|
+
* Bots, routing rules, triggers, brands, transactional email copy and macros
|
|
5
|
+
* are all workspace CONFIGURATION — the things a team wants reviewed in a
|
|
6
|
+
* pull request and applied from CI, not clicked through a settings screen.
|
|
7
|
+
* That is the reason the SDK manifest grew from 55 routes to 142, and it is
|
|
8
|
+
* the reason these are commands rather than `chatly api` calls.
|
|
9
|
+
*
|
|
10
|
+
* ── One idiom for every write ─────────────────────────────────────────────
|
|
11
|
+
*
|
|
12
|
+
* Writes take a JSON document (`--file`, `--file -`, or `--body`), not a flag
|
|
13
|
+
* per field. These bodies are nested — a bot carries guardrails, a KB scope
|
|
14
|
+
* and an escalation-rule array — and `bodyFrom` explains why a flag per field
|
|
15
|
+
* would be both a worse CLI and a second place for a key name to be wrong.
|
|
16
|
+
*
|
|
17
|
+
* ── Transport ─────────────────────────────────────────────────────────────
|
|
18
|
+
*
|
|
19
|
+
* Through `ChatlyClient.request`, the escape hatch the SDK contract defines
|
|
20
|
+
* for key-accessible routes with no typed method. It is the same auth, the
|
|
21
|
+
* same `Idempotency-Key` per write, the same retry policy and the same typed
|
|
22
|
+
* errors a named method would get — `apps/cli` adds no HTTP of its own.
|
|
23
|
+
*/
|
|
24
|
+
import { bool, str } from '../args.js';
|
|
25
|
+
import { bodyFrom, need, pagedVia, queryFrom, renderList, showObject, showRows } from './common.js';
|
|
26
|
+
export async function bots(chatly, args) {
|
|
27
|
+
const sub = args.path[1] ?? 'list';
|
|
28
|
+
switch (sub) {
|
|
29
|
+
case 'list': {
|
|
30
|
+
// A real cursor page, and the only one on this file's surface.
|
|
31
|
+
const pages = pagedVia(chatly, '/v1/bots');
|
|
32
|
+
return renderList(args, ['id', 'name', 'slug', 'status', 'model'], pages.onePage, pages.everyPage);
|
|
33
|
+
}
|
|
34
|
+
case 'get':
|
|
35
|
+
return showObject(await chatly.request('GET', `/v1/bots/${encodeURIComponent(need(args, 2, 'a bot id'))}`));
|
|
36
|
+
case 'create':
|
|
37
|
+
/*
|
|
38
|
+
* `type: "external"` creates a customer's own push-delivered agent and
|
|
39
|
+
* answers with a signing secret and a `cb_` token, EACH SHOWN ONCE.
|
|
40
|
+
* There is no route that reads them back, so this prints the whole
|
|
41
|
+
* response rather than a summary line.
|
|
42
|
+
*/
|
|
43
|
+
return showObject(await chatly.request('POST', '/v1/bots', { body: bodyFrom(args, 'the bot') }));
|
|
44
|
+
case 'update':
|
|
45
|
+
return showObject(await chatly.request('PATCH', `/v1/bots/${encodeURIComponent(need(args, 2, 'a bot id'))}`, {
|
|
46
|
+
body: bodyFrom(args, 'the fields to change'),
|
|
47
|
+
}));
|
|
48
|
+
case 'update-external':
|
|
49
|
+
return showObject(await chatly.request('PATCH', `/v1/bots/${encodeURIComponent(need(args, 2, 'a bot id'))}/external`, { body: bodyFrom(args, "the external agent's webhook, timeout or fallback copy") }));
|
|
50
|
+
case 'rotate-secret':
|
|
51
|
+
// Shown once, and the old secret stops working the moment this returns.
|
|
52
|
+
return showObject(await chatly.request('POST', `/v1/bots/${encodeURIComponent(need(args, 2, 'a bot id'))}/external/rotate-secret`));
|
|
53
|
+
/* ── An external agent's delivery ledger ──────────────────────────── */
|
|
54
|
+
case 'deliveries': {
|
|
55
|
+
/*
|
|
56
|
+
* The columns an integrator reading this is actually looking for.
|
|
57
|
+
*
|
|
58
|
+
* `status` first because it answers the question — `failed` will be
|
|
59
|
+
* retried, `dead` will not — and `httpStatus` beside it because the
|
|
60
|
+
* pair is the diagnosis: `dead` with a 401 is their signature check,
|
|
61
|
+
* `dead` with a null is a timeout or DNS, `failed` with a 503 is their
|
|
62
|
+
* load balancer and will fix itself.
|
|
63
|
+
*
|
|
64
|
+
* The signed `payload` is deliberately not a column and is not on a
|
|
65
|
+
* list row at all: `bots delivery <bot> <delivery>` serves it.
|
|
66
|
+
*/
|
|
67
|
+
const botId = need(args, 2, 'a bot id');
|
|
68
|
+
const status = str(args.flags, 'status');
|
|
69
|
+
const pages = pagedVia(chatly, `/v1/bots/${encodeURIComponent(botId)}/deliveries`, status === undefined ? {} : { status });
|
|
70
|
+
return renderList(args, ['id', 'status', 'attempt', 'httpStatus', 'durationMs', 'createdAt', 'error'], pages.onePage, pages.everyPage);
|
|
71
|
+
}
|
|
72
|
+
case 'delivery':
|
|
73
|
+
// With `payload` — the exact object that was serialised and signed.
|
|
74
|
+
return showObject(await chatly.request('GET', `/v1/bots/${encodeURIComponent(need(args, 2, 'a bot id'))}/deliveries/` +
|
|
75
|
+
encodeURIComponent(need(args, 3, 'a delivery id'))));
|
|
76
|
+
case 'redeliver':
|
|
77
|
+
/*
|
|
78
|
+
* The RECORDED payload, same `eventId`, from attempt 0.
|
|
79
|
+
*
|
|
80
|
+
* Which means a receiver's own dedupe will discard it if they already
|
|
81
|
+
* processed that id — correct behaviour on their side, and the reason
|
|
82
|
+
* this is for deliveries that never got through rather than for
|
|
83
|
+
* re-running one.
|
|
84
|
+
*/
|
|
85
|
+
return showObject(await chatly.request('POST', `/v1/bots/${encodeURIComponent(need(args, 2, 'a bot id'))}/deliveries/` +
|
|
86
|
+
`${encodeURIComponent(need(args, 3, 'a delivery id'))}/redeliver`));
|
|
87
|
+
case 'test-delivery':
|
|
88
|
+
/*
|
|
89
|
+
* Signs a rehearsal payload with the real secret, POSTs it to the real
|
|
90
|
+
* URL, and prints the response — so a 401 from your own verifier
|
|
91
|
+
* arrives here rather than in a log twenty minutes later.
|
|
92
|
+
*
|
|
93
|
+
* Printed WHOLE: `signature` and `payload` are the two fields worth
|
|
94
|
+
* having, because they are the exact header value and the exact object
|
|
95
|
+
* whose serialisation was signed, which is what you need to debug a
|
|
96
|
+
* verifier offline.
|
|
97
|
+
*
|
|
98
|
+
* Writes nothing, so it does not appear under `bots deliveries`.
|
|
99
|
+
*/
|
|
100
|
+
return showObject(await chatly.request('POST', `/v1/bots/${encodeURIComponent(need(args, 2, 'a bot id'))}/test-delivery`));
|
|
101
|
+
default:
|
|
102
|
+
throw new Error(`Unknown: bots ${sub}`);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
export async function botRoutes(chatly, args) {
|
|
106
|
+
const sub = args.path[1] ?? 'list';
|
|
107
|
+
switch (sub) {
|
|
108
|
+
case 'list':
|
|
109
|
+
// A bare array in evaluation order — not a cursor page, so no `--all`.
|
|
110
|
+
return showRows(args, await chatly.request('GET', '/v1/bot-routes'), [
|
|
111
|
+
'id',
|
|
112
|
+
'name',
|
|
113
|
+
'botId',
|
|
114
|
+
'priority',
|
|
115
|
+
'enabled',
|
|
116
|
+
'evaluate',
|
|
117
|
+
]);
|
|
118
|
+
case 'resolve':
|
|
119
|
+
/*
|
|
120
|
+
* The dry-run. `--body` carries the hypothetical visitor:
|
|
121
|
+
*
|
|
122
|
+
* chatly bot-routes resolve --body '{"page":{"path":"/shop/checkout"},
|
|
123
|
+
* "context":{"verified":{"audience":"buyer"}}}'
|
|
124
|
+
*
|
|
125
|
+
* Printed WHOLE rather than as a table. The answer is three things —
|
|
126
|
+
* which route won, why each other route lost, and the event object the
|
|
127
|
+
* conditions were evaluated against — and the third is where you find
|
|
128
|
+
* out that a field path resolves to nothing, which is half of every
|
|
129
|
+
* routing bug.
|
|
130
|
+
*/
|
|
131
|
+
return showObject(await chatly.request('POST', '/v1/bot-routes/resolve', {
|
|
132
|
+
body: args.flags['body'] === undefined ? {} : bodyFrom(args, 'the hypothetical visitor'),
|
|
133
|
+
}));
|
|
134
|
+
case 'fields':
|
|
135
|
+
// The condition vocabulary. Read this before authoring a rule: a
|
|
136
|
+
// condition naming a field that is not here is rejected on the write.
|
|
137
|
+
return showObject(await chatly.request('GET', '/v1/bot-routes/fields'));
|
|
138
|
+
case 'create':
|
|
139
|
+
return showObject(await chatly.request('POST', '/v1/bot-routes', {
|
|
140
|
+
body: bodyFrom(args, 'the routing rule'),
|
|
141
|
+
}));
|
|
142
|
+
case 'update':
|
|
143
|
+
return showObject(await chatly.request('PATCH', `/v1/bot-routes/${encodeURIComponent(need(args, 2, 'a route id'))}`, { body: bodyFrom(args, 'the fields to change') }));
|
|
144
|
+
case 'delete':
|
|
145
|
+
return showObject(await chatly.request('DELETE', `/v1/bot-routes/${encodeURIComponent(need(args, 2, 'a route id'))}`));
|
|
146
|
+
default:
|
|
147
|
+
throw new Error(`Unknown: bot-routes ${sub}`);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
export async function triggers(chatly, args) {
|
|
151
|
+
const sub = args.path[1] ?? 'list';
|
|
152
|
+
switch (sub) {
|
|
153
|
+
case 'list': {
|
|
154
|
+
const pages = pagedVia(chatly, '/v1/triggers');
|
|
155
|
+
return renderList(args, ['id', 'name', 'eventType', 'enabled', 'priority'], pages.onePage, pages.everyPage);
|
|
156
|
+
}
|
|
157
|
+
/*
|
|
158
|
+
* `upsert`, not `create`.
|
|
159
|
+
*
|
|
160
|
+
* The manifest calls this route `triggers.create`, but `POST /v1/triggers`
|
|
161
|
+
* is an UPSERT: the handler is named `upsert`, it answers 201 on both
|
|
162
|
+
* arms, and a body carrying an existing trigger's identity replaces it.
|
|
163
|
+
* `create` is kept as an alias so the manifest's name works, but the
|
|
164
|
+
* documented verb is the one the route performs — a CLI that calls this
|
|
165
|
+
* "create" invites someone to run it twice expecting a second trigger.
|
|
166
|
+
*/
|
|
167
|
+
case 'upsert':
|
|
168
|
+
case 'create':
|
|
169
|
+
return showObject(await chatly.request('POST', '/v1/triggers', { body: bodyFrom(args, 'the trigger') }));
|
|
170
|
+
case 'delete':
|
|
171
|
+
// 200 with `{ ok: true }`, not a 204.
|
|
172
|
+
return showObject(await chatly.request('DELETE', `/v1/triggers/${encodeURIComponent(need(args, 2, 'a trigger id'))}`));
|
|
173
|
+
default:
|
|
174
|
+
throw new Error(`Unknown: triggers ${sub}`);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
export async function brands(chatly, args) {
|
|
178
|
+
const sub = args.path[1] ?? 'list';
|
|
179
|
+
switch (sub) {
|
|
180
|
+
case 'list':
|
|
181
|
+
return showRows(args, await chatly.request('GET', '/v1/brands'), [
|
|
182
|
+
'id',
|
|
183
|
+
'name',
|
|
184
|
+
'slug',
|
|
185
|
+
'domain',
|
|
186
|
+
'isDefault',
|
|
187
|
+
]);
|
|
188
|
+
case 'create':
|
|
189
|
+
return showObject(await chatly.request('POST', '/v1/brands', { body: bodyFrom(args, 'the brand') }));
|
|
190
|
+
case 'update':
|
|
191
|
+
return showObject(await chatly.request('PATCH', `/v1/brands/${encodeURIComponent(need(args, 2, 'a brand id'))}`, { body: bodyFrom(args, 'the fields to change') }));
|
|
192
|
+
case 'delete':
|
|
193
|
+
// A soft delete that answers 200 with `{ id }` — that id is the only
|
|
194
|
+
// confirmation, so print it rather than saying "deleted".
|
|
195
|
+
return showObject(await chatly.request('DELETE', `/v1/brands/${encodeURIComponent(need(args, 2, 'a brand id'))}`));
|
|
196
|
+
default:
|
|
197
|
+
throw new Error(`Unknown: brands ${sub}`);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
export async function emailTemplates(chatly, args) {
|
|
201
|
+
const sub = args.path[1] ?? 'list';
|
|
202
|
+
switch (sub) {
|
|
203
|
+
case 'list':
|
|
204
|
+
/*
|
|
205
|
+
* All five kinds, always — this is the CATALOGUE, not the rows. A kind
|
|
206
|
+
* the workspace has not customised comes back with `override: null`,
|
|
207
|
+
* which is the field to read to tell a customised kind from a built-in
|
|
208
|
+
* one.
|
|
209
|
+
*/
|
|
210
|
+
return showRows(args, await chatly.request('GET', '/v1/email-templates'), [
|
|
211
|
+
'kind',
|
|
212
|
+
'label',
|
|
213
|
+
'audience',
|
|
214
|
+
'whenSent',
|
|
215
|
+
'override',
|
|
216
|
+
]);
|
|
217
|
+
case 'set':
|
|
218
|
+
// A PUT: it REPLACES this workspace's copy, it does not merge into it.
|
|
219
|
+
return showObject(await chatly.request('PUT', `/v1/email-templates/${encodeURIComponent(need(args, 2, 'a template kind'))}`, { body: bodyFrom(args, 'the subject, html and text') }));
|
|
220
|
+
/*
|
|
221
|
+
* `reset`, with `delete` as an alias.
|
|
222
|
+
*
|
|
223
|
+
* `DELETE /v1/email-templates/{kind}` does not delete a template — it
|
|
224
|
+
* drops the workspace's OVERRIDE and puts the built-in copy back, and it
|
|
225
|
+
* answers 200 with `{ restored: false }` when there was nothing to drop.
|
|
226
|
+
* Printing that flag is the only way to tell "I just reverted your copy"
|
|
227
|
+
* from "there was nothing to revert".
|
|
228
|
+
*/
|
|
229
|
+
case 'reset':
|
|
230
|
+
case 'delete':
|
|
231
|
+
return showObject(await chatly.request('DELETE', `/v1/email-templates/${encodeURIComponent(need(args, 2, 'a template kind'))}`));
|
|
232
|
+
default:
|
|
233
|
+
throw new Error(`Unknown: email-templates ${sub}`);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
export async function macros(chatly, args) {
|
|
237
|
+
const sub = args.path[1] ?? 'list';
|
|
238
|
+
switch (sub) {
|
|
239
|
+
case 'list': {
|
|
240
|
+
/*
|
|
241
|
+
* NOT cursor-paginated: `@PaginationExempt`, a bare array capped at 200
|
|
242
|
+
* and ordered by usage rank. The shared manifest flagged this `paged`
|
|
243
|
+
* for a day and two SDKs built a paginator over it.
|
|
244
|
+
*
|
|
245
|
+
* `--query`, not `--q`. The handler's schema names it `query`, and zod
|
|
246
|
+
* would strip `q` without a word — the search would run unnarrowed and
|
|
247
|
+
* answer 200.
|
|
248
|
+
*/
|
|
249
|
+
if (bool(args.flags, 'all')) {
|
|
250
|
+
process.stderr.write('--all is not available for macros (the route is not cursor-paginated; it ' +
|
|
251
|
+
'answers a bare array capped at 200 rows).\n');
|
|
252
|
+
return 1;
|
|
253
|
+
}
|
|
254
|
+
const rows = await chatly.request('GET', '/v1/macros', {
|
|
255
|
+
query: queryFrom(args, [
|
|
256
|
+
['scope', 'scope'],
|
|
257
|
+
['team', 'team'],
|
|
258
|
+
['query', 'query'],
|
|
259
|
+
['language', 'language'],
|
|
260
|
+
]),
|
|
261
|
+
});
|
|
262
|
+
return showRows(args, rows, ['id', 'name', 'shortCode', 'scope', 'language', 'usageCount']);
|
|
263
|
+
}
|
|
264
|
+
case 'get':
|
|
265
|
+
return showObject(await chatly.request('GET', `/v1/macros/${encodeURIComponent(need(args, 2, 'a macro id'))}`));
|
|
266
|
+
case 'actions':
|
|
267
|
+
// The action registry a macro's `actions` array may reference. A macro
|
|
268
|
+
// naming an action that is not here is a 422 at RUN time, not at save
|
|
269
|
+
// time, so this is the list to check before writing one.
|
|
270
|
+
return showObject(await chatly.request('GET', '/v1/macros/actions'));
|
|
271
|
+
case 'templates':
|
|
272
|
+
return showObject(await chatly.request('GET', '/v1/macros/templates'));
|
|
273
|
+
case 'template':
|
|
274
|
+
return showObject(await chatly.request('GET', `/v1/macros/templates/${encodeURIComponent(need(args, 2, 'a template slug'))}`));
|
|
275
|
+
case 'create':
|
|
276
|
+
return showObject(await chatly.request('POST', '/v1/macros', { body: bodyFrom(args, 'the macro') }));
|
|
277
|
+
case 'update':
|
|
278
|
+
return showObject(await chatly.request('PATCH', `/v1/macros/${encodeURIComponent(need(args, 2, 'a macro id'))}`, { body: bodyFrom(args, 'the fields to change') }));
|
|
279
|
+
case 'delete': {
|
|
280
|
+
// The only real 204 on the whole SDK surface: no body comes back, so
|
|
281
|
+
// there is nothing to print but a confirmation.
|
|
282
|
+
const id = need(args, 2, 'a macro id');
|
|
283
|
+
await chatly.request('DELETE', `/v1/macros/${encodeURIComponent(id)}`);
|
|
284
|
+
if (bool(args.flags, 'json')) {
|
|
285
|
+
return showObject({ deleted: id });
|
|
286
|
+
}
|
|
287
|
+
process.stdout.write(`deleted ${id}\n`);
|
|
288
|
+
return 0;
|
|
289
|
+
}
|
|
290
|
+
default:
|
|
291
|
+
throw new Error(`Unknown: macros ${sub}`);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
/**
|
|
295
|
+
* Knowledge gaps: the questions the KB could not answer, and the loop that
|
|
296
|
+
* closes them.
|
|
297
|
+
*
|
|
298
|
+
* `list` → pick the most-asked → `draft` → the article exists → `triage` it
|
|
299
|
+
* resolved. That whole loop is a terminal job, which is why it is a command.
|
|
300
|
+
*/
|
|
301
|
+
export async function knowledgeGaps(chatly, args) {
|
|
302
|
+
const sub = args.path[1] ?? 'list';
|
|
303
|
+
switch (sub) {
|
|
304
|
+
case 'list':
|
|
305
|
+
/*
|
|
306
|
+
* `--limit` here is a plain row cap, NOT a page size: the route is
|
|
307
|
+
* `@PaginationExempt` and the recorder caps a workspace at 200 open
|
|
308
|
+
* gaps, so there is no cursor to follow and `--all` is not offered.
|
|
309
|
+
*/
|
|
310
|
+
return showRows(args, await chatly.request('GET', '/v1/knowledge-gaps', {
|
|
311
|
+
query: queryFrom(args, [
|
|
312
|
+
['status', 'status'],
|
|
313
|
+
['limit', 'limit', true],
|
|
314
|
+
]),
|
|
315
|
+
}), ['id', 'question', 'occurrences', 'status', 'lastSeenAt', 'articleId']);
|
|
316
|
+
case 'triage':
|
|
317
|
+
case 'update': {
|
|
318
|
+
const id = need(args, 2, 'a gap id');
|
|
319
|
+
// `{ status }`, strict — one of open / drafted / resolved / dismissed.
|
|
320
|
+
const status = args.path[3] ?? str(args.flags, 'status');
|
|
321
|
+
if (status === undefined) {
|
|
322
|
+
throw new Error('Give a status: `chatly knowledge-gaps triage <id> <status>`.\n' +
|
|
323
|
+
'One of: open, drafted, resolved, dismissed.');
|
|
324
|
+
}
|
|
325
|
+
return showObject(await chatly.request('PATCH', `/v1/knowledge-gaps/${encodeURIComponent(id)}`, {
|
|
326
|
+
body: { status },
|
|
327
|
+
}));
|
|
328
|
+
}
|
|
329
|
+
case 'draft':
|
|
330
|
+
// Creates a DRAFT article from the gap — 201, and nothing is published.
|
|
331
|
+
return showObject(await chatly.request('POST', `/v1/knowledge-gaps/${encodeURIComponent(need(args, 2, 'a gap id'))}/draft`));
|
|
332
|
+
default:
|
|
333
|
+
throw new Error(`Unknown: knowledge-gaps ${sub}`);
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/commands/config.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { type ParsedArgs } from '../args.js';
|
|
2
|
+
import type { ChatlyClient } from '@chatlychat/sdk-node';
|
|
3
|
+
export declare function reports(chatly: ChatlyClient, args: ParsedArgs): Promise<number>;
|
|
4
|
+
export declare function billing(chatly: ChatlyClient, args: ParsedArgs): Promise<number>;
|
|
5
|
+
//# sourceMappingURL=insights.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"insights.d.ts","sourceRoot":"","sources":["../../src/commands/insights.ts"],"names":[],"mappings":"AA4BA,OAAO,EAAkB,KAAK,UAAU,EAAE,MAAM,YAAY,CAAC;AAI7D,OAAO,KAAK,EAAE,YAAY,EAAkB,MAAM,sBAAsB,CAAC;AAyBzE,wBAAsB,OAAO,CAAC,MAAM,EAAE,YAAY,EAAE,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CAyIrF;AAED,wBAAsB,OAAO,CAAC,MAAM,EAAE,YAAY,EAAE,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CAmIrF"}
|
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Numbers without opening the dashboard: reports, billing and usage.
|
|
3
|
+
*
|
|
4
|
+
* ── The window is NOT the same on every report, and that was a live bug ──
|
|
5
|
+
*
|
|
6
|
+
* These commands used to pass `--from`/`--to` to every report. Three of the
|
|
7
|
+
* four they covered do not read them: `reports/overview`, `reports/agents`
|
|
8
|
+
* and `reports/conversations-by-day` take `@Query('days')` and nothing else,
|
|
9
|
+
* so `chatly reports overview --from 2026-01-01 --to 2026-02-01` answered for
|
|
10
|
+
* the last SEVEN days and said nothing about it. That is the zod-strips-an-
|
|
11
|
+
* unknown-key failure in its query form: a confident report of the wrong
|
|
12
|
+
* window.
|
|
13
|
+
*
|
|
14
|
+
* So the window flags differ per report, deliberately, and each group below
|
|
15
|
+
* says which it accepts:
|
|
16
|
+
*
|
|
17
|
+
* --days overview, agents, conversations-by-day,
|
|
18
|
+
* sources, response-times, voice, attribution
|
|
19
|
+
* --weeks cohorts, and ONLY cohorts
|
|
20
|
+
* --date themes
|
|
21
|
+
* --from --to --days --group-by teams, labels, sla (`reports-extended`)
|
|
22
|
+
*
|
|
23
|
+
* The last group is the only one that resolves a real range, and its two
|
|
24
|
+
* snake_case names (`group_by`, `business_hours`) are the only snake_case
|
|
25
|
+
* query parameters on the whole SDK surface.
|
|
26
|
+
*/
|
|
27
|
+
import { ATTRIBUTION_DIMENSIONS } from '@chatlychat/sdk-node';
|
|
28
|
+
import { bool, num, str } from '../args.js';
|
|
29
|
+
import { printJson } from '../output.js';
|
|
30
|
+
import { DAYS, RANGE, need, queryFrom, showObject, showRows } from './common.js';
|
|
31
|
+
/**
|
|
32
|
+
* The ten dimensions `reports/attribution` accepts.
|
|
33
|
+
*
|
|
34
|
+
* Listed here because the route REFUSES an unknown value rather than
|
|
35
|
+
* defaulting to `utm_source` — a typo quietly answered as something else
|
|
36
|
+
* would be a confident report of the wrong thing, and the caller would have
|
|
37
|
+
* no way to notice. Printing the list on a refusal saves a round trip.
|
|
38
|
+
*/
|
|
39
|
+
/*
|
|
40
|
+
* Imported from the SDK, not re-listed. This file kept its own copy of the
|
|
41
|
+
* ten so it could print them on a refusal; the SDK now exports the array as
|
|
42
|
+
* a value for exactly that, so there is one list in this dependency chain
|
|
43
|
+
* instead of two.
|
|
44
|
+
*/
|
|
45
|
+
/** The four meters `billing/usage` and `billing/usage/daily` accept. */
|
|
46
|
+
const USAGE_EVENT_TYPES = [
|
|
47
|
+
'conversation.opened',
|
|
48
|
+
'message.sent.agent',
|
|
49
|
+
'message.sent.bot',
|
|
50
|
+
'ai.tokens',
|
|
51
|
+
];
|
|
52
|
+
export async function reports(chatly, args) {
|
|
53
|
+
const sub = args.path[1] ?? 'overview';
|
|
54
|
+
const days = () => queryFrom(args, DAYS);
|
|
55
|
+
switch (sub) {
|
|
56
|
+
// ── `?days` only ──────────────────────────────────────────────────────
|
|
57
|
+
case 'overview':
|
|
58
|
+
return showObject(await chatly.reports.overview(days()));
|
|
59
|
+
case 'agents':
|
|
60
|
+
// A BARE ARRAY, and the latency figures here are MEANS. The
|
|
61
|
+
// percentiles are on `reports response-times`.
|
|
62
|
+
return showRows(args, await chatly.reports.agents(days()), [
|
|
63
|
+
'userId',
|
|
64
|
+
'name',
|
|
65
|
+
'conversations',
|
|
66
|
+
'messages',
|
|
67
|
+
'csatAverage',
|
|
68
|
+
]);
|
|
69
|
+
case 'conversations-by-day':
|
|
70
|
+
// A bare array, and days with no conversations are ABSENT rather than
|
|
71
|
+
// zero — a caller plotting a series has to fill the gaps itself.
|
|
72
|
+
return showRows(args, await chatly.reports.conversationsByDay(days()), ['day', 'count']);
|
|
73
|
+
case 'sources':
|
|
74
|
+
return showRows(args, await chatly.request('GET', '/v1/reports/sources', { query: days() }), [
|
|
75
|
+
'channelType',
|
|
76
|
+
'channelId',
|
|
77
|
+
'channelName',
|
|
78
|
+
'conversations',
|
|
79
|
+
]);
|
|
80
|
+
case 'response-times':
|
|
81
|
+
// p50/p95 for first response and resolution. Percentiles are null when
|
|
82
|
+
// `sampleSize` is 0, which is not the same as zero seconds.
|
|
83
|
+
return showObject(await chatly.request('GET', '/v1/reports/response-times', { query: days() }));
|
|
84
|
+
case 'voice':
|
|
85
|
+
// Rates are 0-1, not percentages, and `recordingStorage.exact: false`
|
|
86
|
+
// means the byte total is part extrapolation.
|
|
87
|
+
return showObject(await chatly.request('GET', '/v1/reports/voice', { query: days() }));
|
|
88
|
+
// ── `?dimension` + `?days` ────────────────────────────────────────────
|
|
89
|
+
case 'attribution': {
|
|
90
|
+
const dimension = str(args.flags, 'dimension') ?? args.path[2];
|
|
91
|
+
if (dimension !== undefined &&
|
|
92
|
+
!ATTRIBUTION_DIMENSIONS.includes(dimension)) {
|
|
93
|
+
throw new Error(`--dimension must be one of: ${ATTRIBUTION_DIMENSIONS.join(', ')}.\n` +
|
|
94
|
+
'The route refuses an unknown value rather than defaulting, so a typo ' +
|
|
95
|
+
'cannot become a report of the wrong thing.');
|
|
96
|
+
}
|
|
97
|
+
// The row with `value: null` is the UNTAGGED bucket and is a real row —
|
|
98
|
+
// dropping it inflates every other percentage.
|
|
99
|
+
return showRows(args, await chatly.request('GET', '/v1/reports/attribution', {
|
|
100
|
+
query: { ...days(), ...(dimension === undefined ? {} : { dimension }) },
|
|
101
|
+
}), ['value', 'conversations', 'closed', 'percentage']);
|
|
102
|
+
}
|
|
103
|
+
// ── `?weeks`, and only `?weeks` ───────────────────────────────────────
|
|
104
|
+
case 'cohorts': {
|
|
105
|
+
/*
|
|
106
|
+
* `weeks`, NOT `days`. A `--days` here would be stripped by the
|
|
107
|
+
* handler's query read and the report would silently use its default
|
|
108
|
+
* of 8 weeks. The response echoes the CLAMPED window it actually used
|
|
109
|
+
* (2-26), which can differ from what was asked for — label axes off
|
|
110
|
+
* the echo, not off the input.
|
|
111
|
+
*/
|
|
112
|
+
const weeks = num(args.flags, 'weeks');
|
|
113
|
+
return showObject(await chatly.request('GET', '/v1/reports/cohorts', {
|
|
114
|
+
query: weeks === undefined ? {} : { weeks },
|
|
115
|
+
}));
|
|
116
|
+
}
|
|
117
|
+
// ── `?date` ───────────────────────────────────────────────────────────
|
|
118
|
+
case 'themes':
|
|
119
|
+
/*
|
|
120
|
+
* A SNAPSHOT, generated by a daily worker job. There is no regenerate.
|
|
121
|
+
* `batchDate` names the batch every row was measured over and is null
|
|
122
|
+
* with an empty list when none has been generated. Rows omit
|
|
123
|
+
* `conversationIds` — `reports theme-conversations <id>` serves those.
|
|
124
|
+
*/
|
|
125
|
+
return showObject(await chatly.request('GET', '/v1/reports/themes', {
|
|
126
|
+
query: queryFrom(args, [['date', 'date']]),
|
|
127
|
+
}));
|
|
128
|
+
case 'theme-conversations':
|
|
129
|
+
// An unknown or purged theme id answers 200 with an EMPTY list, not a
|
|
130
|
+
// 404 — this route cannot tell the two apart, and neither can you.
|
|
131
|
+
return showObject(await chatly.request('GET', `/v1/reports/themes/${encodeURIComponent(need(args, 2, 'a theme id'))}/conversations`));
|
|
132
|
+
// ── The full window contract ──────────────────────────────────────────
|
|
133
|
+
case 'teams':
|
|
134
|
+
/*
|
|
135
|
+
* `businessHours.applied: false` means the duration figures are
|
|
136
|
+
* CALENDAR time and `businessHours.reason` says why. The numbers are
|
|
137
|
+
* never zeroed and never relabelled, so read the flag before captioning
|
|
138
|
+
* them.
|
|
139
|
+
*/
|
|
140
|
+
return showObject(await chatly.request('GET', '/v1/reports/teams', { query: queryFrom(args, RANGE) }));
|
|
141
|
+
case 'labels':
|
|
142
|
+
// Capped at the 50 busiest labels, with the remainder in
|
|
143
|
+
// `otherLabels`; `trend` covers the top 8 only. There is no cursor, so
|
|
144
|
+
// there is no way to page to the tail.
|
|
145
|
+
return showObject(await chatly.request('GET', '/v1/reports/labels', { query: queryFrom(args, RANGE) }));
|
|
146
|
+
case 'sla':
|
|
147
|
+
/*
|
|
148
|
+
* `--business-hours` is accepted by the two above and IGNORED here: the
|
|
149
|
+
* handler reads the range and passes only that to the service. It is
|
|
150
|
+
* left in `RANGE` rather than special-cased because the parameter is
|
|
151
|
+
* harmless, but do not read an SLA figure as business-hours-adjusted.
|
|
152
|
+
*
|
|
153
|
+
* `breachesTruncated` is true when the breach list hit its 200-row cap.
|
|
154
|
+
*/
|
|
155
|
+
return showObject(await chatly.reports.sla(queryFrom(args, RANGE)));
|
|
156
|
+
default:
|
|
157
|
+
throw new Error(`Unknown: reports ${sub}`);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
export async function billing(chatly, args) {
|
|
161
|
+
const sub = args.path[1] ?? 'summary';
|
|
162
|
+
const range = () => {
|
|
163
|
+
const out = {};
|
|
164
|
+
const from = str(args.flags, 'from');
|
|
165
|
+
if (from !== undefined) {
|
|
166
|
+
out['from'] = from;
|
|
167
|
+
}
|
|
168
|
+
const to = str(args.flags, 'to');
|
|
169
|
+
if (to !== undefined) {
|
|
170
|
+
out['to'] = to;
|
|
171
|
+
}
|
|
172
|
+
return out;
|
|
173
|
+
};
|
|
174
|
+
/**
|
|
175
|
+
* The meter, from a positional or `--event-type`.
|
|
176
|
+
*
|
|
177
|
+
* Required by `/v1/billing/usage` and `/v1/billing/usage/daily`: both
|
|
178
|
+
* validate it against a closed list and throw before doing anything else,
|
|
179
|
+
* so a call without it is a guaranteed 400 rather than a summary of
|
|
180
|
+
* everything. Four SDKs shipped it as optional.
|
|
181
|
+
*/
|
|
182
|
+
const meter = (what) => {
|
|
183
|
+
const value = args.path[2] ?? str(args.flags, 'event-type');
|
|
184
|
+
if (value === undefined) {
|
|
185
|
+
throw new Error(`Give a meter: \`chatly billing ${what} <event-type>\`.\n` +
|
|
186
|
+
`One of: ${USAGE_EVENT_TYPES.join(', ')}.\n` +
|
|
187
|
+
'The route reports ONE meter over a window, not a summary of everything.');
|
|
188
|
+
}
|
|
189
|
+
/*
|
|
190
|
+
* Checked HERE rather than left to the server.
|
|
191
|
+
*
|
|
192
|
+
* The handler validates against this same closed list and 400s on an
|
|
193
|
+
* unknown value, so passing one through would turn a typo into an HTTP
|
|
194
|
+
* error a user has to decode. We already hold the list — telling them
|
|
195
|
+
* what the four are costs nothing and is the difference between a CLI
|
|
196
|
+
* and a thin curl wrapper.
|
|
197
|
+
*
|
|
198
|
+
* It also narrows the type: `billing.usage` takes `UsageEventType`, and
|
|
199
|
+
* returning a bare `string` is how this went out as `unknown` in four
|
|
200
|
+
* SDKs at once.
|
|
201
|
+
*/
|
|
202
|
+
if (!USAGE_EVENT_TYPES.includes(value)) {
|
|
203
|
+
throw new Error(`\`${value}\` is not a meter.\n` + `One of: ${USAGE_EVENT_TYPES.join(', ')}.`);
|
|
204
|
+
}
|
|
205
|
+
return value;
|
|
206
|
+
};
|
|
207
|
+
switch (sub) {
|
|
208
|
+
case 'summary':
|
|
209
|
+
// `GET /v1/billing` — plan, seats, trial and cap state in one object.
|
|
210
|
+
return showObject(await chatly.request('GET', '/v1/billing'));
|
|
211
|
+
case 'plans':
|
|
212
|
+
return showRows(args, await chatly.billing.plans(), [
|
|
213
|
+
'id',
|
|
214
|
+
'name',
|
|
215
|
+
'priceCents',
|
|
216
|
+
'interval',
|
|
217
|
+
'seats',
|
|
218
|
+
]);
|
|
219
|
+
case 'usage':
|
|
220
|
+
return showObject(await chatly.billing.usage(meter('usage'), range()));
|
|
221
|
+
case 'usage-daily':
|
|
222
|
+
/*
|
|
223
|
+
* DENSE: one entry per UTC day in `[from, to)`, zeros included — unlike
|
|
224
|
+
* `reports conversations-by-day`, which omits empty days. The two look
|
|
225
|
+
* alike and are not interchangeable.
|
|
226
|
+
*/
|
|
227
|
+
return showObject(await chatly.request('GET', '/v1/billing/usage/daily', {
|
|
228
|
+
query: { eventType: meter('usage-daily'), ...range() },
|
|
229
|
+
}));
|
|
230
|
+
case 'usage-monthly':
|
|
231
|
+
// One entry per known usage event type, ALWAYS all four, plus a `total`
|
|
232
|
+
// that is already summed. `--month` is a UTC-midnight ISO date.
|
|
233
|
+
return showObject(await chatly.request('GET', '/v1/billing/usage/monthly', {
|
|
234
|
+
query: queryFrom(args, [['month', 'month']]),
|
|
235
|
+
}));
|
|
236
|
+
case 'invoices': {
|
|
237
|
+
/*
|
|
238
|
+
* Cursor-paginated on the wire — `{ data, nextCursor }` — but its
|
|
239
|
+
* envelope key is `data`, not `items`, so the shared manifest does not
|
|
240
|
+
* flag it `paged` and no SDK offers a walker over it. `--all` is
|
|
241
|
+
* therefore not offered here; follow `--cursor` by hand.
|
|
242
|
+
*/
|
|
243
|
+
if (bool(args.flags, 'all')) {
|
|
244
|
+
process.stderr.write('--all is not available for invoices: the route pages under a `data` key rather ' +
|
|
245
|
+
'than the `items` envelope every other list uses, so there is no SDK walker ' +
|
|
246
|
+
'for it. Follow the printed cursor with --cursor.\n');
|
|
247
|
+
return 1;
|
|
248
|
+
}
|
|
249
|
+
const page = await chatly.request('GET', '/v1/billing/invoices', {
|
|
250
|
+
query: queryFrom(args, [
|
|
251
|
+
['cursor', 'cursor'],
|
|
252
|
+
['limit', 'limit', true],
|
|
253
|
+
]),
|
|
254
|
+
});
|
|
255
|
+
if (bool(args.flags, 'json')) {
|
|
256
|
+
printJson(page);
|
|
257
|
+
return 0;
|
|
258
|
+
}
|
|
259
|
+
showRows(args, page, [
|
|
260
|
+
'id',
|
|
261
|
+
'status',
|
|
262
|
+
'amountCents',
|
|
263
|
+
'currency',
|
|
264
|
+
'periodStart',
|
|
265
|
+
'hostedInvoiceUrl',
|
|
266
|
+
]);
|
|
267
|
+
const next = page.nextCursor;
|
|
268
|
+
if (typeof next === 'string' && next.length > 0) {
|
|
269
|
+
process.stdout.write(`next page: --cursor ${next}\n`);
|
|
270
|
+
}
|
|
271
|
+
return 0;
|
|
272
|
+
}
|
|
273
|
+
default:
|
|
274
|
+
throw new Error(`Unknown: billing ${sub}`);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
//# sourceMappingURL=insights.js.map
|