fraim 2.0.167 → 2.0.168
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/dist/src/ai-hub/catalog.js +28 -14
- package/dist/src/ai-hub/server.js +10 -403
- package/dist/src/cli/commands/init-project.js +1 -98
- package/dist/src/cli/commands/manager.js +40 -0
- package/dist/src/cli/commands/sync.js +17 -21
- package/dist/src/cli/fraim.js +2 -0
- package/dist/src/cli/utils/github-workflow-sync.js +12 -146
- package/dist/src/cli/utils/manager-pack-sync.js +188 -0
- package/dist/src/cli/utils/manager-publish.js +76 -0
- package/dist/src/cli/utils/user-config.js +20 -0
- package/dist/src/core/fraim-config-schema.generated.js +85 -10
- package/dist/src/core/manager-pack.js +26 -0
- package/dist/src/first-run/install-state.js +1 -0
- package/dist/src/first-run/server.js +9 -0
- package/dist/src/first-run/session-service.js +117 -23
- package/dist/src/first-run/types.js +2 -5
- package/dist/src/local-mcp-server/learning-context-builder.js +45 -8
- package/dist/src/local-mcp-server/stdio-server.js +28 -0
- package/index.js +1 -1
- package/package.json +1 -2
- package/public/ai-hub/index.html +0 -81
- package/public/ai-hub/powerpoint-taskpane/index.html +236 -236
- package/public/ai-hub/powerpoint-taskpane/manifest.xml +29 -29
- package/public/ai-hub/script.js +3 -219
- package/public/ai-hub/styles.css +8 -36
- package/public/first-run/index.html +1 -1
- package/public/first-run/script.js +459 -530
- package/public/first-run/styles.css +288 -73
- package/public/portfolio/ashley.html +1 -1
- package/public/portfolio/casey.html +1 -1
- package/public/portfolio/celia.html +1 -1
- package/public/portfolio/gautam.html +1 -1
- package/public/portfolio/hari.html +1 -1
- package/public/portfolio/maestro.html +1 -1
- package/public/portfolio/mandy.html +1 -1
- package/public/portfolio/pam.html +6 -6
- package/public/portfolio/qasm.html +1 -1
- package/public/portfolio/sade.html +1 -1
- package/public/portfolio/sam.html +1 -1
- package/public/portfolio/swen.html +6 -6
- package/dist/src/ai-hub/word-sideload.js +0 -95
- package/dist/src/cli/commands/test-mcp.js +0 -171
- package/dist/src/cli/setup/first-run.js +0 -242
- package/dist/src/config/ai-manager-hiring.js +0 -121
- package/dist/src/config/compat.js +0 -16
- package/dist/src/config/feature-flags.js +0 -25
- package/dist/src/config/persona-capability-bundles.js +0 -273
- package/dist/src/config/persona-hiring.js +0 -270
- package/dist/src/config/portfolio-slug-overrides.js +0 -17
- package/dist/src/config/pricing.js +0 -37
- package/dist/src/config/stripe.js +0 -43
- package/dist/src/core/config-writer.js +0 -75
- package/dist/src/core/utils/job-aliases.js +0 -47
- package/dist/src/core/utils/workflow-parser.js +0 -174
|
@@ -421,14 +421,209 @@ body {
|
|
|
421
421
|
flex-direction: column;
|
|
422
422
|
}
|
|
423
423
|
|
|
424
|
-
.
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
gap:
|
|
431
|
-
}
|
|
424
|
+
.setup-shell {
|
|
425
|
+
list-style: none;
|
|
426
|
+
margin: 0;
|
|
427
|
+
padding: 0;
|
|
428
|
+
display: grid;
|
|
429
|
+
grid-template-columns: minmax(190px, 0.85fr) minmax(0, 2fr);
|
|
430
|
+
gap: 20px;
|
|
431
|
+
}
|
|
432
|
+
.setup-steps {
|
|
433
|
+
display: flex;
|
|
434
|
+
flex-direction: column;
|
|
435
|
+
gap: 8px;
|
|
436
|
+
border-right: 1px solid var(--line);
|
|
437
|
+
padding-right: 16px;
|
|
438
|
+
}
|
|
439
|
+
.setup-step {
|
|
440
|
+
width: 100%;
|
|
441
|
+
min-height: 44px;
|
|
442
|
+
display: flex;
|
|
443
|
+
align-items: center;
|
|
444
|
+
gap: 10px;
|
|
445
|
+
border: 1px solid var(--line);
|
|
446
|
+
border-radius: 8px;
|
|
447
|
+
background: var(--surface);
|
|
448
|
+
color: var(--text);
|
|
449
|
+
padding: 9px 10px;
|
|
450
|
+
font: inherit;
|
|
451
|
+
font-weight: 600;
|
|
452
|
+
text-align: left;
|
|
453
|
+
cursor: pointer;
|
|
454
|
+
}
|
|
455
|
+
.setup-step[aria-current="step"] {
|
|
456
|
+
border-color: var(--accent);
|
|
457
|
+
background: var(--accent-soft);
|
|
458
|
+
}
|
|
459
|
+
.setup-step:disabled {
|
|
460
|
+
opacity: 0.55;
|
|
461
|
+
cursor: not-allowed;
|
|
462
|
+
}
|
|
463
|
+
.step-dot {
|
|
464
|
+
width: 24px;
|
|
465
|
+
height: 24px;
|
|
466
|
+
border-radius: 50%;
|
|
467
|
+
display: inline-flex;
|
|
468
|
+
align-items: center;
|
|
469
|
+
justify-content: center;
|
|
470
|
+
background: var(--line);
|
|
471
|
+
color: var(--muted);
|
|
472
|
+
font-size: 12px;
|
|
473
|
+
flex: 0 0 auto;
|
|
474
|
+
}
|
|
475
|
+
.setup-step[data-status="done"] .step-dot {
|
|
476
|
+
background: var(--accent);
|
|
477
|
+
color: #fff;
|
|
478
|
+
}
|
|
479
|
+
.setup-pane {
|
|
480
|
+
min-width: 0;
|
|
481
|
+
display: flex;
|
|
482
|
+
flex-direction: column;
|
|
483
|
+
gap: 14px;
|
|
484
|
+
}
|
|
485
|
+
.setup-pane h2 {
|
|
486
|
+
margin: 0;
|
|
487
|
+
font-size: 18px;
|
|
488
|
+
font-weight: 650;
|
|
489
|
+
}
|
|
490
|
+
.pane-copy,
|
|
491
|
+
.locked-note {
|
|
492
|
+
margin: 0;
|
|
493
|
+
color: var(--muted);
|
|
494
|
+
font-size: 14px;
|
|
495
|
+
}
|
|
496
|
+
.locked-note { color: var(--warn); }
|
|
497
|
+
.row-list {
|
|
498
|
+
list-style: none;
|
|
499
|
+
margin: 0;
|
|
500
|
+
padding: 0;
|
|
501
|
+
border: 1px solid var(--line);
|
|
502
|
+
border-radius: 10px;
|
|
503
|
+
overflow: hidden;
|
|
504
|
+
}
|
|
505
|
+
.row-list .row {
|
|
506
|
+
display: flex;
|
|
507
|
+
gap: 10px;
|
|
508
|
+
align-items: center;
|
|
509
|
+
padding: 12px;
|
|
510
|
+
border-bottom: 1px solid var(--line);
|
|
511
|
+
}
|
|
512
|
+
.row-list .row:last-child { border-bottom: 0; }
|
|
513
|
+
.agent-grid,
|
|
514
|
+
.choice-grid {
|
|
515
|
+
display: grid;
|
|
516
|
+
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
517
|
+
gap: 12px;
|
|
518
|
+
}
|
|
519
|
+
.ready-strip {
|
|
520
|
+
background: var(--accent-soft);
|
|
521
|
+
border: 1px solid var(--accent);
|
|
522
|
+
color: var(--accent-strong);
|
|
523
|
+
border-radius: 8px;
|
|
524
|
+
padding: 10px 12px;
|
|
525
|
+
font-size: 13px;
|
|
526
|
+
font-weight: 600;
|
|
527
|
+
}
|
|
528
|
+
.ready-strip--muted {
|
|
529
|
+
background: var(--soft);
|
|
530
|
+
border-color: var(--line);
|
|
531
|
+
color: var(--muted);
|
|
532
|
+
}
|
|
533
|
+
.ready-strip--error {
|
|
534
|
+
background: #fff1f2;
|
|
535
|
+
border-color: #fb7185;
|
|
536
|
+
color: #9f1239;
|
|
537
|
+
}
|
|
538
|
+
.btn {
|
|
539
|
+
border-radius: 8px;
|
|
540
|
+
padding: 9px 14px;
|
|
541
|
+
font: inherit;
|
|
542
|
+
font-weight: 600;
|
|
543
|
+
cursor: pointer;
|
|
544
|
+
border: 1px solid var(--line);
|
|
545
|
+
align-self: flex-start;
|
|
546
|
+
}
|
|
547
|
+
.btn:disabled {
|
|
548
|
+
opacity: 0.55;
|
|
549
|
+
cursor: not-allowed;
|
|
550
|
+
}
|
|
551
|
+
.btn-primary {
|
|
552
|
+
background: var(--accent);
|
|
553
|
+
border-color: var(--accent);
|
|
554
|
+
color: #fff;
|
|
555
|
+
}
|
|
556
|
+
.btn-primary:hover { background: var(--accent-strong); }
|
|
557
|
+
.btn-secondary {
|
|
558
|
+
background: var(--surface);
|
|
559
|
+
color: var(--accent-strong);
|
|
560
|
+
}
|
|
561
|
+
.btn-secondary:hover {
|
|
562
|
+
border-color: var(--accent);
|
|
563
|
+
background: var(--soft);
|
|
564
|
+
}
|
|
565
|
+
.btn-ghost {
|
|
566
|
+
background: transparent;
|
|
567
|
+
color: var(--muted);
|
|
568
|
+
}
|
|
569
|
+
.modal-backdrop {
|
|
570
|
+
position: fixed;
|
|
571
|
+
inset: 0;
|
|
572
|
+
z-index: 1000;
|
|
573
|
+
display: flex;
|
|
574
|
+
align-items: center;
|
|
575
|
+
justify-content: center;
|
|
576
|
+
padding: 18px;
|
|
577
|
+
background: rgba(31, 42, 36, 0.36);
|
|
578
|
+
}
|
|
579
|
+
.modal {
|
|
580
|
+
width: min(520px, 100%);
|
|
581
|
+
background: var(--surface);
|
|
582
|
+
border: 1px solid var(--line);
|
|
583
|
+
border-radius: 12px;
|
|
584
|
+
box-shadow: var(--shadow-lg);
|
|
585
|
+
padding: 22px;
|
|
586
|
+
display: flex;
|
|
587
|
+
flex-direction: column;
|
|
588
|
+
gap: 14px;
|
|
589
|
+
}
|
|
590
|
+
.modal h2 {
|
|
591
|
+
margin: 0;
|
|
592
|
+
font-size: 19px;
|
|
593
|
+
}
|
|
594
|
+
.install-status {
|
|
595
|
+
margin: 0;
|
|
596
|
+
border: 1px solid var(--line);
|
|
597
|
+
background: var(--soft);
|
|
598
|
+
border-radius: 8px;
|
|
599
|
+
padding: 10px 12px;
|
|
600
|
+
color: var(--muted);
|
|
601
|
+
}
|
|
602
|
+
.install-status[data-tone="error"] {
|
|
603
|
+
color: var(--danger);
|
|
604
|
+
background: #fbf1f1;
|
|
605
|
+
border-color: var(--danger);
|
|
606
|
+
}
|
|
607
|
+
.modal-help {
|
|
608
|
+
margin: -4px 0 0;
|
|
609
|
+
color: var(--muted);
|
|
610
|
+
font-size: 13px;
|
|
611
|
+
line-height: 1.45;
|
|
612
|
+
}
|
|
613
|
+
.install-actions {
|
|
614
|
+
display: flex;
|
|
615
|
+
flex-wrap: wrap;
|
|
616
|
+
gap: 8px;
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
.recruit-container,
|
|
620
|
+
.start-container {
|
|
621
|
+
gap: 12px;
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
.start-container {
|
|
625
|
+
gap: 14px;
|
|
626
|
+
}
|
|
432
627
|
|
|
433
628
|
.user-type-card {
|
|
434
629
|
background: var(--surface);
|
|
@@ -448,23 +643,23 @@ body {
|
|
|
448
643
|
border-color: var(--accent);
|
|
449
644
|
background: var(--accent-soft);
|
|
450
645
|
}
|
|
451
|
-
.user-type-card--featured:hover {
|
|
452
|
-
box-shadow: 0 4px 20px rgba(61, 138, 110, 0.18);
|
|
453
|
-
}
|
|
454
|
-
|
|
455
|
-
.user-type-card--ide-default {
|
|
456
|
-
padding: 24px;
|
|
457
|
-
gap: 18px;
|
|
458
|
-
}
|
|
459
|
-
|
|
460
|
-
.user-type-card--compact {
|
|
461
|
-
padding: 16px 18px;
|
|
462
|
-
gap: 12px;
|
|
463
|
-
}
|
|
464
|
-
|
|
465
|
-
.user-type-card--alpha {
|
|
466
|
-
background: linear-gradient(180deg, #fbfcfa 0%, #f4f7f2 100%);
|
|
467
|
-
}
|
|
646
|
+
.user-type-card--featured:hover {
|
|
647
|
+
box-shadow: 0 4px 20px rgba(61, 138, 110, 0.18);
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
.user-type-card--ide-default {
|
|
651
|
+
padding: 24px;
|
|
652
|
+
gap: 18px;
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
.user-type-card--compact {
|
|
656
|
+
padding: 16px 18px;
|
|
657
|
+
gap: 12px;
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
.user-type-card--alpha {
|
|
661
|
+
background: linear-gradient(180deg, #fbfcfa 0%, #f4f7f2 100%);
|
|
662
|
+
}
|
|
468
663
|
|
|
469
664
|
.card-header {
|
|
470
665
|
display: flex;
|
|
@@ -492,38 +687,38 @@ body {
|
|
|
492
687
|
border-color: var(--accent);
|
|
493
688
|
}
|
|
494
689
|
|
|
495
|
-
.card-title {
|
|
496
|
-
display: block;
|
|
497
|
-
font-size: 15px;
|
|
498
|
-
font-weight: 600;
|
|
499
|
-
color: var(--text);
|
|
500
|
-
margin-bottom: 3px;
|
|
501
|
-
}
|
|
502
|
-
.card-desc { margin: 0; font-size: 13px; color: var(--muted); line-height: 1.45; }
|
|
503
|
-
|
|
504
|
-
.card-eyebrow {
|
|
505
|
-
display: inline-flex;
|
|
506
|
-
align-self: flex-start;
|
|
507
|
-
padding: 4px 10px;
|
|
508
|
-
border-radius: 999px;
|
|
509
|
-
background: var(--accent-soft);
|
|
510
|
-
color: var(--accent-strong);
|
|
511
|
-
font-size: 11px;
|
|
512
|
-
font-weight: 700;
|
|
513
|
-
letter-spacing: 0.06em;
|
|
514
|
-
text-transform: uppercase;
|
|
515
|
-
}
|
|
516
|
-
|
|
517
|
-
.route-note {
|
|
518
|
-
margin: 0;
|
|
519
|
-
color: var(--muted);
|
|
520
|
-
font-size: 13px;
|
|
521
|
-
line-height: 1.5;
|
|
522
|
-
}
|
|
523
|
-
|
|
524
|
-
.route-note--compact {
|
|
525
|
-
font-size: 12px;
|
|
526
|
-
}
|
|
690
|
+
.card-title {
|
|
691
|
+
display: block;
|
|
692
|
+
font-size: 15px;
|
|
693
|
+
font-weight: 600;
|
|
694
|
+
color: var(--text);
|
|
695
|
+
margin-bottom: 3px;
|
|
696
|
+
}
|
|
697
|
+
.card-desc { margin: 0; font-size: 13px; color: var(--muted); line-height: 1.45; }
|
|
698
|
+
|
|
699
|
+
.card-eyebrow {
|
|
700
|
+
display: inline-flex;
|
|
701
|
+
align-self: flex-start;
|
|
702
|
+
padding: 4px 10px;
|
|
703
|
+
border-radius: 999px;
|
|
704
|
+
background: var(--accent-soft);
|
|
705
|
+
color: var(--accent-strong);
|
|
706
|
+
font-size: 11px;
|
|
707
|
+
font-weight: 700;
|
|
708
|
+
letter-spacing: 0.06em;
|
|
709
|
+
text-transform: uppercase;
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
.route-note {
|
|
713
|
+
margin: 0;
|
|
714
|
+
color: var(--muted);
|
|
715
|
+
font-size: 13px;
|
|
716
|
+
line-height: 1.5;
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
.route-note--compact {
|
|
720
|
+
font-size: 12px;
|
|
721
|
+
}
|
|
527
722
|
|
|
528
723
|
.btn {
|
|
529
724
|
display: inline-flex;
|
|
@@ -567,14 +762,25 @@ body {
|
|
|
567
762
|
.recruit-card {
|
|
568
763
|
cursor: default;
|
|
569
764
|
}
|
|
765
|
+
|
|
766
|
+
.byoa-card {
|
|
767
|
+
grid-column: 1 / -1;
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
.byoa-card .cmd-block {
|
|
771
|
+
align-self: flex-start;
|
|
772
|
+
display: inline-block;
|
|
773
|
+
white-space: nowrap;
|
|
774
|
+
word-break: normal;
|
|
775
|
+
}
|
|
570
776
|
.recruit-card:hover {
|
|
571
777
|
box-shadow: none;
|
|
572
778
|
border-color: var(--line);
|
|
573
779
|
}
|
|
574
780
|
|
|
575
|
-
/* IDE command display — shown after choosing "In my IDE". */
|
|
576
|
-
.cmd-block {
|
|
577
|
-
background: #0d1410;
|
|
781
|
+
/* IDE command display — shown after choosing "In my IDE". */
|
|
782
|
+
.cmd-block {
|
|
783
|
+
background: #0d1410;
|
|
578
784
|
color: #c8d3cd;
|
|
579
785
|
font-family: "JetBrains Mono", "Cascadia Code", Consolas, monospace;
|
|
580
786
|
font-size: 14px;
|
|
@@ -612,16 +818,25 @@ body {
|
|
|
612
818
|
* want the surface to degrade gracefully for users who happen to open it
|
|
613
819
|
* in a narrow window. Stack the label / verb / change link vertically and
|
|
614
820
|
* let the row breathe. */
|
|
615
|
-
@media (max-width: 600px) {
|
|
616
|
-
.page { padding: 24px 16px 24px; }
|
|
617
|
-
.
|
|
618
|
-
.
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
.
|
|
625
|
-
|
|
626
|
-
}
|
|
627
|
-
}
|
|
821
|
+
@media (max-width: 600px) {
|
|
822
|
+
.page { padding: 24px 16px 24px; }
|
|
823
|
+
.setup-shell { grid-template-columns: 1fr; }
|
|
824
|
+
.setup-steps {
|
|
825
|
+
border-right: 0;
|
|
826
|
+
border-bottom: 1px solid var(--line);
|
|
827
|
+
padding-right: 0;
|
|
828
|
+
padding-bottom: 12px;
|
|
829
|
+
}
|
|
830
|
+
.agent-grid,
|
|
831
|
+
.choice-grid { grid-template-columns: 1fr; }
|
|
832
|
+
.checklist .row { gap: 8px; padding: 12px 14px; }
|
|
833
|
+
.row .label { min-width: 0; }
|
|
834
|
+
.row .verb { flex: 1 1 100%; }
|
|
835
|
+
.row .change-link { margin-left: auto; }
|
|
836
|
+
.row .project-picker { gap: 8px; }
|
|
837
|
+
.row .project-picker input { width: 100%; min-width: 0; }
|
|
838
|
+
.user-type-card--ide-default,
|
|
839
|
+
.user-type-card--compact {
|
|
840
|
+
padding: 18px 16px;
|
|
841
|
+
}
|
|
842
|
+
}
|
|
@@ -434,7 +434,7 @@ body { font-family: 'Inter', sans-serif; background: var(--bg); color: var(--tex
|
|
|
434
434
|
<div class="brand-logo">F</div>
|
|
435
435
|
<span class="brand-name">FRAIM</span>
|
|
436
436
|
</a>
|
|
437
|
-
<div class="header-actions">
|
|
437
|
+
<div class="header-actions">
|
|
438
438
|
<button class="theme-btn" onclick="toggleTheme()" title="Toggle dark mode">☾</button>
|
|
439
439
|
</div>
|
|
440
440
|
</header>
|
|
@@ -445,8 +445,8 @@ body { font-family: 'Inter', sans-serif; background: var(--bg); color: var(--tex
|
|
|
445
445
|
</div>
|
|
446
446
|
<div class="role-chip">AI Product Manager</div>
|
|
447
447
|
<h1>Specs that ship,<br>not <span>slide decks</span></h1>
|
|
448
|
-
<p>PaM turns vague ideas into traceable requirements, phased roadmaps, and acceptance criteria that engineers can execute on — without ambiguity, without meetings.</p>
|
|
449
|
-
</section>
|
|
448
|
+
<p>PaM turns vague ideas into traceable requirements, phased roadmaps, and acceptance criteria that engineers can execute on — without ambiguity, without meetings.</p>
|
|
449
|
+
</section>
|
|
450
450
|
|
|
451
451
|
<div class="section-label">
|
|
452
452
|
<h2>Selected Work</h2>
|
|
@@ -792,8 +792,8 @@ await ceq.track(<span class="snippet-string">'purchase'</span>, { memberId, amou
|
|
|
792
792
|
|
|
793
793
|
</div>
|
|
794
794
|
|
|
795
|
-
<footer class="portfolio-footer">
|
|
796
|
-
<div class="footer-sub">
|
|
795
|
+
<footer class="portfolio-footer">
|
|
796
|
+
<div class="footer-sub">
|
|
797
797
|
Part of the <a href="/">FRAIM</a> · 18 AI employees available ·
|
|
798
798
|
<a href="/">View all employees</a>
|
|
799
799
|
</div>
|
|
@@ -882,6 +882,6 @@ function updateWizard() {
|
|
|
882
882
|
// Init brand scale on load
|
|
883
883
|
document.addEventListener('DOMContentLoaded', () => setBrand('#6366f1','#6366f1'));
|
|
884
884
|
</script>
|
|
885
|
-
|
|
885
|
+
|
|
886
886
|
</body>
|
|
887
887
|
</html>
|
|
@@ -245,7 +245,7 @@ code, pre, .mono { font-family: 'JetBrains Mono', 'Fira Code', monospace; }
|
|
|
245
245
|
<div class="brand-logo">F</div>
|
|
246
246
|
<span class="brand-name">FRAIM</span>
|
|
247
247
|
</a>
|
|
248
|
-
<div class="header-actions">
|
|
248
|
+
<div class="header-actions">
|
|
249
249
|
<button class="theme-btn" onclick="toggleTheme()" title="Toggle dark mode">☾</button>
|
|
250
250
|
</div>
|
|
251
251
|
</header>
|
|
@@ -256,8 +256,8 @@ code, pre, .mono { font-family: 'JetBrains Mono', 'Fira Code', monospace; }
|
|
|
256
256
|
</div>
|
|
257
257
|
<div class="role-chip">AI Software Engineer</div>
|
|
258
258
|
<h1>Ships clean code,<br>not <span>promises</span></h1>
|
|
259
|
-
<p>SWEn delivers production-ready implementations: type-safe APIs, test coverage, real artifacts in the codebase — not prototype code masquerading as ship-ready work.</p>
|
|
260
|
-
</section>
|
|
259
|
+
<p>SWEn delivers production-ready implementations: type-safe APIs, test coverage, real artifacts in the codebase — not prototype code masquerading as ship-ready work.</p>
|
|
260
|
+
</section>
|
|
261
261
|
|
|
262
262
|
<div class="section-label">
|
|
263
263
|
<h2>Selected Work</h2>
|
|
@@ -571,8 +571,8 @@ code, pre, .mono { font-family: 'JetBrains Mono', 'Fira Code', monospace; }
|
|
|
571
571
|
|
|
572
572
|
</div>
|
|
573
573
|
|
|
574
|
-
<footer class="portfolio-footer">
|
|
575
|
-
<div class="footer-sub">
|
|
574
|
+
<footer class="portfolio-footer">
|
|
575
|
+
<div class="footer-sub">
|
|
576
576
|
Part of the <a href="/">FRAIM</a> · 18 AI employees available ·
|
|
577
577
|
<a href="/">View all employees</a>
|
|
578
578
|
</div>
|
|
@@ -596,6 +596,6 @@ function toggleCard(num) {
|
|
|
596
596
|
if (!isOpen) { card.classList.add('open'); card.scrollIntoView({ behavior: 'smooth', block: 'nearest' }); }
|
|
597
597
|
}
|
|
598
598
|
</script>
|
|
599
|
-
|
|
599
|
+
|
|
600
600
|
</body>
|
|
601
601
|
</html>
|
|
@@ -1,95 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* Sideloads the Word add-in manifest so it appears in Word's Developer Add-ins
|
|
4
|
-
* list without admin rights or AppSource publishing.
|
|
5
|
-
*
|
|
6
|
-
* Windows: writes a registry value under HKCU\SOFTWARE\Microsoft\Office\16.0\WEF\Developer
|
|
7
|
-
* macOS: writes an entry to ~/Library/Containers/com.microsoft.Word/Data/Documents/wef/
|
|
8
|
-
*
|
|
9
|
-
* Both paths are non-admin and survive app updates (keyed by manifest GUID).
|
|
10
|
-
* Safe to call multiple times — checks before writing.
|
|
11
|
-
*/
|
|
12
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
13
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
14
|
-
};
|
|
15
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
16
|
-
exports.isSideloaded = isSideloaded;
|
|
17
|
-
exports.sideloadManifest = sideloadManifest;
|
|
18
|
-
exports.removeSideload = removeSideload;
|
|
19
|
-
const fs_1 = __importDefault(require("fs"));
|
|
20
|
-
const path_1 = __importDefault(require("path"));
|
|
21
|
-
const os_1 = __importDefault(require("os"));
|
|
22
|
-
const child_process_1 = require("child_process");
|
|
23
|
-
const MANIFEST_GUID = 'd1090951-50cf-4cf2-9d12-b0f8541d265c';
|
|
24
|
-
function resolveManifestPath(projectPath) {
|
|
25
|
-
const candidates = [
|
|
26
|
-
path_1.default.resolve(projectPath, 'extensions/office-word/manifest.xml'),
|
|
27
|
-
path_1.default.resolve(__dirname, '..', '..', 'extensions/office-word/manifest.xml'),
|
|
28
|
-
path_1.default.resolve(__dirname, '..', '..', '..', 'extensions/office-word/manifest.xml'),
|
|
29
|
-
];
|
|
30
|
-
return candidates.find(c => fs_1.default.existsSync(c)) ?? null;
|
|
31
|
-
}
|
|
32
|
-
function isSideloaded() {
|
|
33
|
-
if (process.platform === 'win32') {
|
|
34
|
-
const result = (0, child_process_1.spawnSync)('reg', [
|
|
35
|
-
'query',
|
|
36
|
-
`HKCU\\SOFTWARE\\Microsoft\\Office\\16.0\\WEF\\Developer`,
|
|
37
|
-
'/v', MANIFEST_GUID,
|
|
38
|
-
], { encoding: 'utf8' });
|
|
39
|
-
return result.status === 0 && result.stdout.includes(MANIFEST_GUID);
|
|
40
|
-
}
|
|
41
|
-
if (process.platform === 'darwin') {
|
|
42
|
-
const wefDir = path_1.default.join(os_1.default.homedir(), 'Library', 'Containers', 'com.microsoft.Word', 'Data', 'Documents', 'wef');
|
|
43
|
-
return fs_1.default.existsSync(path_1.default.join(wefDir, `${MANIFEST_GUID}.xml`));
|
|
44
|
-
}
|
|
45
|
-
return false;
|
|
46
|
-
}
|
|
47
|
-
function sideloadManifest(projectPath) {
|
|
48
|
-
const manifestPath = resolveManifestPath(projectPath);
|
|
49
|
-
if (!manifestPath) {
|
|
50
|
-
return { ok: false, reason: 'Manifest file not found — is extensions/office-word/ present?' };
|
|
51
|
-
}
|
|
52
|
-
if (process.platform === 'win32') {
|
|
53
|
-
const result = (0, child_process_1.spawnSync)('reg', [
|
|
54
|
-
'add',
|
|
55
|
-
`HKCU\\SOFTWARE\\Microsoft\\Office\\16.0\\WEF\\Developer`,
|
|
56
|
-
'/v', MANIFEST_GUID,
|
|
57
|
-
'/t', 'REG_SZ',
|
|
58
|
-
'/d', manifestPath,
|
|
59
|
-
'/f',
|
|
60
|
-
], { encoding: 'utf8' });
|
|
61
|
-
if (result.status !== 0) {
|
|
62
|
-
return { ok: false, reason: result.stderr || 'reg add failed' };
|
|
63
|
-
}
|
|
64
|
-
return { ok: true };
|
|
65
|
-
}
|
|
66
|
-
if (process.platform === 'darwin') {
|
|
67
|
-
const wefDir = path_1.default.join(os_1.default.homedir(), 'Library', 'Containers', 'com.microsoft.Word', 'Data', 'Documents', 'wef');
|
|
68
|
-
try {
|
|
69
|
-
fs_1.default.mkdirSync(wefDir, { recursive: true });
|
|
70
|
-
fs_1.default.copyFileSync(manifestPath, path_1.default.join(wefDir, `${MANIFEST_GUID}.xml`));
|
|
71
|
-
return { ok: true };
|
|
72
|
-
}
|
|
73
|
-
catch (err) {
|
|
74
|
-
return { ok: false, reason: String(err) };
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
return { ok: false, reason: `Unsupported platform: ${process.platform}` };
|
|
78
|
-
}
|
|
79
|
-
function removeSideload() {
|
|
80
|
-
if (process.platform === 'win32') {
|
|
81
|
-
(0, child_process_1.spawnSync)('reg', [
|
|
82
|
-
'delete',
|
|
83
|
-
`HKCU\\SOFTWARE\\Microsoft\\Office\\16.0\\WEF\\Developer`,
|
|
84
|
-
'/v', MANIFEST_GUID,
|
|
85
|
-
'/f',
|
|
86
|
-
], { encoding: 'utf8' });
|
|
87
|
-
return;
|
|
88
|
-
}
|
|
89
|
-
if (process.platform === 'darwin') {
|
|
90
|
-
const wefDir = path_1.default.join(os_1.default.homedir(), 'Library', 'Containers', 'com.microsoft.Word', 'Data', 'Documents', 'wef');
|
|
91
|
-
const target = path_1.default.join(wefDir, `${MANIFEST_GUID}.xml`);
|
|
92
|
-
if (fs_1.default.existsSync(target))
|
|
93
|
-
fs_1.default.unlinkSync(target);
|
|
94
|
-
}
|
|
95
|
-
}
|