@skillrecordings/cli 0.1.0 ā 0.2.1
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 +21 -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
package/src/commands/tools.ts
DELETED
|
@@ -1,293 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* CLI commands for testing agent tools against live app integrations.
|
|
3
|
-
*
|
|
4
|
-
* Usage:
|
|
5
|
-
* skill tools search <app-slug> <query>
|
|
6
|
-
* skill tools lookup <app-slug> <email>
|
|
7
|
-
* skill tools list
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
import { AppsTable, eq, getDb } from '@skillrecordings/database'
|
|
11
|
-
import { IntegrationClient } from '@skillrecordings/sdk/client'
|
|
12
|
-
import type { Command } from 'commander'
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Get app config from database by slug
|
|
16
|
-
*/
|
|
17
|
-
async function getAppConfig(slug: string) {
|
|
18
|
-
const db = getDb()
|
|
19
|
-
const results = await db
|
|
20
|
-
.select()
|
|
21
|
-
.from(AppsTable)
|
|
22
|
-
.where(eq(AppsTable.slug, slug))
|
|
23
|
-
.limit(1)
|
|
24
|
-
|
|
25
|
-
const app = results[0]
|
|
26
|
-
if (!app) {
|
|
27
|
-
return null
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
// baseUrl should be the complete endpoint URL
|
|
31
|
-
// SDK client will POST directly to this URL with action in body
|
|
32
|
-
return {
|
|
33
|
-
slug: app.slug,
|
|
34
|
-
name: app.name,
|
|
35
|
-
baseUrl: app.integration_base_url,
|
|
36
|
-
webhookSecret: app.webhook_secret,
|
|
37
|
-
stripeAccountId: app.stripe_account_id,
|
|
38
|
-
instructorTeammateId: app.instructor_teammate_id,
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* List all registered apps
|
|
44
|
-
*/
|
|
45
|
-
async function listApps(options: { json?: boolean }) {
|
|
46
|
-
const db = getDb()
|
|
47
|
-
const apps = await db
|
|
48
|
-
.select({
|
|
49
|
-
slug: AppsTable.slug,
|
|
50
|
-
name: AppsTable.name,
|
|
51
|
-
baseUrl: AppsTable.integration_base_url,
|
|
52
|
-
})
|
|
53
|
-
.from(AppsTable)
|
|
54
|
-
|
|
55
|
-
if (options.json) {
|
|
56
|
-
console.log(JSON.stringify(apps, null, 2))
|
|
57
|
-
process.exit(0)
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
console.log('\nRegistered Apps:')
|
|
61
|
-
console.log('================')
|
|
62
|
-
for (const app of apps) {
|
|
63
|
-
console.log(` ${app.slug} - ${app.name}`)
|
|
64
|
-
console.log(` URL: ${app.baseUrl}`)
|
|
65
|
-
console.log()
|
|
66
|
-
}
|
|
67
|
-
process.exit(0)
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
/**
|
|
71
|
-
* Test content search against an app
|
|
72
|
-
*/
|
|
73
|
-
async function searchContent(
|
|
74
|
-
slug: string,
|
|
75
|
-
query: string,
|
|
76
|
-
options: { types?: string; limit?: string; json?: boolean }
|
|
77
|
-
) {
|
|
78
|
-
const app = await getAppConfig(slug)
|
|
79
|
-
if (!app) {
|
|
80
|
-
console.error(`App not found: ${slug}`)
|
|
81
|
-
console.error('Use "skill tools list" to see registered apps')
|
|
82
|
-
process.exit(1)
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
if (!app.baseUrl || !app.webhookSecret) {
|
|
86
|
-
console.error(`App ${slug} is missing baseUrl or webhookSecret`)
|
|
87
|
-
process.exit(1)
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
const client = new IntegrationClient({
|
|
91
|
-
baseUrl: app.baseUrl,
|
|
92
|
-
webhookSecret: app.webhookSecret,
|
|
93
|
-
})
|
|
94
|
-
|
|
95
|
-
console.log(`\nSearching ${app.name} for: "${query}"`)
|
|
96
|
-
console.log(`Endpoint: ${app.baseUrl}`)
|
|
97
|
-
console.log()
|
|
98
|
-
|
|
99
|
-
try {
|
|
100
|
-
const result = await client.searchContent({
|
|
101
|
-
query,
|
|
102
|
-
types: options.types?.split(',') as any,
|
|
103
|
-
limit: options.limit ? parseInt(options.limit, 10) : 5,
|
|
104
|
-
})
|
|
105
|
-
|
|
106
|
-
if (options.json) {
|
|
107
|
-
console.log(JSON.stringify(result, null, 2))
|
|
108
|
-
process.exit(0)
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
if (!result.results || result.results.length === 0) {
|
|
112
|
-
console.log('No results found.')
|
|
113
|
-
process.exit(0)
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
console.log(`Found ${result.results.length} results:\n`)
|
|
117
|
-
for (const item of result.results) {
|
|
118
|
-
console.log(` [${item.type}] ${item.title}`)
|
|
119
|
-
if (item.url) {
|
|
120
|
-
console.log(` URL: ${item.url}`)
|
|
121
|
-
}
|
|
122
|
-
if (item.description) {
|
|
123
|
-
console.log(
|
|
124
|
-
` ${item.description.slice(0, 200)}${item.description.length > 200 ? '...' : ''}`
|
|
125
|
-
)
|
|
126
|
-
}
|
|
127
|
-
console.log()
|
|
128
|
-
}
|
|
129
|
-
process.exit(0)
|
|
130
|
-
} catch (error) {
|
|
131
|
-
console.error(
|
|
132
|
-
'Search failed:',
|
|
133
|
-
error instanceof Error ? error.message : error
|
|
134
|
-
)
|
|
135
|
-
process.exit(1)
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
/**
|
|
140
|
-
* Test user lookup against an app
|
|
141
|
-
*/
|
|
142
|
-
async function lookupUser(
|
|
143
|
-
slug: string,
|
|
144
|
-
email: string,
|
|
145
|
-
options: { json?: boolean }
|
|
146
|
-
) {
|
|
147
|
-
const app = await getAppConfig(slug)
|
|
148
|
-
if (!app) {
|
|
149
|
-
console.error(`App not found: ${slug}`)
|
|
150
|
-
process.exit(1)
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
if (!app.baseUrl || !app.webhookSecret) {
|
|
154
|
-
console.error(`App ${slug} is missing baseUrl or webhookSecret`)
|
|
155
|
-
process.exit(1)
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
const client = new IntegrationClient({
|
|
159
|
-
baseUrl: app.baseUrl,
|
|
160
|
-
webhookSecret: app.webhookSecret,
|
|
161
|
-
})
|
|
162
|
-
|
|
163
|
-
console.log(`\nLooking up user: ${email}`)
|
|
164
|
-
console.log(`Endpoint: ${app.baseUrl}`)
|
|
165
|
-
console.log()
|
|
166
|
-
|
|
167
|
-
try {
|
|
168
|
-
const user = await client.lookupUser(email)
|
|
169
|
-
|
|
170
|
-
if (options.json) {
|
|
171
|
-
console.log(JSON.stringify(user, null, 2))
|
|
172
|
-
process.exit(0)
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
if (!user) {
|
|
176
|
-
console.log('User not found.')
|
|
177
|
-
process.exit(0)
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
console.log('User found:')
|
|
181
|
-
console.log(` ID: ${user.id}`)
|
|
182
|
-
console.log(` Email: ${user.email}`)
|
|
183
|
-
if (user.name) console.log(` Name: ${user.name}`)
|
|
184
|
-
console.log()
|
|
185
|
-
process.exit(0)
|
|
186
|
-
} catch (error) {
|
|
187
|
-
console.error(
|
|
188
|
-
'Lookup failed:',
|
|
189
|
-
error instanceof Error ? error.message : error
|
|
190
|
-
)
|
|
191
|
-
process.exit(1)
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
/**
|
|
196
|
-
* Test purchases lookup against an app
|
|
197
|
-
*/
|
|
198
|
-
async function getPurchases(
|
|
199
|
-
slug: string,
|
|
200
|
-
userId: string,
|
|
201
|
-
options: { json?: boolean }
|
|
202
|
-
) {
|
|
203
|
-
const app = await getAppConfig(slug)
|
|
204
|
-
if (!app) {
|
|
205
|
-
console.error(`App not found: ${slug}`)
|
|
206
|
-
process.exit(1)
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
if (!app.baseUrl || !app.webhookSecret) {
|
|
210
|
-
console.error(`App ${slug} is missing baseUrl or webhookSecret`)
|
|
211
|
-
process.exit(1)
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
const client = new IntegrationClient({
|
|
215
|
-
baseUrl: app.baseUrl,
|
|
216
|
-
webhookSecret: app.webhookSecret,
|
|
217
|
-
})
|
|
218
|
-
|
|
219
|
-
console.log(`\nFetching purchases for user: ${userId}`)
|
|
220
|
-
console.log(`Endpoint: ${app.baseUrl}`)
|
|
221
|
-
console.log()
|
|
222
|
-
|
|
223
|
-
try {
|
|
224
|
-
const purchases = await client.getPurchases(userId)
|
|
225
|
-
|
|
226
|
-
if (options.json) {
|
|
227
|
-
console.log(JSON.stringify(purchases, null, 2))
|
|
228
|
-
process.exit(0)
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
if (!purchases || purchases.length === 0) {
|
|
232
|
-
console.log('No purchases found.')
|
|
233
|
-
process.exit(0)
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
console.log(`Found ${purchases.length} purchases:\n`)
|
|
237
|
-
for (const p of purchases) {
|
|
238
|
-
console.log(` [${p.id}] ${p.productName}`)
|
|
239
|
-
console.log(` Status: ${p.status}`)
|
|
240
|
-
console.log(` Amount: ${p.amount} ${p.currency}`)
|
|
241
|
-
console.log(` Date: ${new Date(p.purchasedAt).toLocaleDateString()}`)
|
|
242
|
-
console.log()
|
|
243
|
-
}
|
|
244
|
-
process.exit(0)
|
|
245
|
-
} catch (error) {
|
|
246
|
-
console.error(
|
|
247
|
-
'Fetch failed:',
|
|
248
|
-
error instanceof Error ? error.message : error
|
|
249
|
-
)
|
|
250
|
-
process.exit(1)
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
/**
|
|
255
|
-
* Register tools commands
|
|
256
|
-
*/
|
|
257
|
-
export function registerToolsCommands(program: Command) {
|
|
258
|
-
const tools = program
|
|
259
|
-
.command('tools')
|
|
260
|
-
.description('Test agent tools against live app integrations')
|
|
261
|
-
|
|
262
|
-
tools
|
|
263
|
-
.command('list')
|
|
264
|
-
.description('List all registered apps')
|
|
265
|
-
.option('--json', 'Output as JSON')
|
|
266
|
-
.action(listApps)
|
|
267
|
-
|
|
268
|
-
tools
|
|
269
|
-
.command('search')
|
|
270
|
-
.description('Test content search against an app')
|
|
271
|
-
.argument('<app-slug>', 'App slug (e.g., total-typescript)')
|
|
272
|
-
.argument('<query>', 'Search query')
|
|
273
|
-
.option('-t, --types <types>', 'Filter by content types (comma-separated)')
|
|
274
|
-
.option('-l, --limit <limit>', 'Max results (default: 5)')
|
|
275
|
-
.option('--json', 'Output as JSON')
|
|
276
|
-
.action(searchContent)
|
|
277
|
-
|
|
278
|
-
tools
|
|
279
|
-
.command('lookup')
|
|
280
|
-
.description('Test user lookup against an app')
|
|
281
|
-
.argument('<app-slug>', 'App slug')
|
|
282
|
-
.argument('<email>', 'User email to look up')
|
|
283
|
-
.option('--json', 'Output as JSON')
|
|
284
|
-
.action(lookupUser)
|
|
285
|
-
|
|
286
|
-
tools
|
|
287
|
-
.command('purchases')
|
|
288
|
-
.description('Test purchases lookup against an app')
|
|
289
|
-
.argument('<app-slug>', 'App slug')
|
|
290
|
-
.argument('<user-id>', 'User ID')
|
|
291
|
-
.option('--json', 'Output as JSON')
|
|
292
|
-
.action(getPurchases)
|
|
293
|
-
}
|
package/src/commands/wizard.ts
DELETED
|
@@ -1,319 +0,0 @@
|
|
|
1
|
-
import { randomBytes, randomUUID } from 'node:crypto'
|
|
2
|
-
import { checkbox, confirm, input, select } from '@inquirer/prompts'
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* All available capabilities an app can implement
|
|
6
|
-
*/
|
|
7
|
-
const ALL_CAPABILITIES = [
|
|
8
|
-
{
|
|
9
|
-
value: 'lookupUser',
|
|
10
|
-
name: 'lookupUser - Find user by email',
|
|
11
|
-
checked: true,
|
|
12
|
-
},
|
|
13
|
-
{
|
|
14
|
-
value: 'getPurchases',
|
|
15
|
-
name: 'getPurchases - Fetch purchase history',
|
|
16
|
-
checked: true,
|
|
17
|
-
},
|
|
18
|
-
{
|
|
19
|
-
value: 'revokeAccess',
|
|
20
|
-
name: 'revokeAccess - Revoke access after refund',
|
|
21
|
-
checked: true,
|
|
22
|
-
},
|
|
23
|
-
{
|
|
24
|
-
value: 'transferPurchase',
|
|
25
|
-
name: 'transferPurchase - Transfer license to new owner',
|
|
26
|
-
checked: true,
|
|
27
|
-
},
|
|
28
|
-
{
|
|
29
|
-
value: 'generateMagicLink',
|
|
30
|
-
name: 'generateMagicLink - Send login links',
|
|
31
|
-
checked: true,
|
|
32
|
-
},
|
|
33
|
-
{
|
|
34
|
-
value: 'getSubscriptions',
|
|
35
|
-
name: 'getSubscriptions - Fetch subscriptions (optional)',
|
|
36
|
-
checked: false,
|
|
37
|
-
},
|
|
38
|
-
{
|
|
39
|
-
value: 'updateEmail',
|
|
40
|
-
name: 'updateEmail - Change user email (optional)',
|
|
41
|
-
checked: false,
|
|
42
|
-
},
|
|
43
|
-
{
|
|
44
|
-
value: 'updateName',
|
|
45
|
-
name: 'updateName - Change user name (optional)',
|
|
46
|
-
checked: false,
|
|
47
|
-
},
|
|
48
|
-
{
|
|
49
|
-
value: 'getClaimedSeats',
|
|
50
|
-
name: 'getClaimedSeats - Team seat management (optional)',
|
|
51
|
-
checked: false,
|
|
52
|
-
},
|
|
53
|
-
] as const
|
|
54
|
-
|
|
55
|
-
export interface WizardOptions {
|
|
56
|
-
json?: boolean
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
export interface WizardResult {
|
|
60
|
-
success: boolean
|
|
61
|
-
app?: {
|
|
62
|
-
id: string
|
|
63
|
-
slug: string
|
|
64
|
-
name: string
|
|
65
|
-
frontInboxId: string
|
|
66
|
-
integrationBaseUrl: string
|
|
67
|
-
webhookSecret: string
|
|
68
|
-
capabilities: string[]
|
|
69
|
-
stripeAccountId?: string
|
|
70
|
-
escalationSlackChannel?: string
|
|
71
|
-
autoApproveRefundDays: number
|
|
72
|
-
autoApproveTransferDays: number
|
|
73
|
-
}
|
|
74
|
-
error?: string
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
/**
|
|
78
|
-
* Convert name to URL-friendly slug
|
|
79
|
-
*/
|
|
80
|
-
function slugify(name: string): string {
|
|
81
|
-
return name
|
|
82
|
-
.toLowerCase()
|
|
83
|
-
.replace(/[^a-z0-9]+/g, '-')
|
|
84
|
-
.replace(/^-|-$/g, '')
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
/**
|
|
88
|
-
* Interactive wizard for setting up a new property (app)
|
|
89
|
-
*/
|
|
90
|
-
export async function wizard(options: WizardOptions = {}): Promise<void> {
|
|
91
|
-
const { json = false } = options
|
|
92
|
-
|
|
93
|
-
if (!process.stdin.isTTY && !json) {
|
|
94
|
-
console.error(
|
|
95
|
-
'Error: Wizard requires an interactive terminal. Use --json for non-interactive mode.'
|
|
96
|
-
)
|
|
97
|
-
process.exit(1)
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
try {
|
|
101
|
-
console.log('\nš§ Property Setup Wizard\n')
|
|
102
|
-
console.log(
|
|
103
|
-
'This will walk you through setting up a new app in the support platform.\n'
|
|
104
|
-
)
|
|
105
|
-
|
|
106
|
-
// Basic info
|
|
107
|
-
const name = await input({
|
|
108
|
-
message: 'App name (e.g., "Total TypeScript"):',
|
|
109
|
-
validate: (v) => v.trim().length > 0 || 'Name is required',
|
|
110
|
-
})
|
|
111
|
-
|
|
112
|
-
const suggestedSlug = slugify(name)
|
|
113
|
-
const slug = await input({
|
|
114
|
-
message: 'URL slug:',
|
|
115
|
-
default: suggestedSlug,
|
|
116
|
-
validate: (v) =>
|
|
117
|
-
/^[a-z0-9-]+$/.test(v) ||
|
|
118
|
-
'Slug must be lowercase alphanumeric with dashes',
|
|
119
|
-
})
|
|
120
|
-
|
|
121
|
-
// Front inbox
|
|
122
|
-
const frontInboxId = await input({
|
|
123
|
-
message: 'Front inbox ID (e.g., "inb_abc123"):',
|
|
124
|
-
validate: (v) =>
|
|
125
|
-
v.startsWith('inb_') ||
|
|
126
|
-
'Must be a valid Front inbox ID (starts with inb_)',
|
|
127
|
-
})
|
|
128
|
-
|
|
129
|
-
// Integration URL
|
|
130
|
-
const integrationBaseUrl = await input({
|
|
131
|
-
message: 'Integration base URL (where SDK endpoints live):',
|
|
132
|
-
default: `https://${slug}.com`,
|
|
133
|
-
validate: (v) => {
|
|
134
|
-
try {
|
|
135
|
-
new URL(v)
|
|
136
|
-
return true
|
|
137
|
-
} catch {
|
|
138
|
-
return 'Must be a valid URL'
|
|
139
|
-
}
|
|
140
|
-
},
|
|
141
|
-
})
|
|
142
|
-
|
|
143
|
-
// Capabilities
|
|
144
|
-
const capabilities = await checkbox({
|
|
145
|
-
message: 'Select capabilities to implement:',
|
|
146
|
-
choices: ALL_CAPABILITIES.map((c) => ({
|
|
147
|
-
value: c.value,
|
|
148
|
-
name: c.name,
|
|
149
|
-
checked: c.checked,
|
|
150
|
-
})),
|
|
151
|
-
})
|
|
152
|
-
|
|
153
|
-
// Stripe Connect (optional)
|
|
154
|
-
const useStripe = await confirm({
|
|
155
|
-
message: 'Enable Stripe Connect for refund processing?',
|
|
156
|
-
default: true,
|
|
157
|
-
})
|
|
158
|
-
|
|
159
|
-
let stripeAccountId: string | undefined
|
|
160
|
-
if (useStripe) {
|
|
161
|
-
stripeAccountId =
|
|
162
|
-
(await input({
|
|
163
|
-
message:
|
|
164
|
-
'Stripe Connect account ID (e.g., "acct_xxx") or leave blank to connect later:',
|
|
165
|
-
default: '',
|
|
166
|
-
})) || undefined
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
// Escalation channel (optional)
|
|
170
|
-
const useSlackEscalation = await confirm({
|
|
171
|
-
message: 'Configure Slack escalation channel?',
|
|
172
|
-
default: false,
|
|
173
|
-
})
|
|
174
|
-
|
|
175
|
-
let escalationSlackChannel: string | undefined
|
|
176
|
-
if (useSlackEscalation) {
|
|
177
|
-
escalationSlackChannel = await input({
|
|
178
|
-
message: 'Slack channel ID for escalations (e.g., "C0123456789"):',
|
|
179
|
-
validate: (v) =>
|
|
180
|
-
v.startsWith('C') || 'Must be a valid Slack channel ID',
|
|
181
|
-
})
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
// Auto-approval settings
|
|
185
|
-
const configureAutoApproval = await confirm({
|
|
186
|
-
message:
|
|
187
|
-
'Configure auto-approval thresholds? (default: 30 days refund, 14 days transfer)',
|
|
188
|
-
default: false,
|
|
189
|
-
})
|
|
190
|
-
|
|
191
|
-
let autoApproveRefundDays = 30
|
|
192
|
-
let autoApproveTransferDays = 14
|
|
193
|
-
|
|
194
|
-
if (configureAutoApproval) {
|
|
195
|
-
const refundDaysStr = await input({
|
|
196
|
-
message: 'Auto-approve refunds within X days of purchase:',
|
|
197
|
-
default: '30',
|
|
198
|
-
validate: (v) =>
|
|
199
|
-
(!isNaN(parseInt(v)) && parseInt(v) >= 0) ||
|
|
200
|
-
'Must be a non-negative number',
|
|
201
|
-
})
|
|
202
|
-
autoApproveRefundDays = parseInt(refundDaysStr)
|
|
203
|
-
|
|
204
|
-
const transferDaysStr = await input({
|
|
205
|
-
message: 'Auto-approve transfers within X days of purchase:',
|
|
206
|
-
default: '14',
|
|
207
|
-
validate: (v) =>
|
|
208
|
-
(!isNaN(parseInt(v)) && parseInt(v) >= 0) ||
|
|
209
|
-
'Must be a non-negative number',
|
|
210
|
-
})
|
|
211
|
-
autoApproveTransferDays = parseInt(transferDaysStr)
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
// Generate secrets
|
|
215
|
-
const id = `app_${randomUUID().replace(/-/g, '').slice(0, 16)}`
|
|
216
|
-
const webhookSecret = randomBytes(32).toString('hex')
|
|
217
|
-
|
|
218
|
-
const result: WizardResult = {
|
|
219
|
-
success: true,
|
|
220
|
-
app: {
|
|
221
|
-
id,
|
|
222
|
-
slug,
|
|
223
|
-
name,
|
|
224
|
-
frontInboxId,
|
|
225
|
-
integrationBaseUrl,
|
|
226
|
-
webhookSecret,
|
|
227
|
-
capabilities,
|
|
228
|
-
stripeAccountId,
|
|
229
|
-
escalationSlackChannel,
|
|
230
|
-
autoApproveRefundDays,
|
|
231
|
-
autoApproveTransferDays,
|
|
232
|
-
},
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
if (json) {
|
|
236
|
-
console.log(JSON.stringify(result, null, 2))
|
|
237
|
-
} else {
|
|
238
|
-
console.log('\n' + '='.repeat(60))
|
|
239
|
-
console.log('ā
Configuration complete!\n')
|
|
240
|
-
|
|
241
|
-
console.log('š App Details:')
|
|
242
|
-
console.log(` ID: ${id}`)
|
|
243
|
-
console.log(` Slug: ${slug}`)
|
|
244
|
-
console.log(` Name: ${name}`)
|
|
245
|
-
console.log(` URL: ${integrationBaseUrl}`)
|
|
246
|
-
|
|
247
|
-
console.log('\nš Front Integration:')
|
|
248
|
-
console.log(` Inbox ID: ${frontInboxId}`)
|
|
249
|
-
|
|
250
|
-
if (stripeAccountId) {
|
|
251
|
-
console.log('\nš³ Stripe Connect:')
|
|
252
|
-
console.log(` Account ID: ${stripeAccountId}`)
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
console.log('\nš§ Capabilities:')
|
|
256
|
-
capabilities.forEach((c) => console.log(` - ${c}`))
|
|
257
|
-
|
|
258
|
-
console.log('\nā±ļø Auto-Approval:')
|
|
259
|
-
console.log(` Refunds: within ${autoApproveRefundDays} days`)
|
|
260
|
-
console.log(` Transfers: within ${autoApproveTransferDays} days`)
|
|
261
|
-
|
|
262
|
-
console.log('\n' + '='.repeat(60))
|
|
263
|
-
console.log('\nš Next Steps:\n')
|
|
264
|
-
|
|
265
|
-
console.log("1. Add to your app's .env:")
|
|
266
|
-
console.log(' ```')
|
|
267
|
-
console.log(` SUPPORT_WEBHOOK_SECRET=${webhookSecret}`)
|
|
268
|
-
console.log(' ```\n')
|
|
269
|
-
|
|
270
|
-
console.log('2. Implement the SDK handler in your app:')
|
|
271
|
-
console.log(' ```typescript')
|
|
272
|
-
console.log(' // app/api/support/[...action]/route.ts')
|
|
273
|
-
console.log(
|
|
274
|
-
" import { createSupportHandler } from '@skillrecordings/sdk/handler'"
|
|
275
|
-
)
|
|
276
|
-
console.log(" import { integration } from './integration'")
|
|
277
|
-
console.log('')
|
|
278
|
-
console.log(' const handler = createSupportHandler({')
|
|
279
|
-
console.log(' integration,')
|
|
280
|
-
console.log(` secret: process.env.SUPPORT_WEBHOOK_SECRET!,`)
|
|
281
|
-
console.log(' })')
|
|
282
|
-
console.log('')
|
|
283
|
-
console.log(' export { handler as POST }')
|
|
284
|
-
console.log(' ```\n')
|
|
285
|
-
|
|
286
|
-
console.log('3. Insert into database:')
|
|
287
|
-
console.log(' ```sql')
|
|
288
|
-
console.log(
|
|
289
|
-
` INSERT INTO SUPPORT_apps (id, slug, name, front_inbox_id, integration_base_url, webhook_secret, capabilities, auto_approve_refund_days, auto_approve_transfer_days)`
|
|
290
|
-
)
|
|
291
|
-
console.log(
|
|
292
|
-
` VALUES ('${id}', '${slug}', '${name}', '${frontInboxId}', '${integrationBaseUrl}', '${webhookSecret}', '${JSON.stringify(capabilities)}', ${autoApproveRefundDays}, ${autoApproveTransferDays});`
|
|
293
|
-
)
|
|
294
|
-
console.log(' ```\n')
|
|
295
|
-
|
|
296
|
-
if (!stripeAccountId && useStripe) {
|
|
297
|
-
console.log('4. Connect Stripe account:')
|
|
298
|
-
console.log(
|
|
299
|
-
' Visit https://skill-support-agent-web.vercel.app/api/stripe/connect/authorize?appSlug=' +
|
|
300
|
-
slug
|
|
301
|
-
)
|
|
302
|
-
console.log('')
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
console.log(
|
|
306
|
-
'š See docs/support-app-prd/67-sdk.md for full integration guide.\n'
|
|
307
|
-
)
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
process.exit(0)
|
|
311
|
-
} catch (error) {
|
|
312
|
-
if ((error as Error).name === 'ExitPromptError') {
|
|
313
|
-
// User cancelled
|
|
314
|
-
console.log('\n\nWizard cancelled.')
|
|
315
|
-
process.exit(1)
|
|
316
|
-
}
|
|
317
|
-
throw error
|
|
318
|
-
}
|
|
319
|
-
}
|