@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.
- package/bin/skill.mjs +27 -0
- package/dist/chunk-2NCCVTEE.js +22342 -0
- package/dist/chunk-2NCCVTEE.js.map +1 -0
- package/dist/chunk-3E3GYSZR.js +7071 -0
- package/dist/chunk-3E3GYSZR.js.map +1 -0
- package/dist/chunk-F4EM72IH.js +86 -0
- package/dist/chunk-F4EM72IH.js.map +1 -0
- package/dist/chunk-FGP7KUQW.js +432 -0
- package/dist/chunk-FGP7KUQW.js.map +1 -0
- package/dist/chunk-H3D6VCME.js +55 -0
- package/dist/chunk-H3D6VCME.js.map +1 -0
- package/dist/chunk-HK3PEWFD.js +208 -0
- package/dist/chunk-HK3PEWFD.js.map +1 -0
- package/dist/chunk-KEV3QKXP.js +4495 -0
- package/dist/chunk-KEV3QKXP.js.map +1 -0
- package/dist/chunk-MG37YDAK.js +882 -0
- package/dist/chunk-MG37YDAK.js.map +1 -0
- package/dist/chunk-MLNDSBZ4.js +482 -0
- package/dist/chunk-MLNDSBZ4.js.map +1 -0
- package/dist/chunk-N2WIV2JV.js +22 -0
- package/dist/chunk-N2WIV2JV.js.map +1 -0
- package/dist/chunk-PWWRCN5W.js +2067 -0
- package/dist/chunk-PWWRCN5W.js.map +1 -0
- package/dist/chunk-SKHBM3XP.js +7746 -0
- package/dist/chunk-SKHBM3XP.js.map +1 -0
- package/dist/chunk-WFANXVQG.js +64 -0
- package/dist/chunk-WFANXVQG.js.map +1 -0
- package/dist/chunk-WYKL32C3.js +275 -0
- package/dist/chunk-WYKL32C3.js.map +1 -0
- package/dist/chunk-ZNF7XD2S.js +134 -0
- package/dist/chunk-ZNF7XD2S.js.map +1 -0
- package/dist/config-AUAIYDSI.js +20 -0
- package/dist/config-AUAIYDSI.js.map +1 -0
- package/dist/fileFromPath-XN7LXIBI.js +134 -0
- package/dist/fileFromPath-XN7LXIBI.js.map +1 -0
- package/dist/getMachineId-bsd-KW2E7VK3.js +42 -0
- package/dist/getMachineId-bsd-KW2E7VK3.js.map +1 -0
- package/dist/getMachineId-darwin-ROXJUJX5.js +42 -0
- package/dist/getMachineId-darwin-ROXJUJX5.js.map +1 -0
- package/dist/getMachineId-linux-KVZEHQSU.js +34 -0
- package/dist/getMachineId-linux-KVZEHQSU.js.map +1 -0
- package/dist/getMachineId-unsupported-PPRILPPA.js +25 -0
- package/dist/getMachineId-unsupported-PPRILPPA.js.map +1 -0
- package/dist/getMachineId-win-IIF36LEJ.js +44 -0
- package/dist/getMachineId-win-IIF36LEJ.js.map +1 -0
- package/dist/index.js +112703 -0
- package/dist/index.js.map +1 -0
- package/dist/lib-R6DEEJCP.js +7623 -0
- package/dist/lib-R6DEEJCP.js.map +1 -0
- package/dist/pipeline-IAVVAKTU.js +120 -0
- package/dist/pipeline-IAVVAKTU.js.map +1 -0
- package/dist/query-NTP5NVXN.js +25 -0
- package/dist/query-NTP5NVXN.js.map +1 -0
- package/dist/routing-BAEPFB7V.js +390 -0
- package/dist/routing-BAEPFB7V.js.map +1 -0
- package/dist/stripe-lookup-charge-EPRUMZDL.js +56 -0
- package/dist/stripe-lookup-charge-EPRUMZDL.js.map +1 -0
- package/dist/stripe-payment-history-SJPKA63N.js +67 -0
- package/dist/stripe-payment-history-SJPKA63N.js.map +1 -0
- package/dist/stripe-subscription-status-L4Z65GB3.js +58 -0
- package/dist/stripe-subscription-status-L4Z65GB3.js.map +1 -0
- package/dist/stripe-verify-refund-FZDKCIUQ.js +54 -0
- package/dist/stripe-verify-refund-FZDKCIUQ.js.map +1 -0
- package/dist/support-memory-WSG7SDKG.js +10 -0
- package/dist/support-memory-WSG7SDKG.js.map +1 -0
- package/package.json +10 -7
- package/.env.encrypted +0 -0
- package/CHANGELOG.md +0 -35
- package/data/tt-archive-dataset.json +0 -1
- package/data/validate-test-dataset.json +0 -97
- package/docs/CLI-AUTH.md +0 -504
- package/preload.ts +0 -18
- package/src/__tests__/init.test.ts +0 -74
- package/src/alignment-test.ts +0 -64
- package/src/check-apps.ts +0 -16
- package/src/commands/auth/decrypt.ts +0 -123
- package/src/commands/auth/encrypt.ts +0 -81
- package/src/commands/auth/index.ts +0 -50
- package/src/commands/auth/keygen.ts +0 -41
- package/src/commands/auth/status.ts +0 -164
- package/src/commands/axiom/forensic.ts +0 -868
- package/src/commands/axiom/index.ts +0 -697
- package/src/commands/build-dataset.ts +0 -311
- package/src/commands/db-status.ts +0 -47
- package/src/commands/deploys.ts +0 -219
- package/src/commands/eval-local/compare.ts +0 -171
- package/src/commands/eval-local/health.ts +0 -212
- package/src/commands/eval-local/index.ts +0 -76
- package/src/commands/eval-local/real-tools.ts +0 -416
- package/src/commands/eval-local/run.ts +0 -1168
- package/src/commands/eval-local/score-production.ts +0 -256
- package/src/commands/eval-local/seed.ts +0 -276
- package/src/commands/eval-pipeline/index.ts +0 -53
- package/src/commands/eval-pipeline/real-tools.ts +0 -492
- package/src/commands/eval-pipeline/run.ts +0 -1316
- package/src/commands/eval-pipeline/seed.ts +0 -395
- package/src/commands/eval-prompt.ts +0 -496
- package/src/commands/eval.test.ts +0 -253
- package/src/commands/eval.ts +0 -108
- package/src/commands/faq-classify.ts +0 -460
- package/src/commands/faq-cluster.ts +0 -135
- package/src/commands/faq-extract.ts +0 -249
- package/src/commands/faq-mine.ts +0 -432
- package/src/commands/faq-review.ts +0 -426
- package/src/commands/front/index.ts +0 -351
- package/src/commands/front/pull-conversations.ts +0 -275
- package/src/commands/front/tags.ts +0 -825
- package/src/commands/front-cache.ts +0 -1277
- package/src/commands/front-stats.ts +0 -75
- package/src/commands/health.test.ts +0 -82
- package/src/commands/health.ts +0 -362
- package/src/commands/init.test.ts +0 -89
- package/src/commands/init.ts +0 -106
- package/src/commands/inngest/client.ts +0 -294
- package/src/commands/inngest/events.ts +0 -296
- package/src/commands/inngest/investigate.ts +0 -382
- package/src/commands/inngest/runs.ts +0 -149
- package/src/commands/inngest/signal.ts +0 -143
- package/src/commands/kb-sync.ts +0 -498
- package/src/commands/memory/find.ts +0 -135
- package/src/commands/memory/get.ts +0 -87
- package/src/commands/memory/index.ts +0 -97
- package/src/commands/memory/stats.ts +0 -163
- package/src/commands/memory/store.ts +0 -49
- package/src/commands/memory/vote.ts +0 -159
- package/src/commands/pipeline.ts +0 -127
- package/src/commands/responses.ts +0 -856
- package/src/commands/tools.ts +0 -293
- package/src/commands/wizard.ts +0 -319
- package/src/index.ts +0 -172
- package/src/lib/crypto.ts +0 -56
- package/src/lib/env-loader.ts +0 -206
- package/src/lib/onepassword.ts +0 -137
- package/src/test-agent-local.ts +0 -115
- package/tsconfig.json +0 -11
- package/vitest.config.ts +0 -10
|
@@ -1,311 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Build eval dataset from real agent responses
|
|
3
|
-
*
|
|
4
|
-
* Pulls trigger messages and agent responses from Front + DB
|
|
5
|
-
* to create labeled datasets for eval improvement.
|
|
6
|
-
*
|
|
7
|
-
* Usage:
|
|
8
|
-
* skill dataset build --since 2025-01-01 --output /tmp/dataset.json
|
|
9
|
-
* skill dataset build --app total-typescript --labeled-only
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
import { writeFileSync } from 'fs'
|
|
13
|
-
import { createInstrumentedFrontClient } from '@skillrecordings/core/front/instrumented-client'
|
|
14
|
-
import {
|
|
15
|
-
ActionsTable,
|
|
16
|
-
AppsTable,
|
|
17
|
-
ConversationsTable,
|
|
18
|
-
and,
|
|
19
|
-
desc,
|
|
20
|
-
eq,
|
|
21
|
-
getDb,
|
|
22
|
-
gte,
|
|
23
|
-
} from '@skillrecordings/database'
|
|
24
|
-
import { type Message } from '@skillrecordings/front-sdk'
|
|
25
|
-
import type { Command } from 'commander'
|
|
26
|
-
|
|
27
|
-
interface EvalDataPoint {
|
|
28
|
-
id: string
|
|
29
|
-
app: string
|
|
30
|
-
conversationId: string
|
|
31
|
-
customerEmail: string
|
|
32
|
-
triggerMessage: {
|
|
33
|
-
subject: string
|
|
34
|
-
body: string
|
|
35
|
-
timestamp: number
|
|
36
|
-
}
|
|
37
|
-
agentResponse: {
|
|
38
|
-
text: string
|
|
39
|
-
category: string
|
|
40
|
-
timestamp: string
|
|
41
|
-
}
|
|
42
|
-
label?: 'good' | 'bad'
|
|
43
|
-
labeledBy?: string
|
|
44
|
-
conversationHistory?: Array<{
|
|
45
|
-
direction: 'in' | 'out'
|
|
46
|
-
body: string
|
|
47
|
-
timestamp: number
|
|
48
|
-
author?: string
|
|
49
|
-
}>
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
/**
|
|
53
|
-
* Build eval dataset from responses
|
|
54
|
-
*/
|
|
55
|
-
async function buildDataset(options: {
|
|
56
|
-
app?: string
|
|
57
|
-
since?: string
|
|
58
|
-
output?: string
|
|
59
|
-
labeledOnly?: boolean
|
|
60
|
-
limit?: number
|
|
61
|
-
includeHistory?: boolean
|
|
62
|
-
}): Promise<void> {
|
|
63
|
-
const db = getDb()
|
|
64
|
-
const limit = options.limit || 100
|
|
65
|
-
|
|
66
|
-
// Get Front token
|
|
67
|
-
const frontToken = process.env.FRONT_API_TOKEN
|
|
68
|
-
if (!frontToken) {
|
|
69
|
-
console.error('Error: FRONT_API_TOKEN environment variable required')
|
|
70
|
-
console.error('Set it or source the env file: source apps/front/.env.local')
|
|
71
|
-
process.exit(1)
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
const front = createInstrumentedFrontClient({ apiToken: frontToken })
|
|
75
|
-
|
|
76
|
-
try {
|
|
77
|
-
// Build query conditions
|
|
78
|
-
const conditions = [eq(ActionsTable.type, 'draft-response')]
|
|
79
|
-
|
|
80
|
-
if (options.app) {
|
|
81
|
-
const appResults = await db
|
|
82
|
-
.select()
|
|
83
|
-
.from(AppsTable)
|
|
84
|
-
.where(eq(AppsTable.slug, options.app))
|
|
85
|
-
.limit(1)
|
|
86
|
-
|
|
87
|
-
const foundApp = appResults[0]
|
|
88
|
-
if (!foundApp) {
|
|
89
|
-
console.error(`App not found: ${options.app}`)
|
|
90
|
-
process.exit(1)
|
|
91
|
-
}
|
|
92
|
-
conditions.push(eq(ActionsTable.app_id, foundApp.id))
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
if (options.since) {
|
|
96
|
-
const sinceDate = new Date(options.since)
|
|
97
|
-
conditions.push(gte(ActionsTable.created_at, sinceDate))
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
// Query actions
|
|
101
|
-
const results = await db
|
|
102
|
-
.select({
|
|
103
|
-
action: ActionsTable,
|
|
104
|
-
app: AppsTable,
|
|
105
|
-
conversation: ConversationsTable,
|
|
106
|
-
})
|
|
107
|
-
.from(ActionsTable)
|
|
108
|
-
.leftJoin(AppsTable, eq(ActionsTable.app_id, AppsTable.id))
|
|
109
|
-
.leftJoin(
|
|
110
|
-
ConversationsTable,
|
|
111
|
-
eq(
|
|
112
|
-
ActionsTable.conversation_id,
|
|
113
|
-
ConversationsTable.front_conversation_id
|
|
114
|
-
)
|
|
115
|
-
)
|
|
116
|
-
.where(and(...conditions))
|
|
117
|
-
.orderBy(desc(ActionsTable.created_at))
|
|
118
|
-
.limit(limit)
|
|
119
|
-
|
|
120
|
-
console.log(`Found ${results.length} responses, fetching context...`)
|
|
121
|
-
|
|
122
|
-
const dataset: EvalDataPoint[] = []
|
|
123
|
-
let processed = 0
|
|
124
|
-
|
|
125
|
-
for (const r of results) {
|
|
126
|
-
processed++
|
|
127
|
-
process.stdout.write(`\rProcessing ${processed}/${results.length}...`)
|
|
128
|
-
|
|
129
|
-
const params = r.action.parameters as {
|
|
130
|
-
response?: string
|
|
131
|
-
category?: string
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
// Skip if no response
|
|
135
|
-
if (!params.response) continue
|
|
136
|
-
|
|
137
|
-
// Determine label
|
|
138
|
-
let label: 'good' | 'bad' | undefined
|
|
139
|
-
let labeledBy: string | undefined
|
|
140
|
-
|
|
141
|
-
if (r.action.approved_by) {
|
|
142
|
-
label = 'good'
|
|
143
|
-
labeledBy = r.action.approved_by
|
|
144
|
-
} else if (r.action.rejected_by) {
|
|
145
|
-
label = 'bad'
|
|
146
|
-
labeledBy = r.action.rejected_by
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
// Skip unlabeled if labeledOnly
|
|
150
|
-
if (options.labeledOnly && !label) continue
|
|
151
|
-
|
|
152
|
-
// Fetch conversation from Front
|
|
153
|
-
if (!r.action.conversation_id) continue
|
|
154
|
-
|
|
155
|
-
let triggerMessage: EvalDataPoint['triggerMessage'] | undefined
|
|
156
|
-
let conversationHistory: EvalDataPoint['conversationHistory'] | undefined
|
|
157
|
-
|
|
158
|
-
try {
|
|
159
|
-
const messageList = (await front.conversations.listMessages(
|
|
160
|
-
r.action.conversation_id
|
|
161
|
-
)) as { _results?: Message[] }
|
|
162
|
-
const messages = messageList._results ?? []
|
|
163
|
-
|
|
164
|
-
// Find trigger message (most recent inbound before draft)
|
|
165
|
-
const draftTime = r.action.created_at?.getTime() ?? Date.now()
|
|
166
|
-
const inboundBefore = messages
|
|
167
|
-
.filter((m) => m.is_inbound && m.created_at * 1000 < draftTime)
|
|
168
|
-
.sort((a, b) => b.created_at - a.created_at)
|
|
169
|
-
|
|
170
|
-
const trigger = inboundBefore[0]
|
|
171
|
-
if (trigger) {
|
|
172
|
-
triggerMessage = {
|
|
173
|
-
subject: trigger.subject ?? '',
|
|
174
|
-
body:
|
|
175
|
-
trigger.text ??
|
|
176
|
-
trigger.body
|
|
177
|
-
?.replace(/<[^>]*>/g, ' ')
|
|
178
|
-
.replace(/\s+/g, ' ')
|
|
179
|
-
.trim() ??
|
|
180
|
-
'',
|
|
181
|
-
timestamp: trigger.created_at,
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
// Include history if requested
|
|
186
|
-
if (options.includeHistory) {
|
|
187
|
-
conversationHistory = messages.map((m) => ({
|
|
188
|
-
direction: m.is_inbound ? ('in' as const) : ('out' as const),
|
|
189
|
-
body:
|
|
190
|
-
m.text ??
|
|
191
|
-
m.body
|
|
192
|
-
?.replace(/<[^>]*>/g, ' ')
|
|
193
|
-
.replace(/\s+/g, ' ')
|
|
194
|
-
.trim() ??
|
|
195
|
-
'',
|
|
196
|
-
timestamp: m.created_at,
|
|
197
|
-
author: m.author?.email,
|
|
198
|
-
}))
|
|
199
|
-
}
|
|
200
|
-
} catch (err) {
|
|
201
|
-
// Skip if can't fetch context
|
|
202
|
-
continue
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
// Skip if no trigger message
|
|
206
|
-
if (!triggerMessage) continue
|
|
207
|
-
|
|
208
|
-
dataset.push({
|
|
209
|
-
id: r.action.id,
|
|
210
|
-
app: r.app?.slug ?? 'unknown',
|
|
211
|
-
conversationId: r.action.conversation_id,
|
|
212
|
-
customerEmail: r.conversation?.customer_email ?? 'unknown',
|
|
213
|
-
triggerMessage,
|
|
214
|
-
agentResponse: {
|
|
215
|
-
text: params.response,
|
|
216
|
-
category: params.category ?? 'unknown',
|
|
217
|
-
timestamp: r.action.created_at?.toISOString() ?? '',
|
|
218
|
-
},
|
|
219
|
-
label,
|
|
220
|
-
labeledBy,
|
|
221
|
-
conversationHistory,
|
|
222
|
-
})
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
console.log(`\n\nBuilt dataset with ${dataset.length} eval points`)
|
|
226
|
-
console.log(` Labeled: ${dataset.filter((d) => d.label).length}`)
|
|
227
|
-
console.log(` Good: ${dataset.filter((d) => d.label === 'good').length}`)
|
|
228
|
-
console.log(` Bad: ${dataset.filter((d) => d.label === 'bad').length}`)
|
|
229
|
-
console.log(` Unlabeled: ${dataset.filter((d) => !d.label).length}`)
|
|
230
|
-
|
|
231
|
-
const outputJson = JSON.stringify(dataset, null, 2)
|
|
232
|
-
|
|
233
|
-
if (options.output) {
|
|
234
|
-
writeFileSync(options.output, outputJson, 'utf-8')
|
|
235
|
-
console.log(`\nSaved to ${options.output}`)
|
|
236
|
-
} else {
|
|
237
|
-
console.log('\n' + outputJson)
|
|
238
|
-
}
|
|
239
|
-
} catch (error) {
|
|
240
|
-
console.error(
|
|
241
|
-
'\nError:',
|
|
242
|
-
error instanceof Error ? error.message : 'Unknown error'
|
|
243
|
-
)
|
|
244
|
-
process.exit(1)
|
|
245
|
-
}
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
/**
|
|
249
|
-
* Convert dataset to evalite format
|
|
250
|
-
*/
|
|
251
|
-
async function toEvalite(options: {
|
|
252
|
-
input: string
|
|
253
|
-
output?: string
|
|
254
|
-
}): Promise<void> {
|
|
255
|
-
const { readFileSync } = await import('fs')
|
|
256
|
-
|
|
257
|
-
const data = JSON.parse(
|
|
258
|
-
readFileSync(options.input, 'utf-8')
|
|
259
|
-
) as EvalDataPoint[]
|
|
260
|
-
|
|
261
|
-
const evaliteData = data.map((d) => ({
|
|
262
|
-
input: d.triggerMessage.body,
|
|
263
|
-
output: d.agentResponse.text,
|
|
264
|
-
expected: d.label === 'good' ? d.agentResponse.text : '',
|
|
265
|
-
metadata: {
|
|
266
|
-
id: d.id,
|
|
267
|
-
app: d.app,
|
|
268
|
-
category: d.agentResponse.category,
|
|
269
|
-
label: d.label,
|
|
270
|
-
subject: d.triggerMessage.subject,
|
|
271
|
-
},
|
|
272
|
-
}))
|
|
273
|
-
|
|
274
|
-
const outputJson = JSON.stringify(evaliteData, null, 2)
|
|
275
|
-
|
|
276
|
-
if (options.output) {
|
|
277
|
-
writeFileSync(options.output, outputJson, 'utf-8')
|
|
278
|
-
console.log(
|
|
279
|
-
`Converted ${evaliteData.length} points to evalite format: ${options.output}`
|
|
280
|
-
)
|
|
281
|
-
} else {
|
|
282
|
-
console.log(outputJson)
|
|
283
|
-
}
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
/**
|
|
287
|
-
* Register dataset commands
|
|
288
|
-
*/
|
|
289
|
-
export function registerDatasetCommands(program: Command): void {
|
|
290
|
-
const dataset = program
|
|
291
|
-
.command('dataset')
|
|
292
|
-
.description('Build and manage eval datasets')
|
|
293
|
-
|
|
294
|
-
dataset
|
|
295
|
-
.command('build')
|
|
296
|
-
.description('Build eval dataset from agent responses')
|
|
297
|
-
.option('-a, --app <slug>', 'Filter by app slug')
|
|
298
|
-
.option('-s, --since <date>', 'Filter responses since date (YYYY-MM-DD)')
|
|
299
|
-
.option('-o, --output <file>', 'Output file path')
|
|
300
|
-
.option('-l, --limit <n>', 'Max responses to process', parseInt)
|
|
301
|
-
.option('--labeled-only', 'Only include labeled responses')
|
|
302
|
-
.option('--include-history', 'Include full conversation history')
|
|
303
|
-
.action(buildDataset)
|
|
304
|
-
|
|
305
|
-
dataset
|
|
306
|
-
.command('to-evalite')
|
|
307
|
-
.description('Convert dataset to evalite format')
|
|
308
|
-
.requiredOption('-i, --input <file>', 'Input dataset JSON file')
|
|
309
|
-
.option('-o, --output <file>', 'Output file path')
|
|
310
|
-
.action(toEvalite)
|
|
311
|
-
}
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
import { program } from 'commander'
|
|
2
|
-
import {
|
|
3
|
-
ConversationsTable,
|
|
4
|
-
database,
|
|
5
|
-
desc,
|
|
6
|
-
sql,
|
|
7
|
-
} from '@skillrecordings/database'
|
|
8
|
-
|
|
9
|
-
export const registerDbStatusCommand = (prog: typeof program) => {
|
|
10
|
-
prog
|
|
11
|
-
.command('db-status')
|
|
12
|
-
.description('Check database status and conversation counts')
|
|
13
|
-
.action(async () => {
|
|
14
|
-
try {
|
|
15
|
-
// Count by status
|
|
16
|
-
const statusCounts = await database
|
|
17
|
-
.select({
|
|
18
|
-
status: ConversationsTable.status,
|
|
19
|
-
count: sql<number>`COUNT(*)`.as('count'),
|
|
20
|
-
})
|
|
21
|
-
.from(ConversationsTable)
|
|
22
|
-
.groupBy(ConversationsTable.status)
|
|
23
|
-
|
|
24
|
-
console.log('Conversation counts by status:')
|
|
25
|
-
for (const row of statusCounts) {
|
|
26
|
-
console.log(` ${row.status}: ${row.count}`)
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
// Get recent conversations
|
|
30
|
-
const recent = await database
|
|
31
|
-
.select()
|
|
32
|
-
.from(ConversationsTable)
|
|
33
|
-
.orderBy(desc(ConversationsTable.updated_at))
|
|
34
|
-
.limit(5)
|
|
35
|
-
|
|
36
|
-
console.log('\nRecent conversations:')
|
|
37
|
-
for (const c of recent) {
|
|
38
|
-
console.log(` ${c.front_conversation_id}: ${c.status} (${c.updated_at})`)
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
process.exit(0)
|
|
42
|
-
} catch (error) {
|
|
43
|
-
console.error('Error:', error)
|
|
44
|
-
process.exit(1)
|
|
45
|
-
}
|
|
46
|
-
})
|
|
47
|
-
}
|
package/src/commands/deploys.ts
DELETED
|
@@ -1,219 +0,0 @@
|
|
|
1
|
-
import { execSync } from 'child_process'
|
|
2
|
-
import { Command } from 'commander'
|
|
3
|
-
|
|
4
|
-
const VERCEL_SCOPE = 'skillrecordings'
|
|
5
|
-
|
|
6
|
-
// Map of app names to their Vercel project names
|
|
7
|
-
const APPS: Record<string, { vercel: string; description: string }> = {
|
|
8
|
-
front: {
|
|
9
|
-
vercel: 'skill-support-agent-front',
|
|
10
|
-
description: 'Front webhook handler (main support pipeline)',
|
|
11
|
-
},
|
|
12
|
-
slack: {
|
|
13
|
-
vercel: 'skill-support-agent-slack',
|
|
14
|
-
description: 'Slack interactions (approvals, notifications)',
|
|
15
|
-
},
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
function stripAnsi(str: string): string {
|
|
19
|
-
// eslint-disable-next-line no-control-regex
|
|
20
|
-
return str.replace(/\x1B\[[0-9;]*[a-zA-Z]/g, '').replace(/\x1B\].*?\x07/g, '')
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
function runVercel(args: string): string {
|
|
24
|
-
try {
|
|
25
|
-
// Capture both stdout and stderr (Vercel CLI writes table to stderr)
|
|
26
|
-
const raw = execSync(`vercel ${args} --scope ${VERCEL_SCOPE} --yes 2>&1`, {
|
|
27
|
-
encoding: 'utf-8',
|
|
28
|
-
timeout: 30000,
|
|
29
|
-
stdio: ['pipe', 'pipe', 'pipe'],
|
|
30
|
-
env: { ...process.env, FORCE_COLOR: '0', NO_COLOR: '1' },
|
|
31
|
-
}).trim()
|
|
32
|
-
return stripAnsi(raw)
|
|
33
|
-
} catch (err: any) {
|
|
34
|
-
const out = err.stdout?.trim() || err.stderr?.trim() || err.message
|
|
35
|
-
return stripAnsi(out)
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
function listApps() {
|
|
40
|
-
console.log('\nš¦ Support Platform Apps\n')
|
|
41
|
-
for (const [name, app] of Object.entries(APPS)) {
|
|
42
|
-
console.log(
|
|
43
|
-
` ${name.padEnd(10)} ${app.vercel.padEnd(35)} ${app.description}`
|
|
44
|
-
)
|
|
45
|
-
}
|
|
46
|
-
console.log()
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
function resolveApp(
|
|
50
|
-
nameOrAll: string | undefined
|
|
51
|
-
): [string, { vercel: string; description: string }][] {
|
|
52
|
-
if (!nameOrAll || nameOrAll === 'all') {
|
|
53
|
-
return Object.entries(APPS)
|
|
54
|
-
}
|
|
55
|
-
const app = APPS[nameOrAll]
|
|
56
|
-
if (!app) {
|
|
57
|
-
console.error(`ā Unknown app: ${nameOrAll}`)
|
|
58
|
-
console.error(` Available: ${Object.keys(APPS).join(', ')}, all`)
|
|
59
|
-
process.exit(1)
|
|
60
|
-
}
|
|
61
|
-
return [[nameOrAll, app]]
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
async function deploysStatus(
|
|
65
|
-
appName: string | undefined,
|
|
66
|
-
options: { limit?: string; json?: boolean }
|
|
67
|
-
) {
|
|
68
|
-
const apps = resolveApp(appName)
|
|
69
|
-
const limit = parseInt(options.limit || '5')
|
|
70
|
-
|
|
71
|
-
for (const [name, app] of apps) {
|
|
72
|
-
console.log(`\nš ${name} (${app.vercel})`)
|
|
73
|
-
console.log(` ${app.description}\n`)
|
|
74
|
-
|
|
75
|
-
const output = runVercel(`ls ${app.vercel}`)
|
|
76
|
-
const lines = output.split('\n')
|
|
77
|
-
// Deploy lines contain https:// URLs
|
|
78
|
-
const deployLines = lines.filter((l) => l.includes('https://'))
|
|
79
|
-
|
|
80
|
-
if (deployLines.length === 0) {
|
|
81
|
-
console.log(' No recent deployments found')
|
|
82
|
-
} else {
|
|
83
|
-
// Filter production-only for cleaner output, show all if few deploys
|
|
84
|
-
const prodLines = deployLines.filter((l) => l.includes('Production'))
|
|
85
|
-
const showLines = (
|
|
86
|
-
prodLines.length >= limit ? prodLines : deployLines
|
|
87
|
-
).slice(0, limit)
|
|
88
|
-
|
|
89
|
-
for (const line of showLines) {
|
|
90
|
-
const trimmed = line.trim()
|
|
91
|
-
// Extract known tokens from the line
|
|
92
|
-
const hasReady = trimmed.includes('Ready')
|
|
93
|
-
const hasError = trimmed.includes('Error')
|
|
94
|
-
const hasCanceled = trimmed.includes('Canceled')
|
|
95
|
-
const isProd = trimmed.includes('Production')
|
|
96
|
-
const isPreview = trimmed.includes('Preview')
|
|
97
|
-
const prefix = hasError
|
|
98
|
-
? 'ā'
|
|
99
|
-
: hasCanceled
|
|
100
|
-
? 'āŖ'
|
|
101
|
-
: isProd
|
|
102
|
-
? 'š¢'
|
|
103
|
-
: 'šµ'
|
|
104
|
-
|
|
105
|
-
// Pull age (first token) and environment
|
|
106
|
-
const age = trimmed.split(/\s+/)[0] || ''
|
|
107
|
-
const status = hasError
|
|
108
|
-
? 'Error'
|
|
109
|
-
: hasCanceled
|
|
110
|
-
? 'Canceled'
|
|
111
|
-
: hasReady
|
|
112
|
-
? 'Ready'
|
|
113
|
-
: 'Unknown'
|
|
114
|
-
const env = isProd ? 'Production' : isPreview ? 'Preview' : ''
|
|
115
|
-
|
|
116
|
-
// Pull duration (pattern like "30s", "17s")
|
|
117
|
-
const durMatch = trimmed.match(/(\d+s)/)
|
|
118
|
-
const duration = durMatch ? durMatch[1] : ''
|
|
119
|
-
|
|
120
|
-
console.log(
|
|
121
|
-
` ${prefix} ${age.padEnd(6)} ${status.padEnd(10)} ${env.padEnd(12)} ${duration}`
|
|
122
|
-
)
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
console.log()
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
async function deploysLogs(appName: string, options: { lines?: string }) {
|
|
130
|
-
const apps = resolveApp(appName)
|
|
131
|
-
if (apps.length > 1) {
|
|
132
|
-
console.error('ā Specify a single app for logs (front or slack)')
|
|
133
|
-
process.exit(1)
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
const [name, app] = apps[0]!
|
|
137
|
-
console.log(`\nš Recent logs for ${name} (${app.vercel})\n`)
|
|
138
|
-
|
|
139
|
-
// Get latest production deployment URL
|
|
140
|
-
const lsOutput = runVercel(`ls ${app.vercel} --limit 5`)
|
|
141
|
-
const prodLine = lsOutput
|
|
142
|
-
.split('\n')
|
|
143
|
-
.find((l) => l.includes('Production') && l.includes('Ready'))
|
|
144
|
-
const urlMatch = prodLine?.match(/https:\/\/\S+/)
|
|
145
|
-
|
|
146
|
-
if (!urlMatch) {
|
|
147
|
-
console.error(' Could not find latest production deployment')
|
|
148
|
-
process.exit(1)
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
const url = urlMatch[0]
|
|
152
|
-
console.log(` Deployment: ${url}\n`)
|
|
153
|
-
|
|
154
|
-
try {
|
|
155
|
-
const logsOutput = execSync(
|
|
156
|
-
`vercel logs ${url} --scope ${VERCEL_SCOPE} --output short 2>&1 | tail -${options.lines || '30'}`,
|
|
157
|
-
{ encoding: 'utf-8', timeout: 30000 }
|
|
158
|
-
)
|
|
159
|
-
console.log(logsOutput)
|
|
160
|
-
} catch (err: any) {
|
|
161
|
-
console.log(err.stdout || err.message)
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
async function deploysInspect(appName: string) {
|
|
166
|
-
const apps = resolveApp(appName)
|
|
167
|
-
if (apps.length > 1) {
|
|
168
|
-
console.error('ā Specify a single app for inspect (front or slack)')
|
|
169
|
-
process.exit(1)
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
const [name, app] = apps[0]!
|
|
173
|
-
|
|
174
|
-
// Get latest production deployment URL
|
|
175
|
-
const lsOutput = runVercel(`ls ${app.vercel} --limit 3`)
|
|
176
|
-
const prodLine = lsOutput
|
|
177
|
-
.split('\n')
|
|
178
|
-
.find((l) => l.includes('Production') && l.includes('Ready'))
|
|
179
|
-
const urlMatch = prodLine?.match(/https:\/\/\S+/)
|
|
180
|
-
|
|
181
|
-
if (!urlMatch) {
|
|
182
|
-
console.error(' Could not find latest production deployment')
|
|
183
|
-
process.exit(1)
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
console.log(`\nš Inspecting ${name} latest production deploy\n`)
|
|
187
|
-
const output = runVercel(`inspect ${urlMatch[0]}`)
|
|
188
|
-
console.log(output)
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
export function registerDeployCommands(program: Command) {
|
|
192
|
-
const deploys = program
|
|
193
|
-
.command('deploys')
|
|
194
|
-
.description('Check Vercel deployment status for support platform apps')
|
|
195
|
-
|
|
196
|
-
deploys
|
|
197
|
-
.command('status')
|
|
198
|
-
.description('Show recent deployments for one or all apps')
|
|
199
|
-
.argument('[app]', 'App name (front, slack, all)', 'all')
|
|
200
|
-
.option('-n, --limit <number>', 'Number of deploys to show', '5')
|
|
201
|
-
.option('--json', 'JSON output')
|
|
202
|
-
.action(deploysStatus)
|
|
203
|
-
|
|
204
|
-
deploys
|
|
205
|
-
.command('logs')
|
|
206
|
-
.description('Show recent logs for an app')
|
|
207
|
-
.argument('<app>', 'App name (front, slack)')
|
|
208
|
-
.option('-n, --lines <number>', 'Number of log lines', '30')
|
|
209
|
-
.action(deploysLogs)
|
|
210
|
-
|
|
211
|
-
deploys
|
|
212
|
-
.command('inspect')
|
|
213
|
-
.description('Inspect latest production deployment')
|
|
214
|
-
.argument('<app>', 'App name (front, slack)')
|
|
215
|
-
.action(deploysInspect)
|
|
216
|
-
|
|
217
|
-
// Default: `deploys` with no subcommand = status all
|
|
218
|
-
deploys.action(() => deploysStatus('all', { limit: '3' }))
|
|
219
|
-
}
|