openmagic 0.1.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -1
- package/README.md +20 -14
- package/dist/cli.js +800 -76
- package/dist/cli.js.map +1 -1
- package/dist/toolbar/index.global.js +78 -13
- package/dist/toolbar/index.global.js.map +1 -1
- package/package.json +10 -2
package/dist/cli.js
CHANGED
|
@@ -5,6 +5,8 @@ import { Command } from "commander";
|
|
|
5
5
|
import chalk from "chalk";
|
|
6
6
|
import open from "open";
|
|
7
7
|
import { resolve as resolve2 } from "path";
|
|
8
|
+
import { spawn } from "child_process";
|
|
9
|
+
import { createInterface } from "readline";
|
|
8
10
|
|
|
9
11
|
// src/proxy.ts
|
|
10
12
|
import http from "http";
|
|
@@ -286,85 +288,550 @@ function getProjectTree(roots) {
|
|
|
286
288
|
|
|
287
289
|
// src/llm/registry.ts
|
|
288
290
|
var MODEL_REGISTRY = {
|
|
291
|
+
// ─── OpenAI ───────────────────────────────────────────────────
|
|
289
292
|
openai: {
|
|
290
293
|
name: "OpenAI",
|
|
291
294
|
models: [
|
|
292
|
-
|
|
293
|
-
{
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
295
|
+
// GPT-5.4 family (March 2026 — latest flagship)
|
|
296
|
+
{
|
|
297
|
+
id: "gpt-5.4",
|
|
298
|
+
name: "GPT-5.4",
|
|
299
|
+
vision: true,
|
|
300
|
+
context: 105e4,
|
|
301
|
+
maxOutput: 128e3,
|
|
302
|
+
thinking: {
|
|
303
|
+
supported: true,
|
|
304
|
+
paramName: "reasoning_effort",
|
|
305
|
+
paramType: "level",
|
|
306
|
+
levels: ["none", "low", "medium", "high", "xhigh"],
|
|
307
|
+
defaultLevel: "medium"
|
|
308
|
+
}
|
|
309
|
+
},
|
|
310
|
+
{
|
|
311
|
+
id: "gpt-5.4-pro",
|
|
312
|
+
name: "GPT-5.4 Pro",
|
|
313
|
+
vision: true,
|
|
314
|
+
context: 105e4,
|
|
315
|
+
maxOutput: 128e3,
|
|
316
|
+
thinking: {
|
|
317
|
+
supported: true,
|
|
318
|
+
paramName: "reasoning_effort",
|
|
319
|
+
paramType: "level",
|
|
320
|
+
levels: ["none", "low", "medium", "high", "xhigh"],
|
|
321
|
+
defaultLevel: "high"
|
|
322
|
+
}
|
|
323
|
+
},
|
|
324
|
+
{
|
|
325
|
+
id: "gpt-5.4-mini",
|
|
326
|
+
name: "GPT-5.4 Mini",
|
|
327
|
+
vision: true,
|
|
328
|
+
context: 4e5,
|
|
329
|
+
maxOutput: 128e3,
|
|
330
|
+
thinking: {
|
|
331
|
+
supported: true,
|
|
332
|
+
paramName: "reasoning_effort",
|
|
333
|
+
paramType: "level",
|
|
334
|
+
levels: ["none", "low", "medium", "high"],
|
|
335
|
+
defaultLevel: "medium"
|
|
336
|
+
}
|
|
337
|
+
},
|
|
338
|
+
{
|
|
339
|
+
id: "gpt-5.4-nano",
|
|
340
|
+
name: "GPT-5.4 Nano",
|
|
341
|
+
vision: true,
|
|
342
|
+
context: 4e5,
|
|
343
|
+
maxOutput: 128e3,
|
|
344
|
+
thinking: {
|
|
345
|
+
supported: true,
|
|
346
|
+
paramName: "reasoning_effort",
|
|
347
|
+
paramType: "level",
|
|
348
|
+
levels: ["none", "low", "medium", "high"],
|
|
349
|
+
defaultLevel: "low"
|
|
350
|
+
}
|
|
351
|
+
},
|
|
352
|
+
// GPT-5.2 family (reasoning-focused)
|
|
353
|
+
{
|
|
354
|
+
id: "gpt-5.2",
|
|
355
|
+
name: "GPT-5.2 Thinking",
|
|
356
|
+
vision: true,
|
|
357
|
+
context: 272e3,
|
|
358
|
+
maxOutput: 128e3,
|
|
359
|
+
thinking: {
|
|
360
|
+
supported: true,
|
|
361
|
+
paramName: "reasoning_effort",
|
|
362
|
+
paramType: "level",
|
|
363
|
+
levels: ["none", "low", "medium", "high", "xhigh"],
|
|
364
|
+
defaultLevel: "high"
|
|
365
|
+
}
|
|
366
|
+
},
|
|
367
|
+
{
|
|
368
|
+
id: "gpt-5.2-pro",
|
|
369
|
+
name: "GPT-5.2 Pro",
|
|
370
|
+
vision: true,
|
|
371
|
+
context: 272e3,
|
|
372
|
+
maxOutput: 128e3,
|
|
373
|
+
thinking: {
|
|
374
|
+
supported: true,
|
|
375
|
+
paramName: "reasoning_effort",
|
|
376
|
+
paramType: "level",
|
|
377
|
+
levels: ["none", "low", "medium", "high", "xhigh"],
|
|
378
|
+
defaultLevel: "high"
|
|
379
|
+
}
|
|
380
|
+
},
|
|
381
|
+
// o-series reasoning models
|
|
382
|
+
{
|
|
383
|
+
id: "o3",
|
|
384
|
+
name: "o3 (Reasoning)",
|
|
385
|
+
vision: true,
|
|
386
|
+
context: 2e5,
|
|
387
|
+
maxOutput: 1e5,
|
|
388
|
+
thinking: {
|
|
389
|
+
supported: true,
|
|
390
|
+
paramName: "reasoning_effort",
|
|
391
|
+
paramType: "level",
|
|
392
|
+
levels: ["low", "medium", "high"],
|
|
393
|
+
defaultLevel: "medium"
|
|
394
|
+
}
|
|
395
|
+
},
|
|
396
|
+
{
|
|
397
|
+
id: "o4-mini",
|
|
398
|
+
name: "o4-mini (Reasoning)",
|
|
399
|
+
vision: true,
|
|
400
|
+
context: 2e5,
|
|
401
|
+
maxOutput: 1e5,
|
|
402
|
+
thinking: {
|
|
403
|
+
supported: true,
|
|
404
|
+
paramName: "reasoning_effort",
|
|
405
|
+
paramType: "level",
|
|
406
|
+
levels: ["low", "medium", "high"],
|
|
407
|
+
defaultLevel: "medium"
|
|
408
|
+
}
|
|
409
|
+
},
|
|
410
|
+
// GPT-4.1 family
|
|
411
|
+
{
|
|
412
|
+
id: "gpt-4.1",
|
|
413
|
+
name: "GPT-4.1",
|
|
414
|
+
vision: true,
|
|
415
|
+
context: 1047576,
|
|
416
|
+
maxOutput: 32768
|
|
417
|
+
},
|
|
418
|
+
{
|
|
419
|
+
id: "gpt-4.1-mini",
|
|
420
|
+
name: "GPT-4.1 Mini",
|
|
421
|
+
vision: true,
|
|
422
|
+
context: 1047576,
|
|
423
|
+
maxOutput: 32768
|
|
424
|
+
},
|
|
425
|
+
{
|
|
426
|
+
id: "gpt-4.1-nano",
|
|
427
|
+
name: "GPT-4.1 Nano",
|
|
428
|
+
vision: true,
|
|
429
|
+
context: 1047576,
|
|
430
|
+
maxOutput: 32768
|
|
431
|
+
},
|
|
432
|
+
// Codex
|
|
433
|
+
{
|
|
434
|
+
id: "codex-mini-latest",
|
|
435
|
+
name: "Codex Mini",
|
|
436
|
+
vision: false,
|
|
437
|
+
context: 192e3,
|
|
438
|
+
maxOutput: 1e5,
|
|
439
|
+
thinking: {
|
|
440
|
+
supported: true,
|
|
441
|
+
paramName: "reasoning_effort",
|
|
442
|
+
paramType: "level",
|
|
443
|
+
levels: ["low", "medium", "high"],
|
|
444
|
+
defaultLevel: "high"
|
|
445
|
+
}
|
|
446
|
+
}
|
|
299
447
|
],
|
|
300
448
|
apiBase: "https://api.openai.com/v1",
|
|
301
449
|
keyPrefix: "sk-",
|
|
302
450
|
keyPlaceholder: "sk-..."
|
|
303
451
|
},
|
|
452
|
+
// ─── Anthropic ────────────────────────────────────────────────
|
|
304
453
|
anthropic: {
|
|
305
454
|
name: "Anthropic",
|
|
306
455
|
models: [
|
|
307
|
-
|
|
308
|
-
{
|
|
309
|
-
|
|
456
|
+
// Claude 4.6 (latest — Feb 2026)
|
|
457
|
+
{
|
|
458
|
+
id: "claude-opus-4-6",
|
|
459
|
+
name: "Claude Opus 4.6",
|
|
460
|
+
vision: true,
|
|
461
|
+
context: 1e6,
|
|
462
|
+
maxOutput: 128e3,
|
|
463
|
+
thinking: {
|
|
464
|
+
supported: true,
|
|
465
|
+
paramName: "budget_tokens",
|
|
466
|
+
paramType: "budget",
|
|
467
|
+
defaultBudget: 1e4,
|
|
468
|
+
maxBudget: 128e3
|
|
469
|
+
}
|
|
470
|
+
},
|
|
471
|
+
{
|
|
472
|
+
id: "claude-sonnet-4-6",
|
|
473
|
+
name: "Claude Sonnet 4.6",
|
|
474
|
+
vision: true,
|
|
475
|
+
context: 1e6,
|
|
476
|
+
maxOutput: 64e3,
|
|
477
|
+
thinking: {
|
|
478
|
+
supported: true,
|
|
479
|
+
paramName: "budget_tokens",
|
|
480
|
+
paramType: "budget",
|
|
481
|
+
defaultBudget: 8e3,
|
|
482
|
+
maxBudget: 64e3
|
|
483
|
+
}
|
|
484
|
+
},
|
|
485
|
+
// Claude 4.5
|
|
486
|
+
{
|
|
487
|
+
id: "claude-haiku-4-5-20251001",
|
|
488
|
+
name: "Claude Haiku 4.5",
|
|
489
|
+
vision: true,
|
|
490
|
+
context: 2e5,
|
|
491
|
+
maxOutput: 64e3,
|
|
492
|
+
thinking: {
|
|
493
|
+
supported: true,
|
|
494
|
+
paramName: "budget_tokens",
|
|
495
|
+
paramType: "budget",
|
|
496
|
+
defaultBudget: 5e3,
|
|
497
|
+
maxBudget: 64e3
|
|
498
|
+
}
|
|
499
|
+
},
|
|
500
|
+
{
|
|
501
|
+
id: "claude-sonnet-4-5-20250929",
|
|
502
|
+
name: "Claude Sonnet 4.5",
|
|
503
|
+
vision: true,
|
|
504
|
+
context: 1e6,
|
|
505
|
+
maxOutput: 64e3,
|
|
506
|
+
thinking: {
|
|
507
|
+
supported: true,
|
|
508
|
+
paramName: "budget_tokens",
|
|
509
|
+
paramType: "budget",
|
|
510
|
+
defaultBudget: 8e3,
|
|
511
|
+
maxBudget: 64e3
|
|
512
|
+
}
|
|
513
|
+
},
|
|
514
|
+
{
|
|
515
|
+
id: "claude-opus-4-5-20251101",
|
|
516
|
+
name: "Claude Opus 4.5",
|
|
517
|
+
vision: true,
|
|
518
|
+
context: 2e5,
|
|
519
|
+
maxOutput: 64e3,
|
|
520
|
+
thinking: {
|
|
521
|
+
supported: true,
|
|
522
|
+
paramName: "budget_tokens",
|
|
523
|
+
paramType: "budget",
|
|
524
|
+
defaultBudget: 1e4,
|
|
525
|
+
maxBudget: 64e3
|
|
526
|
+
}
|
|
527
|
+
},
|
|
528
|
+
// Claude 4.0
|
|
529
|
+
{
|
|
530
|
+
id: "claude-sonnet-4-20250514",
|
|
531
|
+
name: "Claude Sonnet 4",
|
|
532
|
+
vision: true,
|
|
533
|
+
context: 2e5,
|
|
534
|
+
maxOutput: 64e3,
|
|
535
|
+
thinking: {
|
|
536
|
+
supported: true,
|
|
537
|
+
paramName: "budget_tokens",
|
|
538
|
+
paramType: "budget",
|
|
539
|
+
defaultBudget: 8e3,
|
|
540
|
+
maxBudget: 64e3
|
|
541
|
+
}
|
|
542
|
+
},
|
|
543
|
+
{
|
|
544
|
+
id: "claude-opus-4-20250514",
|
|
545
|
+
name: "Claude Opus 4",
|
|
546
|
+
vision: true,
|
|
547
|
+
context: 2e5,
|
|
548
|
+
maxOutput: 32e3,
|
|
549
|
+
thinking: {
|
|
550
|
+
supported: true,
|
|
551
|
+
paramName: "budget_tokens",
|
|
552
|
+
paramType: "budget",
|
|
553
|
+
defaultBudget: 1e4,
|
|
554
|
+
maxBudget: 32e3
|
|
555
|
+
}
|
|
556
|
+
}
|
|
310
557
|
],
|
|
311
558
|
apiBase: "https://api.anthropic.com/v1",
|
|
312
559
|
keyPrefix: "sk-ant-",
|
|
313
560
|
keyPlaceholder: "sk-ant-..."
|
|
314
561
|
},
|
|
562
|
+
// ─── Google Gemini ────────────────────────────────────────────
|
|
315
563
|
google: {
|
|
316
564
|
name: "Google Gemini",
|
|
317
565
|
models: [
|
|
318
|
-
|
|
319
|
-
{
|
|
320
|
-
|
|
566
|
+
// Gemini 3.1 (latest — Feb-Mar 2026)
|
|
567
|
+
{
|
|
568
|
+
id: "gemini-3.1-pro-preview",
|
|
569
|
+
name: "Gemini 3.1 Pro",
|
|
570
|
+
vision: true,
|
|
571
|
+
context: 1048576,
|
|
572
|
+
maxOutput: 65536,
|
|
573
|
+
thinking: {
|
|
574
|
+
supported: true,
|
|
575
|
+
paramName: "thinking_level",
|
|
576
|
+
paramType: "level",
|
|
577
|
+
levels: ["none", "low", "medium", "high"],
|
|
578
|
+
defaultLevel: "medium"
|
|
579
|
+
}
|
|
580
|
+
},
|
|
581
|
+
// Gemini 3.0
|
|
582
|
+
{
|
|
583
|
+
id: "gemini-3-flash-preview",
|
|
584
|
+
name: "Gemini 3 Flash",
|
|
585
|
+
vision: true,
|
|
586
|
+
context: 1048576,
|
|
587
|
+
maxOutput: 65536,
|
|
588
|
+
thinking: {
|
|
589
|
+
supported: true,
|
|
590
|
+
paramName: "thinking_level",
|
|
591
|
+
paramType: "level",
|
|
592
|
+
levels: ["none", "low", "medium", "high"],
|
|
593
|
+
defaultLevel: "low"
|
|
594
|
+
}
|
|
595
|
+
},
|
|
596
|
+
{
|
|
597
|
+
id: "gemini-3.1-flash-lite-preview",
|
|
598
|
+
name: "Gemini 3.1 Flash Lite",
|
|
599
|
+
vision: true,
|
|
600
|
+
context: 1048576,
|
|
601
|
+
maxOutput: 65536
|
|
602
|
+
},
|
|
603
|
+
// Gemini 2.5
|
|
604
|
+
{
|
|
605
|
+
id: "gemini-2.5-pro",
|
|
606
|
+
name: "Gemini 2.5 Pro",
|
|
607
|
+
vision: true,
|
|
608
|
+
context: 1048576,
|
|
609
|
+
maxOutput: 65536,
|
|
610
|
+
thinking: {
|
|
611
|
+
supported: true,
|
|
612
|
+
paramName: "thinking_level",
|
|
613
|
+
paramType: "level",
|
|
614
|
+
levels: ["none", "low", "medium", "high"],
|
|
615
|
+
defaultLevel: "medium"
|
|
616
|
+
}
|
|
617
|
+
},
|
|
618
|
+
{
|
|
619
|
+
id: "gemini-2.5-flash",
|
|
620
|
+
name: "Gemini 2.5 Flash",
|
|
621
|
+
vision: true,
|
|
622
|
+
context: 1048576,
|
|
623
|
+
maxOutput: 65536,
|
|
624
|
+
thinking: {
|
|
625
|
+
supported: true,
|
|
626
|
+
paramName: "thinking_level",
|
|
627
|
+
paramType: "level",
|
|
628
|
+
levels: ["none", "low", "medium", "high"],
|
|
629
|
+
defaultLevel: "low"
|
|
630
|
+
}
|
|
631
|
+
},
|
|
632
|
+
{
|
|
633
|
+
id: "gemini-2.5-flash-lite",
|
|
634
|
+
name: "Gemini 2.5 Flash Lite",
|
|
635
|
+
vision: true,
|
|
636
|
+
context: 1048576,
|
|
637
|
+
maxOutput: 65536
|
|
638
|
+
}
|
|
321
639
|
],
|
|
322
640
|
apiBase: "https://generativelanguage.googleapis.com/v1beta",
|
|
323
641
|
keyPrefix: "AI",
|
|
324
642
|
keyPlaceholder: "AIza..."
|
|
325
643
|
},
|
|
644
|
+
// ─── xAI (Grok) ──────────────────────────────────────────────
|
|
645
|
+
xai: {
|
|
646
|
+
name: "xAI (Grok)",
|
|
647
|
+
models: [
|
|
648
|
+
{
|
|
649
|
+
id: "grok-4.20-0309-reasoning",
|
|
650
|
+
name: "Grok 4.20 Reasoning",
|
|
651
|
+
vision: true,
|
|
652
|
+
context: 2e6,
|
|
653
|
+
maxOutput: 128e3,
|
|
654
|
+
thinking: {
|
|
655
|
+
supported: true,
|
|
656
|
+
paramName: "reasoning_effort",
|
|
657
|
+
paramType: "level",
|
|
658
|
+
levels: ["low", "medium", "high"],
|
|
659
|
+
defaultLevel: "medium"
|
|
660
|
+
}
|
|
661
|
+
},
|
|
662
|
+
{
|
|
663
|
+
id: "grok-4.20-0309-non-reasoning",
|
|
664
|
+
name: "Grok 4.20",
|
|
665
|
+
vision: true,
|
|
666
|
+
context: 2e6,
|
|
667
|
+
maxOutput: 128e3
|
|
668
|
+
},
|
|
669
|
+
{
|
|
670
|
+
id: "grok-4-1-fast-reasoning",
|
|
671
|
+
name: "Grok 4.1 Fast Reasoning",
|
|
672
|
+
vision: true,
|
|
673
|
+
context: 2e6,
|
|
674
|
+
maxOutput: 128e3,
|
|
675
|
+
thinking: {
|
|
676
|
+
supported: true,
|
|
677
|
+
paramName: "reasoning_effort",
|
|
678
|
+
paramType: "level",
|
|
679
|
+
levels: ["low", "medium", "high"],
|
|
680
|
+
defaultLevel: "low"
|
|
681
|
+
}
|
|
682
|
+
},
|
|
683
|
+
{
|
|
684
|
+
id: "grok-4-1-fast-non-reasoning",
|
|
685
|
+
name: "Grok 4.1 Fast",
|
|
686
|
+
vision: true,
|
|
687
|
+
context: 2e6,
|
|
688
|
+
maxOutput: 128e3
|
|
689
|
+
}
|
|
690
|
+
],
|
|
691
|
+
apiBase: "https://api.x.ai/v1",
|
|
692
|
+
keyPrefix: "xai-",
|
|
693
|
+
keyPlaceholder: "xai-..."
|
|
694
|
+
},
|
|
695
|
+
// ─── DeepSeek ─────────────────────────────────────────────────
|
|
326
696
|
deepseek: {
|
|
327
697
|
name: "DeepSeek",
|
|
328
698
|
models: [
|
|
329
|
-
{
|
|
330
|
-
|
|
699
|
+
{
|
|
700
|
+
id: "deepseek-chat",
|
|
701
|
+
name: "DeepSeek V3.2",
|
|
702
|
+
vision: false,
|
|
703
|
+
context: 128e3,
|
|
704
|
+
maxOutput: 8192
|
|
705
|
+
},
|
|
706
|
+
{
|
|
707
|
+
id: "deepseek-reasoner",
|
|
708
|
+
name: "DeepSeek R1",
|
|
709
|
+
vision: false,
|
|
710
|
+
context: 128e3,
|
|
711
|
+
maxOutput: 8192,
|
|
712
|
+
thinking: {
|
|
713
|
+
supported: true,
|
|
714
|
+
paramName: "reasoning_effort",
|
|
715
|
+
paramType: "level",
|
|
716
|
+
levels: ["low", "medium", "high"],
|
|
717
|
+
defaultLevel: "medium"
|
|
718
|
+
}
|
|
719
|
+
}
|
|
331
720
|
],
|
|
332
721
|
apiBase: "https://api.deepseek.com/v1",
|
|
333
722
|
keyPrefix: "sk-",
|
|
334
723
|
keyPlaceholder: "sk-..."
|
|
335
724
|
},
|
|
336
|
-
|
|
337
|
-
name: "Groq",
|
|
338
|
-
models: [
|
|
339
|
-
{ id: "llama-3.3-70b-versatile", name: "Llama 3.3 70B", vision: false, context: 131072 },
|
|
340
|
-
{ id: "llama-3.1-8b-instant", name: "Llama 3.1 8B", vision: false, context: 131072 },
|
|
341
|
-
{ id: "gemma2-9b-it", name: "Gemma 2 9B", vision: false, context: 8192 }
|
|
342
|
-
],
|
|
343
|
-
apiBase: "https://api.groq.com/openai/v1",
|
|
344
|
-
keyPrefix: "gsk_",
|
|
345
|
-
keyPlaceholder: "gsk_..."
|
|
346
|
-
},
|
|
725
|
+
// ─── Mistral ──────────────────────────────────────────────────
|
|
347
726
|
mistral: {
|
|
348
727
|
name: "Mistral",
|
|
349
728
|
models: [
|
|
350
|
-
{
|
|
351
|
-
|
|
352
|
-
|
|
729
|
+
{
|
|
730
|
+
id: "mistral-large-3-25-12",
|
|
731
|
+
name: "Mistral Large 3",
|
|
732
|
+
vision: true,
|
|
733
|
+
context: 131072,
|
|
734
|
+
maxOutput: 32768
|
|
735
|
+
},
|
|
736
|
+
{
|
|
737
|
+
id: "mistral-small-4-0-26-03",
|
|
738
|
+
name: "Mistral Small 4",
|
|
739
|
+
vision: true,
|
|
740
|
+
context: 131072,
|
|
741
|
+
maxOutput: 32768
|
|
742
|
+
},
|
|
743
|
+
{
|
|
744
|
+
id: "mistral-small-3-2-25-06",
|
|
745
|
+
name: "Mistral Small 3.2",
|
|
746
|
+
vision: true,
|
|
747
|
+
context: 131072,
|
|
748
|
+
maxOutput: 32768
|
|
749
|
+
},
|
|
750
|
+
{
|
|
751
|
+
id: "codestral-2508",
|
|
752
|
+
name: "Codestral",
|
|
753
|
+
vision: false,
|
|
754
|
+
context: 262144,
|
|
755
|
+
maxOutput: 32768
|
|
756
|
+
},
|
|
757
|
+
{
|
|
758
|
+
id: "devstral-2-25-12",
|
|
759
|
+
name: "Devstral 2",
|
|
760
|
+
vision: false,
|
|
761
|
+
context: 131072,
|
|
762
|
+
maxOutput: 32768
|
|
763
|
+
},
|
|
764
|
+
{
|
|
765
|
+
id: "magistral-medium-1-2-25-09",
|
|
766
|
+
name: "Magistral Medium (Reasoning)",
|
|
767
|
+
vision: false,
|
|
768
|
+
context: 131072,
|
|
769
|
+
maxOutput: 32768,
|
|
770
|
+
thinking: {
|
|
771
|
+
supported: true,
|
|
772
|
+
paramName: "reasoning_effort",
|
|
773
|
+
paramType: "level",
|
|
774
|
+
levels: ["low", "medium", "high"],
|
|
775
|
+
defaultLevel: "medium"
|
|
776
|
+
}
|
|
777
|
+
},
|
|
778
|
+
{
|
|
779
|
+
id: "magistral-small-1-2-25-09",
|
|
780
|
+
name: "Magistral Small (Reasoning)",
|
|
781
|
+
vision: false,
|
|
782
|
+
context: 131072,
|
|
783
|
+
maxOutput: 32768,
|
|
784
|
+
thinking: {
|
|
785
|
+
supported: true,
|
|
786
|
+
paramName: "reasoning_effort",
|
|
787
|
+
paramType: "level",
|
|
788
|
+
levels: ["low", "medium", "high"],
|
|
789
|
+
defaultLevel: "medium"
|
|
790
|
+
}
|
|
791
|
+
}
|
|
353
792
|
],
|
|
354
793
|
apiBase: "https://api.mistral.ai/v1",
|
|
355
794
|
keyPrefix: "",
|
|
356
795
|
keyPlaceholder: "Enter API key..."
|
|
357
796
|
},
|
|
358
|
-
|
|
359
|
-
|
|
797
|
+
// ─── Groq ─────────────────────────────────────────────────────
|
|
798
|
+
groq: {
|
|
799
|
+
name: "Groq",
|
|
360
800
|
models: [
|
|
361
|
-
{
|
|
362
|
-
|
|
801
|
+
{
|
|
802
|
+
id: "meta-llama/llama-4-scout-17b-16e-instruct",
|
|
803
|
+
name: "Llama 4 Scout 17B",
|
|
804
|
+
vision: true,
|
|
805
|
+
context: 131072,
|
|
806
|
+
maxOutput: 8192
|
|
807
|
+
},
|
|
808
|
+
{
|
|
809
|
+
id: "llama-3.3-70b-versatile",
|
|
810
|
+
name: "Llama 3.3 70B",
|
|
811
|
+
vision: false,
|
|
812
|
+
context: 131072,
|
|
813
|
+
maxOutput: 32768
|
|
814
|
+
},
|
|
815
|
+
{
|
|
816
|
+
id: "llama-3.1-8b-instant",
|
|
817
|
+
name: "Llama 3.1 8B Instant",
|
|
818
|
+
vision: false,
|
|
819
|
+
context: 131072,
|
|
820
|
+
maxOutput: 8192
|
|
821
|
+
},
|
|
822
|
+
{
|
|
823
|
+
id: "qwen/qwen3-32b",
|
|
824
|
+
name: "Qwen 3 32B",
|
|
825
|
+
vision: false,
|
|
826
|
+
context: 131072,
|
|
827
|
+
maxOutput: 8192
|
|
828
|
+
}
|
|
363
829
|
],
|
|
364
|
-
apiBase: "https://api.
|
|
365
|
-
keyPrefix: "
|
|
366
|
-
keyPlaceholder: "
|
|
830
|
+
apiBase: "https://api.groq.com/openai/v1",
|
|
831
|
+
keyPrefix: "gsk_",
|
|
832
|
+
keyPlaceholder: "gsk_..."
|
|
367
833
|
},
|
|
834
|
+
// ─── Ollama (Local) ───────────────────────────────────────────
|
|
368
835
|
ollama: {
|
|
369
836
|
name: "Ollama (Local)",
|
|
370
837
|
models: [],
|
|
@@ -373,6 +840,7 @@ var MODEL_REGISTRY = {
|
|
|
373
840
|
keyPlaceholder: "not required",
|
|
374
841
|
local: true
|
|
375
842
|
},
|
|
843
|
+
// ─── OpenRouter (200+ models) ─────────────────────────────────
|
|
376
844
|
openrouter: {
|
|
377
845
|
name: "OpenRouter",
|
|
378
846
|
models: [],
|
|
@@ -492,8 +960,8 @@ async function chatOpenAICompatible(provider, model, apiKey, messages, context,
|
|
|
492
960
|
contextParts.consoleLogs = context.consoleLogs.map((l) => `[${l.level}] ${l.args.join(" ")}`).join("\n");
|
|
493
961
|
}
|
|
494
962
|
const enrichedContent = buildUserMessage(msg.content, contextParts);
|
|
495
|
-
const
|
|
496
|
-
if (context.screenshot &&
|
|
963
|
+
const modelInfo2 = providerConfig.models.find((m) => m.id === model);
|
|
964
|
+
if (context.screenshot && modelInfo2?.vision) {
|
|
497
965
|
apiMessages.push({
|
|
498
966
|
role: "user",
|
|
499
967
|
content: [
|
|
@@ -520,6 +988,11 @@ async function chatOpenAICompatible(provider, model, apiKey, messages, context,
|
|
|
520
988
|
stream: true,
|
|
521
989
|
max_tokens: 4096
|
|
522
990
|
};
|
|
991
|
+
const modelInfo = providerConfig.models.find((m) => m.id === model);
|
|
992
|
+
if (modelInfo?.thinking?.supported && modelInfo.thinking.paramType === "level") {
|
|
993
|
+
body.reasoning_effort = modelInfo.thinking.defaultLevel || "medium";
|
|
994
|
+
body.max_tokens = Math.min(modelInfo.maxOutput, 16384);
|
|
995
|
+
}
|
|
523
996
|
try {
|
|
524
997
|
const headers = {
|
|
525
998
|
"Content-Type": "application/json"
|
|
@@ -627,13 +1100,22 @@ async function chatAnthropic(model, apiKey, messages, context, onChunk, onDone,
|
|
|
627
1100
|
});
|
|
628
1101
|
}
|
|
629
1102
|
}
|
|
1103
|
+
const providerConfig = MODEL_REGISTRY.anthropic;
|
|
1104
|
+
const modelInfo = providerConfig?.models.find((m) => m.id === model);
|
|
1105
|
+
const thinkingBudget = modelInfo?.thinking?.defaultBudget || 0;
|
|
630
1106
|
const body = {
|
|
631
1107
|
model,
|
|
632
|
-
max_tokens: 4096,
|
|
1108
|
+
max_tokens: thinkingBudget > 0 ? Math.max(thinkingBudget + 4096, 16384) : 4096,
|
|
633
1109
|
system: SYSTEM_PROMPT,
|
|
634
1110
|
messages: apiMessages,
|
|
635
1111
|
stream: true
|
|
636
1112
|
};
|
|
1113
|
+
if (thinkingBudget > 0) {
|
|
1114
|
+
body.thinking = {
|
|
1115
|
+
type: "enabled",
|
|
1116
|
+
budget_tokens: thinkingBudget
|
|
1117
|
+
};
|
|
1118
|
+
}
|
|
637
1119
|
try {
|
|
638
1120
|
const response = await fetch(url, {
|
|
639
1121
|
method: "POST",
|
|
@@ -728,14 +1210,21 @@ async function chatGoogle(model, apiKey, messages, context, onChunk, onDone, onE
|
|
|
728
1210
|
});
|
|
729
1211
|
}
|
|
730
1212
|
}
|
|
1213
|
+
const providerConfig = MODEL_REGISTRY.google;
|
|
1214
|
+
const modelInfo = providerConfig?.models.find((m) => m.id === model);
|
|
1215
|
+
const thinkingLevel = modelInfo?.thinking?.defaultLevel;
|
|
1216
|
+
const generationConfig = {
|
|
1217
|
+
maxOutputTokens: 8192
|
|
1218
|
+
};
|
|
1219
|
+
if (thinkingLevel && thinkingLevel !== "none") {
|
|
1220
|
+
generationConfig.thinking_level = thinkingLevel.toUpperCase();
|
|
1221
|
+
}
|
|
731
1222
|
const body = {
|
|
732
1223
|
system_instruction: {
|
|
733
1224
|
parts: [{ text: SYSTEM_PROMPT }]
|
|
734
1225
|
},
|
|
735
1226
|
contents,
|
|
736
|
-
generationConfig
|
|
737
|
-
maxOutputTokens: 4096
|
|
738
|
-
}
|
|
1227
|
+
generationConfig
|
|
739
1228
|
};
|
|
740
1229
|
try {
|
|
741
1230
|
const response = await fetch(url, {
|
|
@@ -840,7 +1329,7 @@ function createOpenMagicServer(proxyPort, roots) {
|
|
|
840
1329
|
"Content-Type": "application/json",
|
|
841
1330
|
"Access-Control-Allow-Origin": "*"
|
|
842
1331
|
});
|
|
843
|
-
res.end(JSON.stringify({ status: "ok", version: "0.
|
|
1332
|
+
res.end(JSON.stringify({ status: "ok", version: "0.3.0" }));
|
|
844
1333
|
return;
|
|
845
1334
|
}
|
|
846
1335
|
res.writeHead(404);
|
|
@@ -893,7 +1382,7 @@ async function handleMessage(ws, msg, state, roots, _proxyPort) {
|
|
|
893
1382
|
id: msg.id,
|
|
894
1383
|
type: "handshake.ok",
|
|
895
1384
|
payload: {
|
|
896
|
-
version: "0.
|
|
1385
|
+
version: "0.3.0",
|
|
897
1386
|
roots,
|
|
898
1387
|
config: {
|
|
899
1388
|
provider: config.provider,
|
|
@@ -1044,6 +1533,8 @@ function serveToolbarBundle(res) {
|
|
|
1044
1533
|
|
|
1045
1534
|
// src/detect.ts
|
|
1046
1535
|
import { createConnection } from "net";
|
|
1536
|
+
import { readFileSync as readFileSync4, existsSync as existsSync4 } from "fs";
|
|
1537
|
+
import { join as join4 } from "path";
|
|
1047
1538
|
var COMMON_DEV_PORTS = [
|
|
1048
1539
|
3e3,
|
|
1049
1540
|
// React (CRA), Next.js, Express
|
|
@@ -1063,8 +1554,16 @@ var COMMON_DEV_PORTS = [
|
|
|
1063
1554
|
// Common alternate
|
|
1064
1555
|
4e3,
|
|
1065
1556
|
// Phoenix, generic
|
|
1066
|
-
1234
|
|
1557
|
+
1234,
|
|
1067
1558
|
// Parcel
|
|
1559
|
+
4321,
|
|
1560
|
+
// Astro
|
|
1561
|
+
3333,
|
|
1562
|
+
// Remix
|
|
1563
|
+
8081,
|
|
1564
|
+
// Metro (React Native)
|
|
1565
|
+
9e3
|
|
1566
|
+
// generic
|
|
1068
1567
|
];
|
|
1069
1568
|
function checkPort(port, host = "127.0.0.1") {
|
|
1070
1569
|
return new Promise((resolve3) => {
|
|
@@ -1108,9 +1607,98 @@ async function findAvailablePort(startPort) {
|
|
|
1108
1607
|
}
|
|
1109
1608
|
return port;
|
|
1110
1609
|
}
|
|
1610
|
+
var FRAMEWORK_PATTERNS = [
|
|
1611
|
+
{ match: /\bnext\b/, framework: "Next.js", defaultPort: 3e3 },
|
|
1612
|
+
{ match: /\bvite\b/, framework: "Vite", defaultPort: 5173 },
|
|
1613
|
+
{ match: /\bnuxt\b/, framework: "Nuxt", defaultPort: 3e3 },
|
|
1614
|
+
{ match: /\bng\s+serve\b/, framework: "Angular", defaultPort: 4200 },
|
|
1615
|
+
{ match: /\bvue-cli-service\s+serve\b/, framework: "Vue CLI", defaultPort: 8080 },
|
|
1616
|
+
{ match: /\bsvelte-kit\b/, framework: "SvelteKit", defaultPort: 5173 },
|
|
1617
|
+
{ match: /\bastro\b/, framework: "Astro", defaultPort: 4321 },
|
|
1618
|
+
{ match: /\bremix\b/, framework: "Remix", defaultPort: 3e3 },
|
|
1619
|
+
{ match: /\breact-scripts\s+start\b/, framework: "Create React App", defaultPort: 3e3 },
|
|
1620
|
+
{ match: /\bparcel\b/, framework: "Parcel", defaultPort: 1234 },
|
|
1621
|
+
{ match: /\bwebpack\s+serve\b|webpack-dev-server/, framework: "Webpack", defaultPort: 8080 },
|
|
1622
|
+
{ match: /\bgatsby\b/, framework: "Gatsby", defaultPort: 8e3 },
|
|
1623
|
+
{ match: /\bturborepo\b|\bturbo\b.*dev/, framework: "Turborepo", defaultPort: 3e3 },
|
|
1624
|
+
{ match: /\bexpo\b/, framework: "Expo", defaultPort: 8081 },
|
|
1625
|
+
{ match: /\bnodemon\b|\bts-node\b|\bnode\b/, framework: "Node.js", defaultPort: 3e3 },
|
|
1626
|
+
{ match: /\bflask\b/, framework: "Flask", defaultPort: 5e3 },
|
|
1627
|
+
{ match: /\bdjango\b|manage\.py\s+runserver/, framework: "Django", defaultPort: 8e3 },
|
|
1628
|
+
{ match: /\brails\b/, framework: "Rails", defaultPort: 3e3 },
|
|
1629
|
+
{ match: /\bphp\s+.*serve\b|artisan\s+serve/, framework: "PHP/Laravel", defaultPort: 8e3 }
|
|
1630
|
+
];
|
|
1631
|
+
var DEV_SCRIPT_NAMES = ["dev", "start", "serve", "develop", "dev:start", "start:dev"];
|
|
1632
|
+
function detectDevScripts(cwd = process.cwd()) {
|
|
1633
|
+
const pkgPath = join4(cwd, "package.json");
|
|
1634
|
+
if (!existsSync4(pkgPath)) return [];
|
|
1635
|
+
let pkg;
|
|
1636
|
+
try {
|
|
1637
|
+
pkg = JSON.parse(readFileSync4(pkgPath, "utf-8"));
|
|
1638
|
+
} catch {
|
|
1639
|
+
return [];
|
|
1640
|
+
}
|
|
1641
|
+
if (!pkg.scripts) return [];
|
|
1642
|
+
const scripts = [];
|
|
1643
|
+
for (const name of DEV_SCRIPT_NAMES) {
|
|
1644
|
+
const command = pkg.scripts[name];
|
|
1645
|
+
if (!command) continue;
|
|
1646
|
+
let framework = "Unknown";
|
|
1647
|
+
let defaultPort = 3e3;
|
|
1648
|
+
for (const pattern of FRAMEWORK_PATTERNS) {
|
|
1649
|
+
if (pattern.match.test(command)) {
|
|
1650
|
+
framework = pattern.framework;
|
|
1651
|
+
defaultPort = pattern.defaultPort;
|
|
1652
|
+
break;
|
|
1653
|
+
}
|
|
1654
|
+
}
|
|
1655
|
+
const portMatch = command.match(/(?:--port|-p)\s+(\d+)/);
|
|
1656
|
+
if (portMatch) {
|
|
1657
|
+
defaultPort = parseInt(portMatch[1], 10);
|
|
1658
|
+
}
|
|
1659
|
+
scripts.push({ name, command, framework, defaultPort });
|
|
1660
|
+
}
|
|
1661
|
+
return scripts;
|
|
1662
|
+
}
|
|
1663
|
+
function getProjectName(cwd = process.cwd()) {
|
|
1664
|
+
const pkgPath = join4(cwd, "package.json");
|
|
1665
|
+
if (!existsSync4(pkgPath)) return "this project";
|
|
1666
|
+
try {
|
|
1667
|
+
const pkg = JSON.parse(readFileSync4(pkgPath, "utf-8"));
|
|
1668
|
+
return pkg.name || "this project";
|
|
1669
|
+
} catch {
|
|
1670
|
+
return "this project";
|
|
1671
|
+
}
|
|
1672
|
+
}
|
|
1111
1673
|
|
|
1112
1674
|
// src/cli.ts
|
|
1113
|
-
var VERSION = "0.
|
|
1675
|
+
var VERSION = "0.3.0";
|
|
1676
|
+
function ask(question) {
|
|
1677
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
1678
|
+
return new Promise((resolve3) => {
|
|
1679
|
+
rl.question(question, (answer) => {
|
|
1680
|
+
rl.close();
|
|
1681
|
+
resolve3(answer.trim());
|
|
1682
|
+
});
|
|
1683
|
+
});
|
|
1684
|
+
}
|
|
1685
|
+
function waitForPort(port, timeoutMs = 3e4) {
|
|
1686
|
+
const start = Date.now();
|
|
1687
|
+
return new Promise((resolve3) => {
|
|
1688
|
+
const check = async () => {
|
|
1689
|
+
if (await isPortOpen(port)) {
|
|
1690
|
+
resolve3(true);
|
|
1691
|
+
return;
|
|
1692
|
+
}
|
|
1693
|
+
if (Date.now() - start > timeoutMs) {
|
|
1694
|
+
resolve3(false);
|
|
1695
|
+
return;
|
|
1696
|
+
}
|
|
1697
|
+
setTimeout(check, 500);
|
|
1698
|
+
};
|
|
1699
|
+
check();
|
|
1700
|
+
});
|
|
1701
|
+
}
|
|
1114
1702
|
var program = new Command();
|
|
1115
1703
|
program.name("openmagic").description("AI-powered coding toolbar for any web application").version(VERSION).option("-p, --port <port>", "Dev server port to proxy", "").option(
|
|
1116
1704
|
"-l, --listen <port>",
|
|
@@ -1131,43 +1719,35 @@ program.name("openmagic").description("AI-powered coding toolbar for any web app
|
|
|
1131
1719
|
targetPort = parseInt(opts.port, 10);
|
|
1132
1720
|
const isRunning = await isPortOpen(targetPort);
|
|
1133
1721
|
if (!isRunning) {
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
);
|
|
1139
|
-
console.log(
|
|
1140
|
-
chalk.dim(
|
|
1141
|
-
" Start your dev server first, then run openmagic again."
|
|
1142
|
-
)
|
|
1143
|
-
);
|
|
1144
|
-
console.log("");
|
|
1145
|
-
process.exit(1);
|
|
1722
|
+
const started = await offerToStartDevServer(targetPort);
|
|
1723
|
+
if (!started) {
|
|
1724
|
+
process.exit(1);
|
|
1725
|
+
}
|
|
1146
1726
|
}
|
|
1147
1727
|
} else {
|
|
1148
1728
|
console.log(chalk.dim(" Scanning for dev server..."));
|
|
1149
1729
|
const detected = await detectDevServer();
|
|
1150
|
-
if (
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
)
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
)
|
|
1160
|
-
|
|
1161
|
-
chalk.
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1730
|
+
if (detected) {
|
|
1731
|
+
targetPort = detected.port;
|
|
1732
|
+
targetHost = detected.host;
|
|
1733
|
+
} else {
|
|
1734
|
+
const started = await offerToStartDevServer();
|
|
1735
|
+
if (!started) {
|
|
1736
|
+
process.exit(1);
|
|
1737
|
+
}
|
|
1738
|
+
const redetected = await detectDevServer();
|
|
1739
|
+
if (!redetected) {
|
|
1740
|
+
console.log(chalk.red(" \u2717 Could not detect the dev server after starting."));
|
|
1741
|
+
console.log(chalk.dim(" Try specifying the port: npx openmagic --port 3000"));
|
|
1742
|
+
console.log("");
|
|
1743
|
+
process.exit(1);
|
|
1744
|
+
}
|
|
1745
|
+
targetPort = redetected.port;
|
|
1746
|
+
targetHost = redetected.host;
|
|
1165
1747
|
}
|
|
1166
|
-
targetPort = detected.port;
|
|
1167
|
-
targetHost = detected.host;
|
|
1168
1748
|
}
|
|
1169
1749
|
console.log(
|
|
1170
|
-
chalk.green(` \u2713 Dev server
|
|
1750
|
+
chalk.green(` \u2713 Dev server running at ${targetHost}:${targetPort}`)
|
|
1171
1751
|
);
|
|
1172
1752
|
const roots = (opts.root || [process.cwd()]).map(
|
|
1173
1753
|
(r) => resolve2(r)
|
|
@@ -1218,5 +1798,149 @@ program.name("openmagic").description("AI-powered coding toolbar for any web app
|
|
|
1218
1798
|
process.on("SIGINT", shutdown);
|
|
1219
1799
|
process.on("SIGTERM", shutdown);
|
|
1220
1800
|
});
|
|
1801
|
+
async function offerToStartDevServer(expectedPort) {
|
|
1802
|
+
const projectName = getProjectName();
|
|
1803
|
+
const scripts = detectDevScripts();
|
|
1804
|
+
if (scripts.length === 0) {
|
|
1805
|
+
console.log(
|
|
1806
|
+
chalk.yellow(" \u26A0 No dev server detected and no dev scripts found in package.json")
|
|
1807
|
+
);
|
|
1808
|
+
console.log("");
|
|
1809
|
+
console.log(chalk.white(" Start your dev server manually, then run:"));
|
|
1810
|
+
console.log(chalk.cyan(" npx openmagic --port <your-port>"));
|
|
1811
|
+
console.log("");
|
|
1812
|
+
return false;
|
|
1813
|
+
}
|
|
1814
|
+
let chosen = scripts[0];
|
|
1815
|
+
if (scripts.length === 1) {
|
|
1816
|
+
console.log(
|
|
1817
|
+
chalk.yellow(" \u26A0 No dev server detected.")
|
|
1818
|
+
);
|
|
1819
|
+
console.log("");
|
|
1820
|
+
console.log(
|
|
1821
|
+
chalk.white(` Found `) + chalk.cyan(`npm run ${chosen.name}`) + chalk.white(` in ${projectName}`) + chalk.dim(` (${chosen.framework})`)
|
|
1822
|
+
);
|
|
1823
|
+
console.log(chalk.dim(` \u2192 ${chosen.command}`));
|
|
1824
|
+
console.log("");
|
|
1825
|
+
const answer = await ask(
|
|
1826
|
+
chalk.white(` Start it now? `) + chalk.dim("(Y/n) ")
|
|
1827
|
+
);
|
|
1828
|
+
if (answer.toLowerCase() === "n" || answer.toLowerCase() === "no") {
|
|
1829
|
+
console.log("");
|
|
1830
|
+
console.log(chalk.dim(" Start your dev server first, then run openmagic again."));
|
|
1831
|
+
console.log("");
|
|
1832
|
+
return false;
|
|
1833
|
+
}
|
|
1834
|
+
} else {
|
|
1835
|
+
console.log(
|
|
1836
|
+
chalk.yellow(" \u26A0 No dev server detected.")
|
|
1837
|
+
);
|
|
1838
|
+
console.log("");
|
|
1839
|
+
console.log(
|
|
1840
|
+
chalk.white(` Found ${scripts.length} dev scripts in ${projectName}:`)
|
|
1841
|
+
);
|
|
1842
|
+
console.log("");
|
|
1843
|
+
scripts.forEach((s, i) => {
|
|
1844
|
+
console.log(
|
|
1845
|
+
chalk.cyan(` ${i + 1}) `) + chalk.white(`npm run ${s.name}`) + chalk.dim(` \u2014 ${s.framework} (port ${s.defaultPort})`)
|
|
1846
|
+
);
|
|
1847
|
+
console.log(chalk.dim(` ${s.command}`));
|
|
1848
|
+
});
|
|
1849
|
+
console.log("");
|
|
1850
|
+
const answer = await ask(
|
|
1851
|
+
chalk.white(` Which one to start? `) + chalk.dim(`(1-${scripts.length}, or n to cancel) `)
|
|
1852
|
+
);
|
|
1853
|
+
if (answer.toLowerCase() === "n" || answer.toLowerCase() === "no" || answer === "") {
|
|
1854
|
+
console.log("");
|
|
1855
|
+
console.log(chalk.dim(" Start your dev server first, then run openmagic again."));
|
|
1856
|
+
console.log("");
|
|
1857
|
+
return false;
|
|
1858
|
+
}
|
|
1859
|
+
const idx = parseInt(answer, 10) - 1;
|
|
1860
|
+
if (idx < 0 || idx >= scripts.length || isNaN(idx)) {
|
|
1861
|
+
chosen = scripts[0];
|
|
1862
|
+
} else {
|
|
1863
|
+
chosen = scripts[idx];
|
|
1864
|
+
}
|
|
1865
|
+
}
|
|
1866
|
+
const port = expectedPort || chosen.defaultPort;
|
|
1867
|
+
console.log("");
|
|
1868
|
+
console.log(
|
|
1869
|
+
chalk.dim(` Starting `) + chalk.cyan(`npm run ${chosen.name}`) + chalk.dim("...")
|
|
1870
|
+
);
|
|
1871
|
+
const child = spawn("npm", ["run", chosen.name], {
|
|
1872
|
+
cwd: process.cwd(),
|
|
1873
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
1874
|
+
detached: false,
|
|
1875
|
+
shell: true,
|
|
1876
|
+
env: {
|
|
1877
|
+
...process.env,
|
|
1878
|
+
PORT: String(port),
|
|
1879
|
+
// CRA, Express
|
|
1880
|
+
BROWSER: "none",
|
|
1881
|
+
// Prevent CRA from opening browser
|
|
1882
|
+
BROWSER_NONE: "true"
|
|
1883
|
+
// Some frameworks
|
|
1884
|
+
}
|
|
1885
|
+
});
|
|
1886
|
+
child.stdout?.on("data", (data) => {
|
|
1887
|
+
const lines = data.toString().trim().split("\n");
|
|
1888
|
+
for (const line of lines) {
|
|
1889
|
+
if (line.trim()) {
|
|
1890
|
+
process.stdout.write(chalk.dim(` \u2502 ${line}
|
|
1891
|
+
`));
|
|
1892
|
+
}
|
|
1893
|
+
}
|
|
1894
|
+
});
|
|
1895
|
+
child.stderr?.on("data", (data) => {
|
|
1896
|
+
const lines = data.toString().trim().split("\n");
|
|
1897
|
+
for (const line of lines) {
|
|
1898
|
+
if (line.trim()) {
|
|
1899
|
+
process.stdout.write(chalk.dim(` \u2502 ${line}
|
|
1900
|
+
`));
|
|
1901
|
+
}
|
|
1902
|
+
}
|
|
1903
|
+
});
|
|
1904
|
+
child.on("error", (err) => {
|
|
1905
|
+
console.log(chalk.red(` \u2717 Failed to start: ${err.message}`));
|
|
1906
|
+
});
|
|
1907
|
+
child.on("exit", (code) => {
|
|
1908
|
+
if (code !== null && code !== 0) {
|
|
1909
|
+
console.log(chalk.red(` \u2717 Dev server exited with code ${code}`));
|
|
1910
|
+
}
|
|
1911
|
+
});
|
|
1912
|
+
const cleanup = () => {
|
|
1913
|
+
try {
|
|
1914
|
+
child.kill("SIGTERM");
|
|
1915
|
+
} catch {
|
|
1916
|
+
}
|
|
1917
|
+
};
|
|
1918
|
+
process.on("exit", cleanup);
|
|
1919
|
+
process.on("SIGINT", cleanup);
|
|
1920
|
+
process.on("SIGTERM", cleanup);
|
|
1921
|
+
console.log(
|
|
1922
|
+
chalk.dim(` Waiting for port ${port}...`)
|
|
1923
|
+
);
|
|
1924
|
+
const isUp = await waitForPort(port, 3e4);
|
|
1925
|
+
if (!isUp) {
|
|
1926
|
+
console.log(
|
|
1927
|
+
chalk.yellow(` \u26A0 Port ${port} didn't open after 30s.`)
|
|
1928
|
+
);
|
|
1929
|
+
console.log(
|
|
1930
|
+
chalk.dim(` The server might use a different port. Check the output above.`)
|
|
1931
|
+
);
|
|
1932
|
+
console.log("");
|
|
1933
|
+
const detected = await detectDevServer();
|
|
1934
|
+
if (detected) {
|
|
1935
|
+
console.log(
|
|
1936
|
+
chalk.green(` \u2713 Found server on port ${detected.port} instead.`)
|
|
1937
|
+
);
|
|
1938
|
+
return true;
|
|
1939
|
+
}
|
|
1940
|
+
return false;
|
|
1941
|
+
}
|
|
1942
|
+
console.log("");
|
|
1943
|
+
return true;
|
|
1944
|
+
}
|
|
1221
1945
|
program.parse();
|
|
1222
1946
|
//# sourceMappingURL=cli.js.map
|