@tutorflow/cli 0.1.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 (51) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +441 -0
  3. package/dist/client.d.ts +13 -0
  4. package/dist/client.d.ts.map +1 -0
  5. package/dist/client.js +48 -0
  6. package/dist/client.js.map +1 -0
  7. package/dist/commands/auth.d.ts +3 -0
  8. package/dist/commands/auth.d.ts.map +1 -0
  9. package/dist/commands/auth.js +72 -0
  10. package/dist/commands/auth.js.map +1 -0
  11. package/dist/commands/config.d.ts +3 -0
  12. package/dist/commands/config.d.ts.map +1 -0
  13. package/dist/commands/config.js +65 -0
  14. package/dist/commands/config.js.map +1 -0
  15. package/dist/commands/course.d.ts +3 -0
  16. package/dist/commands/course.d.ts.map +1 -0
  17. package/dist/commands/course.js +113 -0
  18. package/dist/commands/course.js.map +1 -0
  19. package/dist/commands/evaluate.d.ts +3 -0
  20. package/dist/commands/evaluate.d.ts.map +1 -0
  21. package/dist/commands/evaluate.js +68 -0
  22. package/dist/commands/evaluate.js.map +1 -0
  23. package/dist/commands/slide.d.ts +3 -0
  24. package/dist/commands/slide.d.ts.map +1 -0
  25. package/dist/commands/slide.js +116 -0
  26. package/dist/commands/slide.js.map +1 -0
  27. package/dist/commands/video.d.ts +3 -0
  28. package/dist/commands/video.d.ts.map +1 -0
  29. package/dist/commands/video.js +120 -0
  30. package/dist/commands/video.js.map +1 -0
  31. package/dist/config.d.ts +10 -0
  32. package/dist/config.d.ts.map +1 -0
  33. package/dist/config.js +80 -0
  34. package/dist/config.js.map +1 -0
  35. package/dist/index.d.ts +3 -0
  36. package/dist/index.d.ts.map +1 -0
  37. package/dist/index.js +25 -0
  38. package/dist/index.js.map +1 -0
  39. package/dist/types.d.ts +34 -0
  40. package/dist/types.d.ts.map +1 -0
  41. package/dist/types.js +9 -0
  42. package/dist/types.js.map +1 -0
  43. package/dist/utils/output.d.ts +3 -0
  44. package/dist/utils/output.d.ts.map +1 -0
  45. package/dist/utils/output.js +36 -0
  46. package/dist/utils/output.js.map +1 -0
  47. package/dist/utils/spinner.d.ts +8 -0
  48. package/dist/utils/spinner.d.ts.map +1 -0
  49. package/dist/utils/spinner.js +26 -0
  50. package/dist/utils/spinner.js.map +1 -0
  51. package/package.json +54 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 TutorFlow
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,441 @@
1
+ # @tutorflow/cli
2
+
3
+ TutorFlow CLI for creating AI-powered educational content (slides, videos, courses) and evaluating student responses from the terminal. Wraps the TutorFlow Platform API (`/v1/platform/*`).
4
+
5
+ - **Binary:** `tutorflow`
6
+ - **Version:** `0.1.0`
7
+ - **Node:** `>= 18`
8
+ - **Package:** `@tutorflow/cli`
9
+
10
+ ## Installation
11
+
12
+ ### A. End users (other machines)
13
+
14
+ Once published to npm, install globally with any package manager:
15
+
16
+ ```bash
17
+ npm install -g @tutorflow/cli
18
+ pnpm add -g @tutorflow/cli
19
+ yarn global add @tutorflow/cli
20
+ ```
21
+
22
+ Then anywhere on the system:
23
+
24
+ ```bash
25
+ tutorflow --version
26
+ tutorflow auth login
27
+ tutorflow slide create "My first deck"
28
+ ```
29
+
30
+ > **One-shot use without installing** (npm 5.2+):
31
+ >
32
+ > ```bash
33
+ > npx @tutorflow/cli slide create "My first deck"
34
+ > ```
35
+
36
+ If you publish to a private registry (e.g. GitHub Packages or a self-hosted Verdaccio), users first authenticate to that registry and scope the `@tutorflow` namespace to it:
37
+
38
+ ```bash
39
+ npm config set @tutorflow:registry https://npm.pkg.github.com
40
+ npm login --scope=@tutorflow --registry=https://npm.pkg.github.com
41
+ npm install -g @tutorflow/cli
42
+ ```
43
+
44
+ ### B. Local development (this monorepo)
45
+
46
+ From the monorepo root:
47
+
48
+ ```bash
49
+ pnpm install
50
+ pnpm --filter @tutorflow/cli build # tsc → ./dist
51
+ pnpm --filter @tutorflow/cli link # npm link → exposes `tutorflow` globally
52
+ ```
53
+
54
+ Verify:
55
+
56
+ ```bash
57
+ tutorflow --version # 0.1.0
58
+ tutorflow --help
59
+ ```
60
+
61
+ To unlink later: `npm unlink -g @tutorflow/cli`.
62
+
63
+ ## Publishing
64
+
65
+ The package is **scoped** (`@tutorflow/...`), so the first publish needs `--access public` (or a paid org plan for private packages).
66
+
67
+ ### One-time setup
68
+
69
+ 1. Make sure you're a member of the `@tutorflow` npm org with publish rights.
70
+ 2. Login from the CLI:
71
+
72
+ ```bash
73
+ npm login # public registry
74
+ # or:
75
+ npm login --scope=@tutorflow --registry=<your-private-registry>
76
+ ```
77
+
78
+ 3. Confirm `apps/cli/package.json` has the right metadata. Recommended fields to add before the first release:
79
+
80
+ ```json
81
+ {
82
+ "files": ["dist", "README.md"],
83
+ "publishConfig": { "access": "public" },
84
+ "repository": "https://github.com/<org>/tutorflow-monorepo",
85
+ "license": "UNLICENSED"
86
+ }
87
+ ```
88
+
89
+ The `files` allowlist keeps `src/`, `node_modules/`, and `tsconfig.json` out of the tarball.
90
+
91
+ ### Release flow
92
+
93
+ ```bash
94
+ cd apps/cli
95
+
96
+ # 1. bump version (creates a git tag)
97
+ npm version patch # 0.1.0 → 0.1.1
98
+ # or: npm version minor / major / prerelease --preid=beta
99
+
100
+ # 2. clean build
101
+ rm -rf dist
102
+ pnpm build
103
+
104
+ # 3. (optional) inspect what will be published
105
+ npm pack --dry-run
106
+
107
+ # 4. publish
108
+ npm publish --access public
109
+ ```
110
+
111
+ For pre-release channels (so end users don't auto-pull untested versions):
112
+
113
+ ```bash
114
+ npm version prerelease --preid=beta # 0.1.1 → 0.1.2-beta.0
115
+ npm publish --tag beta --access public
116
+ # users install with: npm install -g @tutorflow/cli@beta
117
+ ```
118
+
119
+ ### CI publishing (sketch)
120
+
121
+ In GitHub Actions, after `pnpm build`:
122
+
123
+ ```yaml
124
+ - run: echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" > ~/.npmrc
125
+ - run: npm publish --access public
126
+ working-directory: apps/cli
127
+ env:
128
+ NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
129
+ ```
130
+
131
+ Trigger on tags matching `cli-v*` or use `changesets` for monorepo-aware versioning.
132
+
133
+ ## Development
134
+
135
+ Run with `tsx` (no build step):
136
+
137
+ ```bash
138
+ pnpm --filter @tutorflow/cli dev -- <command> [options]
139
+ # e.g. pnpm --filter @tutorflow/cli dev -- slide list --limit 5
140
+ ```
141
+
142
+ Build artifacts land in `apps/cli/dist/` (entry: `dist/index.js`, declared in `package.json#bin`).
143
+
144
+ ## Authentication
145
+
146
+ Generate an API key in your TutorFlow workspace, then:
147
+
148
+ ```bash
149
+ tutorflow auth login # interactive password prompt for API key
150
+ tutorflow auth whoami # prints masked API key + base URL
151
+ tutorflow auth logout # confirms then deletes ~/.tutorflow/config.json
152
+ ```
153
+
154
+ Credentials are persisted to `~/.tutorflow/config.json`:
155
+
156
+ ```json
157
+ {
158
+ "apiKey": "tf_xxxxxxxxxxxxxxxxxxxx",
159
+ "baseUrl": "https://api.tutorflow.io",
160
+ "workspace": "optional-workspace-slug"
161
+ }
162
+ ```
163
+
164
+ The API key is sent as the `X-API-Key` header on every request.
165
+
166
+ ### Config schema
167
+
168
+ ```ts
169
+ interface IConfig {
170
+ apiKey?: string; // required for all non-auth/config commands
171
+ baseUrl?: string; // default: https://api.tutorflow.io
172
+ workspace?: string; // optional workspace slug
173
+ }
174
+ ```
175
+
176
+ ## Configuration commands
177
+
178
+ ```bash
179
+ tutorflow config get # print full config (apiKey is masked)
180
+ tutorflow config get <key> # print one key as KEY=VALUE
181
+ tutorflow config set <key> <value> # update one key
182
+ ```
183
+
184
+ | Key | Type | Default | Notes |
185
+ | ----------- | -------- | -------------------------- | -------------------------------------- |
186
+ | `apiKey` | `string` | none | Set via `auth login` or `config set` |
187
+ | `baseUrl` | `string` | `https://api.tutorflow.io` | Override for staging / self-hosted |
188
+ | `workspace` | `string` | none | Reserved for future scoping |
189
+
190
+ `config set` rejects unknown keys.
191
+
192
+ ## Commands
193
+
194
+ All resource commands require authentication. Without an API key they print:
195
+ `✗ Not authenticated. Run "tutorflow auth login" first.`
196
+
197
+ ### `tutorflow slide`
198
+
199
+ Create and manage slide decks.
200
+
201
+ #### `slide create [prompt]`
202
+
203
+ Creates a slide deck synchronously (`mode: 'sync'`).
204
+
205
+ | Argument / Option | Type | Default | Description |
206
+ | ----------------------- | --------------------- | ------- | ------------------------------------ |
207
+ | `[prompt]` | `string` (positional) | — | Prompted interactively if omitted |
208
+ | `-n, --count <number>` | `string → int` | `10` | Slide count (parsed via `parseInt`) |
209
+ | `-l, --language <lang>` | `string` | `en` | ISO 639-1 language code |
210
+ | `-t, --theme <theme>` | `string` | none | Theme identifier |
211
+
212
+ POST `/v1/platform/slides` body:
213
+
214
+ ```json
215
+ {
216
+ "prompt": "...",
217
+ "slideCount": 10,
218
+ "language": "en",
219
+ "theme": "...",
220
+ "mode": "sync"
221
+ }
222
+ ```
223
+
224
+ Output: `id`, `status`, `previewUrl` (if returned). After creation, prompts to open the preview URL in the browser.
225
+
226
+ ```bash
227
+ tutorflow slide create "Photosynthesis for 5th graders" -n 12 -l en -t classic
228
+ ```
229
+
230
+ #### `slide list`
231
+
232
+ | Option | Type | Default | Description |
233
+ | ----------------------- | -------- | ------- | -------------------------- |
234
+ | `-l, --limit <number>` | `string` | `10` | Max rows to fetch |
235
+ | `-s, --status <status>` | `string` | none | Server-side status filter |
236
+
237
+ GET `/v1/platform/slides?limit=<n>[&status=<s>]`. Renders an ASCII table with `ID` (8-char prefix), `Title`, `Status`, `Created`.
238
+
239
+ #### `slide open <id>`
240
+
241
+ Fetches `/v1/platform/slides/:id` and opens `previewUrl` in the default browser via [`open`](https://github.com/sindresorhus/open).
242
+
243
+ ---
244
+
245
+ ### `tutorflow video`
246
+
247
+ Create and manage AI-generated videos.
248
+
249
+ #### `video create [prompt]`
250
+
251
+ Creates a video synchronously (`mode: 'sync'`).
252
+
253
+ | Argument / Option | Type | Default | Description |
254
+ | -------------------------- | --------------------- | ------- | ---------------------------------------- |
255
+ | `[prompt]` | `string` (positional) | — | Prompted interactively if omitted |
256
+ | `-s, --scenes <number>` | `string → int` | `5` | Scene count |
257
+ | `-r, --ratio <ratio>` | `string` | `16:9` | Aspect ratio (`16:9`, `9:16`, `1:1`, …) |
258
+ | `-d, --duration <seconds>` | `string → int` | `60` | Target duration in seconds |
259
+ | `-l, --language <lang>` | `string` | `en` | ISO 639-1 language code |
260
+
261
+ POST `/v1/platform/videos` body:
262
+
263
+ ```json
264
+ {
265
+ "prompt": "...",
266
+ "sceneCount": 5,
267
+ "aspectRatio": "16:9",
268
+ "targetDurationSeconds": 60,
269
+ "language": "en",
270
+ "mode": "sync"
271
+ }
272
+ ```
273
+
274
+ Output: `id`, `status`, `sceneCount`, `previewUrl` (if returned). Prompts to open in browser.
275
+
276
+ ```bash
277
+ tutorflow video create "Intro to Newton's laws" -s 6 -r 16:9 -d 90 -l en
278
+ ```
279
+
280
+ #### `video list`
281
+
282
+ | Option | Type | Default | Description |
283
+ | ----------------------- | -------- | ------- | -------------------------- |
284
+ | `-l, --limit <number>` | `string` | `10` | Max rows |
285
+ | `-s, --status <status>` | `string` | none | Server-side status filter |
286
+
287
+ GET `/v1/platform/videos?limit=<n>[&status=<s>]`. Table columns: `ID`, `Title`, `Status`, `Scenes`, `Created`.
288
+
289
+ #### `video open <id>`
290
+
291
+ Opens the video preview URL in the browser.
292
+
293
+ ---
294
+
295
+ ### `tutorflow course`
296
+
297
+ Create and manage multi-lesson courses.
298
+
299
+ #### `course create [prompt]`
300
+
301
+ | Argument / Option | Type | Default | Description |
302
+ | -------------------------- | --------------------- | ------- | --------------------------------- |
303
+ | `[prompt]` | `string` (positional) | — | Prompted interactively if omitted |
304
+ | `-l, --lessons <number>` | `string → int` | `8` | Lesson count |
305
+ | `-lang, --language <lang>` | `string` | `en` | ISO 639-1 language code |
306
+
307
+ > Note: `--language` uses the short flag `-lang` (not `-L`), because `-l` is already taken by `--lessons`.
308
+
309
+ POST `/v1/platform/courses` body:
310
+
311
+ ```json
312
+ {
313
+ "prompt": "...",
314
+ "lessonCount": 8,
315
+ "language": "en",
316
+ "mode": "sync"
317
+ }
318
+ ```
319
+
320
+ ```bash
321
+ tutorflow course create "Beginner Python in Korean" -l 10 -lang ko
322
+ ```
323
+
324
+ #### `course list`
325
+
326
+ | Option | Type | Default | Description |
327
+ | ---------------------- | -------- | ------- | ----------- |
328
+ | `-l, --limit <number>` | `string` | `10` | Max rows |
329
+
330
+ GET `/v1/platform/courses?limit=<n>`. Table columns: `ID`, `Title`, `Status`, `Created`.
331
+
332
+ #### `course open <id>`
333
+
334
+ Opens the course preview URL in the browser.
335
+
336
+ ---
337
+
338
+ ### `tutorflow evaluate`
339
+
340
+ Evaluates a single student response. Fully interactive; only the type can be passed as a flag.
341
+
342
+ | Option | Type | Default | Allowed values |
343
+ | ------------------- | -------- | ---------- | ------------------------------- |
344
+ | `-t, --type <type>` | `string` | (prompted) | `exact`, `rubric`, `open-ended` |
345
+
346
+ Interactive flow:
347
+
348
+ 1. Type (if not supplied)
349
+ 2. Question
350
+ 3. Student answer
351
+ 4. Expected answer — **skipped** when type is `open-ended`
352
+
353
+ POST `/v1/platform/evaluate` body:
354
+
355
+ ```json
356
+ {
357
+ "evaluationType": "rubric",
358
+ "question": "...",
359
+ "studentAnswer": "...",
360
+ "expectedAnswer": "..."
361
+ }
362
+ ```
363
+
364
+ `expectedAnswer` is omitted when `evaluationType` is `open-ended`. Response prints `score` and `feedback`.
365
+
366
+ ```bash
367
+ tutorflow evaluate -t open-ended
368
+ ```
369
+
370
+ ---
371
+
372
+ ### `tutorflow auth`
373
+
374
+ | Subcommand | Description |
375
+ | ---------- | ---------------------------------------------------- |
376
+ | `login` | Interactive prompt for API key, writes to config |
377
+ | `logout` | Prompts to confirm, then deletes config file |
378
+ | `whoami` | Prints masked API key (first 10 chars) and `baseUrl` |
379
+
380
+ ## Exit behavior & errors
381
+
382
+ - All commands wrap their action in `try/catch`. Failures print `✗ <message>` in red and the raw error, but the process exit code is **not** set explicitly — it's whatever Node returns (typically `0`). Don't rely on exit codes for scripting yet.
383
+ - `await prompt(...)` from `@clack/prompts` returns a `symbol` when the user cancels (Ctrl+C); commands check for this and print `Cancelled` instead of throwing.
384
+ - HTTP errors from the API are surfaced as `Error: API Error: <status> <body-json>`.
385
+
386
+ ## API client
387
+
388
+ Internal HTTP client lives in [src/client.ts](src/client.ts):
389
+
390
+ ```ts
391
+ class ApiClient {
392
+ static async create(): Promise<ApiClient>; // loads from ~/.tutorflow/config.json
393
+ request<T>(method: string, endpoint: string, body?: object): Promise<T>;
394
+ setApiKey(apiKey: string): void;
395
+ setBaseUrl(baseUrl: string): void;
396
+ }
397
+ ```
398
+
399
+ - Uses [`undici.fetch`](https://github.com/nodejs/undici).
400
+ - Headers always include `Content-Type: application/json` and `X-API-Key: <apiKey>`.
401
+ - Throws `Error('API Error: <status> <body>')` on non-2xx.
402
+
403
+ ## Project layout
404
+
405
+ ```
406
+ apps/cli/
407
+ ├── package.json # bin: { tutorflow: ./dist/index.js }
408
+ ├── tsconfig.json
409
+ └── src/
410
+ ├── index.ts # commander entrypoint, registers all commands
411
+ ├── client.ts # undici-based ApiClient
412
+ ├── config.ts # ~/.tutorflow/config.json read/write/update/delete
413
+ ├── commands/
414
+ │ ├── auth.ts # login / logout / whoami
415
+ │ ├── config.ts # get / set
416
+ │ ├── course.ts # create / list / open
417
+ │ ├── evaluate.ts # interactive evaluation
418
+ │ ├── slide.ts # create / list / open
419
+ │ └── video.ts # create / list / open
420
+ └── utils/
421
+ ├── output.ts # formatTable, formatJson
422
+ └── spinner.ts
423
+ ```
424
+
425
+ ## npm scripts
426
+
427
+ | Script | Command | Purpose |
428
+ | ------- | ------------------- | ---------------------------- |
429
+ | `dev` | `tsx src/index.ts` | Run from source |
430
+ | `build` | `tsc --outDir dist` | Emit JS + `.d.ts` to `dist/` |
431
+ | `link` | `npm link` | Symlink `tutorflow` globally |
432
+ | `test` | `jest` | (No tests committed yet) |
433
+
434
+ ## Stack
435
+
436
+ - **CLI framework:** [commander](https://github.com/tj/commander.js) `^11.0.0`
437
+ - **Prompts:** [@clack/prompts](https://github.com/natemoo-re/clack) `^0.7.0`
438
+ - **HTTP:** [undici](https://github.com/nodejs/undici) `^5.27.0`
439
+ - **Color:** [chalk](https://github.com/chalk/chalk) `^5.3.0`
440
+ - **Browser open:** [open](https://github.com/sindresorhus/open) `^9.0.0`
441
+ - **Dev:** TypeScript `^5.0.0`, tsx `^3.0.0`, `@types/node` `^20.0.0`
@@ -0,0 +1,13 @@
1
+ export interface IClientConfig {
2
+ apiKey: string;
3
+ baseUrl: string;
4
+ }
5
+ export declare class ApiClient {
6
+ private config;
7
+ constructor(config?: IClientConfig);
8
+ static create(): Promise<ApiClient>;
9
+ request<T>(method: string, endpoint: string, body?: Record<string, unknown>): Promise<T>;
10
+ setApiKey(apiKey: string): void;
11
+ setBaseUrl(baseUrl: string): void;
12
+ }
13
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,qBAAa,SAAS;IACpB,OAAO,CAAC,MAAM,CAAgB;gBAElB,MAAM,CAAC,EAAE,aAAa;WAOrB,MAAM,IAAI,OAAO,CAAC,SAAS,CAAC;IAQnC,OAAO,CAAC,CAAC,EACb,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,EAChB,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC7B,OAAO,CAAC,CAAC,CAAC;IA0Bb,SAAS,CAAC,MAAM,EAAE,MAAM;IAIxB,UAAU,CAAC,OAAO,EAAE,MAAM;CAG3B"}
package/dist/client.js ADDED
@@ -0,0 +1,48 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ApiClient = void 0;
4
+ const undici_1 = require("undici");
5
+ const config_1 = require("./config");
6
+ class ApiClient {
7
+ constructor(config) {
8
+ this.config = config || {
9
+ apiKey: '',
10
+ baseUrl: 'https://api.tutorflow.io',
11
+ };
12
+ }
13
+ static async create() {
14
+ const config = await (0, config_1.readConfig)();
15
+ return new ApiClient({
16
+ apiKey: config.apiKey || '',
17
+ baseUrl: config.baseUrl || 'https://api.tutorflow.io',
18
+ });
19
+ }
20
+ async request(method, endpoint, body) {
21
+ const url = `${this.config.baseUrl}${endpoint}`;
22
+ const headers = {
23
+ 'Content-Type': 'application/json',
24
+ 'X-API-Key': this.config.apiKey,
25
+ };
26
+ const init = {
27
+ method,
28
+ headers,
29
+ };
30
+ if (body) {
31
+ init.body = JSON.stringify(body);
32
+ }
33
+ const response = await (0, undici_1.fetch)(url, init);
34
+ const data = await response.json();
35
+ if (!response.ok) {
36
+ throw new Error(`API Error: ${response.status} ${JSON.stringify(data)}`);
37
+ }
38
+ return data;
39
+ }
40
+ setApiKey(apiKey) {
41
+ this.config.apiKey = apiKey;
42
+ }
43
+ setBaseUrl(baseUrl) {
44
+ this.config.baseUrl = baseUrl;
45
+ }
46
+ }
47
+ exports.ApiClient = ApiClient;
48
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":";;;AAAA,mCAAyC;AACzC,qCAAsC;AAOtC,MAAa,SAAS;IAGpB,YAAY,MAAsB;QAChC,IAAI,CAAC,MAAM,GAAG,MAAM,IAAI;YACtB,MAAM,EAAE,EAAE;YACV,OAAO,EAAE,0BAA0B;SACpC,CAAC;IACJ,CAAC;IAED,MAAM,CAAC,KAAK,CAAC,MAAM;QACjB,MAAM,MAAM,GAAG,MAAM,IAAA,mBAAU,GAAE,CAAC;QAClC,OAAO,IAAI,SAAS,CAAC;YACnB,MAAM,EAAE,MAAM,CAAC,MAAM,IAAI,EAAE;YAC3B,OAAO,EAAE,MAAM,CAAC,OAAO,IAAI,0BAA0B;SACtD,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,OAAO,CACX,MAAc,EACd,QAAgB,EAChB,IAA8B;QAE9B,MAAM,GAAG,GAAG,GAAG,IAAI,CAAC,MAAM,CAAC,OAAO,GAAG,QAAQ,EAAE,CAAC;QAChD,MAAM,OAAO,GAA2B;YACtC,cAAc,EAAE,kBAAkB;YAClC,WAAW,EAAE,IAAI,CAAC,MAAM,CAAC,MAAM;SAChC,CAAC;QAEF,MAAM,IAAI,GAAuE;YAC/E,MAAM;YACN,OAAO;SACR,CAAC;QAEF,IAAI,IAAI,EAAE,CAAC;YACT,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;QACnC,CAAC;QAED,MAAM,QAAQ,GAAG,MAAM,IAAA,cAAM,EAAC,GAAG,EAAE,IAAI,CAAC,CAAC;QACzC,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;QAEnC,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;YACjB,MAAM,IAAI,KAAK,CAAC,cAAc,QAAQ,CAAC,MAAM,IAAI,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAC3E,CAAC;QAED,OAAO,IAAS,CAAC;IACnB,CAAC;IAED,SAAS,CAAC,MAAc;QACtB,IAAI,CAAC,MAAM,CAAC,MAAM,GAAG,MAAM,CAAC;IAC9B,CAAC;IAED,UAAU,CAAC,OAAe;QACxB,IAAI,CAAC,MAAM,CAAC,OAAO,GAAG,OAAO,CAAC;IAChC,CAAC;CACF;AAvDD,8BAuDC"}
@@ -0,0 +1,3 @@
1
+ import { Command } from 'commander';
2
+ export declare function authCommand(): Command;
3
+ //# sourceMappingURL=auth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/commands/auth.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAKpC,wBAAgB,WAAW,IAAI,OAAO,CAoErC"}
@@ -0,0 +1,72 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.authCommand = authCommand;
7
+ const commander_1 = require("commander");
8
+ const prompts_1 = require("@clack/prompts");
9
+ const chalk_1 = __importDefault(require("chalk"));
10
+ const config_1 = require("../config");
11
+ function authCommand() {
12
+ const auth = new commander_1.Command('auth').description('Manage authentication');
13
+ auth
14
+ .command('login')
15
+ .description('Authenticate with your API key')
16
+ .action(async () => {
17
+ try {
18
+ const apiKey = await (0, prompts_1.password)({
19
+ message: 'Enter your TutorFlow API key:',
20
+ });
21
+ if (typeof apiKey === 'symbol') {
22
+ console.log(chalk_1.default.red('Authentication cancelled'));
23
+ return;
24
+ }
25
+ await (0, config_1.updateConfig)({ apiKey });
26
+ console.log(chalk_1.default.green('✓ Authentication successful!'));
27
+ console.log(chalk_1.default.gray(`API key saved to ~/.tutorflow/config.json`));
28
+ }
29
+ catch (error) {
30
+ console.error(chalk_1.default.red('✗ Authentication failed'), error);
31
+ }
32
+ });
33
+ auth
34
+ .command('logout')
35
+ .description('Remove stored credentials')
36
+ .action(async () => {
37
+ try {
38
+ const confirmed = await (0, prompts_1.confirm)({
39
+ message: 'Remove stored credentials?',
40
+ });
41
+ if (!confirmed) {
42
+ console.log(chalk_1.default.gray('Cancelled'));
43
+ return;
44
+ }
45
+ await (0, config_1.deleteConfig)();
46
+ console.log(chalk_1.default.green('✓ Credentials removed'));
47
+ }
48
+ catch (error) {
49
+ console.error(chalk_1.default.red('✗ Failed to logout'), error);
50
+ }
51
+ });
52
+ auth
53
+ .command('whoami')
54
+ .description('Show current authenticated workspace')
55
+ .action(async () => {
56
+ try {
57
+ const config = await (0, config_1.readConfig)();
58
+ if (!config.apiKey) {
59
+ console.log(chalk_1.default.yellow('Not authenticated'));
60
+ return;
61
+ }
62
+ console.log(chalk_1.default.green('✓ Authenticated as:'));
63
+ console.log(chalk_1.default.gray(` API Key: ${config.apiKey.slice(0, 10)}...`));
64
+ console.log(chalk_1.default.gray(` Base URL: ${config.baseUrl || 'https://api.tutorflow.io'}`));
65
+ }
66
+ catch (error) {
67
+ console.error(chalk_1.default.red('✗ Error:'), error);
68
+ }
69
+ });
70
+ return auth;
71
+ }
72
+ //# sourceMappingURL=auth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/commands/auth.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import { Command } from 'commander';
2
+ export declare function configCommand(): Command;
3
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/commands/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAIpC,wBAAgB,aAAa,IAAI,OAAO,CAuDvC"}