@syncular/cli 0.0.0-44

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.
@@ -0,0 +1,700 @@
1
+ import { listBuildpackIds } from './buildpacks';
2
+ import { CLI_NAME } from './constants';
3
+ import { listTemplateIds } from './templates';
4
+ import type { InteractiveCommand, RootCommand } from './types';
5
+
6
+ const USAGE_SUFFIX_LINES = [
7
+ 'doctor',
8
+ 'console [flags]',
9
+ 'login [flags]',
10
+ 'logout [flags]',
11
+ 'whoami [flags]',
12
+ 'create-space --name <name> [flags]',
13
+ 'create --template <id> [flags]',
14
+ 'dev [flags]',
15
+ 'typegen [flags]',
16
+ 'migrate-status [flags]',
17
+ 'migrate-up [flags]',
18
+ 'build [flags]',
19
+ 'eject [flags]',
20
+ 'target [flags]',
21
+ 'deploy --space <spaceId> [flags]',
22
+ 'verify --space <spaceId> [flags]',
23
+ 'deployments --space <spaceId> [flags]',
24
+ 'rollback --space <spaceId> --to <deploymentId> [flags]',
25
+ ];
26
+
27
+ export const USAGE_LINES = USAGE_SUFFIX_LINES.map(
28
+ (line) => `${CLI_NAME} ${line}`
29
+ );
30
+
31
+ const TEMPLATE_IDS = listTemplateIds();
32
+ const TEMPLATE_IDS_LABEL = TEMPLATE_IDS.join('|');
33
+ const DEFAULT_TEMPLATE_ID = TEMPLATE_IDS.includes('syncular-libraries')
34
+ ? 'syncular-libraries'
35
+ : (TEMPLATE_IDS[0] ?? 'syncular-libraries');
36
+ const BUILDPACK_IDS = listBuildpackIds();
37
+ const BUILDPACK_IDS_LABEL = BUILDPACK_IDS.join('|');
38
+
39
+ function parseBooleanInput(value: string): boolean | null {
40
+ const normalized = value.trim().toLowerCase();
41
+ if (
42
+ normalized === 'true' ||
43
+ normalized === '1' ||
44
+ normalized === 'yes' ||
45
+ normalized === 'on'
46
+ ) {
47
+ return true;
48
+ }
49
+ if (
50
+ normalized === 'false' ||
51
+ normalized === '0' ||
52
+ normalized === 'no' ||
53
+ normalized === 'off'
54
+ ) {
55
+ return false;
56
+ }
57
+ return null;
58
+ }
59
+
60
+ function pushOptionalFlag(argv: string[], flag: string, value: string): void {
61
+ const normalized = value.trim();
62
+ if (normalized.length === 0) {
63
+ return;
64
+ }
65
+ argv.push(flag, normalized);
66
+ }
67
+
68
+ const ALL_INTERACTIVE_COMMANDS: InteractiveCommand[] = [
69
+ {
70
+ id: 'doctor',
71
+ label: 'doctor',
72
+ description: 'Run local environment checks',
73
+ buildArgv() {
74
+ return ['doctor'];
75
+ },
76
+ },
77
+ {
78
+ id: 'console',
79
+ label: 'console',
80
+ description: 'Serve local Syncular Console static UI',
81
+ fields: [
82
+ { id: 'host', label: 'Host', defaultValue: '127.0.0.1' },
83
+ { id: 'port', label: 'Port', kind: 'number', defaultValue: '4310' },
84
+ { id: 'server-url', label: 'Sync API base URL (optional)' },
85
+ { id: 'token', label: 'Sync API token (optional)' },
86
+ { id: 'token-env', label: 'Read token from env var (optional)' },
87
+ {
88
+ id: 'open',
89
+ label: 'Open browser',
90
+ kind: 'boolean',
91
+ defaultValue: 'false',
92
+ },
93
+ ],
94
+ buildArgv(values) {
95
+ const argv = ['console'];
96
+ pushOptionalFlag(argv, '--host', values.host ?? '');
97
+ pushOptionalFlag(argv, '--port', values.port ?? '');
98
+ pushOptionalFlag(argv, '--server-url', values['server-url'] ?? '');
99
+ pushOptionalFlag(argv, '--token', values.token ?? '');
100
+ pushOptionalFlag(argv, '--token-env', values['token-env'] ?? '');
101
+ const open = parseBooleanInput(values.open ?? 'false');
102
+ if (open === true) {
103
+ argv.push('--open', 'true');
104
+ }
105
+ return argv;
106
+ },
107
+ },
108
+ {
109
+ id: 'create',
110
+ label: 'create',
111
+ description: 'Scaffold a project from a template',
112
+ fields: [
113
+ {
114
+ id: 'template',
115
+ label: `Template ID (${TEMPLATE_IDS_LABEL})`,
116
+ required: true,
117
+ defaultValue: DEFAULT_TEMPLATE_ID,
118
+ },
119
+ { id: 'dir', label: 'Target directory (optional)' },
120
+ { id: 'name', label: 'Project name (spaces-app only, optional)' },
121
+ {
122
+ id: 'targets',
123
+ label: 'syncular-libraries targets (optional CSV)',
124
+ },
125
+ {
126
+ id: 'server-dialect',
127
+ label: 'syncular-libraries server dialect (optional)',
128
+ },
129
+ {
130
+ id: 'react-dialect',
131
+ label: 'syncular-libraries react dialect (optional)',
132
+ },
133
+ {
134
+ id: 'vanilla-dialect',
135
+ label: 'syncular-libraries vanilla dialect (optional)',
136
+ },
137
+ {
138
+ id: 'electron-dialect',
139
+ label: 'syncular-libraries electron dialect (optional)',
140
+ },
141
+ {
142
+ id: 'force',
143
+ label: 'Overwrite files',
144
+ kind: 'boolean',
145
+ defaultValue: 'false',
146
+ },
147
+ ],
148
+ buildArgv(values) {
149
+ const argv = ['create'];
150
+ pushOptionalFlag(argv, '--template', values.template ?? '');
151
+ pushOptionalFlag(argv, '--dir', values.dir ?? '');
152
+ pushOptionalFlag(argv, '--name', values.name ?? '');
153
+ pushOptionalFlag(argv, '--targets', values.targets ?? '');
154
+ pushOptionalFlag(
155
+ argv,
156
+ '--server-dialect',
157
+ values['server-dialect'] ?? ''
158
+ );
159
+ pushOptionalFlag(argv, '--react-dialect', values['react-dialect'] ?? '');
160
+ pushOptionalFlag(
161
+ argv,
162
+ '--vanilla-dialect',
163
+ values['vanilla-dialect'] ?? ''
164
+ );
165
+ pushOptionalFlag(
166
+ argv,
167
+ '--electron-dialect',
168
+ values['electron-dialect'] ?? ''
169
+ );
170
+ const force = parseBooleanInput(values.force ?? 'false');
171
+ if (force === true) {
172
+ argv.push('--force', 'true');
173
+ }
174
+ return argv;
175
+ },
176
+ },
177
+ {
178
+ id: 'create-space',
179
+ label: 'create-space',
180
+ description: 'Create and provision a new hosted space',
181
+ fields: [
182
+ { id: 'name', label: 'Space name', required: true },
183
+ {
184
+ id: 'database-provider',
185
+ label: 'Database provider sqlite|neon|postgres',
186
+ defaultValue: 'sqlite',
187
+ },
188
+ {
189
+ id: 'connection-string',
190
+ label: 'Database connection string (optional)',
191
+ },
192
+ {
193
+ id: 'region',
194
+ label: 'Region auto|enam|wnam|weur|eeur|apac|oc',
195
+ defaultValue: 'auto',
196
+ },
197
+ { id: 'control-token', label: 'Control-plane bearer token (optional)' },
198
+ { id: 'actor-id', label: 'Actor id fallback (optional)' },
199
+ {
200
+ id: 'json',
201
+ label: 'JSON output',
202
+ kind: 'boolean',
203
+ defaultValue: 'false',
204
+ },
205
+ ],
206
+ buildArgv(values) {
207
+ const argv = ['create-space'];
208
+ pushOptionalFlag(argv, '--name', values.name ?? '');
209
+ pushOptionalFlag(
210
+ argv,
211
+ '--database-provider',
212
+ values['database-provider'] ?? ''
213
+ );
214
+ pushOptionalFlag(
215
+ argv,
216
+ '--connection-string',
217
+ values['connection-string'] ?? ''
218
+ );
219
+ pushOptionalFlag(argv, '--region', values.region ?? '');
220
+ pushOptionalFlag(argv, '--control-token', values['control-token'] ?? '');
221
+ pushOptionalFlag(argv, '--actor-id', values['actor-id'] ?? '');
222
+ const json = parseBooleanInput(values.json ?? 'false');
223
+ if (json === true) {
224
+ argv.push('--json', 'true');
225
+ }
226
+ return argv;
227
+ },
228
+ },
229
+ {
230
+ id: 'dev',
231
+ label: 'dev',
232
+ description: 'Run local runtime with hot reload',
233
+ fields: [
234
+ { id: 'host', label: 'Host', defaultValue: '127.0.0.1' },
235
+ { id: 'port', label: 'Port', kind: 'number', defaultValue: '4312' },
236
+ {
237
+ id: 'db-provider',
238
+ label: 'DB provider sqlite|neon|postgres (optional)',
239
+ },
240
+ { id: 'db', label: 'SQLite path override (optional)' },
241
+ {
242
+ id: 'connection-string',
243
+ label: 'Postgres/Neon connection string (optional)',
244
+ },
245
+ {
246
+ id: 'watch',
247
+ label: 'Watch mode',
248
+ kind: 'boolean',
249
+ defaultValue: 'true',
250
+ },
251
+ {
252
+ id: 'open',
253
+ label: 'Open embedded console',
254
+ kind: 'boolean',
255
+ defaultValue: 'false',
256
+ },
257
+ ],
258
+ buildArgv(values) {
259
+ const argv = ['dev'];
260
+ pushOptionalFlag(argv, '--host', values.host ?? '');
261
+ pushOptionalFlag(argv, '--port', values.port ?? '');
262
+ pushOptionalFlag(argv, '--db-provider', values['db-provider'] ?? '');
263
+ pushOptionalFlag(argv, '--db', values.db ?? '');
264
+ pushOptionalFlag(
265
+ argv,
266
+ '--connection-string',
267
+ values['connection-string'] ?? ''
268
+ );
269
+ const watch = parseBooleanInput(values.watch ?? 'true');
270
+ if (watch === false) {
271
+ argv.push('--watch', 'false');
272
+ }
273
+ const open = parseBooleanInput(values.open ?? 'false');
274
+ if (open === true) {
275
+ argv.push('--open', 'true');
276
+ }
277
+ return argv;
278
+ },
279
+ },
280
+ {
281
+ id: 'typegen',
282
+ label: 'typegen',
283
+ description: 'Generate TS database types from syncular.config.ts',
284
+ fields: [
285
+ { id: 'config', label: 'Config path (optional)' },
286
+ { id: 'out', label: 'Output file path (optional)' },
287
+ { id: 'dialect', label: 'Dialect sqlite|postgres (optional)' },
288
+ { id: 'tables', label: 'Restrict tables CSV (optional)' },
289
+ {
290
+ id: 'extends-sync-client-db',
291
+ label: 'Extend SyncClientDb',
292
+ kind: 'boolean',
293
+ defaultValue: 'true',
294
+ },
295
+ {
296
+ id: 'include-version-history',
297
+ label: 'Include version history',
298
+ kind: 'boolean',
299
+ defaultValue: 'false',
300
+ },
301
+ {
302
+ id: 'syncular-import-type',
303
+ label: 'Import style scoped|umbrella (optional)',
304
+ },
305
+ ],
306
+ buildArgv(values) {
307
+ const argv = ['typegen'];
308
+ pushOptionalFlag(argv, '--config', values.config ?? '');
309
+ pushOptionalFlag(argv, '--out', values.out ?? '');
310
+ pushOptionalFlag(argv, '--dialect', values.dialect ?? '');
311
+ pushOptionalFlag(argv, '--tables', values.tables ?? '');
312
+ const extendsSyncClientDb = parseBooleanInput(
313
+ values['extends-sync-client-db'] ?? 'true'
314
+ );
315
+ if (extendsSyncClientDb !== null) {
316
+ argv.push(
317
+ '--extends-sync-client-db',
318
+ extendsSyncClientDb ? 'true' : 'false'
319
+ );
320
+ }
321
+ const includeVersionHistory = parseBooleanInput(
322
+ values['include-version-history'] ?? 'false'
323
+ );
324
+ if (includeVersionHistory !== null) {
325
+ argv.push(
326
+ '--include-version-history',
327
+ includeVersionHistory ? 'true' : 'false'
328
+ );
329
+ }
330
+ pushOptionalFlag(
331
+ argv,
332
+ '--syncular-import-type',
333
+ values['syncular-import-type'] ?? ''
334
+ );
335
+ return argv;
336
+ },
337
+ },
338
+ {
339
+ id: 'migrate-status',
340
+ label: 'migrate-status',
341
+ description: 'Show migration adapter status',
342
+ fields: [{ id: 'config', label: 'Config path (optional)' }],
343
+ buildArgv(values) {
344
+ const argv = ['migrate-status'];
345
+ pushOptionalFlag(argv, '--config', values.config ?? '');
346
+ return argv;
347
+ },
348
+ },
349
+ {
350
+ id: 'migrate-up',
351
+ label: 'migrate-up',
352
+ description: 'Apply migrations via adapter',
353
+ fields: [
354
+ { id: 'config', label: 'Config path (optional)' },
355
+ {
356
+ id: 'on-checksum-mismatch',
357
+ label: 'Checksum mismatch mode error|reset (optional)',
358
+ },
359
+ {
360
+ id: 'dry-run',
361
+ label: 'Dry run',
362
+ kind: 'boolean',
363
+ defaultValue: 'false',
364
+ },
365
+ {
366
+ id: 'yes',
367
+ label: 'Confirm reset mode',
368
+ kind: 'boolean',
369
+ defaultValue: 'false',
370
+ },
371
+ ],
372
+ buildArgv(values) {
373
+ const argv = ['migrate-up'];
374
+ pushOptionalFlag(argv, '--config', values.config ?? '');
375
+ pushOptionalFlag(
376
+ argv,
377
+ '--on-checksum-mismatch',
378
+ values['on-checksum-mismatch'] ?? ''
379
+ );
380
+ const dryRun = parseBooleanInput(values['dry-run'] ?? 'false');
381
+ if (dryRun === true) {
382
+ argv.push('--dry-run', 'true');
383
+ }
384
+ const yes = parseBooleanInput(values.yes ?? 'false');
385
+ if (yes === true) {
386
+ argv.push('--yes', 'true');
387
+ }
388
+ return argv;
389
+ },
390
+ },
391
+ {
392
+ id: 'build',
393
+ label: 'build',
394
+ description: 'Build artifact from syncular contract + buildpack',
395
+ fields: [
396
+ { id: 'target', label: 'Target (spaces, optional)' },
397
+ {
398
+ id: 'buildpack',
399
+ label: `Buildpack (${BUILDPACK_IDS_LABEL}, optional)`,
400
+ },
401
+ { id: 'config', label: 'Contract config path (optional)' },
402
+ { id: 'entry', label: 'Worker entry override (optional)' },
403
+ { id: 'out', label: 'Artifact output path (optional)' },
404
+ { id: 'manifest', label: 'Manifest output path (optional)' },
405
+ {
406
+ id: 'force',
407
+ label: 'Overwrite outputs',
408
+ kind: 'boolean',
409
+ defaultValue: 'false',
410
+ },
411
+ ],
412
+ buildArgv(values) {
413
+ const argv = ['build'];
414
+ pushOptionalFlag(argv, '--target', values.target ?? '');
415
+ pushOptionalFlag(argv, '--buildpack', values.buildpack ?? '');
416
+ pushOptionalFlag(argv, '--config', values.config ?? '');
417
+ pushOptionalFlag(argv, '--entry', values.entry ?? '');
418
+ pushOptionalFlag(argv, '--out', values.out ?? '');
419
+ pushOptionalFlag(argv, '--manifest', values.manifest ?? '');
420
+ const force = parseBooleanInput(values.force ?? 'false');
421
+ if (force === true) {
422
+ argv.push('--force', 'true');
423
+ }
424
+ return argv;
425
+ },
426
+ },
427
+ {
428
+ id: 'eject',
429
+ label: 'eject',
430
+ description: 'Eject buildpack output for custom deployment',
431
+ fields: [
432
+ { id: 'target', label: 'Target (spaces, optional)' },
433
+ {
434
+ id: 'buildpack',
435
+ label: `Buildpack (${BUILDPACK_IDS_LABEL}, optional)`,
436
+ },
437
+ { id: 'config', label: 'Contract config path (optional)' },
438
+ { id: 'entry', label: 'Worker entry override (optional)' },
439
+ { id: 'out-dir', label: 'Output directory (optional)' },
440
+ {
441
+ id: 'force',
442
+ label: 'Overwrite outputs',
443
+ kind: 'boolean',
444
+ defaultValue: 'false',
445
+ },
446
+ ],
447
+ buildArgv(values) {
448
+ const argv = ['eject'];
449
+ pushOptionalFlag(argv, '--target', values.target ?? '');
450
+ pushOptionalFlag(argv, '--buildpack', values.buildpack ?? '');
451
+ pushOptionalFlag(argv, '--config', values.config ?? '');
452
+ pushOptionalFlag(argv, '--entry', values.entry ?? '');
453
+ pushOptionalFlag(argv, '--out-dir', values['out-dir'] ?? '');
454
+ const force = parseBooleanInput(values.force ?? 'false');
455
+ if (force === true) {
456
+ argv.push('--force', 'true');
457
+ }
458
+ return argv;
459
+ },
460
+ },
461
+ {
462
+ id: 'target',
463
+ label: 'target',
464
+ description: 'Show or set default target profile',
465
+ fields: [{ id: 'set', label: 'Set target (spaces, optional)' }],
466
+ buildArgv(values) {
467
+ const argv = ['target'];
468
+ pushOptionalFlag(argv, '--set', values.set ?? '');
469
+ return argv;
470
+ },
471
+ },
472
+ {
473
+ id: 'deploy',
474
+ label: 'deploy',
475
+ description: 'Deploy runtime build to a space',
476
+ fields: [
477
+ { id: 'space', label: 'Space ID', required: true },
478
+ { id: 'target', label: 'Target (spaces, optional)' },
479
+ { id: 'control-token', label: 'Control-plane bearer token (optional)' },
480
+ { id: 'config', label: 'Contract config path (optional)' },
481
+ { id: 'entry', label: 'Worker entry override (optional)' },
482
+ { id: 'script-name', label: 'Script name override (optional)' },
483
+ { id: 'source', label: 'Source label (optional)' },
484
+ {
485
+ id: 'dry-run',
486
+ label: 'Dry run',
487
+ kind: 'boolean',
488
+ defaultValue: 'false',
489
+ },
490
+ ],
491
+ buildArgv(values) {
492
+ const argv = ['deploy'];
493
+ pushOptionalFlag(argv, '--space', values.space ?? '');
494
+ pushOptionalFlag(argv, '--target', values.target ?? '');
495
+ pushOptionalFlag(argv, '--control-token', values['control-token'] ?? '');
496
+ pushOptionalFlag(argv, '--config', values.config ?? '');
497
+ pushOptionalFlag(argv, '--entry', values.entry ?? '');
498
+ pushOptionalFlag(argv, '--script-name', values['script-name'] ?? '');
499
+ pushOptionalFlag(argv, '--source', values.source ?? '');
500
+ const dryRun = parseBooleanInput(values['dry-run'] ?? 'false');
501
+ if (dryRun === true) {
502
+ argv.push('--dry-run', 'true');
503
+ }
504
+ return argv;
505
+ },
506
+ },
507
+ {
508
+ id: 'verify',
509
+ label: 'verify',
510
+ description: 'Verify deployment/runtime health',
511
+ fields: [
512
+ { id: 'space', label: 'Space ID', required: true },
513
+ { id: 'target', label: 'Target (spaces, optional)' },
514
+ { id: 'control-token', label: 'Control-plane bearer token (optional)' },
515
+ { id: 'base-url', label: 'Runtime URL override (optional)' },
516
+ ],
517
+ buildArgv(values) {
518
+ const argv = ['verify'];
519
+ pushOptionalFlag(argv, '--space', values.space ?? '');
520
+ pushOptionalFlag(argv, '--target', values.target ?? '');
521
+ pushOptionalFlag(argv, '--control-token', values['control-token'] ?? '');
522
+ pushOptionalFlag(argv, '--base-url', values['base-url'] ?? '');
523
+ return argv;
524
+ },
525
+ },
526
+ {
527
+ id: 'deployments',
528
+ label: 'deployments',
529
+ description: 'List deployment history for a space',
530
+ fields: [
531
+ { id: 'space', label: 'Space ID', required: true },
532
+ { id: 'control-token', label: 'Control-plane bearer token (optional)' },
533
+ { id: 'limit', label: 'Limit', kind: 'number', defaultValue: '20' },
534
+ ],
535
+ buildArgv(values) {
536
+ const argv = ['deployments'];
537
+ pushOptionalFlag(argv, '--space', values.space ?? '');
538
+ pushOptionalFlag(argv, '--control-token', values['control-token'] ?? '');
539
+ pushOptionalFlag(argv, '--limit', values.limit ?? '');
540
+ return argv;
541
+ },
542
+ },
543
+ {
544
+ id: 'rollback',
545
+ label: 'rollback',
546
+ description: 'Activate a previous deployment for a space',
547
+ fields: [
548
+ { id: 'space', label: 'Space ID', required: true },
549
+ { id: 'control-token', label: 'Control-plane bearer token (optional)' },
550
+ { id: 'to', label: 'Target deployment ID', required: true },
551
+ { id: 'reason', label: 'Reason (optional)' },
552
+ ],
553
+ buildArgv(values) {
554
+ const argv = ['rollback'];
555
+ pushOptionalFlag(argv, '--space', values.space ?? '');
556
+ pushOptionalFlag(argv, '--control-token', values['control-token'] ?? '');
557
+ pushOptionalFlag(argv, '--to', values.to ?? '');
558
+ pushOptionalFlag(argv, '--reason', values.reason ?? '');
559
+ return argv;
560
+ },
561
+ },
562
+ {
563
+ id: 'login',
564
+ label: 'login',
565
+ description: 'Authenticate CLI with Clerk and save token to keychain',
566
+ fields: [
567
+ { id: 'control-plane', label: 'Control-plane base URL (optional)' },
568
+ ],
569
+ buildArgv(values) {
570
+ const argv = ['login'];
571
+ pushOptionalFlag(argv, '--control-plane', values['control-plane'] ?? '');
572
+ return argv;
573
+ },
574
+ },
575
+ {
576
+ id: 'logout',
577
+ label: 'logout',
578
+ description: 'Clear saved CLI token from keychain',
579
+ fields: [
580
+ { id: 'control-plane', label: 'Control-plane base URL (optional)' },
581
+ ],
582
+ buildArgv(values) {
583
+ const argv = ['logout'];
584
+ pushOptionalFlag(argv, '--control-plane', values['control-plane'] ?? '');
585
+ return argv;
586
+ },
587
+ },
588
+ {
589
+ id: 'whoami',
590
+ label: 'whoami',
591
+ description: 'Validate current CLI auth token and print actor',
592
+ fields: [
593
+ { id: 'control-plane', label: 'Control-plane base URL (optional)' },
594
+ {
595
+ id: 'json',
596
+ label: 'JSON output',
597
+ kind: 'boolean',
598
+ defaultValue: 'false',
599
+ },
600
+ ],
601
+ buildArgv(values) {
602
+ const argv = ['whoami'];
603
+ pushOptionalFlag(argv, '--control-plane', values['control-plane'] ?? '');
604
+ const json = parseBooleanInput(values.json ?? 'false');
605
+ if (json === true) {
606
+ argv.push('--json', 'true');
607
+ }
608
+ return argv;
609
+ },
610
+ },
611
+ {
612
+ id: 'help',
613
+ label: 'help',
614
+ description: 'Show full command reference',
615
+ buildArgv() {
616
+ return ['help'];
617
+ },
618
+ },
619
+ {
620
+ id: 'quit',
621
+ label: 'quit',
622
+ description: 'Exit the interactive shell',
623
+ buildArgv() {
624
+ return null;
625
+ },
626
+ },
627
+ ];
628
+
629
+ const interactiveCommandById = new Map(
630
+ ALL_INTERACTIVE_COMMANDS.map((command) => [command.id, command] as const)
631
+ );
632
+
633
+ function pickInteractiveCommands(
634
+ ...ids: InteractiveCommand['id'][]
635
+ ): InteractiveCommand[] {
636
+ return ids
637
+ .map((id) => interactiveCommandById.get(id) ?? null)
638
+ .filter((command): command is InteractiveCommand => command !== null);
639
+ }
640
+
641
+ export function listInteractiveCommands(
642
+ initialCommand: RootCommand | null
643
+ ): InteractiveCommand[] {
644
+ if (initialCommand === 'doctor') {
645
+ return pickInteractiveCommands('doctor', 'help', 'quit');
646
+ }
647
+ if (initialCommand === 'console') {
648
+ return pickInteractiveCommands('console', 'help', 'quit');
649
+ }
650
+ if (initialCommand === 'create') {
651
+ return pickInteractiveCommands('create', 'help', 'quit');
652
+ }
653
+ if (initialCommand === 'create-space') {
654
+ return pickInteractiveCommands('create-space', 'help', 'quit');
655
+ }
656
+ if (initialCommand === 'dev') {
657
+ return pickInteractiveCommands('dev', 'help', 'quit');
658
+ }
659
+ if (initialCommand === 'typegen') {
660
+ return pickInteractiveCommands('typegen', 'help', 'quit');
661
+ }
662
+ if (initialCommand === 'migrate-status') {
663
+ return pickInteractiveCommands('migrate-status', 'help', 'quit');
664
+ }
665
+ if (initialCommand === 'migrate-up') {
666
+ return pickInteractiveCommands('migrate-up', 'help', 'quit');
667
+ }
668
+ if (initialCommand === 'build') {
669
+ return pickInteractiveCommands('build', 'help', 'quit');
670
+ }
671
+ if (initialCommand === 'eject') {
672
+ return pickInteractiveCommands('eject', 'help', 'quit');
673
+ }
674
+ if (initialCommand === 'target') {
675
+ return pickInteractiveCommands('target', 'help', 'quit');
676
+ }
677
+ if (initialCommand === 'deploy') {
678
+ return pickInteractiveCommands('deploy', 'help', 'quit');
679
+ }
680
+ if (initialCommand === 'verify') {
681
+ return pickInteractiveCommands('verify', 'help', 'quit');
682
+ }
683
+ if (initialCommand === 'deployments') {
684
+ return pickInteractiveCommands('deployments', 'help', 'quit');
685
+ }
686
+ if (initialCommand === 'rollback') {
687
+ return pickInteractiveCommands('rollback', 'help', 'quit');
688
+ }
689
+ if (initialCommand === 'login') {
690
+ return pickInteractiveCommands('login', 'whoami', 'logout', 'help', 'quit');
691
+ }
692
+ if (initialCommand === 'logout') {
693
+ return pickInteractiveCommands('logout', 'login', 'whoami', 'help', 'quit');
694
+ }
695
+ if (initialCommand === 'whoami') {
696
+ return pickInteractiveCommands('whoami', 'login', 'logout', 'help', 'quit');
697
+ }
698
+
699
+ return ALL_INTERACTIVE_COMMANDS;
700
+ }