@positronic/cli 0.0.2 → 0.0.4

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 (83) hide show
  1. package/dist/src/cli.js +16 -1
  2. package/dist/src/commands/helpers.js +11 -25
  3. package/dist/types/cli.d.ts.map +1 -1
  4. package/dist/types/commands/helpers.d.ts.map +1 -1
  5. package/package.json +11 -4
  6. package/dist/src/commands/brain.test.js +0 -2936
  7. package/dist/src/commands/helpers.test.js +0 -832
  8. package/dist/src/commands/project.test.js +0 -1201
  9. package/dist/src/commands/resources.test.js +0 -2511
  10. package/dist/src/commands/schedule.test.js +0 -1235
  11. package/dist/src/commands/secret.test.d.js +0 -1
  12. package/dist/src/commands/secret.test.js +0 -761
  13. package/dist/src/commands/server.test.js +0 -1237
  14. package/dist/src/commands/test-utils.js +0 -737
  15. package/dist/src/components/secret-sync.js +0 -303
  16. package/dist/src/test/mock-api-client.js +0 -371
  17. package/dist/src/test/test-dev-server.js +0 -1376
  18. package/dist/types/commands/test-utils.d.ts +0 -45
  19. package/dist/types/commands/test-utils.d.ts.map +0 -1
  20. package/dist/types/components/secret-sync.d.ts +0 -9
  21. package/dist/types/components/secret-sync.d.ts.map +0 -1
  22. package/dist/types/test/mock-api-client.d.ts +0 -25
  23. package/dist/types/test/mock-api-client.d.ts.map +0 -1
  24. package/dist/types/test/test-dev-server.d.ts +0 -129
  25. package/dist/types/test/test-dev-server.d.ts.map +0 -1
  26. package/src/cli.ts +0 -981
  27. package/src/commands/backend.ts +0 -63
  28. package/src/commands/brain.test.ts +0 -1004
  29. package/src/commands/brain.ts +0 -215
  30. package/src/commands/helpers.test.ts +0 -487
  31. package/src/commands/helpers.ts +0 -870
  32. package/src/commands/project-config-manager.ts +0 -152
  33. package/src/commands/project.test.ts +0 -502
  34. package/src/commands/project.ts +0 -109
  35. package/src/commands/resources.test.ts +0 -1052
  36. package/src/commands/resources.ts +0 -97
  37. package/src/commands/schedule.test.ts +0 -481
  38. package/src/commands/schedule.ts +0 -65
  39. package/src/commands/secret.test.ts +0 -210
  40. package/src/commands/secret.ts +0 -50
  41. package/src/commands/server.test.ts +0 -493
  42. package/src/commands/server.ts +0 -353
  43. package/src/commands/test-utils.ts +0 -324
  44. package/src/components/brain-history.tsx +0 -198
  45. package/src/components/brain-list.tsx +0 -105
  46. package/src/components/brain-rerun.tsx +0 -111
  47. package/src/components/brain-show.tsx +0 -92
  48. package/src/components/error.tsx +0 -24
  49. package/src/components/project-add.tsx +0 -59
  50. package/src/components/project-create.tsx +0 -83
  51. package/src/components/project-list.tsx +0 -83
  52. package/src/components/project-remove.tsx +0 -55
  53. package/src/components/project-select.tsx +0 -200
  54. package/src/components/project-show.tsx +0 -58
  55. package/src/components/resource-clear.tsx +0 -127
  56. package/src/components/resource-delete.tsx +0 -160
  57. package/src/components/resource-list.tsx +0 -177
  58. package/src/components/resource-sync.tsx +0 -170
  59. package/src/components/resource-types.tsx +0 -55
  60. package/src/components/resource-upload.tsx +0 -182
  61. package/src/components/schedule-create.tsx +0 -90
  62. package/src/components/schedule-delete.tsx +0 -116
  63. package/src/components/schedule-list.tsx +0 -186
  64. package/src/components/schedule-runs.tsx +0 -151
  65. package/src/components/secret-bulk.tsx +0 -79
  66. package/src/components/secret-create.tsx +0 -49
  67. package/src/components/secret-delete.tsx +0 -41
  68. package/src/components/secret-list.tsx +0 -41
  69. package/src/components/watch.tsx +0 -155
  70. package/src/hooks/useApi.ts +0 -183
  71. package/src/positronic.ts +0 -40
  72. package/src/test/data/resources/config.json +0 -1
  73. package/src/test/data/resources/data/config.json +0 -1
  74. package/src/test/data/resources/data/logo.png +0 -2
  75. package/src/test/data/resources/docs/api.md +0 -3
  76. package/src/test/data/resources/docs/readme.md +0 -3
  77. package/src/test/data/resources/example.md +0 -3
  78. package/src/test/data/resources/file with spaces.txt +0 -1
  79. package/src/test/data/resources/readme.md +0 -3
  80. package/src/test/data/resources/test.txt +0 -1
  81. package/src/test/mock-api-client.ts +0 -145
  82. package/src/test/test-dev-server.ts +0 -1003
  83. package/tsconfig.json +0 -11
