@decentnetwork/beagle-ui 0.2.22 → 0.2.23
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/beagle-ui.js +365 -39
- package/package.json +1 -1
package/dist/beagle-ui.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export const BEAGLE_UI_VERSION = "0.2.
|
|
1
|
+
export const BEAGLE_UI_VERSION = "0.2.23";
|
|
2
2
|
// src/icons.jsx
|
|
3
3
|
var ICON_PATHS = {
|
|
4
4
|
// ---- tab bar (the four must feel like one set) ----
|
|
@@ -516,23 +516,39 @@ var actions_default = {
|
|
|
516
516
|
name: "help",
|
|
517
517
|
usage: "/help",
|
|
518
518
|
summary: "List what the Assistant can do.",
|
|
519
|
-
params: {
|
|
519
|
+
params: {
|
|
520
|
+
type: "object",
|
|
521
|
+
properties: {},
|
|
522
|
+
required: []
|
|
523
|
+
},
|
|
520
524
|
confirm: "none",
|
|
521
525
|
scope: "assistant"
|
|
522
526
|
},
|
|
523
527
|
{
|
|
524
528
|
name: "friends-list",
|
|
525
529
|
usage: "/friends list [filter\u2026]",
|
|
526
|
-
summary: "Show friends as a
|
|
530
|
+
summary: "Show friends as a list: name, last heard from, messages in/out. With no filter, everyone. Service peers are never included \u2014 they are /nodes.",
|
|
527
531
|
params: {
|
|
528
532
|
type: "object",
|
|
529
533
|
properties: {
|
|
530
534
|
filters: {
|
|
531
535
|
type: "array",
|
|
532
|
-
items: {
|
|
536
|
+
items: {
|
|
537
|
+
enum: [
|
|
538
|
+
"never-messaged",
|
|
539
|
+
"never-replied",
|
|
540
|
+
"silent",
|
|
541
|
+
"pending"
|
|
542
|
+
]
|
|
543
|
+
},
|
|
533
544
|
description: "Several filters are OR'd together."
|
|
534
545
|
},
|
|
535
|
-
silentDays: {
|
|
546
|
+
silentDays: {
|
|
547
|
+
type: "integer",
|
|
548
|
+
minimum: 1,
|
|
549
|
+
default: 90,
|
|
550
|
+
description: "Argument of the silent filter: nothing from them in this many days."
|
|
551
|
+
}
|
|
536
552
|
},
|
|
537
553
|
required: []
|
|
538
554
|
},
|
|
@@ -542,18 +558,94 @@ var actions_default = {
|
|
|
542
558
|
{
|
|
543
559
|
name: "friends-remove",
|
|
544
560
|
usage: "/friends remove <filter\u2026>",
|
|
545
|
-
summary: "Same
|
|
561
|
+
summary: "Same list with a tick per row and a Remove button. Nothing is removed by typing alone. Service peers are never matched.",
|
|
562
|
+
params: {
|
|
563
|
+
type: "object",
|
|
564
|
+
properties: {
|
|
565
|
+
filters: {
|
|
566
|
+
type: "array",
|
|
567
|
+
items: {
|
|
568
|
+
enum: [
|
|
569
|
+
"never-messaged",
|
|
570
|
+
"never-replied",
|
|
571
|
+
"silent",
|
|
572
|
+
"pending"
|
|
573
|
+
]
|
|
574
|
+
},
|
|
575
|
+
minItems: 1
|
|
576
|
+
},
|
|
577
|
+
silentDays: {
|
|
578
|
+
type: "integer",
|
|
579
|
+
minimum: 1,
|
|
580
|
+
default: 90
|
|
581
|
+
}
|
|
582
|
+
},
|
|
583
|
+
required: [
|
|
584
|
+
"filters"
|
|
585
|
+
]
|
|
586
|
+
},
|
|
587
|
+
confirm: "list",
|
|
588
|
+
scope: "assistant"
|
|
589
|
+
},
|
|
590
|
+
{
|
|
591
|
+
name: "nodes-list",
|
|
592
|
+
usage: "/nodes list [filter\u2026]",
|
|
593
|
+
summary: "Service peers \u2014 network nodes and agents \u2014 with their kind and role. The same filters as /friends list.",
|
|
594
|
+
params: {
|
|
595
|
+
type: "object",
|
|
596
|
+
properties: {
|
|
597
|
+
filters: {
|
|
598
|
+
type: "array",
|
|
599
|
+
items: {
|
|
600
|
+
enum: [
|
|
601
|
+
"never-messaged",
|
|
602
|
+
"never-replied",
|
|
603
|
+
"silent",
|
|
604
|
+
"pending"
|
|
605
|
+
]
|
|
606
|
+
},
|
|
607
|
+
description: "Several filters are OR'd together."
|
|
608
|
+
},
|
|
609
|
+
silentDays: {
|
|
610
|
+
type: "integer",
|
|
611
|
+
minimum: 1,
|
|
612
|
+
default: 90
|
|
613
|
+
}
|
|
614
|
+
},
|
|
615
|
+
required: []
|
|
616
|
+
},
|
|
617
|
+
confirm: "none",
|
|
618
|
+
scope: "assistant"
|
|
619
|
+
},
|
|
620
|
+
{
|
|
621
|
+
name: "nodes-remove",
|
|
622
|
+
usage: "/nodes remove <filter\u2026>",
|
|
623
|
+
summary: "The same table with a tick per row and a Remove button, over service peers only.",
|
|
546
624
|
params: {
|
|
547
625
|
type: "object",
|
|
548
626
|
properties: {
|
|
549
627
|
filters: {
|
|
550
628
|
type: "array",
|
|
551
|
-
items: {
|
|
629
|
+
items: {
|
|
630
|
+
enum: [
|
|
631
|
+
"never-messaged",
|
|
632
|
+
"never-replied",
|
|
633
|
+
"silent",
|
|
634
|
+
"pending"
|
|
635
|
+
]
|
|
636
|
+
},
|
|
637
|
+
description: "Several filters are OR'd together.",
|
|
552
638
|
minItems: 1
|
|
553
639
|
},
|
|
554
|
-
silentDays: {
|
|
640
|
+
silentDays: {
|
|
641
|
+
type: "integer",
|
|
642
|
+
minimum: 1,
|
|
643
|
+
default: 90
|
|
644
|
+
}
|
|
555
645
|
},
|
|
556
|
-
required: [
|
|
646
|
+
required: [
|
|
647
|
+
"filters"
|
|
648
|
+
]
|
|
557
649
|
},
|
|
558
650
|
confirm: "list",
|
|
559
651
|
scope: "assistant"
|
|
@@ -564,8 +656,15 @@ var actions_default = {
|
|
|
564
656
|
summary: "Friends whose name, alias, description or ens name contains the text.",
|
|
565
657
|
params: {
|
|
566
658
|
type: "object",
|
|
567
|
-
properties: {
|
|
568
|
-
|
|
659
|
+
properties: {
|
|
660
|
+
text: {
|
|
661
|
+
type: "string",
|
|
662
|
+
minLength: 1
|
|
663
|
+
}
|
|
664
|
+
},
|
|
665
|
+
required: [
|
|
666
|
+
"text"
|
|
667
|
+
]
|
|
569
668
|
},
|
|
570
669
|
confirm: "none",
|
|
571
670
|
scope: "assistant"
|
|
@@ -577,10 +676,19 @@ var actions_default = {
|
|
|
577
676
|
params: {
|
|
578
677
|
type: "object",
|
|
579
678
|
properties: {
|
|
580
|
-
friend: {
|
|
581
|
-
|
|
679
|
+
friend: {
|
|
680
|
+
type: "string",
|
|
681
|
+
description: "Matches name, alias or userid prefix. Ambiguous = nothing happens, the candidates are listed."
|
|
682
|
+
},
|
|
683
|
+
alias: {
|
|
684
|
+
type: "string",
|
|
685
|
+
minLength: 1
|
|
686
|
+
}
|
|
582
687
|
},
|
|
583
|
-
required: [
|
|
688
|
+
required: [
|
|
689
|
+
"friend",
|
|
690
|
+
"alias"
|
|
691
|
+
]
|
|
584
692
|
},
|
|
585
693
|
confirm: "none",
|
|
586
694
|
scope: "assistant"
|
|
@@ -591,8 +699,14 @@ var actions_default = {
|
|
|
591
699
|
summary: "Stop alerts from one conversation.",
|
|
592
700
|
params: {
|
|
593
701
|
type: "object",
|
|
594
|
-
properties: {
|
|
595
|
-
|
|
702
|
+
properties: {
|
|
703
|
+
friend: {
|
|
704
|
+
type: "string"
|
|
705
|
+
}
|
|
706
|
+
},
|
|
707
|
+
required: [
|
|
708
|
+
"friend"
|
|
709
|
+
]
|
|
596
710
|
},
|
|
597
711
|
confirm: "none",
|
|
598
712
|
scope: "assistant"
|
|
@@ -603,8 +717,14 @@ var actions_default = {
|
|
|
603
717
|
summary: "Alerts from that conversation again.",
|
|
604
718
|
params: {
|
|
605
719
|
type: "object",
|
|
606
|
-
properties: {
|
|
607
|
-
|
|
720
|
+
properties: {
|
|
721
|
+
friend: {
|
|
722
|
+
type: "string"
|
|
723
|
+
}
|
|
724
|
+
},
|
|
725
|
+
required: [
|
|
726
|
+
"friend"
|
|
727
|
+
]
|
|
608
728
|
},
|
|
609
729
|
confirm: "none",
|
|
610
730
|
scope: "assistant"
|
|
@@ -615,8 +735,14 @@ var actions_default = {
|
|
|
615
735
|
summary: "Sound on a new message.",
|
|
616
736
|
params: {
|
|
617
737
|
type: "object",
|
|
618
|
-
properties: {
|
|
619
|
-
|
|
738
|
+
properties: {
|
|
739
|
+
on: {
|
|
740
|
+
type: "boolean"
|
|
741
|
+
}
|
|
742
|
+
},
|
|
743
|
+
required: [
|
|
744
|
+
"on"
|
|
745
|
+
]
|
|
620
746
|
},
|
|
621
747
|
confirm: "none",
|
|
622
748
|
scope: "assistant"
|
|
@@ -627,8 +753,14 @@ var actions_default = {
|
|
|
627
753
|
summary: "Flash the window on a new message.",
|
|
628
754
|
params: {
|
|
629
755
|
type: "object",
|
|
630
|
-
properties: {
|
|
631
|
-
|
|
756
|
+
properties: {
|
|
757
|
+
on: {
|
|
758
|
+
type: "boolean"
|
|
759
|
+
}
|
|
760
|
+
},
|
|
761
|
+
required: [
|
|
762
|
+
"on"
|
|
763
|
+
]
|
|
632
764
|
},
|
|
633
765
|
confirm: "none",
|
|
634
766
|
scope: "assistant"
|
|
@@ -636,8 +768,12 @@ var actions_default = {
|
|
|
636
768
|
{
|
|
637
769
|
name: "status",
|
|
638
770
|
usage: "/status",
|
|
639
|
-
summary: "Friends, pending requests, how far back the history on this device goes, app version.",
|
|
640
|
-
params: {
|
|
771
|
+
summary: "Friends and service peers counted separately, pending requests, how far back the history on this device goes, app version.",
|
|
772
|
+
params: {
|
|
773
|
+
type: "object",
|
|
774
|
+
properties: {},
|
|
775
|
+
required: []
|
|
776
|
+
},
|
|
641
777
|
confirm: "none",
|
|
642
778
|
scope: "assistant"
|
|
643
779
|
}
|
|
@@ -660,7 +796,12 @@ var actions_default = {
|
|
|
660
796
|
usage: "silent [days]",
|
|
661
797
|
summary: "Nothing from them for this many days (default 90).",
|
|
662
798
|
guarded: true,
|
|
663
|
-
arg: {
|
|
799
|
+
arg: {
|
|
800
|
+
name: "days",
|
|
801
|
+
type: "integer",
|
|
802
|
+
default: 90,
|
|
803
|
+
minimum: 1
|
|
804
|
+
}
|
|
664
805
|
},
|
|
665
806
|
{
|
|
666
807
|
name: "pending",
|
|
@@ -678,9 +819,145 @@ var actions_default = {
|
|
|
678
819
|
guard: {
|
|
679
820
|
recentDays: 14,
|
|
680
821
|
applies: "Filters marked guarded never match a friend accepted (or, with no acceptance stamp, requested) less than recentDays ago; the result says how many were skipped."
|
|
822
|
+
},
|
|
823
|
+
servicePeers: {
|
|
824
|
+
file: "service-peers.json",
|
|
825
|
+
loader: "loadServicePeers()",
|
|
826
|
+
rule: "A peer is a service peer if and only if its userid is in this list. The Assistant never infers it from a name or from having no messages. /friends excludes them entirely; /nodes is only them.",
|
|
827
|
+
later: "A dora / ENS / bridge / directory service returns the same shape; the bundled file is v1."
|
|
681
828
|
}
|
|
682
829
|
};
|
|
683
830
|
|
|
831
|
+
// src/assistant/service-peers.json
|
|
832
|
+
var service_peers_default = {
|
|
833
|
+
version: 1,
|
|
834
|
+
source: "static",
|
|
835
|
+
updated: "2026-09-24",
|
|
836
|
+
note: "Service providers and network nodes: peers that are infrastructure or agents, not people. The Assistant manages them with /nodes, never /friends. Later this list comes from a service (dora / ENS / bridge / directory) in this same shape; until then it ships inside the app.",
|
|
837
|
+
peers: [
|
|
838
|
+
{
|
|
839
|
+
userid: "7xmbDbouSYEA5mbAgWxV2BTwjc59XGcVPqXSZx6VibUd",
|
|
840
|
+
name: "lens",
|
|
841
|
+
kind: "node",
|
|
842
|
+
role: "bridge + web host"
|
|
843
|
+
},
|
|
844
|
+
{
|
|
845
|
+
userid: "4uNnLnAQHVJ7td657MDaz4UnEuhnTW9sRAPSVeAEEQe4",
|
|
846
|
+
name: "tokyo",
|
|
847
|
+
kind: "node",
|
|
848
|
+
role: "exit + relay + dora + meeting"
|
|
849
|
+
},
|
|
850
|
+
{
|
|
851
|
+
userid: "5Aj6uQMd1cNRb9cGT4AwHCN4RdVZjfaGLtgKGhdP1LzN",
|
|
852
|
+
name: "cn",
|
|
853
|
+
kind: "node",
|
|
854
|
+
role: "exit"
|
|
855
|
+
},
|
|
856
|
+
{
|
|
857
|
+
userid: "DZN2L9RV1YkHjqGHMTA6juZhskKA65AD2RyPt4umZVc7",
|
|
858
|
+
name: "callpass",
|
|
859
|
+
kind: "node",
|
|
860
|
+
role: "server"
|
|
861
|
+
},
|
|
862
|
+
{
|
|
863
|
+
userid: "BcGvk2h5XCxBZWJrYwzKpZ8GbxaZRoakEzt2pQpathyA",
|
|
864
|
+
name: "gojipower",
|
|
865
|
+
kind: "node",
|
|
866
|
+
role: "server"
|
|
867
|
+
},
|
|
868
|
+
{
|
|
869
|
+
userid: "BeMbuKf1poh3U4rjw3eeUAKZsbN2qBSjVZdPSvehmHsw",
|
|
870
|
+
name: "snoopy",
|
|
871
|
+
kind: "node",
|
|
872
|
+
role: "dora + meeting + openclaw host"
|
|
873
|
+
},
|
|
874
|
+
{
|
|
875
|
+
userid: "CzwUZv6LfiGM7EXd9vXr6DutG8ASkA3TnNbqQzjKRJN5",
|
|
876
|
+
name: "GFAX",
|
|
877
|
+
kind: "node",
|
|
878
|
+
role: "server"
|
|
879
|
+
},
|
|
880
|
+
{
|
|
881
|
+
userid: "72a4A7awtn6BqZnBt3eNhZH9MdYXRkM6Ht5HG2sqoiKR",
|
|
882
|
+
name: "nv4090-wsl",
|
|
883
|
+
kind: "node",
|
|
884
|
+
role: "GPU node"
|
|
885
|
+
},
|
|
886
|
+
{
|
|
887
|
+
userid: "6riqxxw18vjwXZEEtFUxbgNBMMe1dcv2kVHqVRQdihjE",
|
|
888
|
+
name: "node-3145",
|
|
889
|
+
kind: "node",
|
|
890
|
+
role: "GPU node (4090w)"
|
|
891
|
+
},
|
|
892
|
+
{
|
|
893
|
+
userid: "6D1PLSVqbSpcxnDMAd8ZXLWycYveeP2hU4g3igDQEqA7",
|
|
894
|
+
name: "node-5123",
|
|
895
|
+
kind: "node",
|
|
896
|
+
role: "node"
|
|
897
|
+
},
|
|
898
|
+
{
|
|
899
|
+
userid: "5EU9Ee3Vai1XwTr4pGaJFt4wmNVPUoYnJsdBM9LwNhrJ",
|
|
900
|
+
name: "node-2998",
|
|
901
|
+
kind: "node",
|
|
902
|
+
role: "node"
|
|
903
|
+
},
|
|
904
|
+
{
|
|
905
|
+
userid: "GujioyKQRucQgw8mwMPNWtYTdNdrooCuFq3R1D4RqjCW",
|
|
906
|
+
name: "beagle-help",
|
|
907
|
+
kind: "agent",
|
|
908
|
+
role: "product support"
|
|
909
|
+
},
|
|
910
|
+
{
|
|
911
|
+
userid: "FhbohSLrj5UjdyFKCEYNeEWAPq3QD9hRg6hsso5ipag2",
|
|
912
|
+
name: "help.beagles.eth",
|
|
913
|
+
kind: "agent",
|
|
914
|
+
role: "bridge agent"
|
|
915
|
+
},
|
|
916
|
+
{
|
|
917
|
+
userid: "9EaFGWuXH2UfREzYMkPPitzjoH6E8tvey4JvqTKtHFJh",
|
|
918
|
+
name: "openclaw",
|
|
919
|
+
kind: "agent",
|
|
920
|
+
role: "OpenClaw agent"
|
|
921
|
+
},
|
|
922
|
+
{
|
|
923
|
+
userid: "6z2o8ojPVjFGRzcffUdd5btvDYAt4tLuwJ8j1jvMBU8p",
|
|
924
|
+
name: "ClawAlan",
|
|
925
|
+
kind: "agent",
|
|
926
|
+
role: "OpenClaw agent on snoopy"
|
|
927
|
+
},
|
|
928
|
+
{
|
|
929
|
+
userid: "G7s3YjVfNLTY5pKBd83qQLAdzDddS7ZstsfrUySwK2Cb",
|
|
930
|
+
name: "interview",
|
|
931
|
+
kind: "agent",
|
|
932
|
+
role: "interview"
|
|
933
|
+
},
|
|
934
|
+
{
|
|
935
|
+
userid: "6jbXjrUWJ4zYvG8BDLnu8KVpoL7rhsdHYSwWER6WTfdt",
|
|
936
|
+
name: "interview editor",
|
|
937
|
+
kind: "agent",
|
|
938
|
+
role: "interview"
|
|
939
|
+
},
|
|
940
|
+
{
|
|
941
|
+
userid: "8BGemh6uqmh4X7KpnPVMeMSU8hZwAc6ZDo4MBRMFrq2L",
|
|
942
|
+
name: "readyplayer",
|
|
943
|
+
kind: "agent",
|
|
944
|
+
role: "readyplayer"
|
|
945
|
+
},
|
|
946
|
+
{
|
|
947
|
+
userid: "Ev1XmWRwtjQf8n33wgTwEhzXnYEyfqTyanehuDE6nczF",
|
|
948
|
+
name: "ReadyPlayer membership manager",
|
|
949
|
+
kind: "agent",
|
|
950
|
+
role: "readyplayer"
|
|
951
|
+
},
|
|
952
|
+
{
|
|
953
|
+
userid: "7xLPH2kjk7Nn8GrsAt7TYwa6b1WMPLkC2XRx7BzhiXYT",
|
|
954
|
+
name: "ai-btc-trader",
|
|
955
|
+
kind: "agent",
|
|
956
|
+
role: "btc signals (245)"
|
|
957
|
+
}
|
|
958
|
+
]
|
|
959
|
+
};
|
|
960
|
+
|
|
684
961
|
// src/assistant/filters.js
|
|
685
962
|
var ASSISTANT_ID = "__assistant__";
|
|
686
963
|
var DAY_MS = 864e5;
|
|
@@ -696,11 +973,11 @@ function parseCommand(line) {
|
|
|
696
973
|
if (!parts.length)
|
|
697
974
|
return null;
|
|
698
975
|
const head = parts[0].toLowerCase();
|
|
699
|
-
if (head === "friends") {
|
|
976
|
+
if (head === "friends" || head === "nodes") {
|
|
700
977
|
const sub = (parts[1] || "").toLowerCase();
|
|
701
978
|
if (sub === "list" || sub === "remove")
|
|
702
|
-
return { ok: true, name:
|
|
703
|
-
return { ok: false, name:
|
|
979
|
+
return { ok: true, name: `${head}-${sub}`, args: parts.slice(2) };
|
|
980
|
+
return { ok: false, name: head, args: parts.slice(1), error: `${head}-subcommand` };
|
|
704
981
|
}
|
|
705
982
|
return { ok: true, name: head, args: parts.slice(1) };
|
|
706
983
|
}
|
|
@@ -727,6 +1004,30 @@ function parseFilters(args) {
|
|
|
727
1004
|
}
|
|
728
1005
|
return { filters, silentDays, unknown };
|
|
729
1006
|
}
|
|
1007
|
+
function servicePeerMap(list) {
|
|
1008
|
+
const map = /* @__PURE__ */ new Map();
|
|
1009
|
+
const peers = list && list.peers || [];
|
|
1010
|
+
for (const p of peers) {
|
|
1011
|
+
if (p && typeof p.userid === "string" && p.userid) {
|
|
1012
|
+
map.set(p.userid, { kind: p.kind === "agent" ? "agent" : "node", role: p.role || "", name: p.name || "" });
|
|
1013
|
+
}
|
|
1014
|
+
}
|
|
1015
|
+
return map;
|
|
1016
|
+
}
|
|
1017
|
+
function partitionPeers(friends, svcMap) {
|
|
1018
|
+
const people = [];
|
|
1019
|
+
const nodes = [];
|
|
1020
|
+
for (const f of friends != null ? friends : []) {
|
|
1021
|
+
if (!f)
|
|
1022
|
+
continue;
|
|
1023
|
+
const svc = svcMap && svcMap.get(f.userid);
|
|
1024
|
+
if (svc)
|
|
1025
|
+
nodes.push(Object.assign({}, f, { kind: svc.kind, role: svc.role, service: true }));
|
|
1026
|
+
else
|
|
1027
|
+
people.push(f);
|
|
1028
|
+
}
|
|
1029
|
+
return { people, nodes };
|
|
1030
|
+
}
|
|
730
1031
|
var acceptedOrRequestedAt = (f) => f.acceptedAt || f.requestedAt || 0;
|
|
731
1032
|
function isRecent(friend, now, recentDays = RECENT_DAYS) {
|
|
732
1033
|
const at = acceptedOrRequestedAt(friend);
|
|
@@ -797,6 +1098,9 @@ function resolveFriend(friends, token) {
|
|
|
797
1098
|
}
|
|
798
1099
|
|
|
799
1100
|
// src/assistant.jsx
|
|
1101
|
+
function loadServicePeers() {
|
|
1102
|
+
return service_peers_default;
|
|
1103
|
+
}
|
|
800
1104
|
var ASSISTANT_PEER = {
|
|
801
1105
|
id: ASSISTANT_ID,
|
|
802
1106
|
userId: ASSISTANT_ID,
|
|
@@ -839,7 +1143,10 @@ function sinceLine(historySince, lang) {
|
|
|
839
1143
|
return lang === "zh" ? `*\u57FA\u4E8E\u672C\u8BBE\u5907\u81EA ${fmtDate(historySince, lang)} \u8D77\u7684\u6D88\u606F*` : `*based on messages on this device since ${fmtDate(historySince, lang)}*`;
|
|
840
1144
|
}
|
|
841
1145
|
function friendTable(rows, now, lang) {
|
|
842
|
-
return rows.map((f) =>
|
|
1146
|
+
return rows.map((f) => {
|
|
1147
|
+
const what = f.service ? ` \`${f.kind}${f.role ? " \xB7 " + f.role : ""}\`` : "";
|
|
1148
|
+
return lang === "zh" ? `- **${nameOf(f)}**${what} \u2014 \u6700\u540E\u6536\u5230 ${fmtAgo(f.lastInboundTs, now, lang)} \xB7 \u6536 ${f.inboundCount || 0} / \u53D1 ${f.outboundCount || 0}` : `- **${nameOf(f)}**${what} \u2014 last heard ${fmtAgo(f.lastInboundTs, now, lang)} \xB7 in ${f.inboundCount || 0} / out ${f.outboundCount || 0}`;
|
|
1149
|
+
}).join("\n");
|
|
843
1150
|
}
|
|
844
1151
|
function helpText(lang) {
|
|
845
1152
|
const intro = lang === "zh" ? "**Beagle \u52A9\u624B** \u2014 \u53EA\u5728\u672C\u673A\u8FD0\u884C\uFF0C\u4E0D\u4F1A\u628A\u4EFB\u4F55\u5185\u5BB9\u53D1\u5230\u7F51\u7EDC\u4E0A\u3002\n\n\u53EF\u7528\u547D\u4EE4\uFF1A" : "**Beagle Assistant** \u2014 runs on this machine only; nothing here is sent over the network.\n\nCommands:";
|
|
@@ -908,7 +1215,15 @@ function useAssistant({ lang, T, muted, onToggleMute, sound, flash, setAlert, on
|
|
|
908
1215
|
}, [lang]);
|
|
909
1216
|
const fetchFriends = async () => {
|
|
910
1217
|
const r = await fetch("/api/assistant/friends").then((x) => x.json());
|
|
911
|
-
|
|
1218
|
+
const { people, nodes } = partitionPeers(r.friends || [], servicePeerMap(loadServicePeers()));
|
|
1219
|
+
return {
|
|
1220
|
+
friends: people,
|
|
1221
|
+
nodes,
|
|
1222
|
+
all: r.friends || [],
|
|
1223
|
+
historySince: r.historySince || null,
|
|
1224
|
+
now: r.now || Date.now(),
|
|
1225
|
+
ver: r.beagleVer || ""
|
|
1226
|
+
};
|
|
912
1227
|
};
|
|
913
1228
|
const listReply = (rows, skipped, data, title) => {
|
|
914
1229
|
if (!rows.length) {
|
|
@@ -940,7 +1255,7 @@ ${cands.map((c) => `- ${nameOf(c)}`).join("\n")}`;
|
|
|
940
1255
|
return append(asMsg("them", lang === "zh" ? "\u6211\u53EA\u8BA4\u8BC6\u4EE5 `/` \u5F00\u5934\u7684\u547D\u4EE4\u3002\u8F93\u5165 `/help` \u770B\u770B\u80FD\u505A\u4EC0\u4E48\u3002" : "I only understand commands starting with `/`. Type `/help` to see them."));
|
|
941
1256
|
}
|
|
942
1257
|
if (cmd.ok === false) {
|
|
943
|
-
return append(asMsg("them", lang === "zh" ?
|
|
1258
|
+
return append(asMsg("them", lang === "zh" ? `\`/${cmd.name}\` \u9700\u8981 \`list\` \u6216 \`remove\`\uFF0C\u4F8B\u5982 \`/${cmd.name} list silent 90\`\u3002` : `\`/${cmd.name}\` needs \`list\` or \`remove\`, for example \`/${cmd.name} list silent 90\`.`));
|
|
944
1259
|
}
|
|
945
1260
|
try {
|
|
946
1261
|
switch (cmd.name) {
|
|
@@ -948,37 +1263,48 @@ ${cands.map((c) => `- ${nameOf(c)}`).join("\n")}`;
|
|
|
948
1263
|
return append(asMsg("them", helpText(lang)));
|
|
949
1264
|
case "status": {
|
|
950
1265
|
const d = await fetchFriends();
|
|
951
|
-
const pending = d.
|
|
1266
|
+
const pending = d.all.filter((f) => f.pending).length;
|
|
1267
|
+
const nodeCount = d.nodes.filter((n) => n.kind === "node").length;
|
|
1268
|
+
const agentCount = d.nodes.length - nodeCount;
|
|
952
1269
|
return append(asMsg("them", lang === "zh" ? `**\u72B6\u6001**
|
|
953
1270
|
|
|
954
1271
|
- \u597D\u53CB\uFF1A${d.friends.length}
|
|
1272
|
+
- \u670D\u52A1\u8282\u70B9\uFF1A${d.nodes.length}\uFF08\u8282\u70B9 ${nodeCount} \xB7 \u667A\u80FD\u4F53 ${agentCount}\uFF09\uFF0C\u7528 \`/nodes list\` \u67E5\u770B
|
|
955
1273
|
- \u5F85\u5BF9\u65B9\u63A5\u53D7\uFF1A${pending}
|
|
956
1274
|
- \u672C\u673A\u5386\u53F2\u8D77\u81EA\uFF1A${fmtDate(d.historySince, lang)}
|
|
957
1275
|
- \u7248\u672C\uFF1A${d.ver}` : `**Status**
|
|
958
1276
|
|
|
959
1277
|
- Friends: ${d.friends.length}
|
|
1278
|
+
- Service peers: ${d.nodes.length} (${nodeCount} node${nodeCount === 1 ? "" : "s"} \xB7 ${agentCount} agent${agentCount === 1 ? "" : "s"}) \u2014 see \`/nodes list\`
|
|
960
1279
|
- Waiting to be accepted: ${pending}
|
|
961
1280
|
- History on this device since: ${fmtDate(d.historySince, lang)}
|
|
962
1281
|
- Version: ${d.ver}`));
|
|
963
1282
|
}
|
|
1283
|
+
case "nodes-list":
|
|
964
1284
|
case "friends-list": {
|
|
1285
|
+
const nodesMode = cmd.name === "nodes-list";
|
|
965
1286
|
const { filters, silentDays, unknown } = parseFilters(cmd.args);
|
|
966
1287
|
if (unknown.length) {
|
|
967
1288
|
return append(asMsg("them", lang === "zh" ? `\u4E0D\u8BA4\u8BC6\u7684\u7B5B\u9009\u6761\u4EF6\uFF1A${unknown.join(", ")}\u3002\u53EF\u7528\uFF1A${actions_default.filters.map((f) => f.name).join(", ")}\u3002` : `I don't know the filter${unknown.length > 1 ? "s" : ""} ${unknown.join(", ")}. Try: ${actions_default.filters.map((f) => f.name).join(", ")}.`));
|
|
968
1289
|
}
|
|
969
1290
|
const d = await fetchFriends();
|
|
970
|
-
const
|
|
971
|
-
const
|
|
1291
|
+
const pool = nodesMode ? d.nodes : d.friends;
|
|
1292
|
+
const { matched, skippedRecent } = selectFriends(pool, filters, { now: d.now, silentDays });
|
|
1293
|
+
const all = nodesMode ? lang === "zh" ? `**\u5168\u90E8\u670D\u52A1\u8282\u70B9** \u2014 ${matched.length}` : `**All service peers** \u2014 ${matched.length}` : lang === "zh" ? `**\u5168\u90E8\u597D\u53CB** \u2014 ${matched.length}` : `**All friends** \u2014 ${matched.length}`;
|
|
1294
|
+
const title = filters.length ? `**${nodesMode ? (lang === "zh" ? "\u670D\u52A1\u8282\u70B9" : "service peers") + ": " : ""}${filters.join(" + ")}**${filters.includes("silent") ? ` (${silentDays}d)` : ""} \u2014 ${matched.length}` : all;
|
|
972
1295
|
return append(asMsg("them", listReply(matched, skippedRecent, d, title)));
|
|
973
1296
|
}
|
|
1297
|
+
case "nodes-remove":
|
|
974
1298
|
case "friends-remove": {
|
|
1299
|
+
const nodesMode = cmd.name === "nodes-remove";
|
|
975
1300
|
const { filters, silentDays, unknown } = parseFilters(cmd.args);
|
|
976
1301
|
if (unknown.length || !filters.length) {
|
|
977
1302
|
return append(asMsg("them", lang === "zh" ? `\u5220\u9664\u9700\u8981\u7B5B\u9009\u6761\u4EF6\uFF0C\u4F8B\u5982 \`/friends remove never-messaged\`\u3002\u53EF\u7528\uFF1A${actions_default.filters.map((f) => f.name).join(", ")}\u3002` : `Removing needs a filter, for example \`/friends remove never-messaged\`. Available: ${actions_default.filters.map((f) => f.name).join(", ")}.`));
|
|
978
1303
|
}
|
|
979
1304
|
const d = await fetchFriends();
|
|
980
|
-
const { matched, skippedRecent } = selectFriends(d.friends, filters, { now: d.now, silentDays });
|
|
981
|
-
const
|
|
1305
|
+
const { matched, skippedRecent } = selectFriends(nodesMode ? d.nodes : d.friends, filters, { now: d.now, silentDays });
|
|
1306
|
+
const what = nodesMode ? lang === "zh" ? "\u4E2A\u670D\u52A1\u8282\u70B9" : `service peer${matched.length === 1 ? "" : "s"}` : lang === "zh" ? "\u4F4D\u597D\u53CB" : `friend${matched.length === 1 ? "" : "s"}`;
|
|
1307
|
+
const title = lang === "zh" ? `**\u5C06\u8981\u5220\u9664 ${matched.length} ${what}** \u2014 \u8BF7\u5148\u6838\u5BF9\u8FD9\u4EFD\u540D\u5355` : `**${matched.length} ${what} to remove** \u2014 check the list first`;
|
|
982
1308
|
return append(asMsg(
|
|
983
1309
|
"them",
|
|
984
1310
|
listReply(matched, skippedRecent, d, title),
|
|
@@ -990,7 +1316,7 @@ ${cands.map((c) => `- ${nameOf(c)}`).join("\n")}`;
|
|
|
990
1316
|
if (!q)
|
|
991
1317
|
return append(asMsg("them", lang === "zh" ? "\u7528\u6CD5\uFF1A`/find <\u6587\u5B57>`" : "Usage: `/find <text>`"));
|
|
992
1318
|
const d = await fetchFriends();
|
|
993
|
-
const rows = findFriends(d.friends, q);
|
|
1319
|
+
const rows = findFriends(d.friends.concat(d.nodes), q);
|
|
994
1320
|
const title = lang === "zh" ? `**"${q}"** \u2014 ${rows.length} \u4F4D` : `**"${q}"** \u2014 ${rows.length} found`;
|
|
995
1321
|
return append(asMsg("them", listReply(rows, [], d, title)));
|
|
996
1322
|
}
|
|
@@ -1000,7 +1326,7 @@ ${cands.map((c) => `- ${nameOf(c)}`).join("\n")}`;
|
|
|
1000
1326
|
if (!tok || !alias)
|
|
1001
1327
|
return append(asMsg("them", lang === "zh" ? "\u7528\u6CD5\uFF1A`/rename <\u597D\u53CB> <\u65B0\u540D\u5B57>`" : "Usage: `/rename <friend> <new name>`"));
|
|
1002
1328
|
const d = await fetchFriends();
|
|
1003
|
-
const { match, candidates } = resolveFriend(d.friends, tok);
|
|
1329
|
+
const { match, candidates } = resolveFriend(d.friends.concat(d.nodes), tok);
|
|
1004
1330
|
if (!match)
|
|
1005
1331
|
return append(asMsg("them", candidates.length ? ambiguous(candidates) : notFound(tok)));
|
|
1006
1332
|
const was = nameOf(match);
|
|
@@ -1015,7 +1341,7 @@ ${cands.map((c) => `- ${nameOf(c)}`).join("\n")}`;
|
|
|
1015
1341
|
if (!tok)
|
|
1016
1342
|
return append(asMsg("them", `${lang === "zh" ? "\u7528\u6CD5" : "Usage"}: \`/${cmd.name} <friend>\``));
|
|
1017
1343
|
const d = await fetchFriends();
|
|
1018
|
-
const { match, candidates } = resolveFriend(d.friends, tok);
|
|
1344
|
+
const { match, candidates } = resolveFriend(d.friends.concat(d.nodes), tok);
|
|
1019
1345
|
if (!match)
|
|
1020
1346
|
return append(asMsg("them", candidates.length ? ambiguous(candidates) : notFound(tok)));
|
|
1021
1347
|
const isMuted = (muted || []).includes(match.userid);
|
package/package.json
CHANGED