@bolloon/bolloon-agent 0.2.9 → 0.2.11
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 +467 -467
- package/bin/bolloon-cli.cjs +183 -183
- package/bin/bolloon.cjs +0 -0
- package/bin/bolloon.js +157 -0
- package/dist/agents/agent-manifest-protocol.js +0 -52
- package/dist/agents/constraint-layer.js +19 -19
- package/dist/agents/pi-sdk.js +287 -1213
- package/dist/agents/shell-guard.js +2 -2
- package/dist/agents/workflow-pivot-loop.js +2 -81
- package/dist/bollharness/src/scripts/deploy-guard.js +8 -8
- package/dist/bollharness-integration/context-router-judgment.js +4 -4
- package/dist/bollharness-integration/context-router.js +292 -292
- package/dist/bollharness-integration/gate-state-machine.js +13 -26
- package/dist/bollharness-integration/integration.js +0 -71
- package/dist/bollharness-integration/llm-judgment-engine.js +134 -134
- package/dist/bollharness-integration/skill-adapter.js +127 -52
- package/dist/bootstrap/context-collector.js +3 -6
- package/dist/bootstrap/lifecycle-hooks.js +1 -15
- package/dist/cli-entry.js +35 -39
- package/dist/constraint-runtime/src/_archive_helper.js +9 -0
- package/dist/constraint-runtime/src/agent/coordinator.js +48 -0
- package/dist/constraint-runtime/src/agent/index.js +1 -0
- package/dist/constraint-runtime/src/assistant/index.js +12 -0
- package/dist/constraint-runtime/src/bootstrap/index.js +12 -0
- package/dist/constraint-runtime/src/bootstrap_graph.js +13 -0
- package/dist/constraint-runtime/src/bridge/index.js +12 -0
- package/dist/constraint-runtime/src/buddy/index.js +12 -0
- package/dist/constraint-runtime/src/cli/index.js +12 -0
- package/dist/constraint-runtime/src/command_graph.js +10 -0
- package/dist/constraint-runtime/src/commands.js +60 -0
- package/dist/constraint-runtime/src/components/index.js +12 -0
- package/dist/constraint-runtime/src/constants/index.js +12 -0
- package/dist/constraint-runtime/src/constraint/budget.js +22 -0
- package/dist/constraint-runtime/src/constraint/index.js +2 -0
- package/dist/constraint-runtime/src/constraint/permission.js +20 -0
- package/dist/constraint-runtime/src/context.js +30 -0
- package/dist/constraint-runtime/src/coordinator/index.js +12 -0
- package/dist/constraint-runtime/src/cost_hook.js +4 -0
- package/dist/constraint-runtime/src/cost_tracker.js +8 -0
- package/dist/constraint-runtime/src/deferred_init.js +10 -0
- package/dist/constraint-runtime/src/direct_modes.js +6 -0
- package/dist/constraint-runtime/src/dynamic-tool-loader.js +85 -0
- package/dist/constraint-runtime/src/entrypoints/index.js +12 -0
- package/dist/constraint-runtime/src/execution_registry.js +44 -0
- package/dist/constraint-runtime/src/history.js +9 -0
- package/dist/constraint-runtime/src/hooks/index.js +12 -0
- package/dist/constraint-runtime/src/index.js +26 -0
- package/dist/constraint-runtime/src/ink.js +4 -0
- package/dist/constraint-runtime/src/keybindings/index.js +12 -0
- package/dist/constraint-runtime/src/memdir/index.js +12 -0
- package/dist/constraint-runtime/src/migrations/index.js +12 -0
- package/dist/constraint-runtime/src/models.js +1 -0
- package/dist/constraint-runtime/src/moreright/index.js +12 -0
- package/dist/constraint-runtime/src/native_ts/index.js +12 -0
- package/dist/constraint-runtime/src/output_styles/index.js +12 -0
- package/dist/constraint-runtime/src/parity_audit.js +12 -0
- package/dist/constraint-runtime/src/plugins/index.js +12 -0
- package/dist/constraint-runtime/src/port_manifest.js +11 -0
- package/dist/constraint-runtime/src/prefetch.js +9 -0
- package/dist/constraint-runtime/src/query.js +1 -0
- package/dist/constraint-runtime/src/remote/index.js +12 -0
- package/dist/constraint-runtime/src/remote_runtime.js +9 -0
- package/dist/constraint-runtime/src/runtime/index.js +1 -0
- package/dist/constraint-runtime/src/runtime/session.js +35 -0
- package/dist/constraint-runtime/src/schemas/index.js +12 -0
- package/dist/constraint-runtime/src/screens/index.js +12 -0
- package/dist/constraint-runtime/src/server/index.js +12 -0
- package/dist/constraint-runtime/src/services/index.js +12 -0
- package/dist/constraint-runtime/src/session_store.js +22 -0
- package/dist/constraint-runtime/src/setup.js +30 -0
- package/dist/constraint-runtime/src/skills/index.js +1 -0
- package/dist/constraint-runtime/src/skills/skill-registry.js +28 -0
- package/dist/constraint-runtime/src/state/index.js +12 -0
- package/dist/constraint-runtime/src/system_init.js +20 -0
- package/dist/constraint-runtime/src/thinking/engine.js +42 -0
- package/dist/constraint-runtime/src/thinking/index.js +1 -0
- package/dist/constraint-runtime/src/tool_pool.js +8 -0
- package/dist/constraint-runtime/src/tools/OpenCLI/execAdapter.js +7 -0
- package/dist/constraint-runtime/src/tools/OpenCLI/listAdapters.js +7 -0
- package/dist/constraint-runtime/src/tools/OpenCLI/runCommand.js +7 -0
- package/dist/constraint-runtime/src/tools/SafeSDK/confirmTransaction.js +6 -0
- package/dist/constraint-runtime/src/tools/SafeSDK/createTransaction.js +8 -0
- package/dist/constraint-runtime/src/tools/SafeSDK/executeTransaction.js +6 -0
- package/dist/constraint-runtime/src/tools/SafeSDK/getBalance.js +6 -0
- package/dist/constraint-runtime/src/tools/SafeSDK/getPendingTransactions.js +6 -0
- package/dist/constraint-runtime/src/tools/SafeSDK/proposeTransaction.js +6 -0
- package/dist/constraint-runtime/src/tools.js +80 -0
- package/dist/constraint-runtime/src/transcript.js +19 -0
- package/dist/constraint-runtime/src/types/index.js +12 -0
- package/dist/constraint-runtime/src/upstream_proxy/index.js +12 -0
- package/dist/constraint-runtime/src/utils/index.js +12 -0
- package/dist/constraint-runtime/src/vim/index.js +12 -0
- package/dist/constraint-runtime/src/voice/index.js +12 -0
- package/dist/constraint-runtime/tests/agent.test.js +16 -0
- package/dist/constraint-runtime/tests/constraint.test.js +41 -0
- package/dist/constraint-runtime/tests/skill.test.js +19 -0
- package/dist/constraint-runtime/tests/thinking.test.js +22 -0
- package/dist/context-compaction/auto-compact.js +7 -7
- package/dist/electron-preload.js +1 -18
- package/dist/electron-preload.js.map +1 -1
- package/dist/electron.js +168 -3
- package/dist/electron.js.map +1 -1
- package/dist/index.js +116 -136
- package/dist/llm/config-store.js +8 -24
- package/dist/llm/llm-judgment-client.js +102 -102
- package/dist/llm/pi-ai.js +85 -126
- package/dist/llm/system-prompt/layers/channel/local.md +14 -14
- package/dist/llm/system-prompt/layers/channel/p2p-agent.md +18 -18
- package/dist/llm/system-prompt/layers/channel/p2p-visitor.md +19 -19
- package/dist/llm/system-prompt/layers/core/artifacts_storage.md +89 -89
- package/dist/llm/system-prompt/layers/core/evenhandedness.md +21 -21
- package/dist/llm/system-prompt/layers/core/hibs_reminders.md +15 -15
- package/dist/llm/system-prompt/layers/core/identity.md +37 -37
- package/dist/llm/system-prompt/layers/core/knowledge.md +17 -17
- package/dist/llm/system-prompt/layers/core/memory_system.md +12 -12
- package/dist/llm/system-prompt/layers/core/network_filesystem.md +28 -28
- package/dist/llm/system-prompt/layers/core/refusal.md +37 -37
- package/dist/llm/system-prompt/layers/core/tone.md +31 -31
- package/dist/llm/system-prompt/layers/core/tools.thin.md +13 -13
- package/dist/llm/system-prompt/layers/core/wellbeing.md +41 -41
- package/dist/llm/system-prompt/layers/role/architect.md +20 -20
- package/dist/llm/system-prompt/layers/role/expert.md +19 -19
- package/dist/llm/system-prompt/layers/role/implementer.md +15 -15
- package/dist/llm/system-prompt/layers/role/security.md +15 -15
- package/dist/llm/system-prompt/layers/tool/artifacts.md +72 -72
- package/dist/llm/system-prompt/layers/tool/bash.md +25 -25
- package/dist/llm/system-prompt/layers/tool/hibs_api.md +171 -171
- package/dist/llm/system-prompt/layers/tool/image_search.md +70 -70
- package/dist/llm/system-prompt/layers/tool/manifest.md +89 -89
- package/dist/llm/system-prompt/layers/tool/mcp_apps.md +53 -53
- package/dist/llm/system-prompt/layers/tool/web_search.md +83 -83
- package/dist/llm/tool-manifest/ask_user_input.js +1 -1
- package/dist/llm/tool-manifest/bash.js +3 -3
- package/dist/llm/tool-manifest/create_file.js +4 -4
- package/dist/llm/tool-manifest/fetch_sports_data.js +3 -3
- package/dist/llm/tool-manifest/image_search.js +2 -2
- package/dist/llm/tool-manifest/mcp.js +2 -2
- package/dist/llm/tool-manifest/message_compose.js +3 -3
- package/dist/llm/tool-manifest/places.js +2 -2
- package/dist/llm/tool-manifest/present_files.js +1 -1
- package/dist/llm/tool-manifest/recipe.js +2 -2
- package/dist/llm/tool-manifest/str_replace.js +5 -5
- package/dist/llm/tool-manifest/view.js +3 -3
- package/dist/llm/tool-manifest/weather.js +4 -4
- package/dist/llm/tool-manifest/web.js +4 -4
- package/dist/pi-ecosystem-colony/index.js +365 -0
- package/dist/pi-ecosystem-judgment/causal-judge.js +51 -51
- package/dist/pi-ecosystem-judgment/distill-prompt.js +42 -42
- package/dist/pi-ecosystem-judgment/distillation.js +14 -14
- package/dist/pi-ecosystem-judgment/monitor-gate.js +28 -28
- package/dist/pi-ecosystem-judgment/value-injection.js +102 -102
- package/dist/security/context-router-tool.js +15 -15
- package/dist/security/tool-gate.js +0 -11
- package/dist/social/ant-colony/AdaptiveHeartbeat.js +101 -0
- package/dist/social/ant-colony/PheromoneEngine.js +227 -0
- package/dist/social/ant-colony/index.js +6 -0
- package/dist/social/ant-colony/types.js +24 -0
- package/dist/test/ai-judgment-test.js +80 -0
- package/dist/test/bollharness-integration.test.js +318 -0
- package/dist/test/channel-agent-multi-dialogue.js +205 -0
- package/dist/test/channel-heartbeat-agent-test.js +201 -0
- package/dist/test/constraint-layer.test.js +164 -0
- package/dist/test/diap-identity-test.js +172 -0
- package/dist/test/diap-quick-test.js +62 -0
- package/dist/test/global-shared-context.test.js +315 -0
- package/dist/test/harness-judgment-injection.test.js +246 -0
- package/dist/test/harness-workflow-integrator-test.js +228 -0
- package/dist/test/human-value-store.test.js +243 -0
- package/dist/test/hybrid-integration-test.js +118 -0
- package/dist/test/hybrid-messenger-verify.js +55 -0
- package/dist/test/iroh-bistream-debug.js +38 -0
- package/dist/test/iroh-communication.test.js +66 -0
- package/dist/test/iroh-debug-test.js +57 -0
- package/dist/test/iroh-diap-test.js +71 -0
- package/dist/test/iroh-direct-connect.js +55 -0
- package/dist/test/iroh-e2e-fixed.js +89 -0
- package/dist/test/iroh-e2e-same-process.js +63 -0
- package/dist/test/iroh-e2e.js +66 -0
- package/dist/test/iroh-final-e2e.js +72 -0
- package/dist/test/iroh-relay-test.js +37 -0
- package/dist/test/iroh-simple-test.js +41 -0
- package/dist/test/iroh-transport-verify.js +54 -0
- package/dist/test/iroh-transport.test.js +37 -0
- package/dist/test/iroh-two-nodes.js +70 -0
- package/dist/test/iroh-verify.js +44 -0
- package/dist/test/judgment-decision.test.js +219 -0
- package/dist/test/llm-judgment-integration.test.js +220 -0
- package/dist/test/p2p-agent-complex-dialogue.js +385 -0
- package/dist/test/p2p-agent-dialogue.js +341 -0
- package/dist/test/p2p-agent-full-bidirectional.js +510 -0
- package/dist/test/p2p-agent-harness-flow.js +437 -0
- package/dist/test/p2p-agent-harness-single.js +143 -0
- package/dist/test/p2p-ai-dialogue-test.js +318 -0
- package/dist/test/p2p-cid-connect-test.js +195 -0
- package/dist/test/p2p-connect-receiver.js +69 -0
- package/dist/test/p2p-doc-transfer.js +110 -0
- package/dist/test/p2p-identity-page-test.js +77 -0
- package/dist/test/p2p-iroh-test.js +171 -0
- package/dist/test/p2p-minimal-test.js +241 -0
- package/dist/test/p2p-node-1.js +148 -0
- package/dist/test/p2p-node-2.js +148 -0
- package/dist/test/p2p-server.js +281 -0
- package/dist/test/p2p-two-nodes-test.js +438 -0
- package/dist/test/pi-sdk.test.js +44 -0
- package/dist/test/set-persona.js +40 -0
- package/dist/test/simple.test.js +9 -0
- package/dist/test/storage-integration.test.js +150 -0
- package/dist/test/subagent-manager.test.js +276 -0
- package/dist/test/test-gate-flow.test.js +81 -0
- package/dist/test/workflow-engine.test.js +87 -0
- package/dist/test/workflow-pivot-loop.test.js +246 -0
- package/dist/web/api-config.html +520 -520
- package/dist/web/client.js +2917 -3791
- package/dist/web/components/p2p/index.js +234 -276
- package/dist/web/components/wallet-viem.mjs +118 -118
- package/dist/web/index.html +374 -373
- package/dist/web/manifest.json +20 -20
- package/dist/web/server.js +23 -775
- package/dist/web/style.css +4737 -4737
- package/dist/web/ui/message-renderer.js +373 -451
- package/dist/web/ui/step-timeline.js +255 -351
- package/package.json +160 -160
- package/scripts/build-cli.js +215 -215
- package/scripts/build-web.ts +125 -125
- package/scripts/postinstall.js +152 -152
- package/bin/bolloon-daemon.sh +0 -207
- package/bin/ipfs +0 -0
- package/dist/agents/chat-segmenter.js +0 -298
- package/dist/agents/judgment-protocol.js +0 -479
- package/dist/agents/parse-tool-call.js +0 -304
- package/dist/agents/peer-manifest-loader.js +0 -210
- package/dist/agents/react-loop.js +0 -120
- package/dist/agents/session-store.js +0 -171
- package/dist/agents/tool-registry.js +0 -136
- package/dist/bootstrap/chat-archiver.js +0 -276
- package/dist/bootstrap/memory-compressor.js +0 -170
- package/dist/bootstrap/persona-loader.js +0 -94
- package/dist/electron/config.js +0 -16
- package/dist/electron/config.js.map +0 -1
- package/dist/electron/dialogs.js +0 -71
- package/dist/electron/dialogs.js.map +0 -1
- package/dist/electron/first-run.js +0 -129
- package/dist/electron/first-run.js.map +0 -1
- package/dist/electron/ipc.js +0 -16
- package/dist/electron/ipc.js.map +0 -1
- package/dist/electron/logger.js +0 -77
- package/dist/electron/logger.js.map +0 -1
- package/dist/electron/main.js +0 -60
- package/dist/electron/main.js.map +0 -1
- package/dist/electron/menu.js +0 -140
- package/dist/electron/menu.js.map +0 -1
- package/dist/electron/paths.js +0 -33
- package/dist/electron/paths.js.map +0 -1
- package/dist/electron/server.js +0 -74
- package/dist/electron/server.js.map +0 -1
- package/dist/electron/tray.js +0 -73
- package/dist/electron/tray.js.map +0 -1
- package/dist/electron/window.js +0 -69
- package/dist/electron/window.js.map +0 -1
- package/dist/network/local-inbox-bus.js +0 -73
- package/dist/network/p2p-outbox.js +0 -161
- package/dist/network/peer-fs.js +0 -420
- package/dist/network/peer-resource-bridge.js +0 -216
- package/dist/web/components/p2p/P2PModal.js +0 -188
- package/dist/web/components/p2p/p2p-modal.js +0 -664
- package/dist/web/components/p2p/p2p-tools.js +0 -248
- package/dist/web/input-validator.js +0 -103
- package/dist/web/util/safe-name.js +0 -32
|
@@ -0,0 +1,438 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* P2P Two-Node Discovery Test
|
|
3
|
+
* 测试两个不同端口的节点能否发现彼此并进行 P2P 交流
|
|
4
|
+
*
|
|
5
|
+
* 运行方式: npx tsx src/test/p2p-two-nodes-test.ts
|
|
6
|
+
*/
|
|
7
|
+
import { config } from 'dotenv';
|
|
8
|
+
import * as fs from 'fs/promises';
|
|
9
|
+
import * as path from 'path';
|
|
10
|
+
import express from 'express';
|
|
11
|
+
import crypto from 'crypto';
|
|
12
|
+
config();
|
|
13
|
+
const CONFIG_DIR = path.join(process.env.HOME || '/tmp', '.bolloon', 'p2p-test');
|
|
14
|
+
const NODES_FILE = path.join(CONFIG_DIR, 'nodes.json');
|
|
15
|
+
class NodeRegistry {
|
|
16
|
+
nodes = new Map();
|
|
17
|
+
async init() {
|
|
18
|
+
await fs.mkdir(CONFIG_DIR, { recursive: true });
|
|
19
|
+
try {
|
|
20
|
+
const data = await fs.readFile(NODES_FILE, 'utf-8');
|
|
21
|
+
const nodes = JSON.parse(data);
|
|
22
|
+
for (const n of nodes) {
|
|
23
|
+
this.nodes.set(n.id, n);
|
|
24
|
+
}
|
|
25
|
+
console.log(`[Registry] Loaded ${nodes.length} nodes`);
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
console.log('[Registry] Starting fresh');
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
async registerNode(node) {
|
|
32
|
+
node.lastSeen = Date.now();
|
|
33
|
+
this.nodes.set(node.id, node);
|
|
34
|
+
await this.save();
|
|
35
|
+
}
|
|
36
|
+
async unregisterNode(id) {
|
|
37
|
+
this.nodes.delete(id);
|
|
38
|
+
await this.save();
|
|
39
|
+
}
|
|
40
|
+
getNodes() {
|
|
41
|
+
return Array.from(this.nodes.values()).filter(n => Date.now() - n.lastSeen < 60000);
|
|
42
|
+
}
|
|
43
|
+
getNodeById(id) {
|
|
44
|
+
return this.nodes.get(id);
|
|
45
|
+
}
|
|
46
|
+
getOtherNodes(myId) {
|
|
47
|
+
return this.getNodes().filter(n => n.id !== myId);
|
|
48
|
+
}
|
|
49
|
+
async save() {
|
|
50
|
+
const nodes = Array.from(this.nodes.values());
|
|
51
|
+
await fs.writeFile(NODES_FILE, JSON.stringify(nodes, null, 2));
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
class P2PNode {
|
|
55
|
+
id;
|
|
56
|
+
name;
|
|
57
|
+
port;
|
|
58
|
+
topic;
|
|
59
|
+
privateKey;
|
|
60
|
+
publicKey;
|
|
61
|
+
server = null;
|
|
62
|
+
registry;
|
|
63
|
+
connectedPeers = new Set();
|
|
64
|
+
messageHandlers = new Map();
|
|
65
|
+
heartbeatInterval = null;
|
|
66
|
+
constructor(name, port, registry) {
|
|
67
|
+
this.id = crypto.randomUUID();
|
|
68
|
+
this.name = name;
|
|
69
|
+
this.port = port;
|
|
70
|
+
this.topic = 'bolloon-p2p-test';
|
|
71
|
+
this.privateKey = crypto.getRandomValues(new Uint8Array(32));
|
|
72
|
+
this.publicKey = Buffer.from(this.privateKey.slice(0, 16)).toString('hex');
|
|
73
|
+
this.registry = registry;
|
|
74
|
+
}
|
|
75
|
+
async start() {
|
|
76
|
+
const app = express();
|
|
77
|
+
app.use(express.json());
|
|
78
|
+
// 健康检查
|
|
79
|
+
app.get('/health', (req, res) => {
|
|
80
|
+
res.json({
|
|
81
|
+
id: this.id,
|
|
82
|
+
name: this.name,
|
|
83
|
+
port: this.port,
|
|
84
|
+
peers: Array.from(this.connectedPeers),
|
|
85
|
+
publicKey: this.publicKey.substring(0, 16) + '...'
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
// 发现端点 - 广播自己的存在
|
|
89
|
+
app.get('/discovery', (req, res) => {
|
|
90
|
+
res.json({
|
|
91
|
+
id: this.id,
|
|
92
|
+
name: this.name,
|
|
93
|
+
port: this.port,
|
|
94
|
+
topic: this.topic,
|
|
95
|
+
publicKey: this.publicKey
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
// 发现所有节点
|
|
99
|
+
app.get('/nodes', async (req, res) => {
|
|
100
|
+
// 先尝试发现本地网络上的其他节点
|
|
101
|
+
const localNodes = await this.discoverLocalNodes();
|
|
102
|
+
const registeredNodes = this.registry.getOtherNodes(this.id);
|
|
103
|
+
const allNodes = [...localNodes, ...registeredNodes];
|
|
104
|
+
const uniqueNodes = new Map();
|
|
105
|
+
for (const n of allNodes) {
|
|
106
|
+
uniqueNodes.set(n.id, n);
|
|
107
|
+
}
|
|
108
|
+
res.json(Array.from(uniqueNodes.values()));
|
|
109
|
+
});
|
|
110
|
+
// 消息端点
|
|
111
|
+
app.post('/message', (req, res) => {
|
|
112
|
+
const { type, to, content } = req.body;
|
|
113
|
+
const msg = {
|
|
114
|
+
type,
|
|
115
|
+
from: this.id,
|
|
116
|
+
to,
|
|
117
|
+
content,
|
|
118
|
+
timestamp: Date.now(),
|
|
119
|
+
id: crypto.randomUUID()
|
|
120
|
+
};
|
|
121
|
+
this.handleMessage(msg, 'local');
|
|
122
|
+
res.json({ ok: true, msgId: msg.id });
|
|
123
|
+
});
|
|
124
|
+
// 接收其他节点的消息
|
|
125
|
+
app.post('/relay', async (req, res) => {
|
|
126
|
+
const { type, from, content } = req.body;
|
|
127
|
+
const msg = {
|
|
128
|
+
type,
|
|
129
|
+
from,
|
|
130
|
+
to: this.id,
|
|
131
|
+
content,
|
|
132
|
+
timestamp: Date.now(),
|
|
133
|
+
id: crypto.randomUUID()
|
|
134
|
+
};
|
|
135
|
+
this.handleMessage(msg, 'relay');
|
|
136
|
+
res.json({ ok: true });
|
|
137
|
+
});
|
|
138
|
+
this.server = app.listen(this.port, () => {
|
|
139
|
+
console.log(`✓ [${this.name}] 启动成功 on port ${this.port}`);
|
|
140
|
+
console.log(` ID: ${this.id}`);
|
|
141
|
+
console.log(` Public Key: ${this.publicKey.substring(0, 16)}...`);
|
|
142
|
+
});
|
|
143
|
+
// 注册到中心注册表
|
|
144
|
+
await this.registry.registerNode({
|
|
145
|
+
id: this.id,
|
|
146
|
+
port: this.port,
|
|
147
|
+
name: this.name,
|
|
148
|
+
publicKey: this.publicKey,
|
|
149
|
+
lastSeen: Date.now(),
|
|
150
|
+
topic: this.topic
|
|
151
|
+
});
|
|
152
|
+
// 启动心跳
|
|
153
|
+
this.startHeartbeat();
|
|
154
|
+
}
|
|
155
|
+
async discoverLocalNodes() {
|
|
156
|
+
const nodes = [];
|
|
157
|
+
const ports = [3001, 3002, 3003, 3004, 3005];
|
|
158
|
+
for (const p of ports) {
|
|
159
|
+
if (p === this.port)
|
|
160
|
+
continue;
|
|
161
|
+
try {
|
|
162
|
+
const resp = await fetch(`http://localhost:${p}/discovery`, {
|
|
163
|
+
signal: AbortSignal.timeout(1000)
|
|
164
|
+
});
|
|
165
|
+
if (resp.ok) {
|
|
166
|
+
const info = await resp.json();
|
|
167
|
+
if (info.topic === this.topic) {
|
|
168
|
+
nodes.push({
|
|
169
|
+
id: info.id,
|
|
170
|
+
port: info.port,
|
|
171
|
+
name: info.name,
|
|
172
|
+
publicKey: info.publicKey,
|
|
173
|
+
lastSeen: Date.now(),
|
|
174
|
+
topic: info.topic
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
catch {
|
|
180
|
+
// 节点不存在
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
return nodes;
|
|
184
|
+
}
|
|
185
|
+
startHeartbeat() {
|
|
186
|
+
// 每 5 秒更新注册表并尝试发现新节点
|
|
187
|
+
this.heartbeatInterval = setInterval(async () => {
|
|
188
|
+
await this.registry.registerNode({
|
|
189
|
+
id: this.id,
|
|
190
|
+
port: this.port,
|
|
191
|
+
name: this.name,
|
|
192
|
+
publicKey: this.publicKey,
|
|
193
|
+
lastSeen: Date.now(),
|
|
194
|
+
topic: this.topic
|
|
195
|
+
});
|
|
196
|
+
// 发现邻居
|
|
197
|
+
const neighbors = this.registry.getOtherNodes(this.id);
|
|
198
|
+
for (const n of neighbors) {
|
|
199
|
+
if (!this.connectedPeers.has(n.id)) {
|
|
200
|
+
await this.connectToPeer(n);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
// 广播存在到本地网络
|
|
204
|
+
this.broadcastPresence();
|
|
205
|
+
}, 5000);
|
|
206
|
+
}
|
|
207
|
+
async connectToPeer(peer) {
|
|
208
|
+
try {
|
|
209
|
+
// 尝试直接连接
|
|
210
|
+
const resp = await fetch(`http://localhost:${peer.port}/discovery`, {
|
|
211
|
+
signal: AbortSignal.timeout(1000)
|
|
212
|
+
});
|
|
213
|
+
if (resp.ok) {
|
|
214
|
+
this.connectedPeers.add(peer.id);
|
|
215
|
+
console.log(`✓ [${this.name}] 连接成功: ${peer.name} (${peer.id.substring(0, 8)}...)`);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
catch {
|
|
219
|
+
// 连接失败
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
async broadcastPresence() {
|
|
223
|
+
// 广播自己的存在到本地网络
|
|
224
|
+
const ports = [3001, 3002, 3003, 3004, 3005];
|
|
225
|
+
for (const p of ports) {
|
|
226
|
+
if (p === this.port)
|
|
227
|
+
continue;
|
|
228
|
+
try {
|
|
229
|
+
await fetch(`http://localhost:${p}/relay`, {
|
|
230
|
+
method: 'POST',
|
|
231
|
+
headers: { 'Content-Type': 'application/json' },
|
|
232
|
+
body: JSON.stringify({
|
|
233
|
+
type: 'presence',
|
|
234
|
+
from: this.id,
|
|
235
|
+
content: JSON.stringify({
|
|
236
|
+
name: this.name,
|
|
237
|
+
port: this.port,
|
|
238
|
+
publicKey: this.publicKey
|
|
239
|
+
})
|
|
240
|
+
}),
|
|
241
|
+
signal: AbortSignal.timeout(500)
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
catch {
|
|
245
|
+
// 节点不存在
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
onMessage(type, handler) {
|
|
250
|
+
this.messageHandlers.set(type, handler);
|
|
251
|
+
}
|
|
252
|
+
handleMessage(msg, source) {
|
|
253
|
+
const handler = this.messageHandlers.get(msg.type);
|
|
254
|
+
if (handler) {
|
|
255
|
+
handler(msg, source === 'local' ? msg.from : source);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
async sendMessage(toId, type, content) {
|
|
259
|
+
const peer = this.registry.getNodeById(toId);
|
|
260
|
+
if (!peer) {
|
|
261
|
+
console.log(`[${this.name}] Unknown peer: ${toId}`);
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
try {
|
|
265
|
+
await fetch(`http://localhost:${peer.port}/message`, {
|
|
266
|
+
method: 'POST',
|
|
267
|
+
headers: { 'Content-Type': 'application/json' },
|
|
268
|
+
body: JSON.stringify({ type, to: toId, content })
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
catch (err) {
|
|
272
|
+
console.log(`[${this.name}] Failed to send message to ${toId}:`, err);
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
async broadcast(type, content) {
|
|
276
|
+
const msg = {
|
|
277
|
+
type,
|
|
278
|
+
from: this.id,
|
|
279
|
+
content,
|
|
280
|
+
timestamp: Date.now(),
|
|
281
|
+
id: crypto.randomUUID()
|
|
282
|
+
};
|
|
283
|
+
// 广播到本地网络
|
|
284
|
+
const ports = [3001, 3002, 3003, 3004, 3005];
|
|
285
|
+
for (const p of ports) {
|
|
286
|
+
if (p === this.port)
|
|
287
|
+
continue;
|
|
288
|
+
try {
|
|
289
|
+
await fetch(`http://localhost:${p}/relay`, {
|
|
290
|
+
method: 'POST',
|
|
291
|
+
headers: { 'Content-Type': 'application/json' },
|
|
292
|
+
body: JSON.stringify({
|
|
293
|
+
type,
|
|
294
|
+
from: this.id,
|
|
295
|
+
content: JSON.stringify(msg)
|
|
296
|
+
}),
|
|
297
|
+
signal: AbortSignal.timeout(500)
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
catch {
|
|
301
|
+
// 节点不存在
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
getPeers() {
|
|
306
|
+
return Array.from(this.connectedPeers);
|
|
307
|
+
}
|
|
308
|
+
async stop() {
|
|
309
|
+
if (this.heartbeatInterval) {
|
|
310
|
+
clearInterval(this.heartbeatInterval);
|
|
311
|
+
}
|
|
312
|
+
if (this.server) {
|
|
313
|
+
await new Promise(resolve => this.server.close(resolve));
|
|
314
|
+
}
|
|
315
|
+
await this.registry.unregisterNode(this.id);
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
// ============================================================================
|
|
319
|
+
// 测试场景
|
|
320
|
+
// ============================================================================
|
|
321
|
+
async function runTest() {
|
|
322
|
+
console.log('\n========================================');
|
|
323
|
+
console.log(' P2P Two-Node Discovery & Harness Test');
|
|
324
|
+
console.log('========================================\n');
|
|
325
|
+
const registry = new NodeRegistry();
|
|
326
|
+
await registry.init();
|
|
327
|
+
// 清理旧数据
|
|
328
|
+
try {
|
|
329
|
+
await fs.unlink(NODES_FILE);
|
|
330
|
+
}
|
|
331
|
+
catch { }
|
|
332
|
+
// 创建两个节点
|
|
333
|
+
const node1 = new P2PNode('Alice-Node', 3001, registry);
|
|
334
|
+
const node2 = new P2PNode('Bob-Node', 3002, registry);
|
|
335
|
+
console.log('━━━ 步骤 1: 启动两个节点 ━━━\n');
|
|
336
|
+
await node1.start();
|
|
337
|
+
await node2.start();
|
|
338
|
+
console.log('\n━━━ 步骤 2: 等待节点发现 (3秒) ━━━\n');
|
|
339
|
+
await new Promise(resolve => setTimeout(resolve, 3000));
|
|
340
|
+
// 列出发现的节点
|
|
341
|
+
console.log('━━━ 步骤 3: 检查节点发现 ━━━\n');
|
|
342
|
+
const node1Peers = node1.getPeers();
|
|
343
|
+
const node2Peers = node2.getPeers();
|
|
344
|
+
console.log(`[Alice] 发现 ${node1Peers.length} 个邻居: ${node1Peers.map(p => p.substring(0, 8)).join(', ') || '无'}`);
|
|
345
|
+
console.log(`[Bob] 发现 ${node2Peers.length} 个邻居: ${node2Peers.map(p => p.substring(0, 8)).join(', ') || '无'}`);
|
|
346
|
+
// 如果没有发现,尝试手动连接
|
|
347
|
+
if (node1Peers.length === 0 && node2Peers.length === 0) {
|
|
348
|
+
console.log('\n⚠️ 节点未自动发现,手动连接...\n');
|
|
349
|
+
// 模拟手动连接
|
|
350
|
+
node1.connectedPeers.add(node2.id);
|
|
351
|
+
node2.connectedPeers.add(node1.id);
|
|
352
|
+
console.log(`✓ [Alice] 手动连接到 Bob`);
|
|
353
|
+
console.log(`✓ [Bob] 手动连接到 Alice`);
|
|
354
|
+
}
|
|
355
|
+
console.log('\n━━━ 步骤 4: 测试消息发送 ━━━\n');
|
|
356
|
+
// 设置消息处理器
|
|
357
|
+
node2.onMessage('ping', (msg, from) => {
|
|
358
|
+
console.log(`[Bob] 收到来自 ${msg.from.substring(0, 8)}... 的消息: ${msg.content}`);
|
|
359
|
+
// 回复 pong
|
|
360
|
+
node2.sendMessage(msg.from, 'pong', 'Pong from Bob!');
|
|
361
|
+
});
|
|
362
|
+
node1.onMessage('pong', (msg, from) => {
|
|
363
|
+
console.log(`[Alice] 收到回复: ${msg.content}`);
|
|
364
|
+
});
|
|
365
|
+
node1.onMessage('presence', (msg, from) => {
|
|
366
|
+
const info = JSON.parse(msg.content);
|
|
367
|
+
console.log(`[Alice] 发现新节点: ${info.name} on port ${info.port}`);
|
|
368
|
+
node1.connectedPeers.add(msg.from);
|
|
369
|
+
});
|
|
370
|
+
node2.onMessage('presence', (msg, from) => {
|
|
371
|
+
const info = JSON.parse(msg.content);
|
|
372
|
+
console.log(`[Bob] 发现新节点: ${info.name} on port ${info.port}`);
|
|
373
|
+
node2.connectedPeers.add(msg.from);
|
|
374
|
+
});
|
|
375
|
+
// Alice 发送 ping 给 Bob
|
|
376
|
+
await node1.sendMessage(node2.id, 'ping', 'Hello Bob, this is Alice!');
|
|
377
|
+
console.log('[Alice] 发送 ping 给 Bob...');
|
|
378
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
379
|
+
console.log('\n━━━ 步骤 5: 测试 Harness 同步 ━━━\n');
|
|
380
|
+
// 模拟 Harness 同步消息
|
|
381
|
+
const harnessSync = {
|
|
382
|
+
gate: 3,
|
|
383
|
+
skills: ['arch', 'lead', 'harness-eng'],
|
|
384
|
+
agents: [node1.id, node2.id],
|
|
385
|
+
timestamp: Date.now()
|
|
386
|
+
};
|
|
387
|
+
console.log(`[Harness Sync] Gate: ${harnessSync.gate}`);
|
|
388
|
+
console.log(`[Harness Sync] Skills: ${harnessSync.skills.join(', ')}`);
|
|
389
|
+
console.log(`[Harness Sync] Agents: ${harnessSync.agents.map(a => a.substring(0, 8)).join(', ')}...`);
|
|
390
|
+
// 设置 harness 消息处理器
|
|
391
|
+
node1.onMessage('harness-sync', (msg, from) => {
|
|
392
|
+
console.log(`\n[Alice] 收到 Harness 同步消息:`);
|
|
393
|
+
const sync = JSON.parse(msg.content);
|
|
394
|
+
console.log(` Gate: ${sync.gate}`);
|
|
395
|
+
console.log(` Skills: ${sync.skills.join(', ')}`);
|
|
396
|
+
});
|
|
397
|
+
node2.onMessage('harness-sync', (msg, from) => {
|
|
398
|
+
console.log(`\n[Bob] 收到 Harness 同步消息:`);
|
|
399
|
+
const sync = JSON.parse(msg.content);
|
|
400
|
+
console.log(` Gate: ${sync.gate}`);
|
|
401
|
+
console.log(` Skills: ${sync.skills.join(', ')}`);
|
|
402
|
+
});
|
|
403
|
+
// 广播 Harness 同步消息
|
|
404
|
+
await node1.broadcast('harness-sync', JSON.stringify(harnessSync));
|
|
405
|
+
console.log('\n[Alice] 广播 Harness 同步消息到网络...\n');
|
|
406
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
407
|
+
console.log('\n━━━ 步骤 6: 最终状态检查 ━━━\n');
|
|
408
|
+
const finalNode1Peers = node1.getPeers();
|
|
409
|
+
const finalNode2Peers = node2.getPeers();
|
|
410
|
+
console.log(`[Alice] 最终邻居数: ${finalNode1Peers.length}`);
|
|
411
|
+
console.log(`[Bob] 最终邻居数: ${finalNode2Peers.length}`);
|
|
412
|
+
// 获取节点健康信息
|
|
413
|
+
try {
|
|
414
|
+
const aliceHealth = await fetch('http://localhost:3001/health').then(r => r.json());
|
|
415
|
+
const bobHealth = await fetch('http://localhost:3002/health').then(r => r.json());
|
|
416
|
+
console.log('\n[Alice Health]');
|
|
417
|
+
console.log(` ID: ${aliceHealth.id.substring(0, 16)}...`);
|
|
418
|
+
console.log(` Peers: ${aliceHealth.peers.length}`);
|
|
419
|
+
console.log('\n[Bob Health]');
|
|
420
|
+
console.log(` ID: ${bobHealth.id.substring(0, 16)}...`);
|
|
421
|
+
console.log(` Peers: ${bobHealth.peers.length}`);
|
|
422
|
+
}
|
|
423
|
+
catch (err) {
|
|
424
|
+
console.log('Health check failed:', err);
|
|
425
|
+
}
|
|
426
|
+
// 清理
|
|
427
|
+
console.log('\n━━━ 清理资源 ━━━\n');
|
|
428
|
+
await node1.stop();
|
|
429
|
+
await node2.stop();
|
|
430
|
+
console.log('✓ 两个节点已停止');
|
|
431
|
+
console.log('\n========================================');
|
|
432
|
+
console.log(' 测试完成');
|
|
433
|
+
console.log('========================================\n');
|
|
434
|
+
}
|
|
435
|
+
runTest().catch(err => {
|
|
436
|
+
console.error('Test failed:', err);
|
|
437
|
+
process.exit(1);
|
|
438
|
+
});
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { config } from 'dotenv';
|
|
2
|
+
import { describe, it, expect } from 'vitest';
|
|
3
|
+
import { createAgentSession } from '../agents/pi-sdk.js';
|
|
4
|
+
import { initMinimax } from '../constraints/index.js';
|
|
5
|
+
import * as path from 'path';
|
|
6
|
+
config();
|
|
7
|
+
async function isMinimaxAvailable() {
|
|
8
|
+
const apiKey = process.env.MINIMAX_API_KEY;
|
|
9
|
+
if (!apiKey)
|
|
10
|
+
return false;
|
|
11
|
+
try {
|
|
12
|
+
initMinimax({ apiKey });
|
|
13
|
+
return true;
|
|
14
|
+
}
|
|
15
|
+
catch {
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
describe('Pi SDK', () => {
|
|
20
|
+
it('basic agent session', async () => {
|
|
21
|
+
const session = await createAgentSession({ cwd: process.cwd() });
|
|
22
|
+
const result = await session.prompt('简单问候');
|
|
23
|
+
expect(result).toContain('我是一个文档处理智能体');
|
|
24
|
+
});
|
|
25
|
+
it('document analysis', async () => {
|
|
26
|
+
const available = await isMinimaxAvailable();
|
|
27
|
+
if (!available) {
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
const testFile = path.join(process.cwd(), 'README.md');
|
|
31
|
+
const session = await createAgentSession({ cwd: process.cwd() });
|
|
32
|
+
const result = await session.summarizeDocument(testFile, '测试文档分析');
|
|
33
|
+
expect(result.summary).toBeDefined();
|
|
34
|
+
});
|
|
35
|
+
it('minimax LLM integration', { timeout: 30000 }, async () => {
|
|
36
|
+
const available = await isMinimaxAvailable();
|
|
37
|
+
if (!available) {
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
const session = await createAgentSession({ cwd: process.cwd() });
|
|
41
|
+
const result = await session.prompt('总结: 这是一个测试文档,用于验证LLM摘要功能。');
|
|
42
|
+
expect(result).toBeDefined();
|
|
43
|
+
});
|
|
44
|
+
});
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { config } from 'dotenv';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import * as fs from 'fs/promises';
|
|
4
|
+
config();
|
|
5
|
+
const PERSONA_TEMPLATE_PATH = path.join('src', 'bollharness', 'templates', 'persona', 'default.json');
|
|
6
|
+
const PERSONA_PATH = path.join(process.env.HOME || '/tmp', '.bolloon', 'persona.json');
|
|
7
|
+
async function loadPersonaTemplate() {
|
|
8
|
+
const content = await fs.readFile(PERSONA_TEMPLATE_PATH, 'utf-8');
|
|
9
|
+
const persona = JSON.parse(content);
|
|
10
|
+
persona.createdAt = new Date().toISOString();
|
|
11
|
+
persona.updatedAt = new Date().toISOString();
|
|
12
|
+
return persona;
|
|
13
|
+
}
|
|
14
|
+
async function testSetPersona() {
|
|
15
|
+
console.log('=== 测试 set_persona 功能 ===\n');
|
|
16
|
+
const { createAgentSession } = await import('../agents/pi-sdk.js');
|
|
17
|
+
const session = await createAgentSession({ cwd: process.cwd() });
|
|
18
|
+
const originalIdentity = session.getIdentity();
|
|
19
|
+
console.log('原始身份:', originalIdentity.name);
|
|
20
|
+
const originalPersona = session.getPersona();
|
|
21
|
+
console.log('原始 persona:', originalPersona ? JSON.stringify(originalPersona, null, 2) : 'null');
|
|
22
|
+
const newPersona = await loadPersonaTemplate();
|
|
23
|
+
console.log('\n📄 从模板加载 persona:', JSON.stringify(newPersona, null, 2));
|
|
24
|
+
await session.setPersona(newPersona);
|
|
25
|
+
console.log('\n✅ 已设置新 persona');
|
|
26
|
+
const updatedIdentity = session.getIdentity();
|
|
27
|
+
console.log('更新后身份:', updatedIdentity.name);
|
|
28
|
+
const updatedPersona = session.getPersona();
|
|
29
|
+
console.log('更新后 persona:', updatedPersona ? JSON.stringify(updatedPersona, null, 2) : 'null');
|
|
30
|
+
console.log('\n📁 persona.json 路径:', PERSONA_PATH);
|
|
31
|
+
try {
|
|
32
|
+
const fileContent = await fs.readFile(PERSONA_PATH, 'utf-8');
|
|
33
|
+
console.log('📄 文件内容:', fileContent);
|
|
34
|
+
}
|
|
35
|
+
catch (e) {
|
|
36
|
+
console.log('读取文件失败:', e);
|
|
37
|
+
}
|
|
38
|
+
console.log('\n=== 测试完成 ===');
|
|
39
|
+
}
|
|
40
|
+
testSetPersona().catch(console.error);
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Storage Layer Integration Test
|
|
3
|
+
* 测试消息存储、离线队列和响应持久化
|
|
4
|
+
*
|
|
5
|
+
* 运行: npx tsx src/test/storage-integration.test.ts
|
|
6
|
+
*/
|
|
7
|
+
import { config } from 'dotenv';
|
|
8
|
+
import * as fs from 'fs/promises';
|
|
9
|
+
import * as path from 'path';
|
|
10
|
+
config();
|
|
11
|
+
const TEST_DIR = path.join(process.env.HOME || '/tmp', '.bolloon', 'test-messages');
|
|
12
|
+
const TEST_CLEANUP = true;
|
|
13
|
+
async function runStorageTest() {
|
|
14
|
+
console.log('\n========================================');
|
|
15
|
+
console.log(' Storage Layer Integration Test');
|
|
16
|
+
console.log('========================================\n');
|
|
17
|
+
// 清理测试目录
|
|
18
|
+
if (TEST_CLEANUP) {
|
|
19
|
+
try {
|
|
20
|
+
await fs.rm(TEST_DIR, { recursive: true, force: true });
|
|
21
|
+
console.log('✓ Cleaned test directory');
|
|
22
|
+
}
|
|
23
|
+
catch { }
|
|
24
|
+
}
|
|
25
|
+
// 导入存储层
|
|
26
|
+
console.log('━━━ 导入存储层 ━━━\n');
|
|
27
|
+
const { JsonMessageStore } = await import('../network/storage/adapters/json-adapter.js');
|
|
28
|
+
const { createMessageStore, createInMemoryStore } = await import('../network/storage/index.js');
|
|
29
|
+
// 测试 1: JSON 存储初始化
|
|
30
|
+
console.log('━━━ 测试 1: JSON 存储初始化 ━━━\n');
|
|
31
|
+
const jsonStore = new JsonMessageStore({ baseDir: TEST_DIR });
|
|
32
|
+
await jsonStore.initialize();
|
|
33
|
+
console.log('✓ JsonMessageStore initialized');
|
|
34
|
+
console.log(` Base dir: ${TEST_DIR}`);
|
|
35
|
+
// 测试 2: 保存消息
|
|
36
|
+
console.log('\n━━━ 测试 2: 保存消息 ━━━\n');
|
|
37
|
+
const msg1 = await jsonStore.saveMessage({
|
|
38
|
+
direction: 'sent',
|
|
39
|
+
type: 'task',
|
|
40
|
+
payload: Buffer.from('Hello World').toString('base64'),
|
|
41
|
+
from: 'node-A',
|
|
42
|
+
to: 'node-B',
|
|
43
|
+
timestamp: Date.now(),
|
|
44
|
+
status: 'delivered',
|
|
45
|
+
retryCount: 0,
|
|
46
|
+
});
|
|
47
|
+
console.log('✓ Saved message');
|
|
48
|
+
console.log(` ID: ${msg1.id}`);
|
|
49
|
+
console.log(` Type: ${msg1.type}`);
|
|
50
|
+
console.log(` Status: ${msg1.status}`);
|
|
51
|
+
// 测试 3: 查询消息
|
|
52
|
+
console.log('\n━━━ 测试 3: 查询消息 ━━━\n');
|
|
53
|
+
const allMessages = await jsonStore.getMessages();
|
|
54
|
+
console.log(`✓ Found ${allMessages.length} messages`);
|
|
55
|
+
const sentMessages = await jsonStore.getMessages({ direction: 'sent' });
|
|
56
|
+
console.log(`✓ Found ${sentMessages.length} sent messages`);
|
|
57
|
+
const taskMessages = await jsonStore.getMessages({ type: 'task' });
|
|
58
|
+
console.log(`✓ Found ${taskMessages.length} task messages`);
|
|
59
|
+
// 测试 4: 离线消息队列
|
|
60
|
+
console.log('\n━━━ 测试 4: 离线消息队列 ━━━\n');
|
|
61
|
+
const offline1 = await jsonStore.enqueueOfflineMessage({
|
|
62
|
+
targetNodeId: 'node-B',
|
|
63
|
+
type: 'urgent-task',
|
|
64
|
+
payload: Buffer.from('Urgent message').toString('base64'),
|
|
65
|
+
createdAt: Date.now(),
|
|
66
|
+
transport: 'libp2p',
|
|
67
|
+
retryCount: 0,
|
|
68
|
+
});
|
|
69
|
+
console.log('✓ Enqueued offline message');
|
|
70
|
+
console.log(` ID: ${offline1.id}`);
|
|
71
|
+
console.log(` Target: ${offline1.targetNodeId}`);
|
|
72
|
+
const pendingCount = await jsonStore.getPendingOfflineCount();
|
|
73
|
+
console.log(`✓ Pending offline count: ${pendingCount}`);
|
|
74
|
+
const nodeBMessages = await jsonStore.getOfflineMessages('node-B');
|
|
75
|
+
console.log(`✓ Messages for node-B: ${nodeBMessages.length}`);
|
|
76
|
+
// 测试 5: 待响应请求
|
|
77
|
+
console.log('\n━━━ 测试 5: 待响应请求 ━━━\n');
|
|
78
|
+
const pending = await jsonStore.savePendingResponse({
|
|
79
|
+
requestId: 'req-123',
|
|
80
|
+
type: 'task-request',
|
|
81
|
+
payload: 'Request payload',
|
|
82
|
+
fromNodeId: 'node-A',
|
|
83
|
+
timestamp: Date.now(),
|
|
84
|
+
timeout: 30000,
|
|
85
|
+
});
|
|
86
|
+
console.log('✓ Saved pending response');
|
|
87
|
+
console.log(` ID: ${pending.id}`);
|
|
88
|
+
console.log(` Request ID: ${pending.requestId}`);
|
|
89
|
+
const retrieved = await jsonStore.getPendingResponse('req-123');
|
|
90
|
+
console.log(`✓ Retrieved pending: ${retrieved ? 'found' : 'not found'}`);
|
|
91
|
+
await jsonStore.removePendingResponse('req-123');
|
|
92
|
+
console.log('✓ Removed pending response');
|
|
93
|
+
// 测试 6: 内存存储
|
|
94
|
+
console.log('\n━━━ 测试 6: 内存存储 ━━━\n');
|
|
95
|
+
const memStore = createInMemoryStore();
|
|
96
|
+
await memStore.initialize();
|
|
97
|
+
const memMsg = await memStore.saveMessage({
|
|
98
|
+
direction: 'received',
|
|
99
|
+
type: 'chat',
|
|
100
|
+
payload: 'In-memory message',
|
|
101
|
+
from: 'peer-X',
|
|
102
|
+
to: 'me',
|
|
103
|
+
timestamp: Date.now(),
|
|
104
|
+
status: 'pending',
|
|
105
|
+
retryCount: 0,
|
|
106
|
+
});
|
|
107
|
+
console.log('✓ Saved to in-memory store');
|
|
108
|
+
console.log(` ID: ${memMsg.id}`);
|
|
109
|
+
await memStore.shutdown();
|
|
110
|
+
console.log('✓ In-memory store shutdown');
|
|
111
|
+
// 测试 7: 统计和清理
|
|
112
|
+
console.log('\n━━━ 测试 7: 统计和清理 ━━━\n');
|
|
113
|
+
const msgCount = await jsonStore.getMessageCount();
|
|
114
|
+
console.log(`✓ Total messages: ${msgCount}`);
|
|
115
|
+
const oldMessages = await jsonStore.getMessages({
|
|
116
|
+
endTime: Date.now() - 1000, // 1秒前
|
|
117
|
+
});
|
|
118
|
+
console.log(`✓ Old messages (< 1s): ${oldMessages.length}`);
|
|
119
|
+
// 测试 8: 工厂函数
|
|
120
|
+
console.log('\n━━━ 测试 8: 工厂函数 ━━━\n');
|
|
121
|
+
const store = await createMessageStore('libp2p', { baseDir: TEST_DIR });
|
|
122
|
+
console.log('✓ Created store via factory');
|
|
123
|
+
await store.shutdown();
|
|
124
|
+
console.log('✓ Store shutdown');
|
|
125
|
+
// 验证文件已创建
|
|
126
|
+
console.log('\n━━━ 验证存储文件 ━━━\n');
|
|
127
|
+
try {
|
|
128
|
+
const files = await fs.readdir(TEST_DIR);
|
|
129
|
+
console.log(`✓ Storage files created: ${files.length}`);
|
|
130
|
+
for (const file of files) {
|
|
131
|
+
console.log(` - ${file}`);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
catch (e) {
|
|
135
|
+
console.log('No storage files yet (messages not persisted to disk in this session)');
|
|
136
|
+
}
|
|
137
|
+
// 最终清理
|
|
138
|
+
await jsonStore.shutdown();
|
|
139
|
+
console.log('\n✓ All stores shutdown');
|
|
140
|
+
// 统计
|
|
141
|
+
console.log('\n========================================');
|
|
142
|
+
console.log(' 测试完成');
|
|
143
|
+
console.log(` 消息数: ${msgCount}`);
|
|
144
|
+
console.log(` 离线消息: ${pendingCount}`);
|
|
145
|
+
console.log('========================================\n');
|
|
146
|
+
}
|
|
147
|
+
runStorageTest().catch(err => {
|
|
148
|
+
console.error('Test failed:', err);
|
|
149
|
+
process.exit(1);
|
|
150
|
+
});
|