package/src/cli.ts DELETED
@@ -1,981 +0,0 @@
1
- import yargs from 'yargs';
2
- import { hideBin } from 'yargs/helpers';
3
- import { ProjectCommand } from './commands/project.js';
4
- import { ServerCommand } from './commands/server.js';
5
- import { BrainCommand } from './commands/brain.js';
6
- import { ResourcesCommand } from './commands/resources.js';
7
- import { ScheduleCommand } from './commands/schedule.js';
8
- import { SecretCommand } from './commands/secret.js';
9
- import type { PositronicDevServer } from '@positronic/spec';
10
-
11
- export interface CliOptions {
12
- argv?: string[];
13
- server?: PositronicDevServer;
14
- exitProcess?: boolean;
15
- render: (element: React.ReactElement) => any;
16
- }
17
-
18
- export function buildCli(options: CliOptions) {
19
- const {
20
- argv = hideBin(process.argv),
21
- server,
22
- exitProcess = false,
23
- render,
24
- } = options;
25
-
26
- const isLocalDevMode = server !== undefined;
27
-
28
- // Instantiate command classes, passing the determined mode and path
29
- const projectCommand = new ProjectCommand();
30
- const brainCommand = new BrainCommand();
31
- const scheduleCommand = new ScheduleCommand();
32
- const secretCommand = new SecretCommand(server);
33
-
34
- // Main CLI definition
35
- let cli = yargs(argv)
36
- .scriptName('positronic')
37
- .usage('Usage: $0 <command> [options]')
38
- .version()
39
- .alias('v', 'version')
40
- .help('h')
41
- .alias('h', 'help')
42
- .wrap(null)
43
- .strictCommands()
44
- .exitProcess(exitProcess);
45
-
46
- // --- Project Management Commands (Global Mode Only) ---
47
- cli = cli.command(
48
- 'project',
49
- 'Manage your Positronic projects\n',
50
- (yargsProject) => {
51
- if (!isLocalDevMode) {
52
- yargsProject.command(
53
- 'new <name>',
54
- 'Create a new Positronic project directory',
55
- (yargsNew) => {
56
- return yargsNew.positional('name', {
57
- describe: 'Name of the new project directory to create',
58
- type: 'string',
59
- demandOption: true,
60
- });
61
- },
62
- (argv) => {
63
- const element = projectCommand.create(argv);
64
- render(element);
65
- }
66
- );
67
- }
68
-
69
- yargsProject
70
- .command(
71
- 'add <name>',
72
- 'Add a project to your list of projects',
73
- (yargsAdd) => {
74
- return yargsAdd
75
- .positional('name', {
76
- describe: 'Name for the project',
77
- type: 'string',
78
- demandOption: true,
79
- })
80
- .option('url', {
81
- describe: 'Project API URL',
82
- type: 'string',
83
- demandOption: true,
84
- })
85
- .example(
86
- '$0 project add my-project --url https://api.my-project.positronic.sh',
87
- 'Add a project configuration'
88
- );
89
- },
90
- (argv) => {
91
- const element = projectCommand.add(argv);
92
- render(element);
93
- }
94
- )
95
- .command(
96
- 'select [name]',
97
- 'Switch the active project',
98
- (yargsSelect) => {
99
- return yargsSelect
100
- .positional('name', {
101
- describe: 'Project name to select',
102
- type: 'string',
103
- })
104
- .example(
105
- '$0 project select my-project',
106
- 'Switch the active project'
107
- )
108
- .example('$0 project select', 'Interactive project selection');
109
- },
110
- (argv) => {
111
- const element = projectCommand.select(argv);
112
- render(element);
113
- }
114
- )
115
- .command(
116
- 'list',
117
- 'List all of your Positronic projects',
118
- () => {},
119
- () => {
120
- const element = projectCommand.list();
121
- render(element);
122
- }
123
- )
124
- .command(
125
- 'show',
126
- 'Display your currently selected project',
127
- () => {},
128
- () => {
129
- const element = projectCommand.show();
130
- render(element);
131
- }
132
- )
133
- .command(
134
- 'rm <name>',
135
- 'Remove a project from your list of projects',
136
- (yargsRm) => {
137
- return yargsRm
138
- .positional('name', {
139
- describe: 'Name of the project to remove',
140
- type: 'string',
141
- demandOption: true,
142
- })
143
- .example(
144
- '$0 project rm my-project',
145
- 'Remove a project configuration'
146
- );
147
- },
148
- (argv) => {
149
- const element = projectCommand.remove(argv);
150
- render(element);
151
- }
152
- )
153
- .demandCommand(
154
- 1,
155
- 'You need to specify a project command (add, select, list, show, rm) in Local Dev Mode.'
156
- );
157
-
158
- return yargsProject;
159
- }
160
- );
161
-
162
- // --- Add the 'new' command alias (Global Mode Only) ---
163
- if (!isLocalDevMode) {
164
- cli = cli.command(
165
- 'new <name>',
166
- 'Alias for `project new`. Create a new Positronic project directory.\n',
167
- (yargsNewAlias) => {
168
- return yargsNewAlias.positional('name', {
169
- describe: 'Name of the new project directory to create',
170
- type: 'string',
171
- demandOption: true,
172
- });
173
- },
174
- (argv) => {
175
- const element = projectCommand.create(argv);
176
- render(element);
177
- }
178
- );
179
- }
180
-
181
- // --- Add the Server Command ---
182
- // The server command should only be available in local dev mode;
183
- // Wrap its registration in the check
184
- if (isLocalDevMode) {
185
- const serverCommand = new ServerCommand(server);
186
- cli = cli.command(
187
- ['server', 's'],
188
- 'Start the local development server for the current project',
189
- (yargsServer) => {
190
- return yargsServer
191
- .option('force', {
192
- describe: 'Force regeneration of the .positronic server directory',
193
- type: 'boolean',
194
- default: false,
195
- })
196
- .option('port', {
197
- describe: 'Port number for the server to listen on',
198
- type: 'number',
199
- alias: 'p',
200
- })
201
- .option('log-file', {
202
- describe: 'File to redirect server output to (for AI agents)',
203
- type: 'string',
204
- alias: 'l',
205
- })
206
- .option('d', {
207
- describe: 'Run server in detached/background mode',
208
- type: 'boolean',
209
- default: false,
210
- })
211
- .option('k', {
212
- describe: 'Kill the default background server',
213
- type: 'boolean',
214
- default: false,
215
- });
216
- },
217
- (argv) => serverCommand.handle(argv)
218
- );
219
-
220
- // Add deploy command (only in local dev mode)
221
- cli = cli.command(
222
- 'deploy',
223
- 'Deploy the project to production',
224
- () => {},
225
- async () => {
226
- try {
227
- // Deploy
228
- await server.deploy();
229
- } catch (error) {
230
- console.error(
231
- 'Deployment failed:',
232
- error instanceof Error ? error.message : String(error)
233
- );
234
- process.exit(1);
235
- }
236
- }
237
- );
238
- }
239
-
240
- // --- List Brains Command ---
241
- cli = cli.command(
242
- 'list',
243
- 'List all brains in the active project\n',
244
- () => {},
245
- (argv) => {
246
- const element = brainCommand.list(argv);
247
- render(element);
248
- }
249
- );
250
-
251
- // --- Brain History Command ---
252
- cli = cli.command(
253
- 'history <name>',
254
- 'List recent runs of a specific brain\n',
255
- (yargsHistory) => {
256
- return yargsHistory
257
- .positional('name', {
258
- describe: 'Name of the brain',
259
- type: 'string',
260
- demandOption: true,
261
- })
262
- .option('limit', {
263
- describe: 'Maximum number of runs to show',
264
- type: 'number',
265
- default: 10,
266
- })
267
- .example('$0 history my-brain', 'List recent runs for my-brain')
268
- .example('$0 history my-brain --limit=20', 'List more recent runs');
269
- },
270
- (argv) => {
271
- const element = brainCommand.history(argv);
272
- render(element);
273
- }
274
- );
275
-
276
- // --- Show Brain Command ---
277
- cli = cli.command(
278
- 'show <name>',
279
- 'List all steps and other details for the brain\n',
280
- (yargsShow) => {
281
- return yargsShow.positional('name', {
282
- describe: 'Name of the brain',
283
- type: 'string',
284
- demandOption: true,
285
- });
286
- },
287
- (argv) => {
288
- const element = brainCommand.show(argv);
289
- render(element);
290
- }
291
- );
292
-
293
- // --- Rerun Brain Command ---
294
- cli = cli.command(
295
- 'rerun <name> [run-id]',
296
- 'Rerun an existing brain run\n',
297
- (yargsRerun) => {
298
- return yargsRerun
299
- .positional('name', {
300
- describe: 'Name of the brain',
301
- type: 'string',
302
- demandOption: true,
303
- })
304
- .positional('run-id', {
305
- describe:
306
- 'ID of the brain run to rerun (defaults to the most recent run)',
307
- type: 'string',
308
- })
309
- .option('starts-at', {
310
- describe: 'Step number to start execution from',
311
- type: 'number',
312
- })
313
- .alias('starts-at', 's')
314
- .option('stops-after', {
315
- describe: 'Step number to stop execution after',
316
- type: 'number',
317
- })
318
- .alias('stops-after', 'e')
319
- .example(
320
- '$0 rerun my-brain',
321
- 'Rerun the most recent execution of my-brain'
322
- )
323
- .example('$0 rerun my-brain abc123', 'Rerun a specific brain run')
324
- .example('$0 rerun my-brain --starts-at=3', 'Rerun from step 3')
325
- .example(
326
- '$0 rerun my-brain --stops-after=5',
327
- 'Rerun and stop after step 5'
328
- )
329
- .example(
330
- '$0 rerun my-brain --starts-at=3 --stops-after=5',
331
- 'Rerun steps 3 through 5'
332
- );
333
- },
334
- (argv) => {
335
- const element = brainCommand.rerun(argv);
336
- render(element);
337
- }
338
- );
339
-
340
- // --- Run Brain Command ---
341
- cli = cli.command(
342
- 'run <name>',
343
- 'Run a brain and optionally watch its execution\n',
344
- (yargsRun) => {
345
- return yargsRun
346
- .positional('name', {
347
- describe: 'Name of the brain',
348
- type: 'string',
349
- demandOption: true,
350
- })
351
- .option('watch', {
352
- describe: 'Watch the brain run immediately after starting',
353
- type: 'boolean',
354
- alias: 'w',
355
- default: false,
356
- })
357
- .example('$0 run my-brain', 'Run a brain by name')
358
- .example(
359
- '$0 run my-brain --watch',
360
- 'Run a brain and watch its execution'
361
- );
362
- },
363
- async (argv) => {
364
- const element = await brainCommand.run(argv);
365
- if (element) {
366
- render(element);
367
- }
368
- }
369
- );
370
-
371
- // --- Watch Brain Run Command ---
372
- cli = cli.command(
373
- 'watch [name]',
374
- 'Watch a brain run: latest by name (default) or specific by ID\n',
375
- (yargsWatch) => {
376
- return yargsWatch
377
- .positional('name', {
378
- describe: 'Name of the brain to watch (watches the most recent run)',
379
- type: 'string',
380
- })
381
- .option('run-id', {
382
- describe: 'ID of the specific brain run to watch',
383
- type: 'string',
384
- alias: 'id',
385
- })
386
- .conflicts('name', 'run-id')
387
- .check((argv) => {
388
- if (!argv.name && !argv.runId) {
389
- throw new Error(
390
- 'You must provide either a brain name or a --run-id.'
391
- );
392
- }
393
- return true;
394
- })
395
- .example(
396
- '$0 watch my-brain',
397
- "Watch the latest run of the brain named 'my-brain'"
398
- )
399
- .example(
400
- '$0 watch --run-id abc123def',
401
- 'Watch a specific brain run by its ID'
402
- );
403
- },
404
- async (argv) => {
405
- const element = await brainCommand.watch(argv);
406
- if (element) {
407
- render(element);
408
- }
409
- }
410
- );
411
-
412
- // --- Brain Commands ---
413
- cli = cli.command('brain', 'Manage your brains\n', (yargsBrain) => {
414
- yargsBrain
415
- .command(
416
- 'list',
417
- 'List all brains in the active project\n',
418
- () => {},
419
- (argv) => {
420
- const element = brainCommand.list(argv);
421
- render(element);
422
- }
423
- )
424
- .command(
425
- 'history <name>',
426
- 'List recent runs of a specific brain\n',
427
- (yargsHistory) => {
428
- return yargsHistory
429
- .positional('name', {
430
- describe: 'Name of the brain',
431
- type: 'string',
432
- demandOption: true,
433
- })
434
- .option('limit', {
435
- describe: 'Maximum number of runs to show',
436
- type: 'number',
437
- default: 10,
438
- })
439
- .example('$0 brain history my-brain', 'List recent runs for my-brain')
440
- .example('$0 brain history my-brain --limit=20', 'List more recent runs');
441
- },
442
- (argv) => {
443
- const element = brainCommand.history(argv);
444
- render(element);
445
- }
446
- )
447
- .command(
448
- 'show <name>',
449
- 'List all steps and other details for the brain\n',
450
- (yargsShow) => {
451
- return yargsShow.positional('name', {
452
- describe: 'Name of the brain',
453
- type: 'string',
454
- demandOption: true,
455
- });
456
- },
457
- (argv) => {
458
- const element = brainCommand.show(argv);
459
- render(element);
460
- }
461
- )
462
- .command(
463
- 'rerun <name> [run-id]',
464
- 'Rerun an existing brain run\n',
465
- (yargsRerun) => {
466
- return yargsRerun
467
- .positional('name', {
468
- describe: 'Name of the brain',
469
- type: 'string',
470
- demandOption: true,
471
- })
472
- .positional('run-id', {
473
- describe:
474
- 'ID of the brain run to rerun (defaults to the most recent run)',
475
- type: 'string',
476
- })
477
- .option('starts-at', {
478
- describe: 'Step number to start execution from',
479
- type: 'number',
480
- })
481
- .alias('starts-at', 's')
482
- .option('stops-after', {
483
- describe: 'Step number to stop execution after',
484
- type: 'number',
485
- })
486
- .alias('stops-after', 'e')
487
- .example(
488
- '$0 brain rerun my-brain',
489
- 'Rerun the most recent execution of my-brain'
490
- )
491
- .example('$0 brain rerun my-brain abc123', 'Rerun a specific brain run')
492
- .example('$0 brain rerun my-brain --starts-at=3', 'Rerun from step 3')
493
- .example(
494
- '$0 brain rerun my-brain --stops-after=5',
495
- 'Rerun and stop after step 5'
496
- )
497
- .example(
498
- '$0 brain rerun my-brain --starts-at=3 --stops-after=5',
499
- 'Rerun steps 3 through 5'
500
- );
501
- },
502
- (argv) => {
503
- const element = brainCommand.rerun(argv);
504
- render(element);
505
- }
506
- )
507
- .command(
508
- 'run <name>',
509
- 'Run a brain and optionally watch its execution\n',
510
- (yargsRun) => {
511
- return yargsRun
512
- .positional('name', {
513
- describe: 'Name of the brain',
514
- type: 'string',
515
- demandOption: true,
516
- })
517
- .option('watch', {
518
- describe: 'Watch the brain run immediately after starting',
519
- type: 'boolean',
520
- alias: 'w',
521
- default: false,
522
- })
523
- .example('$0 brain run my-brain', 'Run a brain by name')
524
- .example(
525
- '$0 brain run my-brain --watch',
526
- 'Run a brain and watch its execution'
527
- );
528
- },
529
- async (argv) => {
530
- const element = await brainCommand.run(argv);
531
- if (element) {
532
- render(element);
533
- }
534
- }
535
- )
536
- .command(
537
- 'watch [name]',
538
- 'Watch a brain run: latest by name (default) or specific by ID\n',
539
- (yargsWatch) => {
540
- return yargsWatch
541
- .positional('name', {
542
- describe: 'Name of the brain to watch (watches the most recent run)',
543
- type: 'string',
544
- })
545
- .option('run-id', {
546
- describe: 'ID of the specific brain run to watch',
547
- type: 'string',
548
- alias: 'id',
549
- })
550
- .conflicts('name', 'run-id')
551
- .check((argv) => {
552
- if (!argv.name && !argv.runId) {
553
- throw new Error(
554
- 'You must provide either a brain name or a --run-id.'
555
- );
556
- }
557
- return true;
558
- })
559
- .example(
560
- '$0 brain watch my-brain',
561
- "Watch the latest run of the brain named 'my-brain'"
562
- )
563
- .example(
564
- '$0 brain watch --run-id abc123def',
565
- 'Watch a specific brain run by its ID'
566
- );
567
- },
568
- async (argv) => {
569
- const element = await brainCommand.watch(argv);
570
- if (element) {
571
- render(element);
572
- }
573
- }
574
- )
575
- .demandCommand(
576
- 1,
577
- 'You need to specify a brain command (list, history, show, rerun, run, watch).'
578
- );
579
-
580
- return yargsBrain;
581
- });
582
-
583
-
584
- // --- Resource Management Commands ---
585
- cli = cli.command(
586
- 'resources',
587
- 'Resources are any data that can be used in your brains, agents, and prompts. They can be text or binaries.\n',
588
- (yargsResource) => {
589
- const resourcesCommand = new ResourcesCommand(server);
590
-
591
- yargsResource.command(
592
- 'list',
593
- 'List all resources in the active project\n',
594
- (yargsListCmd) => {
595
- return yargsListCmd.example(
596
- '$0 resources list',
597
- 'List all resources in the active project'
598
- );
599
- },
600
- () => {
601
- const element = resourcesCommand.list();
602
- render(element);
603
- }
604
- );
605
-
606
- // Command available ONLY in Local Dev Mode
607
-
608
- yargsResource.command(
609
- 'sync',
610
- 'Sync local resources folder with the server so they are available to brains when they run\n',
611
- (yargsSyncCmd) => {
612
- return yargsSyncCmd.example(
613
- '$0 resources sync',
614
- 'Upload new or modified resources to the server'
615
- );
616
- },
617
- () => {
618
- const element = resourcesCommand.sync();
619
- render(element);
620
- }
621
- );
622
-
623
- yargsResource.command(
624
- 'types',
625
- 'Generate TypeScript type definitions for resources\n',
626
- (yargsTypesCmd) => {
627
- return yargsTypesCmd.example(
628
- '$0 resources types',
629
- 'Generate a resources.d.ts file with type definitions for all resources'
630
- );
631
- },
632
- () => {
633
- const element = resourcesCommand.types();
634
- render(element);
635
- }
636
- );
637
-
638
- yargsResource.command(
639
- 'clear',
640
- 'Delete ALL resources from the server (development only)\n',
641
- (yargsClearCmd) => {
642
- return yargsClearCmd.example(
643
- '$0 resources clear',
644
- 'Delete all resources from the server'
645
- );
646
- },
647
- async () => {
648
- const element = await resourcesCommand.clear();
649
- if (element) {
650
- render(element);
651
- }
652
- }
653
- );
654
-
655
- // Upload/delete command available in both dev and production modes
656
- yargsResource.command(
657
- 'upload <file>',
658
- 'Upload a file as a resource, or delete with -d flag\n',
659
- (yargsUploadCmd) => {
660
- return yargsUploadCmd
661
- .positional('file', {
662
- describe:
663
- 'File path to upload, or resource key when using -d flag',
664
- type: 'string',
665
- demandOption: true,
666
- })
667
- .option('key', {
668
- describe:
669
- 'Custom key/path for the resource (defaults to filename)',
670
- type: 'string',
671
- alias: 'k',
672
- })
673
- .option('delete', {
674
- describe: 'Delete the resource instead of uploading',
675
- type: 'boolean',
676
- alias: 'd',
677
- default: false,
678
- })
679
- .option('force', {
680
- describe: 'Skip confirmation prompts (use with --delete)',
681
- type: 'boolean',
682
- alias: 'f',
683
- default: false,
684
- })
685
- .example('$0 resources upload video.mp4', 'Upload a video file')
686
- .example(
687
- '$0 resources upload /path/to/large-file.zip --key archive/backup.zip',
688
- 'Upload with custom resource key'
689
- )
690
- .example(
691
- '$0 resources upload -d video.mp4',
692
- 'Delete the resource with key "video.mp4"'
693
- )
694
- .example(
695
- '$0 resources upload -d archive/backup.zip',
696
- 'Delete a resource with a nested key'
697
- )
698
- .example(
699
- '$0 resources upload -d -f video.mp4',
700
- 'Delete a resource without confirmation'
701
- );
702
- },
703
- (argv) => {
704
- if (argv.delete) {
705
- const element = resourcesCommand.delete(
706
- argv.file as string,
707
- argv.force as boolean
708
- );
709
- render(element);
710
- } else {
711
- const element = resourcesCommand.upload(
712
- argv.file as string,
713
- argv.key as string | undefined
714
- );
715
- render(element);
716
- }
717
- }
718
- );
719
-
720
- return yargsResource.demandCommand(
721
- 1,
722
- 'You need to specify a resources command'
723
- );
724
- }
725
- );
726
-
727
- // --- Schedule Management Commands ---
728
- cli = cli.command(
729
- 'schedule',
730
- 'Schedule brain runs to execute automatically on a cron schedule\n',
731
- (yargsSchedule) => {
732
- // Add top-level options for list and delete
733
- yargsSchedule
734
- .option('list', {
735
- describe: 'List all scheduled brain runs',
736
- type: 'boolean',
737
- alias: 'l',
738
- })
739
- .option('delete', {
740
- describe: 'Delete a schedule by ID',
741
- type: 'string',
742
- alias: 'd',
743
- })
744
- .option('brain', {
745
- describe: 'Filter schedules by brain name (used with -l)',
746
- type: 'string',
747
- alias: 'b',
748
- })
749
- .option('force', {
750
- describe: 'Skip confirmation prompt (used with -d)',
751
- type: 'boolean',
752
- alias: 'f',
753
- default: false,
754
- })
755
- .example('$0 schedule -l', 'List all schedules')
756
- .example(
757
- '$0 schedule -l --brain my-brain',
758
- 'List schedules for a specific brain'
759
- )
760
- .example('$0 schedule -d abc123', 'Delete schedule with ID abc123')
761
- .example('$0 schedule -d abc123 --force', 'Delete without confirmation')
762
- .check((argv) => {
763
- // Count how many operations are specified
764
- const operations = [
765
- argv.list,
766
- argv.delete,
767
- argv._.includes('create') || argv._.includes('c'),
768
- argv._.includes('runs'),
769
- ].filter(Boolean).length;
770
-
771
- if (operations === 0) {
772
- throw new Error(
773
- 'You must specify an operation: create, -l (list), -d (delete), or runs'
774
- );
775
- }
776
- if (operations > 1) {
777
- throw new Error('You can only specify one operation at a time');
778
- }
779
- return true;
780
- });
781
-
782
- // Handle top-level list/delete options
783
- yargsSchedule.middleware((argv) => {
784
- // If -l flag is used, call list command
785
- if (argv.list) {
786
- const element = scheduleCommand.list({ brain: argv.brain } as any);
787
- render(element);
788
- // Exit after completion to prevent further command processing
789
- process.exit(0);
790
- }
791
- // If -d flag is used, call delete command
792
- if (argv.delete) {
793
- const element = scheduleCommand.delete({
794
- scheduleId: argv.delete as string,
795
- force: argv.force,
796
- } as any);
797
- render(element);
798
- // Exit after completion to prevent further command processing
799
- process.exit(0);
800
- }
801
- }, true);
802
-
803
- yargsSchedule
804
- .command(
805
- [
806
- 'create <brain-name> <cron-expression>',
807
- 'c <brain-name> <cron-expression>',
808
- ],
809
- 'Create a new schedule for a brain\n',
810
- (yargsCreate) => {
811
- return yargsCreate
812
- .positional('brain-name', {
813
- describe: 'Name of the brain to schedule',
814
- type: 'string',
815
- demandOption: true,
816
- })
817
- .positional('cron-expression', {
818
- describe:
819
- 'Cron expression for the schedule (e.g., "0 3 * * *" for daily at 3am)',
820
- type: 'string',
821
- demandOption: true,
822
- })
823
- .example(
824
- '$0 schedule create my-brain "0 3 * * *"',
825
- 'Run my-brain daily at 3am'
826
- )
827
- .example(
828
- '$0 schedule c my-brain "0 3 * * *"',
829
- 'Run my-brain daily at 3am (shorthand)'
830
- )
831
- .example(
832
- '$0 schedule create data-sync "*/30 * * * *"',
833
- 'Run data-sync every 30 minutes'
834
- )
835
- .example(
836
- '$0 schedule create weekly-report "0 9 * * 1"',
837
- 'Run weekly-report every Monday at 9am'
838
- );
839
- },
840
- (argv) => {
841
- const element = scheduleCommand.create(argv);
842
- render(element);
843
- }
844
- )
845
- .command(
846
- 'runs',
847
- 'List scheduled run history\n',
848
- (yargsRuns) => {
849
- return yargsRuns
850
- .option('schedule-id', {
851
- describe: 'Filter runs by schedule ID',
852
- type: 'string',
853
- alias: 's',
854
- })
855
- .option('limit', {
856
- describe: 'Maximum number of runs to show',
857
- type: 'number',
858
- alias: 'n',
859
- default: 20,
860
- })
861
- .option('status', {
862
- describe: 'Filter by run status',
863
- type: 'string',
864
- choices: ['triggered', 'failed', 'complete'],
865
- })
866
- .example('$0 schedule runs', 'List recent scheduled runs')
867
- .example(
868
- '$0 schedule runs --schedule-id abc123',
869
- 'List runs for a specific schedule'
870
- )
871
- .example(
872
- '$0 schedule runs --status failed --limit 50',
873
- 'List last 50 failed scheduled runs'
874
- );
875
- },
876
- (argv) => {
877
- const element = scheduleCommand.runs(argv as any);
878
- render(element);
879
- }
880
- );
881
-
882
- return yargsSchedule;
883
- }
884
- );
885
-
886
- // --- Secret Management Commands ---
887
- cli = cli.command(
888
- 'secret',
889
- 'Manage secrets for your brains\n',
890
- (yargsSecret) => {
891
- yargsSecret
892
- .command(
893
- 'list',
894
- 'List all secrets\n',
895
- {},
896
- () => {
897
- const element = secretCommand.list();
898
- render(element);
899
- }
900
- )
901
- .command(
902
- 'create <name>',
903
- 'Create a new secret\n',
904
- (yargsCreate) => {
905
- return yargsCreate
906
- .positional('name', {
907
- describe: 'Name of the secret (e.g., ANTHROPIC_API_KEY)',
908
- type: 'string',
909
- demandOption: true,
910
- })
911
- .option('value', {
912
- describe: 'Secret value (omit for secure input)',
913
- type: 'string',
914
- })
915
- .example(
916
- '$0 secret create ANTHROPIC_API_KEY',
917
- 'Create a secret with secure input'
918
- )
919
- .example(
920
- '$0 secret create DATABASE_URL --value "postgres://..."',
921
- 'Create a secret with direct value (not recommended)'
922
- );
923
- },
924
- (argv) => {
925
- const element = secretCommand.create(argv);
926
- render(element);
927
- }
928
- )
929
- .command(
930
- 'delete <name>',
931
- 'Delete a secret\n',
932
- (yargsDelete) => {
933
- return yargsDelete
934
- .positional('name', {
935
- describe: 'Name of the secret to delete',
936
- type: 'string',
937
- demandOption: true,
938
- })
939
- .example(
940
- '$0 secret delete ANTHROPIC_API_KEY',
941
- 'Delete a secret'
942
- );
943
- },
944
- (argv) => {
945
- const element = secretCommand.delete(argv);
946
- render(element);
947
- }
948
- )
949
- .command(
950
- 'bulk [file]',
951
- 'Bulk upload secrets from a .env file\n',
952
- (yargsBulk) => {
953
- return yargsBulk
954
- .positional('file', {
955
- describe: 'Path to the .env file (defaults to .env in project root)',
956
- type: 'string',
957
- })
958
- .example(
959
- '$0 secret bulk',
960
- 'Upload secrets from .env file in project root'
961
- )
962
- .example(
963
- '$0 secret bulk .env.production',
964
- 'Upload secrets from a specific .env file'
965
- );
966
- },
967
- (argv) => {
968
- const element = secretCommand.bulk(argv);
969
- render(element);
970
- }
971
- )
972
- .demandCommand(1, 'You need to specify a subcommand');
973
-
974
- return yargsSecret;
975
- }
976
- );
977
-
978
- cli = cli.epilogue('For more information, visit https://positronic.sh');
979
-
980
- return cli;
981
- }