@skillrecordings/cli 0.1.0 → 0.2.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.
Files changed (136) hide show
  1. package/bin/skill.mjs +27 -0
  2. package/dist/chunk-2NCCVTEE.js +22342 -0
  3. package/dist/chunk-2NCCVTEE.js.map +1 -0
  4. package/dist/chunk-3E3GYSZR.js +7071 -0
  5. package/dist/chunk-3E3GYSZR.js.map +1 -0
  6. package/dist/chunk-F4EM72IH.js +86 -0
  7. package/dist/chunk-F4EM72IH.js.map +1 -0
  8. package/dist/chunk-FGP7KUQW.js +432 -0
  9. package/dist/chunk-FGP7KUQW.js.map +1 -0
  10. package/dist/chunk-H3D6VCME.js +55 -0
  11. package/dist/chunk-H3D6VCME.js.map +1 -0
  12. package/dist/chunk-HK3PEWFD.js +208 -0
  13. package/dist/chunk-HK3PEWFD.js.map +1 -0
  14. package/dist/chunk-KEV3QKXP.js +4495 -0
  15. package/dist/chunk-KEV3QKXP.js.map +1 -0
  16. package/dist/chunk-MG37YDAK.js +882 -0
  17. package/dist/chunk-MG37YDAK.js.map +1 -0
  18. package/dist/chunk-MLNDSBZ4.js +482 -0
  19. package/dist/chunk-MLNDSBZ4.js.map +1 -0
  20. package/dist/chunk-N2WIV2JV.js +22 -0
  21. package/dist/chunk-N2WIV2JV.js.map +1 -0
  22. package/dist/chunk-PWWRCN5W.js +2067 -0
  23. package/dist/chunk-PWWRCN5W.js.map +1 -0
  24. package/dist/chunk-SKHBM3XP.js +7746 -0
  25. package/dist/chunk-SKHBM3XP.js.map +1 -0
  26. package/dist/chunk-WFANXVQG.js +64 -0
  27. package/dist/chunk-WFANXVQG.js.map +1 -0
  28. package/dist/chunk-WYKL32C3.js +275 -0
  29. package/dist/chunk-WYKL32C3.js.map +1 -0
  30. package/dist/chunk-ZNF7XD2S.js +134 -0
  31. package/dist/chunk-ZNF7XD2S.js.map +1 -0
  32. package/dist/config-AUAIYDSI.js +20 -0
  33. package/dist/config-AUAIYDSI.js.map +1 -0
  34. package/dist/fileFromPath-XN7LXIBI.js +134 -0
  35. package/dist/fileFromPath-XN7LXIBI.js.map +1 -0
  36. package/dist/getMachineId-bsd-KW2E7VK3.js +42 -0
  37. package/dist/getMachineId-bsd-KW2E7VK3.js.map +1 -0
  38. package/dist/getMachineId-darwin-ROXJUJX5.js +42 -0
  39. package/dist/getMachineId-darwin-ROXJUJX5.js.map +1 -0
  40. package/dist/getMachineId-linux-KVZEHQSU.js +34 -0
  41. package/dist/getMachineId-linux-KVZEHQSU.js.map +1 -0
  42. package/dist/getMachineId-unsupported-PPRILPPA.js +25 -0
  43. package/dist/getMachineId-unsupported-PPRILPPA.js.map +1 -0
  44. package/dist/getMachineId-win-IIF36LEJ.js +44 -0
  45. package/dist/getMachineId-win-IIF36LEJ.js.map +1 -0
  46. package/dist/index.js +112703 -0
  47. package/dist/index.js.map +1 -0
  48. package/dist/lib-R6DEEJCP.js +7623 -0
  49. package/dist/lib-R6DEEJCP.js.map +1 -0
  50. package/dist/pipeline-IAVVAKTU.js +120 -0
  51. package/dist/pipeline-IAVVAKTU.js.map +1 -0
  52. package/dist/query-NTP5NVXN.js +25 -0
  53. package/dist/query-NTP5NVXN.js.map +1 -0
  54. package/dist/routing-BAEPFB7V.js +390 -0
  55. package/dist/routing-BAEPFB7V.js.map +1 -0
  56. package/dist/stripe-lookup-charge-EPRUMZDL.js +56 -0
  57. package/dist/stripe-lookup-charge-EPRUMZDL.js.map +1 -0
  58. package/dist/stripe-payment-history-SJPKA63N.js +67 -0
  59. package/dist/stripe-payment-history-SJPKA63N.js.map +1 -0
  60. package/dist/stripe-subscription-status-L4Z65GB3.js +58 -0
  61. package/dist/stripe-subscription-status-L4Z65GB3.js.map +1 -0
  62. package/dist/stripe-verify-refund-FZDKCIUQ.js +54 -0
  63. package/dist/stripe-verify-refund-FZDKCIUQ.js.map +1 -0
  64. package/dist/support-memory-WSG7SDKG.js +10 -0
  65. package/dist/support-memory-WSG7SDKG.js.map +1 -0
  66. package/package.json +10 -7
  67. package/.env.encrypted +0 -0
  68. package/CHANGELOG.md +0 -35
  69. package/data/tt-archive-dataset.json +0 -1
  70. package/data/validate-test-dataset.json +0 -97
  71. package/docs/CLI-AUTH.md +0 -504
  72. package/preload.ts +0 -18
  73. package/src/__tests__/init.test.ts +0 -74
  74. package/src/alignment-test.ts +0 -64
  75. package/src/check-apps.ts +0 -16
  76. package/src/commands/auth/decrypt.ts +0 -123
  77. package/src/commands/auth/encrypt.ts +0 -81
  78. package/src/commands/auth/index.ts +0 -50
  79. package/src/commands/auth/keygen.ts +0 -41
  80. package/src/commands/auth/status.ts +0 -164
  81. package/src/commands/axiom/forensic.ts +0 -868
  82. package/src/commands/axiom/index.ts +0 -697
  83. package/src/commands/build-dataset.ts +0 -311
  84. package/src/commands/db-status.ts +0 -47
  85. package/src/commands/deploys.ts +0 -219
  86. package/src/commands/eval-local/compare.ts +0 -171
  87. package/src/commands/eval-local/health.ts +0 -212
  88. package/src/commands/eval-local/index.ts +0 -76
  89. package/src/commands/eval-local/real-tools.ts +0 -416
  90. package/src/commands/eval-local/run.ts +0 -1168
  91. package/src/commands/eval-local/score-production.ts +0 -256
  92. package/src/commands/eval-local/seed.ts +0 -276
  93. package/src/commands/eval-pipeline/index.ts +0 -53
  94. package/src/commands/eval-pipeline/real-tools.ts +0 -492
  95. package/src/commands/eval-pipeline/run.ts +0 -1316
  96. package/src/commands/eval-pipeline/seed.ts +0 -395
  97. package/src/commands/eval-prompt.ts +0 -496
  98. package/src/commands/eval.test.ts +0 -253
  99. package/src/commands/eval.ts +0 -108
  100. package/src/commands/faq-classify.ts +0 -460
  101. package/src/commands/faq-cluster.ts +0 -135
  102. package/src/commands/faq-extract.ts +0 -249
  103. package/src/commands/faq-mine.ts +0 -432
  104. package/src/commands/faq-review.ts +0 -426
  105. package/src/commands/front/index.ts +0 -351
  106. package/src/commands/front/pull-conversations.ts +0 -275
  107. package/src/commands/front/tags.ts +0 -825
  108. package/src/commands/front-cache.ts +0 -1277
  109. package/src/commands/front-stats.ts +0 -75
  110. package/src/commands/health.test.ts +0 -82
  111. package/src/commands/health.ts +0 -362
  112. package/src/commands/init.test.ts +0 -89
  113. package/src/commands/init.ts +0 -106
  114. package/src/commands/inngest/client.ts +0 -294
  115. package/src/commands/inngest/events.ts +0 -296
  116. package/src/commands/inngest/investigate.ts +0 -382
  117. package/src/commands/inngest/runs.ts +0 -149
  118. package/src/commands/inngest/signal.ts +0 -143
  119. package/src/commands/kb-sync.ts +0 -498
  120. package/src/commands/memory/find.ts +0 -135
  121. package/src/commands/memory/get.ts +0 -87
  122. package/src/commands/memory/index.ts +0 -97
  123. package/src/commands/memory/stats.ts +0 -163
  124. package/src/commands/memory/store.ts +0 -49
  125. package/src/commands/memory/vote.ts +0 -159
  126. package/src/commands/pipeline.ts +0 -127
  127. package/src/commands/responses.ts +0 -856
  128. package/src/commands/tools.ts +0 -293
  129. package/src/commands/wizard.ts +0 -319
  130. package/src/index.ts +0 -172
  131. package/src/lib/crypto.ts +0 -56
  132. package/src/lib/env-loader.ts +0 -206
  133. package/src/lib/onepassword.ts +0 -137
  134. package/src/test-agent-local.ts +0 -115
  135. package/tsconfig.json +0 -11
  136. package/vitest.config.ts +0 -10
