skrypt-ai 0.3.4 → 0.4.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/README.md +1 -1
- package/dist/auth/index.d.ts +0 -1
- package/dist/auth/index.js +3 -5
- package/dist/autofix/index.js +15 -3
- package/dist/cli.js +19 -4
- package/dist/commands/check-links.js +164 -174
- package/dist/commands/deploy.js +5 -2
- package/dist/commands/generate.js +206 -199
- package/dist/commands/i18n.js +3 -20
- package/dist/commands/init.js +47 -40
- package/dist/commands/lint.js +3 -20
- package/dist/commands/mcp.js +125 -122
- package/dist/commands/monitor.js +125 -108
- package/dist/commands/review-pr.js +1 -1
- package/dist/commands/sdk.js +1 -1
- package/dist/config/loader.js +21 -2
- package/dist/generator/organizer.d.ts +3 -0
- package/dist/generator/organizer.js +4 -9
- package/dist/generator/writer.js +2 -10
- package/dist/github/pr-comments.js +21 -8
- package/dist/plugins/index.js +1 -0
- package/dist/scanner/index.js +8 -2
- package/dist/template/docs.json +2 -1
- package/dist/template/next.config.mjs +2 -1
- package/dist/template/package.json +17 -15
- package/dist/template/public/favicon.svg +4 -0
- package/dist/template/public/search-index.json +1 -1
- package/dist/template/scripts/build-search-index.mjs +120 -25
- package/dist/template/src/app/api/chat/route.ts +11 -3
- package/dist/template/src/app/docs/README.md +28 -0
- package/dist/template/src/app/docs/[...slug]/page.tsx +139 -16
- package/dist/template/src/app/docs/auth/page.mdx +589 -0
- package/dist/template/src/app/docs/autofix/page.mdx +624 -0
- package/dist/template/src/app/docs/cli/page.mdx +217 -0
- package/dist/template/src/app/docs/config/page.mdx +428 -0
- package/dist/template/src/app/docs/configuration/page.mdx +86 -0
- package/dist/template/src/app/docs/deployment/page.mdx +112 -0
- package/dist/template/src/app/docs/error.tsx +20 -0
- package/dist/template/src/app/docs/generator/generator.md +504 -0
- package/dist/template/src/app/docs/generator/organizer.md +779 -0
- package/dist/template/src/app/docs/generator/page.mdx +613 -0
- package/dist/template/src/app/docs/github/page.mdx +502 -0
- package/dist/template/src/app/docs/llm/anthropic-client.md +549 -0
- package/dist/template/src/app/docs/llm/index.md +471 -0
- package/dist/template/src/app/docs/llm/page.mdx +428 -0
- package/dist/template/src/app/docs/llms-full.md +256 -0
- package/dist/template/src/app/docs/llms.txt +2971 -0
- package/dist/template/src/app/docs/not-found.tsx +23 -0
- package/dist/template/src/app/docs/page.mdx +0 -3
- package/dist/template/src/app/docs/plugins/page.mdx +1793 -0
- package/dist/template/src/app/docs/pro/page.mdx +121 -0
- package/dist/template/src/app/docs/quickstart/page.mdx +93 -0
- package/dist/template/src/app/docs/scanner/content-type.md +599 -0
- package/dist/template/src/app/docs/scanner/index.md +212 -0
- package/dist/template/src/app/docs/scanner/page.mdx +307 -0
- package/dist/template/src/app/docs/scanner/python.md +469 -0
- package/dist/template/src/app/docs/scanner/python_parser.md +1056 -0
- package/dist/template/src/app/docs/scanner/rust.md +325 -0
- package/dist/template/src/app/docs/scanner/typescript.md +201 -0
- package/dist/template/src/app/error.tsx +3 -3
- package/dist/template/src/app/icon.tsx +29 -0
- package/dist/template/src/app/layout.tsx +42 -0
- package/dist/template/src/app/not-found.tsx +35 -0
- package/dist/template/src/app/page.tsx +62 -28
- package/dist/template/src/components/ai-chat.tsx +26 -21
- package/dist/template/src/components/breadcrumbs.tsx +46 -2
- package/dist/template/src/components/copy-button.tsx +17 -3
- package/dist/template/src/components/docs-layout.tsx +142 -8
- package/dist/template/src/components/feedback.tsx +4 -2
- package/dist/template/src/components/footer.tsx +42 -0
- package/dist/template/src/components/header.tsx +29 -5
- package/dist/template/src/components/mdx/accordion.tsx +7 -6
- package/dist/template/src/components/mdx/card.tsx +19 -7
- package/dist/template/src/components/mdx/code-block.tsx +17 -3
- package/dist/template/src/components/mdx/code-group.tsx +65 -18
- package/dist/template/src/components/mdx/code-playground.tsx +3 -0
- package/dist/template/src/components/mdx/go-playground.tsx +3 -0
- package/dist/template/src/components/mdx/highlighted-code.tsx +171 -76
- package/dist/template/src/components/mdx/python-playground.tsx +2 -0
- package/dist/template/src/components/mdx/tabs.tsx +74 -6
- package/dist/template/src/components/page-header.tsx +19 -0
- package/dist/template/src/components/scroll-to-top.tsx +33 -0
- package/dist/template/src/components/search-dialog.tsx +206 -52
- package/dist/template/src/components/sidebar.tsx +136 -77
- package/dist/template/src/components/table-of-contents.tsx +23 -7
- package/dist/template/src/lib/highlight.ts +90 -31
- package/dist/template/src/lib/search.ts +14 -4
- package/dist/template/src/lib/theme-utils.ts +140 -0
- package/dist/template/src/styles/globals.css +307 -166
- package/dist/template/src/types/remark-gfm.d.ts +2 -0
- package/dist/utils/files.d.ts +9 -0
- package/dist/utils/files.js +33 -0
- package/dist/utils/validation.d.ts +4 -0
- package/dist/utils/validation.js +38 -0
- package/package.json +1 -4
|
@@ -0,0 +1,589 @@
|
|
|
1
|
+
## Functions
|
|
2
|
+
|
|
3
|
+
### `getAuthConfig`
|
|
4
|
+
|
|
5
|
+
```typescript
|
|
6
|
+
function getAuthConfig(): AuthConfig
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
Use this to retrieve the current authentication configuration from the local auth file. This is the primary way to check if a user is logged in, access their stored credentials, or verify token expiration before making API calls.
|
|
10
|
+
|
|
11
|
+
Returns an `AuthConfig` object with the user's stored auth data, or an empty object `{}` if no auth file exists yet (unauthenticated state).
|
|
12
|
+
|
|
13
|
+
#### Parameters
|
|
14
|
+
|
|
15
|
+
This function takes no parameters.
|
|
16
|
+
|
|
17
|
+
#### Returns
|
|
18
|
+
|
|
19
|
+
| Field | Type | Description |
|
|
20
|
+
|-------|------|-------------|
|
|
21
|
+
| `token` | `string` (optional) | The authentication token for API requests |
|
|
22
|
+
| `email` | `string` (optional) | The authenticated user's email address |
|
|
23
|
+
| `expiresAt` | `string` (optional) | ISO timestamp indicating when the token expires |
|
|
24
|
+
| `error` | `string` (optional) | Any error message associated with the auth state |
|
|
25
|
+
|
|
26
|
+
**Returns `{}`** (empty object) when no auth file is found — treat this as an unauthenticated state.
|
|
27
|
+
|
|
28
|
+
**Returns a populated `AuthConfig`** when the user has previously authenticated.
|
|
29
|
+
|
|
30
|
+
**Example:**
|
|
31
|
+
|
|
32
|
+
```typescript example.ts
|
|
33
|
+
import { existsSync, readFileSync } from 'fs'
|
|
34
|
+
import { join } from 'path'
|
|
35
|
+
import { homedir } from 'os'
|
|
36
|
+
|
|
37
|
+
// Inline the AuthConfig type
|
|
38
|
+
type AuthConfig = {
|
|
39
|
+
token?: string
|
|
40
|
+
email?: string
|
|
41
|
+
expiresAt?: string
|
|
42
|
+
error?: string
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Inline the auth file path logic (mirrors the real implementation)
|
|
46
|
+
const AUTH_FILE = join(homedir(), '.config', 'autodocs', 'auth.json')
|
|
47
|
+
|
|
48
|
+
// Self-contained implementation of getAuthConfig
|
|
49
|
+
function getAuthConfig(): AuthConfig {
|
|
50
|
+
if (!existsSync(AUTH_FILE)) {
|
|
51
|
+
return {}
|
|
52
|
+
}
|
|
53
|
+
try {
|
|
54
|
+
return JSON.parse(readFileSync(AUTH_FILE, 'utf-8')) as AuthConfig
|
|
55
|
+
} catch {
|
|
56
|
+
return { error: 'Failed to parse auth config' }
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// --- Usage Examples ---
|
|
61
|
+
|
|
62
|
+
function isAuthenticated(config: AuthConfig): boolean {
|
|
63
|
+
return !!config.token && !config.error
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function isTokenExpired(config: AuthConfig): boolean {
|
|
67
|
+
if (!config.expiresAt) return false
|
|
68
|
+
return new Date(config.expiresAt) < new Date()
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
async function main() {
|
|
72
|
+
try {
|
|
73
|
+
const authConfig = getAuthConfig()
|
|
74
|
+
|
|
75
|
+
// Case 1: No auth file found
|
|
76
|
+
if (Object.keys(authConfig).length === 0) {
|
|
77
|
+
console.log('Status: Not authenticated — please run `autodocs login`')
|
|
78
|
+
// Output: Status: Not authenticated — please run `autodocs login`
|
|
79
|
+
return
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Case 2: Auth file exists, check validity
|
|
83
|
+
if (!isAuthenticated(authConfig)) {
|
|
84
|
+
console.log('Auth error:', authConfig.error ?? 'Unknown error')
|
|
85
|
+
return
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (isTokenExpired(authConfig)) {
|
|
89
|
+
console.log('Token expired at:', authConfig.expiresAt)
|
|
90
|
+
console.log('Please re-authenticate with `autodocs login`')
|
|
91
|
+
return
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Case 3: Valid, active session
|
|
95
|
+
console.log('Authenticated as:', authConfig.email)
|
|
96
|
+
console.log('Token (truncated):', authConfig.token?.slice(0, 8) + '...')
|
|
97
|
+
console.log('Session expires:', authConfig.expiresAt)
|
|
98
|
+
// Output:
|
|
99
|
+
// Authenticated as: user@example.com
|
|
100
|
+
// Token (truncated): eyJhbGci...
|
|
101
|
+
// Session expires: 2025-12-31T23:59:59.000Z
|
|
102
|
+
|
|
103
|
+
} catch (error) {
|
|
104
|
+
console.error('Unexpected error reading auth config:', error)
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
main()
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### `saveAuthConfig`
|
|
112
|
+
|
|
113
|
+
```typescript
|
|
114
|
+
function saveAuthConfig(config: AuthConfig): void
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Use this to persist authentication credentials to a local config file, ensuring the config directory exists before writing.
|
|
118
|
+
|
|
119
|
+
Saves an `AuthConfig` object as formatted JSON to a file on disk. Automatically creates any missing parent directories in the config path before writing, so you don't need to pre-create the directory structure.
|
|
120
|
+
|
|
121
|
+
### Parameters
|
|
122
|
+
|
|
123
|
+
| Name | Type | Required | Description |
|
|
124
|
+
|------|------|----------|-------------|
|
|
125
|
+
| config | `AuthConfig` | ✅ | Authentication configuration object containing credentials/tokens to persist |
|
|
126
|
+
|
|
127
|
+
#### Returns
|
|
128
|
+
|
|
129
|
+
`void` — Writes synchronously to disk. Throws a filesystem error if the path is not writable or permissions are insufficient.
|
|
130
|
+
|
|
131
|
+
### Notes
|
|
132
|
+
- Creates the config directory recursively if it does not exist (`mkdirSync` with `{ recursive: true }`)
|
|
133
|
+
- Writes JSON with 2-space indentation for human readability
|
|
134
|
+
- Overwrites any existing config file at the target path
|
|
135
|
+
- Throws if the write fails (e.g., permission denied, disk full)
|
|
136
|
+
|
|
137
|
+
**Example:**
|
|
138
|
+
|
|
139
|
+
```typescript example.ts
|
|
140
|
+
import { mkdirSync, writeFileSync, readFileSync, existsSync } from 'fs'
|
|
141
|
+
import { join } from 'path'
|
|
142
|
+
import { homedir } from 'os'
|
|
143
|
+
|
|
144
|
+
// --- Inline type definition ---
|
|
145
|
+
interface AuthConfig {
|
|
146
|
+
token: string
|
|
147
|
+
userId: string
|
|
148
|
+
expiresAt?: number
|
|
149
|
+
refreshToken?: string
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// --- Inline config path constants ---
|
|
153
|
+
const CONFIG_DIR = join(homedir(), '.config', 'myapp')
|
|
154
|
+
const AUTH_FILE = join(CONFIG_DIR, 'auth.json')
|
|
155
|
+
|
|
156
|
+
// --- Inlined implementation of saveAuthConfig ---
|
|
157
|
+
function saveAuthConfig(config: AuthConfig): void {
|
|
158
|
+
mkdirSync(CONFIG_DIR, { recursive: true })
|
|
159
|
+
writeFileSync(AUTH_FILE, JSON.stringify(config, null, 2))
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// --- Usage example ---
|
|
163
|
+
const authConfig: AuthConfig = {
|
|
164
|
+
token: process.env.AUTH_TOKEN || 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.example',
|
|
165
|
+
userId: process.env.USER_ID || 'usr_4f8a2b1c9d3e',
|
|
166
|
+
expiresAt: Date.now() + 1000 * 60 * 60 * 24, // 24 hours from now
|
|
167
|
+
refreshToken: process.env.REFRESH_TOKEN || 'ref_7k2m9p4q1r8s',
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
try {
|
|
171
|
+
saveAuthConfig(authConfig)
|
|
172
|
+
console.log(`✅ Auth config saved to: ${AUTH_FILE}`)
|
|
173
|
+
|
|
174
|
+
// Verify the file was written correctly
|
|
175
|
+
if (existsSync(AUTH_FILE)) {
|
|
176
|
+
const saved = JSON.parse(readFileSync(AUTH_FILE, 'utf-8')) as AuthConfig
|
|
177
|
+
console.log('Saved config:', saved)
|
|
178
|
+
// Output:
|
|
179
|
+
// {
|
|
180
|
+
// token: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.example',
|
|
181
|
+
// userId: 'usr_4f8a2b1c9d3e',
|
|
182
|
+
// expiresAt: 1718123456789,
|
|
183
|
+
// refreshToken: 'ref_7k2m9p4q1r8s'
|
|
184
|
+
// }
|
|
185
|
+
}
|
|
186
|
+
} catch (error) {
|
|
187
|
+
if (error instanceof Error) {
|
|
188
|
+
console.error('❌ Failed to save auth config:', error.message)
|
|
189
|
+
// Common causes: permission denied, disk full, invalid path
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
### `clearAuth`
|
|
195
|
+
|
|
196
|
+
```typescript
|
|
197
|
+
function clearAuth(): void
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
Use this to log a user out by wiping stored authentication credentials from the local config file. Calling this resets the auth file to an empty state (`{}`) without deleting the file itself — safe to call even if no credentials have been saved yet.
|
|
201
|
+
|
|
202
|
+
**Parameters**
|
|
203
|
+
|
|
204
|
+
None.
|
|
205
|
+
|
|
206
|
+
**Returns**
|
|
207
|
+
|
|
208
|
+
`void` — no return value. The auth config file is overwritten with `{}` if it exists; if no file is found, the function exits silently.
|
|
209
|
+
|
|
210
|
+
**Behavior**
|
|
211
|
+
- ✅ Auth file exists → contents replaced with `{}`
|
|
212
|
+
- ✅ Auth file does not exist → no-op, no error thrown
|
|
213
|
+
|
|
214
|
+
**Example:**
|
|
215
|
+
|
|
216
|
+
```typescript example.ts
|
|
217
|
+
import { existsSync, writeFileSync, readFileSync, mkdirSync } from 'fs'
|
|
218
|
+
import { join } from 'path'
|
|
219
|
+
import { homedir } from 'os'
|
|
220
|
+
|
|
221
|
+
// --- Inline config (mirrors autodocs internals) ---
|
|
222
|
+
const CONFIG_DIR = join(homedir(), '.config', 'autodocs-demo')
|
|
223
|
+
const AUTH_FILE = join(CONFIG_DIR, 'auth.json')
|
|
224
|
+
|
|
225
|
+
type AuthConfig = {
|
|
226
|
+
token?: string
|
|
227
|
+
userId?: string
|
|
228
|
+
expiresAt?: string
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
function saveAuthConfig(config: AuthConfig): void {
|
|
232
|
+
mkdirSync(CONFIG_DIR, { recursive: true })
|
|
233
|
+
writeFileSync(AUTH_FILE, JSON.stringify(config, null, 2))
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
function clearAuth(): void {
|
|
237
|
+
if (existsSync(AUTH_FILE)) {
|
|
238
|
+
writeFileSync(AUTH_FILE, '{}')
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// --- Example: save credentials, then clear them on logout ---
|
|
243
|
+
async function main() {
|
|
244
|
+
try {
|
|
245
|
+
// Simulate a user logging in
|
|
246
|
+
const credentials: AuthConfig = {
|
|
247
|
+
token: process.env.AUTH_TOKEN || 'tok_abc123xyz',
|
|
248
|
+
userId: 'user_9876',
|
|
249
|
+
expiresAt: new Date(Date.now() + 3600 * 1000).toISOString(),
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
saveAuthConfig(credentials)
|
|
253
|
+
console.log('Logged in. Auth file contents:')
|
|
254
|
+
console.log(readFileSync(AUTH_FILE, 'utf-8'))
|
|
255
|
+
// Output: { "token": "tok_abc123xyz", "userId": "user_9876", "expiresAt": "..." }
|
|
256
|
+
|
|
257
|
+
// Simulate the user logging out
|
|
258
|
+
clearAuth()
|
|
259
|
+
console.log('\nLogged out. Auth file contents after clearAuth():')
|
|
260
|
+
console.log(readFileSync(AUTH_FILE, 'utf-8'))
|
|
261
|
+
// Output: {}
|
|
262
|
+
|
|
263
|
+
// Calling clearAuth again when already cleared is safe (no-op if file missing)
|
|
264
|
+
clearAuth()
|
|
265
|
+
console.log('\nCalled clearAuth() again — no error thrown. ✅')
|
|
266
|
+
} catch (error) {
|
|
267
|
+
console.error('Unexpected error during auth lifecycle:', error)
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
main()
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
### `checkPlan`
|
|
275
|
+
|
|
276
|
+
```typescript
|
|
277
|
+
async function checkPlan(apiKey: string): Promise<PlanCheckResponse>
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
Use this to verify an API key's validity and retrieve the associated subscription plan details before making API calls.
|
|
281
|
+
|
|
282
|
+
`checkPlan` hits the `/v1/plan` endpoint with a Bearer token and returns plan metadata — useful for gating features, displaying plan info to users, or validating credentials at startup.
|
|
283
|
+
|
|
284
|
+
#### Parameters
|
|
285
|
+
|
|
286
|
+
| Name | Type | Required | Description |
|
|
287
|
+
|------|------|----------|-------------|
|
|
288
|
+
| `apiKey` | `string` | ✅ Yes | The Bearer token used to authenticate against the API |
|
|
289
|
+
|
|
290
|
+
#### Returns
|
|
291
|
+
|
|
292
|
+
Returns a `Promise<PlanCheckResponse>` that resolves with the plan details for the given API key.
|
|
293
|
+
|
|
294
|
+
| Scenario | Result |
|
|
295
|
+
|----------|--------|
|
|
296
|
+
| Valid API key | Resolves with plan metadata (e.g. plan name, limits, status) |
|
|
297
|
+
| Invalid / expired key | Rejects or resolves with an error response depending on server behavior |
|
|
298
|
+
| Network failure | Rejects with a fetch/network error |
|
|
299
|
+
|
|
300
|
+
### `PlanCheckResponse` shape (typical)
|
|
301
|
+
```typescript
|
|
302
|
+
type PlanCheckResponse = {
|
|
303
|
+
plan: string // e.g. "free", "pro", "enterprise"
|
|
304
|
+
valid: boolean // whether the key is active
|
|
305
|
+
limits?: {
|
|
306
|
+
requests: number
|
|
307
|
+
storage: number
|
|
308
|
+
}
|
|
309
|
+
error?: string // present if the key was rejected
|
|
310
|
+
}
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
**Example:**
|
|
314
|
+
|
|
315
|
+
```typescript example.ts
|
|
316
|
+
// Inline types — no external imports needed
|
|
317
|
+
type PlanCheckResponse = {
|
|
318
|
+
plan: string
|
|
319
|
+
valid: boolean
|
|
320
|
+
limits?: {
|
|
321
|
+
requests: number
|
|
322
|
+
storage: number
|
|
323
|
+
}
|
|
324
|
+
error?: string
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
// Self-contained implementation mirroring the real checkPlan behavior
|
|
328
|
+
async function checkPlan(apiKey: string): Promise<PlanCheckResponse> {
|
|
329
|
+
const API_BASE = 'https://api.autodocs.dev'
|
|
330
|
+
|
|
331
|
+
const response = await fetch(`${API_BASE}/v1/plan`, {
|
|
332
|
+
headers: {
|
|
333
|
+
'Authorization': `Bearer ${apiKey}`,
|
|
334
|
+
'Content-Type': 'application/json',
|
|
335
|
+
},
|
|
336
|
+
})
|
|
337
|
+
|
|
338
|
+
if (!response.ok) {
|
|
339
|
+
const errorBody = await response.json().catch(() => ({}))
|
|
340
|
+
return {
|
|
341
|
+
plan: 'unknown',
|
|
342
|
+
valid: false,
|
|
343
|
+
error: (errorBody as any).message || `HTTP ${response.status}: ${response.statusText}`,
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
return response.json() as Promise<PlanCheckResponse>
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
// --- Usage ---
|
|
351
|
+
async function main() {
|
|
352
|
+
const apiKey = process.env.AUTODOCS_API_KEY || 'sk-your-api-key-here'
|
|
353
|
+
|
|
354
|
+
try {
|
|
355
|
+
console.log('Checking plan for API key...')
|
|
356
|
+
const planInfo = await checkPlan(apiKey)
|
|
357
|
+
|
|
358
|
+
if (!planInfo.valid) {
|
|
359
|
+
console.error('API key is invalid or expired:', planInfo.error)
|
|
360
|
+
process.exit(1)
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
console.log('Plan details:', planInfo)
|
|
364
|
+
// Expected output:
|
|
365
|
+
// Plan details: { plan: 'pro', valid: true, limits: { requests: 10000, storage: 5000 } }
|
|
366
|
+
|
|
367
|
+
// Example: gate a feature based on plan
|
|
368
|
+
if (planInfo.plan === 'free') {
|
|
369
|
+
console.log('Upgrade to Pro to unlock advanced features.')
|
|
370
|
+
} else {
|
|
371
|
+
console.log(`Welcome, ${planInfo.plan} user! All features unlocked.`)
|
|
372
|
+
}
|
|
373
|
+
} catch (error) {
|
|
374
|
+
// Network errors, DNS failures, etc.
|
|
375
|
+
console.error('Failed to reach the plan endpoint:', error)
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
main()
|
|
380
|
+
```
|
|
381
|
+
|
|
382
|
+
### `requirePro`
|
|
383
|
+
|
|
384
|
+
```typescript
|
|
385
|
+
async function requirePro(commandName: string): Promise<boolean>
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
Use this to gate CLI commands behind a Pro subscription — it checks the user's auth config and prints a helpful upgrade message if they're not on a paid plan.
|
|
389
|
+
|
|
390
|
+
Call `requirePro` at the top of any command handler that requires a Pro account. It returns `true` if the user is authorized to proceed, or `false` (after printing an error) if they are not.
|
|
391
|
+
|
|
392
|
+
#### Parameters
|
|
393
|
+
|
|
394
|
+
| Name | Type | Required | Description |
|
|
395
|
+
|------|------|----------|-------------|
|
|
396
|
+
| `commandName` | `string` | ✅ | The name of the CLI command being gated. Used in the error message shown to the user (e.g. `"sync"`, `"generate"`). |
|
|
397
|
+
|
|
398
|
+
#### Returns
|
|
399
|
+
|
|
400
|
+
| Value | When |
|
|
401
|
+
|-------|------|
|
|
402
|
+
| `Promise<true>` | User has a valid Pro API key and is authorized |
|
|
403
|
+
| `Promise<false>` | User has no API key, is on the free plan, or the auth check fails — an error message is printed to `stderr` automatically |
|
|
404
|
+
|
|
405
|
+
## Usage Pattern
|
|
406
|
+
|
|
407
|
+
```typescript
|
|
408
|
+
// In your command handler:
|
|
409
|
+
if (!await requirePro('my-command')) return;
|
|
410
|
+
// ... rest of Pro-only logic
|
|
411
|
+
```
|
|
412
|
+
|
|
413
|
+
**Example:**
|
|
414
|
+
|
|
415
|
+
```typescript example.ts
|
|
416
|
+
import * as fs from 'fs'
|
|
417
|
+
import * as path from 'path'
|
|
418
|
+
import * as os from 'os'
|
|
419
|
+
|
|
420
|
+
// --- Inline types and helpers (self-contained, no external imports) ---
|
|
421
|
+
|
|
422
|
+
type AuthConfig = {
|
|
423
|
+
apiKey: string | null
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
type LicenseStatus = {
|
|
427
|
+
valid: boolean
|
|
428
|
+
plan: 'free' | 'pro'
|
|
429
|
+
email: string
|
|
430
|
+
error?: string
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
// Simulates reading auth config from ~/.skrypt/config.json
|
|
434
|
+
function getAuthConfig(): AuthConfig {
|
|
435
|
+
const configPath = path.join(os.homedir(), '.skrypt', 'config.json')
|
|
436
|
+
try {
|
|
437
|
+
if (fs.existsSync(configPath)) {
|
|
438
|
+
const raw = fs.readFileSync(configPath, 'utf-8')
|
|
439
|
+
const parsed = JSON.parse(raw)
|
|
440
|
+
return { apiKey: parsed.apiKey || null }
|
|
441
|
+
}
|
|
442
|
+
} catch {
|
|
443
|
+
// Config unreadable
|
|
444
|
+
}
|
|
445
|
+
return { apiKey: null }
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
// Simulates a license validation API call
|
|
449
|
+
async function validateLicense(apiKey: string): Promise<LicenseStatus> {
|
|
450
|
+
// In real usage this would hit an API endpoint
|
|
451
|
+
// Here we simulate: keys starting with "pro_" are valid Pro keys
|
|
452
|
+
if (apiKey.startsWith('pro_')) {
|
|
453
|
+
return { valid: true, plan: 'pro', email: 'user@example.com' }
|
|
454
|
+
}
|
|
455
|
+
return { valid: false, plan: 'free', email: '', error: 'Not a Pro key' }
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
// The function being documented
|
|
459
|
+
async function requirePro(commandName: string): Promise<boolean> {
|
|
460
|
+
const config = getAuthConfig()
|
|
461
|
+
|
|
462
|
+
if (!config.apiKey) {
|
|
463
|
+
console.error(`\n ⚡ ${commandName} requires Skrypt Pro\n`)
|
|
464
|
+
console.error(' Get started:')
|
|
465
|
+
console.error(' https://skrypt.dev/pro\n')
|
|
466
|
+
return false
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
let status: LicenseStatus
|
|
470
|
+
try {
|
|
471
|
+
status = await validateLicense(config.apiKey)
|
|
472
|
+
} catch {
|
|
473
|
+
status = { valid: false, plan: 'free', email: '', error: 'Failed to connect to API' }
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
if (!status.valid || status.plan !== 'pro') {
|
|
477
|
+
console.error(`\n ⚡ ${commandName} requires Skrypt Pro\n`)
|
|
478
|
+
console.error(` Current plan: ${status.plan}`)
|
|
479
|
+
console.error(' Upgrade at: https://skrypt.dev/pro\n')
|
|
480
|
+
return false
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
return true
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
// --- Demo ---
|
|
487
|
+
|
|
488
|
+
async function main() {
|
|
489
|
+
try {
|
|
490
|
+
// Scenario 1: No API key configured
|
|
491
|
+
console.log('--- Scenario 1: No API key ---')
|
|
492
|
+
const noKeyResult = await requirePro('generate')
|
|
493
|
+
console.log('Authorized:', noKeyResult)
|
|
494
|
+
// Output:
|
|
495
|
+
// ⚡ generate requires Skrypt Pro
|
|
496
|
+
// Get started:
|
|
497
|
+
// https://skrypt.dev/pro
|
|
498
|
+
// Authorized: false
|
|
499
|
+
|
|
500
|
+
// Scenario 2: Free-tier key
|
|
501
|
+
console.log('\n--- Scenario 2: Free-tier key ---')
|
|
502
|
+
// Temporarily mock getAuthConfig by overriding the config file path behavior
|
|
503
|
+
// (In a real test you'd inject the config; here we call validateLicense directly)
|
|
504
|
+
const freeStatus = await validateLicense('free_abc123')
|
|
505
|
+
console.log('License status:', freeStatus)
|
|
506
|
+
// Output: { valid: false, plan: 'free', email: '', error: 'Not a Pro key' }
|
|
507
|
+
|
|
508
|
+
// Scenario 3: Valid Pro key
|
|
509
|
+
console.log('\n--- Scenario 3: Valid Pro key ---')
|
|
510
|
+
const proStatus = await validateLicense(
|
|
511
|
+
process.env.SKRYPT_API_KEY || 'pro_sk_live_abc123xyz'
|
|
512
|
+
)
|
|
513
|
+
console.log('License status:', proStatus)
|
|
514
|
+
// Output: { valid: true, plan: 'pro', email: 'user@example.com' }
|
|
515
|
+
|
|
516
|
+
} catch (error) {
|
|
517
|
+
console.error('Unexpected error:', error)
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
main()
|
|
522
|
+
```
|
|
523
|
+
|
|
524
|
+
### `isProCommand`
|
|
525
|
+
|
|
526
|
+
```typescript
|
|
527
|
+
function isProCommand(command: string): boolean
|
|
528
|
+
```
|
|
529
|
+
|
|
530
|
+
Use this to check whether a given CLI command requires a Pro subscription before executing it, enabling you to gate features and show appropriate upgrade prompts.
|
|
531
|
+
|
|
532
|
+
#### Parameters
|
|
533
|
+
|
|
534
|
+
| Name | Type | Required | Description |
|
|
535
|
+
|------|------|----------|-------------|
|
|
536
|
+
| command | string | Yes | The CLI command name to check against the list of Pro-only commands |
|
|
537
|
+
|
|
538
|
+
#### Returns
|
|
539
|
+
|
|
540
|
+
- **`true`** — the command is Pro-only and requires an upgraded account
|
|
541
|
+
- **`false`** — the command is available on the free tier
|
|
542
|
+
|
|
543
|
+
**Example:**
|
|
544
|
+
|
|
545
|
+
```typescript example.ts
|
|
546
|
+
// Define the Pro commands list inline (mirrors the source)
|
|
547
|
+
const PRO_COMMANDS = ['gh-action', 'ci', 'mcp']
|
|
548
|
+
|
|
549
|
+
// Inline implementation of isProCommand
|
|
550
|
+
function isProCommand(command: string): boolean {
|
|
551
|
+
return PRO_COMMANDS.includes(command)
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
// Simulate a user running a CLI command
|
|
555
|
+
function runCommand(command: string, isProUser: boolean) {
|
|
556
|
+
if (isProCommand(command)) {
|
|
557
|
+
if (!isProUser) {
|
|
558
|
+
console.log(`⛔ "${command}" is a Pro-only command. Upgrade at https://example.com/pro`)
|
|
559
|
+
return
|
|
560
|
+
}
|
|
561
|
+
console.log(`✅ Running Pro command: "${command}"`)
|
|
562
|
+
} else {
|
|
563
|
+
console.log(`✅ Running free command: "${command}"`)
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
const userCommand = process.env.CLI_COMMAND || 'ci'
|
|
568
|
+
const userIsPro = process.env.IS_PRO === 'true' // set IS_PRO=true to simulate a Pro user
|
|
569
|
+
|
|
570
|
+
// Check a few commands
|
|
571
|
+
const commands = ['gh-action', 'ci', 'mcp', 'generate', 'help']
|
|
572
|
+
|
|
573
|
+
console.log('--- isProCommand checks ---')
|
|
574
|
+
for (const cmd of commands) {
|
|
575
|
+
console.log(`isProCommand("${cmd}") =>`, isProCommand(cmd))
|
|
576
|
+
}
|
|
577
|
+
// Output:
|
|
578
|
+
// isProCommand("gh-action") => true
|
|
579
|
+
// isProCommand("ci") => true
|
|
580
|
+
// isProCommand("mcp") => true
|
|
581
|
+
// isProCommand("generate") => false
|
|
582
|
+
// isProCommand("help") => false
|
|
583
|
+
|
|
584
|
+
console.log('\n--- Gating example ---')
|
|
585
|
+
runCommand(userCommand, userIsPro)
|
|
586
|
+
// With CLI_COMMAND=ci and IS_PRO unset:
|
|
587
|
+
// ⛔ "ci" is a Pro-only command. Upgrade at https://example.com/pro
|
|
588
|
+
```
|
|
589
|
+
|