@bolloon/bolloon-agent 0.1.33 → 0.1.34
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/README.md +7 -2
- package/dist/bollharness-integration/index.js +8 -1
- package/dist/heartbeat/Watchdog.js +9 -1
- package/dist/network/p2p-direct.js +59 -2
- package/dist/pi-ecosystem/index.js +9 -6
- package/dist/pi-ecosystem-judgment/decision.js +5 -2
- package/dist/social/heartbeat.js +19 -2
- package/dist/web/api-config.html +3 -3
- package/dist/web/client.js +667 -154
- package/dist/web/index.html +10 -27
- package/dist/web/server.js +597 -48
- package/dist/web/style.css +370 -0
- package/package.json +2 -1
- package/src/bollharness-integration/index.ts +8 -32
- package/src/heartbeat/Watchdog.ts +9 -1
- package/src/network/p2p-direct.ts +59 -3
- package/src/social/ant-colony/index.js +19 -0
- package/src/social/heartbeat.ts +18 -2
- package/src/web/api-config.html +3 -3
- package/src/web/client.js +667 -154
- package/src/web/index.html +10 -27
- package/src/web/server.ts +583 -43
- package/src/web/style.css +370 -0
- package/src/social/ant-colony/AdaptiveHeartbeat.ts +0 -131
- package/src/social/ant-colony/PheromoneEngine.ts +0 -302
- package/src/social/ant-colony/index.ts +0 -18
- package/src/social/ant-colony/types.ts +0 -94
package/dist/web/style.css
CHANGED
|
@@ -4083,3 +4083,373 @@ body:has(> .api-config-page) {
|
|
|
4083
4083
|
cursor: pointer;
|
|
4084
4084
|
accent-color: var(--accent);
|
|
4085
4085
|
}
|
|
4086
|
+
|
|
4087
|
+
/* ============================================================
|
|
4088
|
+
* 2026-06-10: 远程聊天 modal — 完全对齐本地聊天风格
|
|
4089
|
+
* 用 CSS 变量, 字体继承全局 JetBrains Mono, 支持深/浅色主题切换
|
|
4090
|
+
* ============================================================ */
|
|
4091
|
+
.remote-chat-overlay {
|
|
4092
|
+
position: fixed;
|
|
4093
|
+
inset: 0;
|
|
4094
|
+
background: rgba(0, 0, 0, 0.55);
|
|
4095
|
+
z-index: 10002;
|
|
4096
|
+
display: flex;
|
|
4097
|
+
align-items: center;
|
|
4098
|
+
justify-content: center;
|
|
4099
|
+
}
|
|
4100
|
+
.remote-chat-shell {
|
|
4101
|
+
background: var(--bg-main);
|
|
4102
|
+
color: var(--text);
|
|
4103
|
+
border: 1px solid var(--border);
|
|
4104
|
+
border-radius: 8px;
|
|
4105
|
+
width: 720px;
|
|
4106
|
+
max-width: 92vw;
|
|
4107
|
+
height: 80vh;
|
|
4108
|
+
max-height: 80vh;
|
|
4109
|
+
display: flex;
|
|
4110
|
+
flex-direction: column;
|
|
4111
|
+
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.35);
|
|
4112
|
+
font-family: inherit;
|
|
4113
|
+
}
|
|
4114
|
+
.remote-chat-header {
|
|
4115
|
+
padding: 12px 16px;
|
|
4116
|
+
border-bottom: 1px solid var(--border);
|
|
4117
|
+
display: flex;
|
|
4118
|
+
align-items: center;
|
|
4119
|
+
justify-content: space-between;
|
|
4120
|
+
gap: 8px;
|
|
4121
|
+
background: var(--bg-sidebar);
|
|
4122
|
+
border-radius: 8px 8px 0 0;
|
|
4123
|
+
}
|
|
4124
|
+
.remote-chat-title {
|
|
4125
|
+
font-size: 15px;
|
|
4126
|
+
font-weight: 600;
|
|
4127
|
+
color: var(--text);
|
|
4128
|
+
}
|
|
4129
|
+
.remote-chat-meta {
|
|
4130
|
+
font-size: 11px;
|
|
4131
|
+
color: var(--text-muted);
|
|
4132
|
+
margin-top: 2px;
|
|
4133
|
+
overflow: hidden;
|
|
4134
|
+
text-overflow: ellipsis;
|
|
4135
|
+
white-space: nowrap;
|
|
4136
|
+
}
|
|
4137
|
+
.remote-chat-btn-secondary {
|
|
4138
|
+
background: var(--bg-hover);
|
|
4139
|
+
border: 1px solid var(--border);
|
|
4140
|
+
color: var(--text-secondary);
|
|
4141
|
+
cursor: pointer;
|
|
4142
|
+
padding: 4px 10px;
|
|
4143
|
+
border-radius: 4px;
|
|
4144
|
+
font-size: 11px;
|
|
4145
|
+
font-family: inherit;
|
|
4146
|
+
}
|
|
4147
|
+
.remote-chat-btn-secondary:hover {
|
|
4148
|
+
background: var(--bg-active);
|
|
4149
|
+
color: var(--text);
|
|
4150
|
+
}
|
|
4151
|
+
.remote-chat-btn-close {
|
|
4152
|
+
background: none;
|
|
4153
|
+
border: none;
|
|
4154
|
+
font-size: 20px;
|
|
4155
|
+
color: var(--text-muted);
|
|
4156
|
+
cursor: pointer;
|
|
4157
|
+
padding: 0 8px;
|
|
4158
|
+
line-height: 1;
|
|
4159
|
+
}
|
|
4160
|
+
.remote-chat-btn-close:hover {
|
|
4161
|
+
color: var(--text);
|
|
4162
|
+
}
|
|
4163
|
+
.remote-chat-thinking {
|
|
4164
|
+
padding: 8px 16px;
|
|
4165
|
+
background: var(--bg-hover);
|
|
4166
|
+
color: var(--text-secondary);
|
|
4167
|
+
font-size: 12px;
|
|
4168
|
+
border-bottom: 1px solid var(--border);
|
|
4169
|
+
}
|
|
4170
|
+
.remote-chat-log {
|
|
4171
|
+
flex: 1;
|
|
4172
|
+
overflow-y: auto;
|
|
4173
|
+
padding: 12px 16px;
|
|
4174
|
+
background: var(--bg-main);
|
|
4175
|
+
}
|
|
4176
|
+
.remote-chat-input-row {
|
|
4177
|
+
padding: 10px 12px;
|
|
4178
|
+
border-top: 1px solid var(--border);
|
|
4179
|
+
display: flex;
|
|
4180
|
+
gap: 6px;
|
|
4181
|
+
background: var(--bg-sidebar);
|
|
4182
|
+
border-radius: 0 0 8px 8px;
|
|
4183
|
+
}
|
|
4184
|
+
.remote-chat-input {
|
|
4185
|
+
flex: 1;
|
|
4186
|
+
padding: 8px 10px;
|
|
4187
|
+
border: 1px solid var(--border);
|
|
4188
|
+
border-radius: 4px;
|
|
4189
|
+
font-size: 13px;
|
|
4190
|
+
background: var(--bg-main);
|
|
4191
|
+
color: var(--text);
|
|
4192
|
+
font-family: inherit;
|
|
4193
|
+
}
|
|
4194
|
+
.remote-chat-input:focus {
|
|
4195
|
+
outline: none;
|
|
4196
|
+
border-color: var(--accent, #a4b630);
|
|
4197
|
+
}
|
|
4198
|
+
.remote-chat-btn-send {
|
|
4199
|
+
padding: 8px 14px;
|
|
4200
|
+
background: var(--user-bg);
|
|
4201
|
+
color: var(--user-text);
|
|
4202
|
+
border: none;
|
|
4203
|
+
border-radius: 4px;
|
|
4204
|
+
cursor: pointer;
|
|
4205
|
+
font-size: 13px;
|
|
4206
|
+
font-weight: 600;
|
|
4207
|
+
font-family: inherit;
|
|
4208
|
+
}
|
|
4209
|
+
.remote-chat-btn-send:hover:not(:disabled) {
|
|
4210
|
+
filter: brightness(1.1);
|
|
4211
|
+
}
|
|
4212
|
+
.remote-chat-btn-send:disabled {
|
|
4213
|
+
opacity: 0.5;
|
|
4214
|
+
cursor: wait;
|
|
4215
|
+
}
|
|
4216
|
+
.remote-chat-sysmsg {
|
|
4217
|
+
margin: 6px 0;
|
|
4218
|
+
padding: 6px 10px;
|
|
4219
|
+
border-radius: 4px;
|
|
4220
|
+
font-size: 11px;
|
|
4221
|
+
text-align: center;
|
|
4222
|
+
border: 1px solid var(--border);
|
|
4223
|
+
}
|
|
4224
|
+
.remote-chat-sysmsg-info {
|
|
4225
|
+
background: var(--bg-hover);
|
|
4226
|
+
color: var(--text-secondary);
|
|
4227
|
+
}
|
|
4228
|
+
.remote-chat-sysmsg-warn {
|
|
4229
|
+
background: var(--bg-hover);
|
|
4230
|
+
color: var(--text);
|
|
4231
|
+
border-color: var(--border-light);
|
|
4232
|
+
}
|
|
4233
|
+
.remote-chat-sysmsg-error {
|
|
4234
|
+
background: rgba(220, 38, 38, 0.12);
|
|
4235
|
+
color: #fca5a5;
|
|
4236
|
+
border-color: rgba(220, 38, 38, 0.3);
|
|
4237
|
+
}
|
|
4238
|
+
.remote-chat-judgments {
|
|
4239
|
+
margin: 0 0 8px;
|
|
4240
|
+
padding: 8px 10px;
|
|
4241
|
+
background: var(--bg-hover);
|
|
4242
|
+
border-left: 3px solid var(--accent, #a4b630);
|
|
4243
|
+
border-radius: 4px;
|
|
4244
|
+
font-size: 12px;
|
|
4245
|
+
}
|
|
4246
|
+
.remote-chat-judgments-title {
|
|
4247
|
+
font-weight: 600;
|
|
4248
|
+
color: var(--text);
|
|
4249
|
+
margin-bottom: 4px;
|
|
4250
|
+
}
|
|
4251
|
+
.remote-chat-judgment-item {
|
|
4252
|
+
margin: 3px 0;
|
|
4253
|
+
padding-left: 8px;
|
|
4254
|
+
color: var(--text);
|
|
4255
|
+
}
|
|
4256
|
+
.remote-chat-judgment-tag {
|
|
4257
|
+
color: var(--text-muted);
|
|
4258
|
+
font-size: 10px;
|
|
4259
|
+
}
|
|
4260
|
+
.remote-chat-judgment-reason {
|
|
4261
|
+
color: var(--text-secondary);
|
|
4262
|
+
font-size: 11px;
|
|
4263
|
+
}
|
|
4264
|
+
.remote-chat-judgments-foot {
|
|
4265
|
+
margin-top: 6px;
|
|
4266
|
+
color: var(--text-muted);
|
|
4267
|
+
font-size: 11px;
|
|
4268
|
+
}
|
|
4269
|
+
|
|
4270
|
+
/* ============================================================
|
|
4271
|
+
* 2026-06-10: 好友申请 modal — 对齐本地风格 + CSS 变量
|
|
4272
|
+
* ============================================================ */
|
|
4273
|
+
.friend-req-overlay {
|
|
4274
|
+
position: fixed;
|
|
4275
|
+
inset: 0;
|
|
4276
|
+
background: rgba(0, 0, 0, 0.55);
|
|
4277
|
+
z-index: 10004;
|
|
4278
|
+
display: flex;
|
|
4279
|
+
align-items: center;
|
|
4280
|
+
justify-content: center;
|
|
4281
|
+
}
|
|
4282
|
+
.friend-req-shell {
|
|
4283
|
+
background: var(--bg-main);
|
|
4284
|
+
color: var(--text);
|
|
4285
|
+
border: 1px solid var(--border);
|
|
4286
|
+
border-radius: 8px;
|
|
4287
|
+
width: 460px;
|
|
4288
|
+
max-width: 92vw;
|
|
4289
|
+
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.4);
|
|
4290
|
+
font-family: inherit;
|
|
4291
|
+
}
|
|
4292
|
+
.friend-req-header {
|
|
4293
|
+
padding: 14px 18px;
|
|
4294
|
+
border-bottom: 1px solid var(--border);
|
|
4295
|
+
background: var(--bg-sidebar);
|
|
4296
|
+
display: flex;
|
|
4297
|
+
align-items: center;
|
|
4298
|
+
gap: 10px;
|
|
4299
|
+
border-radius: 8px 8px 0 0;
|
|
4300
|
+
}
|
|
4301
|
+
.friend-req-title {
|
|
4302
|
+
font-size: 15px;
|
|
4303
|
+
font-weight: 600;
|
|
4304
|
+
color: var(--text);
|
|
4305
|
+
}
|
|
4306
|
+
.friend-req-meta {
|
|
4307
|
+
font-size: 11px;
|
|
4308
|
+
color: var(--text-muted);
|
|
4309
|
+
margin-top: 2px;
|
|
4310
|
+
overflow: hidden;
|
|
4311
|
+
text-overflow: ellipsis;
|
|
4312
|
+
white-space: nowrap;
|
|
4313
|
+
}
|
|
4314
|
+
.friend-req-body {
|
|
4315
|
+
padding: 14px 18px;
|
|
4316
|
+
font-size: 13px;
|
|
4317
|
+
color: var(--text);
|
|
4318
|
+
line-height: 1.5;
|
|
4319
|
+
}
|
|
4320
|
+
.friend-req-actions {
|
|
4321
|
+
padding: 10px 18px;
|
|
4322
|
+
border-top: 1px solid var(--border);
|
|
4323
|
+
display: flex;
|
|
4324
|
+
gap: 8px;
|
|
4325
|
+
justify-content: flex-end;
|
|
4326
|
+
background: var(--bg-sidebar);
|
|
4327
|
+
border-radius: 0 0 8px 8px;
|
|
4328
|
+
}
|
|
4329
|
+
.friend-req-btn-deny {
|
|
4330
|
+
padding: 6px 14px;
|
|
4331
|
+
background: var(--bg-hover);
|
|
4332
|
+
color: var(--text-secondary);
|
|
4333
|
+
border: 1px solid var(--border);
|
|
4334
|
+
border-radius: 4px;
|
|
4335
|
+
cursor: pointer;
|
|
4336
|
+
font-size: 13px;
|
|
4337
|
+
font-family: inherit;
|
|
4338
|
+
}
|
|
4339
|
+
.friend-req-btn-deny:hover {
|
|
4340
|
+
background: var(--bg-active);
|
|
4341
|
+
color: var(--text);
|
|
4342
|
+
}
|
|
4343
|
+
.friend-req-btn-accept {
|
|
4344
|
+
padding: 6px 14px;
|
|
4345
|
+
background: var(--user-bg);
|
|
4346
|
+
color: var(--user-text);
|
|
4347
|
+
border: none;
|
|
4348
|
+
border-radius: 4px;
|
|
4349
|
+
cursor: pointer;
|
|
4350
|
+
font-size: 13px;
|
|
4351
|
+
font-weight: 600;
|
|
4352
|
+
font-family: inherit;
|
|
4353
|
+
}
|
|
4354
|
+
.friend-req-btn-accept:hover {
|
|
4355
|
+
filter: brightness(1.1);
|
|
4356
|
+
}
|
|
4357
|
+
|
|
4358
|
+
/* ============================================================
|
|
4359
|
+
* 2026-06-10: Toast 入场动画 (simple-toast 用)
|
|
4360
|
+
* ============================================================ */
|
|
4361
|
+
@keyframes toast-in {
|
|
4362
|
+
from {
|
|
4363
|
+
opacity: 0;
|
|
4364
|
+
transform: translateX(20px);
|
|
4365
|
+
}
|
|
4366
|
+
to {
|
|
4367
|
+
opacity: 1;
|
|
4368
|
+
transform: translateX(0);
|
|
4369
|
+
}
|
|
4370
|
+
}
|
|
4371
|
+
|
|
4372
|
+
/* ============================================================
|
|
4373
|
+
* 2026-06-10: P2P peer-group 可折叠 — 复用 #remote-agents-section.collapsed 模式
|
|
4374
|
+
* peer header 加 .peer-caret, 点击切 .remote-peer-group.collapsed
|
|
4375
|
+
* 折叠后隐藏 .remote-peer-channels, caret 旋转 -90deg
|
|
4376
|
+
* ============================================================ */
|
|
4377
|
+
.remote-peer-group .peer-caret {
|
|
4378
|
+
display: inline-block;
|
|
4379
|
+
font-size: 10px;
|
|
4380
|
+
color: var(--text-muted);
|
|
4381
|
+
transition: transform 0.15s ease;
|
|
4382
|
+
user-select: none;
|
|
4383
|
+
width: 12px;
|
|
4384
|
+
text-align: center;
|
|
4385
|
+
}
|
|
4386
|
+
.remote-peer-group.collapsed .peer-caret {
|
|
4387
|
+
transform: rotate(-90deg);
|
|
4388
|
+
}
|
|
4389
|
+
.remote-peer-group.collapsed > .remote-peer-channels {
|
|
4390
|
+
display: none;
|
|
4391
|
+
}
|
|
4392
|
+
.remote-peer-group .remote-peer-header {
|
|
4393
|
+
/* 让 caret 单独成可点区域时不撑破 header */
|
|
4394
|
+
user-select: none;
|
|
4395
|
+
}
|
|
4396
|
+
|
|
4397
|
+
/* ============================================================
|
|
4398
|
+
* 2026-06-11: 分享 channel modal (P2P peer 分享) — 用 friend-req-shell 风格
|
|
4399
|
+
* ============================================================ */
|
|
4400
|
+
.share-modal-shell {
|
|
4401
|
+
width: 520px;
|
|
4402
|
+
max-height: 80vh;
|
|
4403
|
+
}
|
|
4404
|
+
.share-modal-hint {
|
|
4405
|
+
padding: 8px 18px;
|
|
4406
|
+
font-size: 12px;
|
|
4407
|
+
color: var(--text-muted);
|
|
4408
|
+
background: var(--bg-sidebar);
|
|
4409
|
+
border-bottom: 1px solid var(--border);
|
|
4410
|
+
}
|
|
4411
|
+
.share-modal-list {
|
|
4412
|
+
flex: 1;
|
|
4413
|
+
overflow-y: auto;
|
|
4414
|
+
padding: 4px 18px;
|
|
4415
|
+
max-height: 50vh;
|
|
4416
|
+
}
|
|
4417
|
+
.share-modal-empty {
|
|
4418
|
+
color: var(--text-muted);
|
|
4419
|
+
padding: 20px;
|
|
4420
|
+
text-align: center;
|
|
4421
|
+
}
|
|
4422
|
+
.share-modal-row {
|
|
4423
|
+
display: flex;
|
|
4424
|
+
align-items: flex-start;
|
|
4425
|
+
gap: 10px;
|
|
4426
|
+
padding: 8px 4px;
|
|
4427
|
+
cursor: pointer;
|
|
4428
|
+
border-bottom: 1px solid var(--border);
|
|
4429
|
+
font-family: inherit;
|
|
4430
|
+
}
|
|
4431
|
+
.share-modal-row:hover {
|
|
4432
|
+
background: var(--bg-hover);
|
|
4433
|
+
}
|
|
4434
|
+
.share-modal-cb {
|
|
4435
|
+
margin-top: 4px;
|
|
4436
|
+
cursor: pointer;
|
|
4437
|
+
width: 16px;
|
|
4438
|
+
height: 16px;
|
|
4439
|
+
accent-color: var(--accent);
|
|
4440
|
+
}
|
|
4441
|
+
.share-modal-row-info {
|
|
4442
|
+
flex: 1;
|
|
4443
|
+
min-width: 0;
|
|
4444
|
+
}
|
|
4445
|
+
.share-modal-row-name {
|
|
4446
|
+
font-size: 13px;
|
|
4447
|
+
font-weight: 500;
|
|
4448
|
+
color: var(--text);
|
|
4449
|
+
}
|
|
4450
|
+
.share-modal-row-meta {
|
|
4451
|
+
font-size: 10px;
|
|
4452
|
+
color: var(--text-muted);
|
|
4453
|
+
margin-top: 2px;
|
|
4454
|
+
font-family: 'JetBrains Mono', monospace;
|
|
4455
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bolloon/bolloon-agent",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.34",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "P2P AI Document Agent - 全局安装后执行 `bolloon` 启动产品",
|
|
6
6
|
"main": "dist/cli.js",
|
|
@@ -60,6 +60,7 @@
|
|
|
60
60
|
"viem": "^2.52.0"
|
|
61
61
|
},
|
|
62
62
|
"devDependencies": {
|
|
63
|
+
"@playwright/test": "^1.60.0",
|
|
63
64
|
"@types/express": "^5.0.6",
|
|
64
65
|
"@types/node": "^25.9.1",
|
|
65
66
|
"@types/pdf-parse": "^1.1.5",
|
|
@@ -96,38 +96,14 @@ export {
|
|
|
96
96
|
type SubagentResult,
|
|
97
97
|
} from '../pi-ecosystem-subagents/index.js';
|
|
98
98
|
|
|
99
|
-
export
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
antTick,
|
|
108
|
-
createTask,
|
|
109
|
-
dispatchTask,
|
|
110
|
-
recordResult,
|
|
111
|
-
getAnt,
|
|
112
|
-
listAnts,
|
|
113
|
-
listAntsByRole,
|
|
114
|
-
listAntsBySignal,
|
|
115
|
-
getActiveAnts,
|
|
116
|
-
getTask,
|
|
117
|
-
listTasks,
|
|
118
|
-
getSignalHistory,
|
|
119
|
-
getColonyStatus,
|
|
120
|
-
getColonyDump,
|
|
121
|
-
persistColony,
|
|
122
|
-
loadColony,
|
|
123
|
-
onColonyEvent,
|
|
124
|
-
offColonyEvent,
|
|
125
|
-
type Ant,
|
|
126
|
-
type ColonyTask,
|
|
127
|
-
type ColonySignalEvent,
|
|
128
|
-
type ColonySignal,
|
|
129
|
-
type AntRole,
|
|
130
|
-
} from '../pi-ecosystem-colony/index.js';
|
|
99
|
+
// 2026-06-11: 蚁群模块 (pi-ecosystem-colony) 已被用户删除, 移除对应 re-export 防止启动失败
|
|
100
|
+
// export {
|
|
101
|
+
// registerAnt, antScouting, antWorking, antReviewing, antComplete, antFail, antAbort, antTick,
|
|
102
|
+
// createTask, dispatchTask, recordResult, getAnt, listAnts, listAntsByRole, listAntsBySignal,
|
|
103
|
+
// getActiveAnts, getTask, listTasks, getSignalHistory, getColonyStatus, getColonyDump,
|
|
104
|
+
// persistColony, loadColony, onColonyEvent, offColonyEvent,
|
|
105
|
+
// type Ant, type ColonyTask, type ColonySignalEvent, type ColonySignal, type AntRole,
|
|
106
|
+
// } from '../pi-ecosystem-colony/index.js';
|
|
131
107
|
|
|
132
108
|
// Judgment exports
|
|
133
109
|
export {
|
|
@@ -42,11 +42,19 @@ export class Watchdog {
|
|
|
42
42
|
|
|
43
43
|
/**
|
|
44
44
|
* 记录活动(调用后更新 lastActivityTime)
|
|
45
|
+
* 2026-06-10: 加 5s 去抖, 防 broadcast / SSE 高频路径炸 log + CPU
|
|
46
|
+
* - lastActivityTime 始终更新 (cheap)
|
|
47
|
+
* - 但 onLog 回调只在距上次 ≥5s 时触发, 避免每秒几十次 console.log
|
|
45
48
|
*/
|
|
49
|
+
private _lastLogAt = 0;
|
|
46
50
|
recordActivity(component?: string): void {
|
|
47
51
|
this.state.lastActivityTime = Date.now();
|
|
48
52
|
if (component) {
|
|
49
|
-
|
|
53
|
+
const now = Date.now();
|
|
54
|
+
if (now - this._lastLogAt >= 5000) {
|
|
55
|
+
this._lastLogAt = now;
|
|
56
|
+
this.callbacks.onLog?.(`[Watchdog] Activity from ${component}`);
|
|
57
|
+
}
|
|
50
58
|
}
|
|
51
59
|
}
|
|
52
60
|
|
|
@@ -88,10 +88,9 @@ export class P2PDirect extends EventEmitter {
|
|
|
88
88
|
// 双向记录 (inbound + outbound 都能拿到)
|
|
89
89
|
this.conns.set(remotePubKeyHex, conn);
|
|
90
90
|
|
|
91
|
-
// v3: 触发 'connection' 事件, 上层 (web server) 可以主动给新连接发消息
|
|
92
|
-
this.emit('connection', { remotePublicKey: remotePubKeyHex, conn });
|
|
93
|
-
|
|
94
91
|
// 收到数据时 → 触发 'data' 事件
|
|
92
|
+
// 注意: data 监听器必须在 emit('connection') 之前注册,
|
|
93
|
+
// 否则 server 的 connection handler 发送消息后, 对端回复可能在 data 监听器就绪前到达
|
|
95
94
|
conn.on('data', (chunk: Buffer | Uint8Array) => {
|
|
96
95
|
const buf = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
|
97
96
|
console.log(`[P2PDirect:${this.name}] 收到数据 from ${remotePubKeyHex.substring(0,12)}... (${buf.length} bytes)`);
|
|
@@ -108,6 +107,10 @@ export class P2PDirect extends EventEmitter {
|
|
|
108
107
|
conn.on('close', () => {
|
|
109
108
|
this.conns.delete(remotePubKeyHex);
|
|
110
109
|
});
|
|
110
|
+
|
|
111
|
+
// v3: 触发 'connection' 事件, 上层 (web server) 可以主动给新连接发消息
|
|
112
|
+
// 注意: 放在 data/error/close 监听器之后, 确保 server 的 connection handler 不会先于 data 就绪
|
|
113
|
+
this.emit('connection', { remotePublicKey: remotePubKeyHex, conn });
|
|
111
114
|
});
|
|
112
115
|
|
|
113
116
|
await this.swarm.listen(); // server 模式
|
|
@@ -159,6 +162,59 @@ export class P2PDirect extends EventEmitter {
|
|
|
159
162
|
}
|
|
160
163
|
}
|
|
161
164
|
|
|
165
|
+
/**
|
|
166
|
+
* 2026-06-10: 真"主动发, 等握手完成"版本 — 修复好友申请 fire-and-forget bug.
|
|
167
|
+
*
|
|
168
|
+
* 之前的问题: server.ts:2914 `await swarm.joinPeer(...)` 只触发握手, conn 还没 push 进 this.conns,
|
|
169
|
+
* 立即调 sendTo 找不到 conn → 静默返回 false → 消息扔进虚空.
|
|
170
|
+
*
|
|
171
|
+
* 现在: sendToWithWait 监听 'connection' 事件, 等到 targetPublicKey 真正出现在 this.conns,
|
|
172
|
+
* 才 write; 超时返回 NO_CONN; 写失败返回 WRITE_FAIL; 成功返回 SENT.
|
|
173
|
+
*
|
|
174
|
+
* 上层调用: const r = await p2p.sendToWithWait(pk, rpc, 5000);
|
|
175
|
+
* if (r !== 'SENT') return 502 给前端.
|
|
176
|
+
*/
|
|
177
|
+
async sendToWithWait(
|
|
178
|
+
publicKeyHex: string,
|
|
179
|
+
data: Buffer | string,
|
|
180
|
+
timeoutMs: number = 5000
|
|
181
|
+
): Promise<'SENT' | 'NO_CONN' | 'WRITE_FAIL'> {
|
|
182
|
+
// 2026-06-11: 先主动触发 joinPeer, 否则 DHT 上对面可能没 push conn
|
|
183
|
+
if (this.swarm) {
|
|
184
|
+
try { await this.swarm.joinPeer(Buffer.from(publicKeyHex, 'hex')); } catch {}
|
|
185
|
+
}
|
|
186
|
+
// 1) 已有 conn → 立即试
|
|
187
|
+
let conn = this.conns.get(publicKeyHex);
|
|
188
|
+
if (!conn || conn.destroyed) {
|
|
189
|
+
// 2) 等 'connection' 事件 (this.emit('connection', { remotePublicKey, conn }))
|
|
190
|
+
const waitResult = await new Promise<'READY' | 'TIMEOUT'>((resolve) => {
|
|
191
|
+
const timer = setTimeout(() => {
|
|
192
|
+
this.off('connection', onConn);
|
|
193
|
+
resolve('TIMEOUT');
|
|
194
|
+
}, timeoutMs);
|
|
195
|
+
const onConn = (evt: { remotePublicKey: string; conn: any }) => {
|
|
196
|
+
if (evt.remotePublicKey === publicKeyHex) {
|
|
197
|
+
clearTimeout(timer);
|
|
198
|
+
this.off('connection', onConn);
|
|
199
|
+
resolve('READY');
|
|
200
|
+
}
|
|
201
|
+
};
|
|
202
|
+
this.on('connection', onConn);
|
|
203
|
+
});
|
|
204
|
+
if (waitResult === 'TIMEOUT') return 'NO_CONN';
|
|
205
|
+
conn = this.conns.get(publicKeyHex);
|
|
206
|
+
if (!conn || conn.destroyed) return 'NO_CONN'; // 双保险
|
|
207
|
+
}
|
|
208
|
+
const buf = typeof data === 'string' ? Buffer.from(data) : data;
|
|
209
|
+
try {
|
|
210
|
+
conn.write(buf);
|
|
211
|
+
return 'SENT';
|
|
212
|
+
} catch (err) {
|
|
213
|
+
console.error(`[P2PDirect:${this.name}] sendToWithWait 写失败 (${publicKeyHex.substring(0,12)}...):`, (err as Error).message);
|
|
214
|
+
return 'WRITE_FAIL';
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
162
218
|
getPublicKey(): string {
|
|
163
219
|
if (!this.swarm) return '';
|
|
164
220
|
return b4a.toString(this.swarm.keyPair.publicKey, 'hex');
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// 2026-06-11: 蚁群 stub
|
|
2
|
+
export class PheromoneEngine {
|
|
3
|
+
constructor() {}
|
|
4
|
+
emit() {}
|
|
5
|
+
subscribe() {}
|
|
6
|
+
decay() {}
|
|
7
|
+
getSignals() { return []; }
|
|
8
|
+
}
|
|
9
|
+
export const PheromoneType = {
|
|
10
|
+
SCOUT: 'scout',
|
|
11
|
+
RECRUIT: 'recruit',
|
|
12
|
+
ALARM: 'alarm',
|
|
13
|
+
};
|
|
14
|
+
export class AdaptiveHeartbeat {
|
|
15
|
+
constructor() {}
|
|
16
|
+
start() {}
|
|
17
|
+
stop() {}
|
|
18
|
+
tick() {}
|
|
19
|
+
}
|
package/src/social/heartbeat.ts
CHANGED
|
@@ -1,8 +1,24 @@
|
|
|
1
1
|
import * as fs from 'fs/promises';
|
|
2
2
|
import * as path from 'path';
|
|
3
3
|
import * as crypto from 'crypto';
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
// 2026-06-11: 蚁群模块 (PheromoneEngine / AdaptiveHeartbeat) 已被用户删除, 仅保留最小 stub
|
|
5
|
+
// 让 heartbeat 内的 antColony 代码块继续类型合法, 但运行时所有方法都是 no-op
|
|
6
|
+
class PheromoneEngine {
|
|
7
|
+
async initialize() { /* stub */ }
|
|
8
|
+
shutdown() { /* stub */ }
|
|
9
|
+
getStats() { return { totalTrails: 0, avgStrength: 0, capabilityCount: 0, density: 0 }; }
|
|
10
|
+
async deposit(_type: any, _from: string, _to: string, _strength: number, _meta?: any) { /* stub */ }
|
|
11
|
+
}
|
|
12
|
+
class AdaptiveHeartbeat {
|
|
13
|
+
constructor(_opts: any) { /* stub */ }
|
|
14
|
+
shutdown() { /* stub */ }
|
|
15
|
+
decide() { return { interval: 0, priorityLevel: 'normal' }; }
|
|
16
|
+
setPheromoneDensity(_d: number) { /* stub */ }
|
|
17
|
+
recordActivity(_t: string) { /* stub */ }
|
|
18
|
+
}
|
|
19
|
+
enum PheromoneType {
|
|
20
|
+
DISCOVERY = 'discovery',
|
|
21
|
+
}
|
|
6
22
|
import { ChannelManager } from './channels/ChannelManager.js';
|
|
7
23
|
import { ChannelType } from './channels/types.js';
|
|
8
24
|
import { DiapChannelBridge } from './channels/DiapChannelBridge.js';
|
package/src/web/api-config.html
CHANGED
|
@@ -4,9 +4,9 @@
|
|
|
4
4
|
<meta charset="UTF-8">
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
6
|
<title>API 配置 - Bolloon</title>
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
<!-- 2026-06-11: 移除 Google Fonts 外部 CSS — 在大陆/跨公网时经常 30s+ timeout 阻塞首屏
|
|
8
|
+
style.css 里只用字体名字符串 (JetBrains Mono / monospace), 浏览器看字面量自动落回系统字体
|
|
9
|
+
2026-06-11 提速: 不再等 Google CDN 即可渲染 -->
|
|
10
10
|
<link rel="stylesheet" href="/style.css">
|
|
11
11
|
</head>
|
|
12
12
|
<body>
|