@@ -1,697 +0,0 @@
1
- /**
2
- * CLI commands for querying Axiom logs and traces
3
- *
4
- * Usage:
5
- * skill axiom query "['support-traces'] | where name == 'agent.run' | limit 10"
6
- * skill axiom agents --app total-typescript --limit 20
7
- * skill axiom errors --since 1h
8
- * skill axiom conversation <conversationId>
9
- */
10
-
11
- import { Axiom } from '@axiomhq/js'
12
- import type { Command } from 'commander'
13
- import { registerForensicCommands } from './forensic'
14
-
15
- /**
16
- * Get dataset name from env or default
17
- */
18
- function getDataset(): string {
19
- return process.env.AXIOM_DATASET || 'support-agent'
20
- }
21
-
22
- /**
23
- * Get Axiom client (requires AXIOM_TOKEN env var)
24
- */
25
- function getAxiomClient(): Axiom {
26
- const token = process.env.AXIOM_TOKEN
27
- if (!token) {
28
- console.error('AXIOM_TOKEN environment variable is required')
29
- process.exit(1)
30
- }
31
- return new Axiom({ token })
32
- }
33
-
34
- /**
35
- * Format duration in milliseconds to human-readable
36
- */
37
- function formatDuration(ms: number): string {
38
- if (ms < 1000) return `${ms}ms`
39
- if (ms < 60000) return `${(ms / 1000).toFixed(1)}s`
40
- return `${(ms / 60000).toFixed(1)}m`
41
- }
42
-
43
- /**
44
- * Format timestamp
45
- */
46
- function formatTime(timestamp: string | Date): string {
47
- const date = new Date(timestamp)
48
- return date.toLocaleString('en-US', {
49
- month: 'short',
50
- day: 'numeric',
51
- hour: '2-digit',
52
- minute: '2-digit',
53
- second: '2-digit',
54
- })
55
- }
56
-
57
- /**
58
- * Parse time range string to start/end dates
59
- */
60
- function parseTimeRange(since: string): { startTime: Date; endTime: Date } {
61
- const endTime = new Date()
62
- let startTime: Date
63
-
64
- // Parse duration strings like "1h", "24h", "7d"
65
- const match = since.match(/^(\d+)([hmd])$/)
66
- if (match && match[1] && match[2]) {
67
- const value = parseInt(match[1], 10)
68
- const unit = match[2] as 'h' | 'm' | 'd'
69
- const msPerUnit: Record<'h' | 'm' | 'd', number> = {
70
- h: 60 * 60 * 1000,
71
- m: 60 * 1000,
72
- d: 24 * 60 * 60 * 1000,
73
- }
74
- startTime = new Date(endTime.getTime() - value * msPerUnit[unit])
75
- } else {
76
- // Try ISO date
77
- startTime = new Date(since)
78
- if (isNaN(startTime.getTime())) {
79
- console.error(
80
- `Invalid time range: ${since}. Use format like "1h", "24h", "7d" or ISO date.`
81
- )
82
- process.exit(1)
83
- }
84
- }
85
-
86
- return { startTime, endTime }
87
- }
88
-
89
- /**
90
- * Run a raw APL query
91
- */
92
- async function runQuery(
93
- apl: string,
94
- options: { since?: string; json?: boolean }
95
- ): Promise<void> {
96
- const client = getAxiomClient()
97
- const { startTime, endTime } = parseTimeRange(options.since ?? '24h')
98
-
99
- try {
100
- const result = await client.query(apl, {
101
- startTime: startTime.toISOString(),
102
- endTime: endTime.toISOString(),
103
- })
104
-
105
- if (options.json) {
106
- console.log(JSON.stringify(result, null, 2))
107
- return
108
- }
109
-
110
- // Display results in table format
111
- const matches = result.matches ?? []
112
- if (matches.length === 0) {
113
- console.log('No results found')
114
- return
115
- }
116
-
117
- console.log(
118
- `\nFound ${matches.length} results (${result.status?.elapsedTime}ms)`
119
- )
120
- console.log('='.repeat(80))
121
-
122
- for (const match of matches) {
123
- const data = match.data as Record<string, unknown>
124
- console.log(`\n[${formatTime(match._time)}]`)
125
- for (const [key, value] of Object.entries(data)) {
126
- if (key.startsWith('_')) continue
127
- const displayValue =
128
- typeof value === 'object' ? JSON.stringify(value) : value
129
- console.log(` ${key}: ${displayValue}`)
130
- }
131
- }
132
- } catch (error) {
133
- console.error(
134
- 'Query failed:',
135
- error instanceof Error ? error.message : error
136
- )
137
- process.exit(1)
138
- }
139
- }
140
-
141
- /**
142
- * List recent agent runs
143
- */
144
- async function listAgentRuns(options: {
145
- app?: string
146
- limit?: number
147
- since?: string
148
- json?: boolean
149
- }): Promise<void> {
150
- const client = getAxiomClient()
151
- const limit = options.limit ?? 20
152
- const { startTime, endTime } = parseTimeRange(options.since ?? '24h')
153
-
154
- let apl = `['${getDataset()}']
155
- | where name == 'agent.run'`
156
-
157
- if (options.app) {
158
- apl += `
159
- | where appId == '${options.app}'`
160
- }
161
-
162
- apl += `
163
- | sort by _time desc
164
- | limit ${limit}`
165
-
166
- try {
167
- const result = await client.query(apl, {
168
- startTime: startTime.toISOString(),
169
- endTime: endTime.toISOString(),
170
- })
171
-
172
- const matches = result.matches ?? []
173
-
174
- if (options.json) {
175
- console.log(
176
- JSON.stringify(
177
- matches.map((m) => m.data),
178
- null,
179
- 2
180
- )
181
- )
182
- return
183
- }
184
-
185
- if (matches.length === 0) {
186
- console.log('No agent runs found')
187
- return
188
- }
189
-
190
- console.log('\nRecent Agent Runs')
191
- console.log('='.repeat(100))
192
-
193
- for (const match of matches) {
194
- const d = match.data as Record<string, unknown>
195
- const time = formatTime(match._time)
196
- const duration = formatDuration(Number(d.durationMs) || 0)
197
- const app = d.appId ?? 'unknown'
198
- const tools = (d.toolCallsCount ?? 0) + ' tools'
199
- const model = String(d.model ?? '').replace('anthropic/', '')
200
- const approval = d.requiresApproval ? '!' : d.autoSent ? '+' : '-'
201
-
202
- console.log(`\n[${time}] ${app} (${duration})`)
203
- console.log(` Model: ${model} | Tools: ${tools} | Auto: ${approval}`)
204
- console.log(` Conv: ${d.conversationId}`)
205
- if (d.customerEmail) console.log(` Customer: ${d.customerEmail}`)
206
- }
207
-
208
- // Summary stats
209
- const durations = matches.map(
210
- (m) => Number((m.data as Record<string, unknown>).durationMs) || 0
211
- )
212
- const avgDuration = durations.reduce((a, b) => a + b, 0) / durations.length
213
- const autoSent = matches.filter(
214
- (m) => (m.data as Record<string, unknown>).autoSent
215
- ).length
216
- const approvals = matches.filter(
217
- (m) => (m.data as Record<string, unknown>).requiresApproval
218
- ).length
219
-
220
- console.log('\n' + '-'.repeat(100))
221
- console.log(
222
- `Total: ${matches.length} | Avg duration: ${formatDuration(avgDuration)} | Auto-sent: ${autoSent} | Approvals: ${approvals}`
223
- )
224
- } catch (error) {
225
- console.error(
226
- 'Query failed:',
227
- error instanceof Error ? error.message : error
228
- )
229
- process.exit(1)
230
- }
231
- }
232
-
233
- /**
234
- * List recent errors
235
- */
236
- async function listErrors(options: {
237
- since?: string
238
- limit?: number
239
- json?: boolean
240
- }): Promise<void> {
241
- const client = getAxiomClient()
242
- const limit = options.limit ?? 50
243
- const { startTime, endTime } = parseTimeRange(options.since ?? '24h')
244
-
245
- const apl = `['${getDataset()}']
246
- | where status == 'error' or error != ''
247
- | sort by _time desc
248
- | limit ${limit}`
249
-
250
- try {
251
- const result = await client.query(apl, {
252
- startTime: startTime.toISOString(),
253
- endTime: endTime.toISOString(),
254
- })
255
-
256
- const matches = result.matches ?? []
257
-
258
- if (options.json) {
259
- console.log(
260
- JSON.stringify(
261
- matches.map((m) => m.data),
262
- null,
263
- 2
264
- )
265
- )
266
- return
267
- }
268
-
269
- if (matches.length === 0) {
270
- console.log('No errors found')
271
- return
272
- }
273
-
274
- console.log('\nRecent Errors')
275
- console.log('='.repeat(100))
276
-
277
- for (const match of matches) {
278
- const d = match.data as Record<string, unknown>
279
- const time = formatTime(match._time)
280
- const name = d.name ?? 'unknown'
281
- const error =
282
- d.error ??
283
- d.errorStack ??
284
- d.message ??
285
- `[${d.level ?? 'error'}] ${d.name ?? 'unknown event'}`
286
-
287
- console.log(`\n[${time}] ${name}`)
288
- console.log(` Error: ${String(error).slice(0, 200)}`)
289
- if (d.conversationId) console.log(` Conv: ${d.conversationId}`)
290
- if (d.appId) console.log(` App: ${d.appId}`)
291
- }
292
-
293
- console.log('\n' + '-'.repeat(100))
294
- console.log(`Total errors: ${matches.length}`)
295
- } catch (error) {
296
- console.error(
297
- 'Query failed:',
298
- error instanceof Error ? error.message : error
299
- )
300
- process.exit(1)
301
- }
302
- }
303
-
304
- /**
305
- * Get all events for a conversation
306
- */
307
- async function getConversation(
308
- conversationId: string,
309
- options: { since?: string; json?: boolean }
310
- ): Promise<void> {
311
- const client = getAxiomClient()
312
- const { startTime, endTime } = parseTimeRange(options.since ?? '7d')
313
-
314
- const apl = `['${getDataset()}']
315
- | where conversationId == '${conversationId}'
316
- | sort by _time asc`
317
-
318
- try {
319
- const result = await client.query(apl, {
320
- startTime: startTime.toISOString(),
321
- endTime: endTime.toISOString(),
322
- })
323
-
324
- const matches = result.matches ?? []
325
-
326
- if (options.json) {
327
- console.log(
328
- JSON.stringify(
329
- matches.map((m) => ({ _time: m._time, ...(m.data as object) })),
330
- null,
331
- 2
332
- )
333
- )
334
- return
335
- }
336
-
337
- if (matches.length === 0) {
338
- console.log(`No events found for conversation: ${conversationId}`)
339
- return
340
- }
341
-
342
- console.log(`\nConversation Timeline: ${conversationId}`)
343
- console.log('='.repeat(100))
344
-
345
- for (const match of matches) {
346
- const d = match.data as Record<string, unknown>
347
- const time = formatTime(match._time)
348
- const name = d.name ?? d.type ?? 'event'
349
- const duration = d.durationMs
350
- ? ` (${formatDuration(Number(d.durationMs))})`
351
- : ''
352
-
353
- console.log(`\n[${time}] ${name}${duration}`)
354
-
355
- // Show relevant fields based on event type
356
- if (d.category) console.log(` Category: ${d.category} (${d.confidence})`)
357
- if (d.complexity) console.log(` Complexity: ${d.complexity}`)
358
- if (d.routingType) console.log(` Routing: ${d.routingType}`)
359
- if (d.model) console.log(` Model: ${d.model}`)
360
- if (d.toolCallsCount)
361
- console.log(
362
- ` Tools: ${d.toolCallsCount} (${(d.toolNames as string[])?.join(', ') ?? ''})`
363
- )
364
- if (d.memoriesRetrieved) console.log(` Memories: ${d.memoriesRetrieved}`)
365
- if (d.error) console.log(` Error: ${d.error}`)
366
- if (d.reasoning)
367
- console.log(` Reasoning: ${String(d.reasoning).slice(0, 150)}`)
368
- }
369
-
370
- console.log('\n' + '-'.repeat(100))
371
- console.log(`Total events: ${matches.length}`)
372
- } catch (error) {
373
- console.error(
374
- 'Query failed:',
375
- error instanceof Error ? error.message : error
376
- )
377
- process.exit(1)
378
- }
379
- }
380
-
381
- /**
382
- * Get classification distribution
383
- */
384
- async function getClassificationStats(options: {
385
- app?: string
386
- since?: string
387
- json?: boolean
388
- }): Promise<void> {
389
- const client = getAxiomClient()
390
- const { startTime, endTime } = parseTimeRange(options.since ?? '24h')
391
-
392
- let apl = `['${getDataset()}']
393
- | where name == 'classifier.run'`
394
-
395
- if (options.app) {
396
- apl += `
397
- | where appId == '${options.app}'`
398
- }
399
-
400
- apl += `
401
- | summarize count = count() by category, complexity`
402
-
403
- try {
404
- const result = await client.query(apl, {
405
- startTime: startTime.toISOString(),
406
- endTime: endTime.toISOString(),
407
- })
408
-
409
- const buckets = result.buckets?.totals ?? []
410
-
411
- if (options.json) {
412
- console.log(JSON.stringify(buckets, null, 2))
413
- return
414
- }
415
-
416
- if (buckets.length === 0) {
417
- console.log('No classification data found')
418
- return
419
- }
420
-
421
- console.log('\nClassification Distribution')
422
- console.log('='.repeat(60))
423
-
424
- // Group by category
425
- const byCategory: Record<
426
- string,
427
- { total: number; complexities: Record<string, number> }
428
- > = {}
429
- for (const bucket of buckets) {
430
- const group = bucket.group as Record<string, string>
431
- const category = group.category ?? 'unknown'
432
- const complexity = group.complexity ?? 'unknown'
433
- const count = Number(bucket.aggregations?.[0]?.value ?? 0)
434
-
435
- if (!byCategory[category]) {
436
- byCategory[category] = { total: 0, complexities: {} }
437
- }
438
- byCategory[category].total += count
439
- byCategory[category].complexities[complexity] =
440
- (byCategory[category].complexities[complexity] ?? 0) + count
441
- }
442
-
443
- // Sort by total count
444
- const sorted = Object.entries(byCategory).sort(
445
- (a, b) => b[1].total - a[1].total
446
- )
447
- const grandTotal = sorted.reduce((sum, [, v]) => sum + v.total, 0)
448
-
449
- for (const [category, { total, complexities }] of sorted) {
450
- const pct = ((total / grandTotal) * 100).toFixed(1)
451
- const complexityStr = Object.entries(complexities)
452
- .map(([c, n]) => `${c}:${n}`)
453
- .join(', ')
454
- console.log(
455
- `${category.padEnd(25)} ${String(total).padStart(5)} (${pct.padStart(5)}%) [${complexityStr}]`
456
- )
457
- }
458
-
459
- console.log('-'.repeat(60))
460
- console.log(`Total classifications: ${grandTotal}`)
461
- } catch (error) {
462
- console.error(
463
- 'Query failed:',
464
- error instanceof Error ? error.message : error
465
- )
466
- process.exit(1)
467
- }
468
- }
469
-
470
- /**
471
- * List workflow step traces (for debugging timeout issues)
472
- */
473
- async function listWorkflowSteps(options: {
474
- workflow?: string
475
- conversation?: string
476
- since?: string
477
- limit?: number
478
- json?: boolean
479
- }): Promise<void> {
480
- const client = getAxiomClient()
481
- const limit = options.limit ?? 50
482
- const { startTime, endTime } = parseTimeRange(options.since ?? '24h')
483
-
484
- let apl = `['${getDataset()}']
485
- | where type == 'workflow-step'`
486
-
487
- if (options.workflow) {
488
- apl += `
489
- | where workflowName == '${options.workflow}'`
490
- }
491
-
492
- if (options.conversation) {
493
- apl += `
494
- | where conversationId == '${options.conversation}'`
495
- }
496
-
497
- apl += `
498
- | sort by _time desc
499
- | limit ${limit}`
500
-
501
- try {
502
- const result = await client.query(apl, {
503
- startTime: startTime.toISOString(),
504
- endTime: endTime.toISOString(),
505
- })
506
-
507
- const matches = result.matches ?? []
508
-
509
- if (options.json) {
510
- console.log(
511
- JSON.stringify(
512
- matches.map((m) => m.data),
513
- null,
514
- 2
515
- )
516
- )
517
- return
518
- }
519
-
520
- if (matches.length === 0) {
521
- console.log('No workflow steps found')
522
- return
523
- }
524
-
525
- console.log('\nWorkflow Steps')
526
- console.log('='.repeat(100))
527
-
528
- for (const match of matches) {
529
- const d = match.data as Record<string, unknown>
530
- const time = formatTime(match._time)
531
- const workflow = d.workflowName ?? 'unknown'
532
- const step = d.stepName ?? 'unknown'
533
- const duration = formatDuration(Number(d.durationMs) || 0)
534
- const success = d.success ? '✓' : '✗'
535
-
536
- console.log(`\n[${time}] ${workflow} > ${step} ${success} (${duration})`)
537
- if (d.conversationId) console.log(` Conv: ${d.conversationId}`)
538
- if (d.error) console.log(` Error: ${d.error}`)
539
- if (d.metadata) console.log(` Meta: ${JSON.stringify(d.metadata)}`)
540
- }
541
-
542
- console.log('\n' + '-'.repeat(100))
543
- console.log(`Total: ${matches.length}`)
544
- } catch (error) {
545
- console.error(
546
- 'Query failed:',
547
- error instanceof Error ? error.message : error
548
- )
549
- process.exit(1)
550
- }
551
- }
552
-
553
- /**
554
- * List approval-related traces (for debugging HITL flow)
555
- */
556
- async function listApprovals(options: {
557
- since?: string
558
- limit?: number
559
- json?: boolean
560
- }): Promise<void> {
561
- const client = getAxiomClient()
562
- const limit = options.limit ?? 30
563
- const { startTime, endTime } = parseTimeRange(options.since ?? '24h')
564
-
565
- const apl = `['${getDataset()}']
566
- | where type in ('approval', 'slack')
567
- | sort by _time desc
568
- | limit ${limit}`
569
-
570
- try {
571
- const result = await client.query(apl, {
572
- startTime: startTime.toISOString(),
573
- endTime: endTime.toISOString(),
574
- })
575
-
576
- const matches = result.matches ?? []
577
-
578
- if (options.json) {
579
- console.log(
580
- JSON.stringify(
581
- matches.map((m) => m.data),
582
- null,
583
- 2
584
- )
585
- )
586
- return
587
- }
588
-
589
- if (matches.length === 0) {
590
- console.log('No approval traces found')
591
- return
592
- }
593
-
594
- console.log('\nApproval Flow Traces')
595
- console.log('='.repeat(100))
596
-
597
- for (const match of matches) {
598
- const d = match.data as Record<string, unknown>
599
- const time = formatTime(match._time)
600
- const name = d.name ?? 'unknown'
601
- const actionId = d.actionId ?? ''
602
- const success = d.success !== undefined ? (d.success ? '✓' : '✗') : ''
603
- const duration = d.durationMs
604
- ? ` (${formatDuration(Number(d.durationMs))})`
605
- : ''
606
-
607
- console.log(`\n[${time}] ${name} ${success}${duration}`)
608
- if (actionId) console.log(` Action: ${actionId}`)
609
- if (d.actionType) console.log(` Type: ${d.actionType}`)
610
- if (d.conversationId) console.log(` Conv: ${d.conversationId}`)
611
- if (d.channel) console.log(` Channel: ${d.channel}`)
612
- if (d.messageTs) console.log(` Slack TS: ${d.messageTs}`)
613
- if (d.error) console.log(` Error: ${d.error}`)
614
- if (d.customerEmail) console.log(` Customer: ${d.customerEmail}`)
615
- }
616
-
617
- console.log('\n' + '-'.repeat(100))
618
- console.log(`Total: ${matches.length}`)
619
- } catch (error) {
620
- console.error(
621
- 'Query failed:',
622
- error instanceof Error ? error.message : error
623
- )
624
- process.exit(1)
625
- }
626
- }
627
-
628
- /**
629
- * Register Axiom commands with Commander
630
- */
631
- export function registerAxiomCommands(program: Command): void {
632
- const axiom = program
633
- .command('axiom')
634
- .description('Query Axiom logs and traces')
635
-
636
- axiom
637
- .command('query')
638
- .description('Run a raw APL query')
639
- .argument('<apl>', 'APL query string')
640
- .option('-s, --since <time>', 'Time range (e.g., 1h, 24h, 7d)', '24h')
641
- .option('--json', 'Output as JSON')
642
- .action(runQuery)
643
-
644
- axiom
645
- .command('agents')
646
- .description('List recent agent runs')
647
- .option('-a, --app <slug>', 'Filter by app')
648
- .option('-l, --limit <n>', 'Number of results', parseInt)
649
- .option('-s, --since <time>', 'Time range (e.g., 1h, 24h, 7d)', '24h')
650
- .option('--json', 'Output as JSON')
651
- .action(listAgentRuns)
652
-
653
- axiom
654
- .command('errors')
655
- .description('List recent errors')
656
- .option('-l, --limit <n>', 'Number of results', parseInt)
657
- .option('-s, --since <time>', 'Time range (e.g., 1h, 24h, 7d)', '24h')
658
- .option('--json', 'Output as JSON')
659
- .action(listErrors)
660
-
661
- axiom
662
- .command('conversation')
663
- .description('Get all events for a conversation')
664
- .argument('<conversationId>', 'Front conversation ID')
665
- .option('-s, --since <time>', 'Time range (e.g., 1h, 24h, 7d)', '7d')
666
- .option('--json', 'Output as JSON')
667
- .action(getConversation)
668
-
669
- axiom
670
- .command('classifications')
671
- .description('Show classification distribution')
672
- .option('-a, --app <slug>', 'Filter by app')
673
- .option('-s, --since <time>', 'Time range (e.g., 1h, 24h, 7d)', '24h')
674
- .option('--json', 'Output as JSON')
675
- .action(getClassificationStats)
676
-
677
- axiom
678
- .command('workflow-steps')
679
- .description('List workflow step traces (for debugging timeouts)')
680
- .option('-w, --workflow <name>', 'Filter by workflow name')
681
- .option('-c, --conversation <id>', 'Filter by conversation ID')
682
- .option('-l, --limit <n>', 'Number of results', parseInt)
683
- .option('-s, --since <time>', 'Time range (e.g., 1h, 24h, 7d)', '24h')
684
- .option('--json', 'Output as JSON')
685
- .action(listWorkflowSteps)
686
-
687
- axiom
688
- .command('approvals')
689
- .description('List approval flow traces (HITL debugging)')
690
- .option('-l, --limit <n>', 'Number of results', parseInt)
691
- .option('-s, --since <time>', 'Time range (e.g., 1h, 24h, 7d)', '24h')
692
- .option('--json', 'Output as JSON')
693
- .action(listApprovals)
694
-
695
- // Register forensic / self-diagnosis queries
696
- registerForensicCommands(axiom)
697
- }