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