clew-code 0.2.7 → 0.2.9
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 +292 -299
- package/dist/main.js +2745 -2869
- package/docs/architecture.html +148 -145
- package/docs/architecture.th.html +79 -78
- package/docs/clew-code-architecture.html +1125 -0
- package/docs/commands.html +224 -223
- package/docs/commands.th.html +131 -130
- package/docs/configuration.html +147 -145
- package/docs/configuration.th.html +108 -106
- package/docs/css/styles.css +48 -42
- package/docs/daemon.html +129 -128
- package/docs/daemon.th.html +73 -72
- package/docs/features/bridge-mode.html +99 -98
- package/docs/features/bridge-mode.th.html +90 -89
- package/docs/features/evals.html +182 -181
- package/docs/features/evals.th.html +90 -89
- package/docs/features/peer.html +178 -177
- package/docs/features/searxng-search.html +151 -150
- package/docs/features/searxng-search.th.html +95 -94
- package/docs/features/sentry-setup.html +157 -156
- package/docs/features/sentry-setup.th.html +97 -96
- package/docs/index.html +299 -298
- package/docs/index.th.html +292 -290
- package/docs/installation.html +105 -103
- package/docs/installation.th.html +105 -103
- package/docs/internals/growthbook-ab-testing.html +113 -112
- package/docs/internals/growthbook-ab-testing.th.html +81 -80
- package/docs/internals/hidden-features.html +149 -147
- package/docs/internals/hidden-features.th.html +109 -107
- package/docs/js/main.js +83 -3
- package/docs/loop.html +181 -180
- package/docs/loop.th.html +227 -226
- package/docs/mcp.html +247 -246
- package/docs/mcp.th.html +207 -206
- package/docs/models.html +111 -110
- package/docs/models.th.html +61 -60
- package/docs/peer.html +236 -235
- package/docs/peer.th.html +280 -279
- package/docs/permission-model.html +102 -101
- package/docs/permission-model.th.html +67 -66
- package/docs/plugins.html +102 -101
- package/docs/plugins.th.html +79 -78
- package/docs/providers.html +126 -117
- package/docs/providers.th.html +80 -78
- package/docs/quick-start.html +93 -92
- package/docs/quick-start.th.html +40 -39
- package/docs/research-memory.html +82 -79
- package/docs/research-memory.th.html +72 -71
- package/docs/skills.html +117 -116
- package/docs/skills.th.html +90 -89
- package/docs/tools.html +170 -169
- package/docs/tools.th.html +84 -83
- package/docs/troubleshooting.html +106 -105
- package/docs/troubleshooting.th.html +85 -84
- package/package.json +164 -162
- package/docs/taste.html +0 -436
- package/docs/taste.th.html +0 -236
package/docs/peer.th.html
CHANGED
|
@@ -1,279 +1,280 @@
|
|
|
1
|
-
<!DOCTYPE html>
|
|
2
|
-
<html lang="th">
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="UTF-8">
|
|
5
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
-
<title
|
|
7
|
-
<meta name="description" content="
|
|
8
|
-
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
9
|
-
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
10
|
-
<link href="https://fonts.googleapis.com/css2?family=DM+Sans:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500;600;700&family=Noto+Sans+Thai:wght@400;500;600;700&display=swap" rel="stylesheet">
|
|
11
|
-
<link rel="stylesheet" href="css/styles.css">
|
|
12
|
-
<link rel="icon" type="image/svg+xml" href="./assets/clew.svg">
|
|
13
|
-
</head>
|
|
14
|
-
<body>
|
|
15
|
-
<header class="header"></header>
|
|
16
|
-
<div class="app">
|
|
17
|
-
<aside class="sidebar" id="sidebar"></aside>
|
|
18
|
-
<div class="sidebar-overlay" id="sidebarOverlay"></div>
|
|
19
|
-
<div class="content-wrap">
|
|
20
|
-
<main class="content">
|
|
21
|
-
<div class="breadcrumbs"><a href="index.th.html"
|
|
22
|
-
<h1
|
|
23
|
-
<p class="section-subtitle">Peer-to-peer
|
|
24
|
-
|
|
25
|
-
<p
|
|
26
|
-
|
|
27
|
-
<h2
|
|
28
|
-
<pre><code>
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
Peer
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
/peer discover
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
[peer list]
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
/peer join /peer send_task /peer run
|
|
92
|
-
(POST /peer-info) (POST /peer-todo) (POST /peer-exec)
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
/peer send_message
|
|
97
|
-
/peer broadcast
|
|
98
|
-
/peer ping
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
advertise
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
stop share
|
|
119
|
-
delete peer file
|
|
120
|
-
close HTTP server</code></pre>
|
|
121
|
-
|
|
122
|
-
<h2
|
|
123
|
-
|
|
124
|
-
<h3>Discovery
|
|
125
|
-
<p
|
|
126
|
-
<ul>
|
|
127
|
-
<li><strong>File-based</strong>
|
|
128
|
-
<li><strong>UDP Multicast</strong>
|
|
129
|
-
<li><strong
|
|
130
|
-
</ul>
|
|
131
|
-
|
|
132
|
-
<h3>Lease-based Task Claiming</h3>
|
|
133
|
-
<p
|
|
134
|
-
<ul>
|
|
135
|
-
<li><strong>Atomic lease</strong>
|
|
136
|
-
<li><strong>Lease expiry</strong>
|
|
137
|
-
<li><strong>Stale lease cleanup</strong>
|
|
138
|
-
<li><strong
|
|
139
|
-
</ul>
|
|
140
|
-
|
|
141
|
-
<h3>Heartbeat + Stale Eviction</h3>
|
|
142
|
-
<p>Peer discovery
|
|
143
|
-
<ul>
|
|
144
|
-
<li><strong>Push-based heartbeat</strong>
|
|
145
|
-
<li><strong>Pull-based eviction</strong>
|
|
146
|
-
<li><strong>3
|
|
147
|
-
<li><strong>Graceful shutdown</strong>
|
|
148
|
-
</ul>
|
|
149
|
-
|
|
150
|
-
<h3>Random Port Binding</h3>
|
|
151
|
-
<p>PeerServer bind
|
|
152
|
-
<ul>
|
|
153
|
-
<li><strong>No port conflicts</strong>
|
|
154
|
-
<li><strong>Security through obscurity</strong>
|
|
155
|
-
<li><strong>Discovery solves discovery</strong>
|
|
156
|
-
</ul>
|
|
157
|
-
|
|
158
|
-
<h3>Message Broadcasting</h3>
|
|
159
|
-
<p><code>peer_broadcast</code>
|
|
160
|
-
<ul>
|
|
161
|
-
<li
|
|
162
|
-
<li
|
|
163
|
-
<li
|
|
164
|
-
</ul>
|
|
165
|
-
|
|
166
|
-
<h2
|
|
167
|
-
|
|
168
|
-
<h3>PeerServer <code>src/peer/PeerServer.ts</code></h3>
|
|
169
|
-
<p>HTTP server
|
|
170
|
-
<table>
|
|
171
|
-
<tr><th>Endpoint</th><th>Method</th><th
|
|
172
|
-
<tr><td><code>/peer-info</code></td><td>GET</td><td
|
|
173
|
-
<tr><td><code>/peer-msg</code></td><td>POST</td><td
|
|
174
|
-
<tr><td><code>/peer-todo</code></td><td>POST</td><td
|
|
175
|
-
<tr><td><code>/peer-exec</code></td><td>POST</td><td
|
|
176
|
-
</table>
|
|
177
|
-
|
|
178
|
-
<h3>PeerDiscovery <code>src/peer/PeerDiscovery.ts</code></h3>
|
|
179
|
-
<p
|
|
180
|
-
<ul>
|
|
181
|
-
<li><strong>File-based</strong>
|
|
182
|
-
<li><strong>UDP Multicast</strong>
|
|
183
|
-
<li><strong>Stale eviction</strong>
|
|
184
|
-
</ul>
|
|
185
|
-
|
|
186
|
-
<h3>PeerStore <code>src/peer/PeerStore.ts</code></h3>
|
|
187
|
-
<p>Singleton registry
|
|
188
|
-
<ul>
|
|
189
|
-
<li><strong>Discovered peers</strong>
|
|
190
|
-
<li><strong>Joined connections</strong>
|
|
191
|
-
<li><strong>Tags</strong>
|
|
192
|
-
</ul>
|
|
193
|
-
|
|
194
|
-
<h2>Discovery Protocol</h2>
|
|
195
|
-
<pre><code>DiscoveryMessage =
|
|
196
|
-
| { type: "clew-peer-query", version: 1 } // broadcast scan
|
|
197
|
-
| { type: "clew-peer-info", version: 1, // heartbeat / response
|
|
198
|
-
id: string, hostname: string, ip: string, port: number,
|
|
199
|
-
cwd: string, sessionId?: string, appVersion: string,
|
|
200
|
-
shell?: string, platform?: string, term?: string,
|
|
201
|
-
status: "online" | "offline" }
|
|
202
|
-
|
|
203
|
-
UDP port: 42069
|
|
204
|
-
Multicast IP: 239.255.37.37
|
|
205
|
-
Heartbeat:
|
|
206
|
-
Stale after: 90
|
|
207
|
-
|
|
208
|
-
<h2
|
|
209
|
-
<table>
|
|
210
|
-
<tr><th
|
|
211
|
-
<tr><td><code>/peer</code></td><td
|
|
212
|
-
<tr><td><code>/peer share</code></td><td
|
|
213
|
-
<tr><td><code>/peer stop</code></td><td
|
|
214
|
-
<tr><td><code>/peer discover</code></td><td
|
|
215
|
-
<tr><td><code>/peer join <host> <port></code></td><td
|
|
216
|
-
<tr><td><code>/peer name <
|
|
217
|
-
<tr><td><code>/peer role <
|
|
218
|
-
<tr><td><code>/peer inbox</code></td><td
|
|
219
|
-
<tr><td><code>/peer disconnect <peer></code></td><td
|
|
220
|
-
<tr><td><code>/peer todos</code></td><td
|
|
221
|
-
<tr><td><code>/peer todo done <id></code></td><td
|
|
222
|
-
</table>
|
|
223
|
-
|
|
224
|
-
<h2>AI Tools</h2>
|
|
225
|
-
<p
|
|
226
|
-
<table>
|
|
227
|
-
<tr><th>Tool</th><th
|
|
228
|
-
<tr><td><code>peer_discover</code></td><td
|
|
229
|
-
<tr><td><code>peer_join</code></td><td
|
|
230
|
-
<tr><td><code>peer_ping</code></td><td
|
|
231
|
-
<tr><td><code>peer_info</code></td><td
|
|
232
|
-
<tr><td><code>peer_send_message</code></td><td
|
|
233
|
-
<tr><td><code>peer_send_task</code></td><td
|
|
234
|
-
<tr><td><code>peer_broadcast</code></td><td
|
|
235
|
-
<tr><td><code>peer_run</code></td><td
|
|
236
|
-
<tr><td><code>peer_disconnect</code></td><td
|
|
237
|
-
<tr><td><code>peer_list_messages</code></td><td
|
|
238
|
-
<tr><td><code>peer_list_roles</code></td><td
|
|
239
|
-
<tr><td><code>peer_list_tasks</code></td><td
|
|
240
|
-
<tr><td><code>peer_set_name</code></td><td
|
|
241
|
-
<tr><td><code>peer_set_role</code></td><td
|
|
242
|
-
<tr><td><code>peer_share</code></td><td
|
|
243
|
-
</table>
|
|
244
|
-
|
|
245
|
-
<h2
|
|
246
|
-
<p>Autonomous agent loop (<code>src/services/autonomous/agentLoop.ts</code>)
|
|
247
|
-
<ul>
|
|
248
|
-
<li
|
|
249
|
-
<li
|
|
250
|
-
<li
|
|
251
|
-
<li
|
|
252
|
-
</ul>
|
|
253
|
-
|
|
254
|
-
<h2
|
|
255
|
-
<table>
|
|
256
|
-
<tr><th
|
|
257
|
-
<tr><td><code>src/peer/PeerServer.ts</code></td><td>HTTP server
|
|
258
|
-
<tr><td><code>src/peer/PeerDiscovery.ts</code></td><td
|
|
259
|
-
<tr><td><code>src/peer/PeerStore.ts</code></td><td>Singleton registry
|
|
260
|
-
<tr><td><code>src/peer/types.ts</code></td><td>Shared types
|
|
261
|
-
<tr><td><code>src/commands/peer/</code></td><td>Interactive peer menu
|
|
262
|
-
<tr><td><code>src/tools/Peer*Tool/</code></td><td>16 AI agent tools
|
|
263
|
-
<tr><td><code>src/services/autonomous/agentLoop.ts</code></td><td>Daemon integration:
|
|
264
|
-
</table>
|
|
265
|
-
|
|
266
|
-
<footer class="footer">
|
|
267
|
-
<span>Clew Code v0.2.
|
|
268
|
-
<div class="footer-links">
|
|
269
|
-
<a href="https://github.com/
|
|
270
|
-
<a href="https://github.com/
|
|
271
|
-
</div>
|
|
272
|
-
</footer>
|
|
273
|
-
</main>
|
|
274
|
-
<nav class="toc-sidebar"></nav>
|
|
275
|
-
</div>
|
|
276
|
-
</div>
|
|
277
|
-
<script src="js/main.js"></script>
|
|
278
|
-
</body>
|
|
279
|
-
</html>
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="th">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>ระบบ Peer — Clew</title>
|
|
7
|
+
<meta name="description" content="ระบบ peer-to-peer บน LAN — ค้นหา, ส่งข้à¸à¸„วาม, มà¸à¸šà¸«à¸¡à¸²à¸¢à¸‡à¸²à¸™, à¹à¸¥à¸°à¸£à¸±à¸™à¸„ำสั่งระยะไà¸à¸¥à¸£à¸°à¸«à¸§à¹ˆà¸²à¸‡ Clew instances">
|
|
8
|
+
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
9
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
10
|
+
<link href="https://fonts.googleapis.com/css2?family=DM+Sans:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500;600;700&family=Noto+Sans+Thai:wght@400;500;600;700&display=swap" rel="stylesheet">
|
|
11
|
+
<link rel="stylesheet" href="css/styles.css">
|
|
12
|
+
<link rel="icon" type="image/svg+xml" href="./assets/clew.svg">
|
|
13
|
+
</head>
|
|
14
|
+
<body>
|
|
15
|
+
<header class="header"></header>
|
|
16
|
+
<div class="app">
|
|
17
|
+
<aside class="sidebar" id="sidebar"></aside>
|
|
18
|
+
<div class="sidebar-overlay" id="sidebarOverlay"></div>
|
|
19
|
+
<div class="content-wrap">
|
|
20
|
+
<main class="content">
|
|
21
|
+
<div class="breadcrumbs"><a href="index.th.html">หน้าà¹à¸£à¸</a><span class="sep">/</span><span>ระบบ Peer</span></div>
|
|
22
|
+
<h1>ระบบ Peer</h1>
|
|
23
|
+
<p class="section-subtitle">Peer-to-peer บน LAN — ค้นหา Clew instances à¸à¸·à¹ˆà¸™, ส่งข้à¸à¸„วาม, มà¸à¸šà¸«à¸¡à¸²à¸¢à¸‡à¸²à¸™, à¹à¸¥à¸°à¸£à¸±à¸™à¸„ำสั่งระยะไà¸à¸¥</p>
|
|
24
|
+
|
|
25
|
+
<p>ระบบ peer à¸à¸¢à¸¹à¹ˆà¹ƒà¸™ <code>src/peer/</code> à¹à¸¥à¸°à¸›à¸£à¸°à¸à¸à¸šà¸”้วย 3 ชั้นหลัà¸: <strong>PeerServer</strong> (HTTP server), <strong>PeerDiscovery</strong> (สà¹à¸à¸™ LAN), à¹à¸¥à¸° <strong>PeerStore</strong> (registry ในหน่วยความจำ)</p>
|
|
26
|
+
|
|
27
|
+
<h2>สถาปัตยà¸à¸£à¸£à¸¡</h2>
|
|
28
|
+
<pre><code> ┌─────────────────────────────────────────────────────────────────────────────â”
|
|
29
|
+
│ PEER SYSTEM FLOW │
|
|
30
|
+
└─────────────────────────────────────────────────────────────────────────────┘
|
|
31
|
+
|
|
32
|
+
┌──────────────────────────â”
|
|
33
|
+
│ /peer share │
|
|
34
|
+
│ (Machine A — Worker) │
|
|
35
|
+
└────────────┬─────────────┘
|
|
36
|
+
│
|
|
37
|
+
┌─────────────────┼─────────────────â”
|
|
38
|
+
â–¼ â–¼ â–¼
|
|
39
|
+
┌──────────────┠┌──────────────┠┌──────────────â”
|
|
40
|
+
│ PeerServer │ │PeerDiscovery │ │ PeerStore │
|
|
41
|
+
│ HTTP :random │ │ │ │ (in-memory) │
|
|
42
|
+
│ port │ │ │ │ │
|
|
43
|
+
└──────┬───────┘ └──────┬───────┘ └──────────────┘
|
|
44
|
+
│ │
|
|
45
|
+
│ ┌────────────┼────────────â”
|
|
46
|
+
│ │ │ │
|
|
47
|
+
│ ▼ ▼ ▼
|
|
48
|
+
│ ┌────────┠┌────────┠┌─────────────â”
|
|
49
|
+
│ │ File │ │ UDP │ │ PeerStore │
|
|
50
|
+
│ │ ~/.cl/ │ │multicast│ │ (singleton) │
|
|
51
|
+
│ │ peers/ │ │239... │ │ │
|
|
52
|
+
│ │{pid}. │ │:42069 │ │ │
|
|
53
|
+
│ │ json │ │ │ │ │
|
|
54
|
+
│ └────┬───┘ └────┬───┘ └─────────────┘
|
|
55
|
+
│ │ │
|
|
56
|
+
│ │ │ heartbeat ทุภ30s
|
|
57
|
+
│ │ │ stale timeout 90s
|
|
58
|
+
│ │ │
|
|
59
|
+
â–¼ â–¼ â–¼
|
|
60
|
+
┌─────────────────────────────────────────────────────â”
|
|
61
|
+
│ LAN NETWORK │
|
|
62
|
+
│ │
|
|
63
|
+
│ ┌─────────┠┌─────────┠│
|
|
64
|
+
│ │ Machine B│◄───────►│ Machine C│ ... │
|
|
65
|
+
│ │ Clew Code│ query │ Clew Code│ │
|
|
66
|
+
│ └────┬─────┘ └────┬─────┘ │
|
|
67
|
+
│ │ │ │
|
|
68
|
+
└────────┼────────────────────┼─────────────────────────┘
|
|
69
|
+
│ │
|
|
70
|
+
│ /peer discover │ /peer discover
|
|
71
|
+
â–¼ â–¼
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
â•â•â• WORKER (Machine A) ENDPOINTS â•â•â•
|
|
75
|
+
|
|
76
|
+
Peer à¸à¸·à¹ˆà¸™ POST /peer-info ──► { hostname, ip, cwd, shell, ... }
|
|
77
|
+
│ POST /peer-msg ──► รับข้à¸à¸„วาม chat
|
|
78
|
+
├───────────────► POST /peer-todo ──► รับ task (ไปà¸à¸¢à¸¹à¹ˆà¹ƒà¸™ inbox)
|
|
79
|
+
│ POST /peer-exec ──► รัน shell command + คืน stdout/stderr
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
â•â•â• CLIENT (Machine B) TOOLS â•â•â•
|
|
83
|
+
|
|
84
|
+
/peer discover ──► scan files + UDP query (3s timeout)
|
|
85
|
+
│
|
|
86
|
+
â–¼
|
|
87
|
+
[peer list]
|
|
88
|
+
│
|
|
89
|
+
┌───────────────────┼───────────────────â”
|
|
90
|
+
â–¼ â–¼ â–¼
|
|
91
|
+
/peer join /peer send_task /peer run
|
|
92
|
+
(POST /peer-info) (POST /peer-todo) (POST /peer-exec)
|
|
93
|
+
|
|
94
|
+
│
|
|
95
|
+
â–¼
|
|
96
|
+
/peer send_message ──► POST /peer-msg ──► inbox ปลายทาง
|
|
97
|
+
/peer broadcast ──► POST /peer-msg ──► ทุภpeer ที่เชื่à¸à¸¡à¸•่à¸
|
|
98
|
+
/peer ping ──► GET /peer-info ──► เช็ค alive
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
â•â•â• DAEMON (agentLoop.ts) â•â•â•
|
|
102
|
+
|
|
103
|
+
┌───────────────────────────────────â”
|
|
104
|
+
│ Autonomous Agent Loop │
|
|
105
|
+
│ - ฟัง /peer-todo ตลà¸à¸” 24/7 │
|
|
106
|
+
│ - รับ task จาภremote peers │
|
|
107
|
+
│ - execute ใน background worker │
|
|
108
|
+
│ - สูงสุด 3 workers พร้à¸à¸¡à¸à¸±à¸™ │
|
|
109
|
+
└───────────────────────────────────┘
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
â•â•â• DATA LIFECYCLE â•â•â•
|
|
113
|
+
|
|
114
|
+
advertise ──► heartbeat ทุภ30s
|
|
115
|
+
│
|
|
116
|
+
├── 90s ไม่มี heartbeat ──► stale → evict
|
|
117
|
+
│
|
|
118
|
+
stop share ──► send UDP "offline"
|
|
119
|
+
delete peer file
|
|
120
|
+
close HTTP server</code></pre>
|
|
121
|
+
|
|
122
|
+
<h2>เทคนิคà¹à¸¥à¸°à¸«à¸¥à¸±à¸à¸à¸²à¸£</h2>
|
|
123
|
+
|
|
124
|
+
<h3>Discovery à¹à¸šà¸šà¸ªà¸à¸‡à¸Šà¸±à¹‰à¸™ (Two-Layer Discovery)</h3>
|
|
125
|
+
<p>ทำไมไม่ใช้à¹à¸„่ UDP multicast หรืà¸à¹à¸„่ file-based à¸à¸¢à¹ˆà¸²à¸‡à¹€à¸”ียว?</p>
|
|
126
|
+
<ul>
|
|
127
|
+
<li><strong>File-based</strong> — ทำงานบนเครื่à¸à¸‡à¹€à¸”ียวà¸à¸±à¸™à¹„ด้ทันที โดยไม่ต้à¸à¸‡à¹ƒà¸Šà¹‰ network เลย à¹à¸„่à¸à¹ˆà¸²à¸™/เขียนไฟล์ JSON ใน <code>~/.claude/peers/</code> ซึ่งเร็วà¸à¸§à¹ˆà¸²à¹à¸¥à¸°à¹„ม่ต้à¸à¸‡à¸à¸±à¸‡à¸§à¸¥à¹€à¸£à¸·à¹ˆà¸à¸‡ firewall</li>
|
|
128
|
+
<li><strong>UDP Multicast</strong> — ข้ามเครื่à¸à¸‡à¹„ด้ผ่าน LAN ใช้ multicast group <code>239.255.37.37:42069</code> ไม่ต้à¸à¸‡à¸£à¸¹à¹‰ IP ปลายทางล่วงหน้า</li>
|
|
129
|
+
<li><strong>ทำไมต้à¸à¸‡à¸—ั้งสà¸à¸‡?</strong> — à¹à¸•่ละวิธีมีข้à¸à¸ˆà¸³à¸à¸±à¸”: file-based ข้ามเครื่à¸à¸‡à¹„ม่ได้, UDP multicast à¸à¸²à¸ˆà¸–ูà¸à¸šà¸¥à¹‡à¸à¸„ด้วย network policy à¸à¸²à¸£à¹ƒà¸Šà¹‰à¸—ั้งสà¸à¸‡à¸§à¸´à¸˜à¸µà¸—ำให้ discovery ทำงานได้ในทุà¸à¸ªà¸ าพà¹à¸§à¸”ล้à¸à¸¡ โดยไม่ต้à¸à¸‡à¹ƒà¸«à¹‰à¸œà¸¹à¹‰à¹ƒà¸Šà¹‰ configure à¸à¸°à¹„รเลย</li>
|
|
130
|
+
</ul>
|
|
131
|
+
|
|
132
|
+
<h3>Lease-based Task Claiming</h3>
|
|
133
|
+
<p>เมื่à¸à¸¡à¸µà¸«à¸¥à¸²à¸¢ worker (หรืà¸à¸«à¸¥à¸²à¸¢ daemon process) วิ่งพร้à¸à¸¡à¸à¸±à¸™ จะป้à¸à¸‡à¸à¸±à¸™à¹„ม่ให้ task ถูà¸à¸—ำซ้ำได้à¸à¸¢à¹ˆà¸²à¸‡à¹„ร?</p>
|
|
134
|
+
<ul>
|
|
135
|
+
<li><strong>Atomic lease</strong> — à¸à¹ˆà¸à¸™à¹€à¸£à¸´à¹ˆà¸¡ task, worker ต้à¸à¸‡à¸‚ภlease ด้วย <code>leaseTask(id, agentId)</code> ซึ่งเช็คว่า task ยังไม่มีใครจà¸à¸‡ à¹à¸¥à¸°à¹€à¸‚ียน lease owner + expiry timestamp ลงไฟล์à¹à¸šà¸š atomic (read-modify-write ภายใต้ lock)</li>
|
|
136
|
+
<li><strong>Lease expiry</strong> — ถ้า worker crash หรืภdisconnect, lease จะหมดà¸à¸²à¸¢à¸¸à¹€à¸à¸‡ (default 30 นาที) ทำให้ worker à¸à¸·à¹ˆà¸™à¸¡à¸²à¸£à¸±à¸šà¸‡à¸²à¸™à¸•่à¸à¹„ด้ — <em>crash recovery โดยไม่ต้à¸à¸‡à¸¡à¸µ distributed coordinator</em></li>
|
|
137
|
+
<li><strong>Stale lease cleanup</strong> — ตà¸à¸™ startLoop จะรภ2 วินาที à¹à¸¥à¹‰à¸§à¹€à¸£à¸µà¸¢à¸ <code>expireLeases()</code> เพื่ภclear lease เà¸à¹ˆà¸²à¸ˆà¸²à¸ session à¸à¹ˆà¸à¸™à¸«à¸™à¹‰à¸²</li>
|
|
138
|
+
<li><strong>ทำไมไม่ใช้ lock file?</strong> — lock file à¹à¸šà¸š OS-level (flock) ใช้ข้าม process บนเครื่à¸à¸‡à¹€à¸”ียวà¸à¸±à¸™à¹„ด้ à¹à¸•่ใช้ข้ามเครื่à¸à¸‡à¹„ม่ได้ Lease บนไฟล์ JSON à¹à¸à¹‰à¸›à¸±à¸à¸«à¸²à¸™à¸µà¹‰à¹„ด้โดยไม่ต้à¸à¸‡à¸žà¸¶à¹ˆà¸‡ external service à¸à¸¢à¹ˆà¸²à¸‡ Redis/ZooKeeper</li>
|
|
139
|
+
</ul>
|
|
140
|
+
|
|
141
|
+
<h3>Heartbeat + Stale Eviction</h3>
|
|
142
|
+
<p>Peer discovery ต้à¸à¸‡à¸£à¸¹à¹‰à¸§à¹ˆà¸²à¹ƒà¸„รยัง alive à¸à¸¢à¸¹à¹ˆ โดยไม่ต้à¸à¸‡ polling:</p>
|
|
143
|
+
<ul>
|
|
144
|
+
<li><strong>Push-based heartbeat</strong> — à¹à¸•่ละ peer ส่ง beacon ทุภ30 วินาที ผ่านทั้ง file (เขียน timestamp ใหม่) à¹à¸¥à¸° UDP multicast</li>
|
|
145
|
+
<li><strong>Pull-based eviction</strong> — consumer เช็คà¸à¸²à¸¢à¸¸à¸‚à¸à¸‡ peer entry ถ้าเà¸à¸´à¸™ 90 วินาที (<code>PEER_STALE_TIMEOUT</code>) ถืà¸à¸§à¹ˆà¸² stale → evict</li>
|
|
146
|
+
<li><strong>3× heartbeat interval</strong> — timeout = 3 × heartbeat interval เป็นค่า default ในระบบ distributed systems (ให้โà¸à¸à¸²à¸ª retry à¸à¹ˆà¸à¸™å®£å¸ƒà¸•าย)</li>
|
|
147
|
+
<li><strong>Graceful shutdown</strong> — ตà¸à¸™ <code>/peer stop</code> จะส่ง UDP "offline" message + ลบไฟล์ peer ทันที ไม่ต้à¸à¸‡à¸£à¸ timeout</li>
|
|
148
|
+
</ul>
|
|
149
|
+
|
|
150
|
+
<h3>Random Port Binding</h3>
|
|
151
|
+
<p>PeerServer bind ที่ port 0 (OS เลืà¸à¸ port ว่างให้):</p>
|
|
152
|
+
<ul>
|
|
153
|
+
<li><strong>No port conflicts</strong> — ถ้าใช้ fixed port à¹à¸¥à¹‰à¸§à¸¡à¸µà¸«à¸¥à¸²à¸¢ instance จะชนà¸à¸±à¸™</li>
|
|
154
|
+
<li><strong>Security through obscurity</strong> — port สุ่มทำให้ attacker สà¹à¸à¸™à¸«à¸²à¸¢à¸²à¸à¸à¸§à¹ˆà¸² fixed port (à¹à¸•่ไม่ใช่ security หลัภ— security จริงà¸à¸¢à¸¹à¹ˆà¸—ี่ authentication layer)</li>
|
|
155
|
+
<li><strong>Discovery solves discovery</strong> — ไม่ต้à¸à¸‡à¸£à¸¹à¹‰ port ล่วงหน้า เพราะ discovery protocol จะบà¸à¸ port ให้เà¸à¸‡</li>
|
|
156
|
+
</ul>
|
|
157
|
+
|
|
158
|
+
<h3>Message Broadcasting</h3>
|
|
159
|
+
<p><code>peer_broadcast</code> ส่ง task ไปทุภpeer ที่เชื่à¸à¸¡à¸•่à¸à¸žà¸£à¹‰à¸à¸¡à¸à¸±à¸™:</p>
|
|
160
|
+
<ul>
|
|
161
|
+
<li>ไม่ใช่ UDP broadcast — ใช้ HTTP POST sequential ไปทีละ peer (fire-and-forget)</li>
|
|
162
|
+
<li>รà¸à¸‡à¸£à¸±à¸š parallel สูงสุด 8 peers ต่ภ1 batch</li>
|
|
163
|
+
<li>เหมาะสำหรับ: deploy to all machines, run tests everywhere, health check ทุภnode</li>
|
|
164
|
+
</ul>
|
|
165
|
+
|
|
166
|
+
<h2>à¸à¸‡à¸„์ประà¸à¸à¸šà¸«à¸¥à¸±à¸</h2>
|
|
167
|
+
|
|
168
|
+
<h3>PeerServer <code>src/peer/PeerServer.ts</code></h3>
|
|
169
|
+
<p>HTTP server ขนาดเล็à¸à¸—ี่เริ่มบน port สุ่ม (OS เลืà¸à¸à¹ƒà¸«à¹‰) เมื่à¸à¸£à¸±à¸™ <code>/peer share</code> à¹à¸•่ละ endpoint รà¸à¸‡à¸£à¸±à¸šà¸à¸²à¸£à¹‚ต้ตà¸à¸šà¹à¸šà¸šà¸•่างๆ:</p>
|
|
170
|
+
<table>
|
|
171
|
+
<tr><th>Endpoint</th><th>Method</th><th>หน้าที่</th></tr>
|
|
172
|
+
<tr><td><code>/peer-info</code></td><td>GET</td><td>ส่งข้à¸à¸¡à¸¹à¸¥ peer (hostname, IP, cwd, shell, platform)</td></tr>
|
|
173
|
+
<tr><td><code>/peer-msg</code></td><td>POST</td><td>รับข้à¸à¸„วาม chat จาภpeer à¸à¸·à¹ˆà¸™</td></tr>
|
|
174
|
+
<tr><td><code>/peer-todo</code></td><td>POST</td><td>รับ task ที่ถูà¸à¸¡à¸à¸šà¸«à¸¡à¸²à¸¢à¸ˆà¸²à¸ peer à¸à¸·à¹ˆà¸™</td></tr>
|
|
175
|
+
<tr><td><code>/peer-exec</code></td><td>POST</td><td>รันคำสั่ง shell à¹à¸¥à¸°à¸ªà¹ˆà¸‡à¸„ืน stdout/stderr</td></tr>
|
|
176
|
+
</table>
|
|
177
|
+
|
|
178
|
+
<h3>PeerDiscovery <code>src/peer/PeerDiscovery.ts</code></h3>
|
|
179
|
+
<p>à¸à¸¥à¹„à¸à¸„้นหา 2 วิธีทำงานร่วมà¸à¸±à¸™:</p>
|
|
180
|
+
<ul>
|
|
181
|
+
<li><strong>File-based</strong> — à¹à¸•่ละ instance เขียนไฟล์ <code>~/.claude/peers/{pid}.json</code> เà¸à¹‡à¸šà¸‚้à¸à¸¡à¸¹à¸¥ hostname, IP, port, shell, cwd instances à¸à¸·à¹ˆà¸™à¸à¹ˆà¸²à¸™à¹„ฟล์เพื่à¸à¸„้นหา peer ในเครื่à¸à¸‡à¹€à¸”ียวà¸à¸±à¸™</li>
|
|
182
|
+
<li><strong>UDP Multicast</strong> — ส่ง heartbeat beacon ไปที่ <code>239.255.37.37:42069</code> ทุภ30 วินาที ตà¸à¸šà¸à¸¥à¸±à¸š <code>clew-peer-query</code> ด้วย <code>clew-peer-info</code> ใช้ข้ามเครื่à¸à¸‡</li>
|
|
183
|
+
<li><strong>Stale eviction</strong> — Peer ที่ไม่เห็น 90 วินาที (<code>PEER_STALE_TIMEOUT</code>) จะถูà¸à¸¥à¸šà¸à¸±à¸•โนมัติ</li>
|
|
184
|
+
</ul>
|
|
185
|
+
|
|
186
|
+
<h3>PeerStore <code>src/peer/PeerStore.ts</code></h3>
|
|
187
|
+
<p>Singleton registry ในหน่วยความจำที่เà¸à¹‡à¸š peer ทั้งหมดที่รู้จัภ(ทั้งที่ค้นพบà¹à¸¥à¸°à¸—ี่ join เà¸à¸‡), ข้à¸à¸„วาม chat, todos, à¹à¸¥à¸° custom tags (ชื่à¸à¸—ี่à¸à¸³à¸«à¸™à¸”เà¸à¸‡, บทบาท)</p>
|
|
188
|
+
<ul>
|
|
189
|
+
<li><strong>Discovered peers</strong> — ถูà¸à¸¥à¸šà¸à¸±à¸•โนมัติเมื่ภstale</li>
|
|
190
|
+
<li><strong>Joined connections</strong> — คงà¸à¸¢à¸¹à¹ˆà¸–าวร ไม่ถูà¸à¸¥à¸šà¸à¸±à¸•โนมัติ</li>
|
|
191
|
+
<li><strong>Tags</strong> — ชื่à¸à¸—ี่à¸à¸³à¸«à¸™à¸”เà¸à¸‡à¹à¸¥à¸°à¸šà¸—บาทต่ภpeer</li>
|
|
192
|
+
</ul>
|
|
193
|
+
|
|
194
|
+
<h2>Discovery Protocol</h2>
|
|
195
|
+
<pre><code>DiscoveryMessage =
|
|
196
|
+
| { type: "clew-peer-query", version: 1 } // broadcast scan
|
|
197
|
+
| { type: "clew-peer-info", version: 1, // heartbeat / response
|
|
198
|
+
id: string, hostname: string, ip: string, port: number,
|
|
199
|
+
cwd: string, sessionId?: string, appVersion: string,
|
|
200
|
+
shell?: string, platform?: string, term?: string,
|
|
201
|
+
status: "online" | "offline" }
|
|
202
|
+
|
|
203
|
+
UDP port: 42069
|
|
204
|
+
Multicast IP: 239.255.37.37
|
|
205
|
+
Heartbeat: ทุภ30 วินาที
|
|
206
|
+
Stale after: 90 วินาที</code></pre>
|
|
207
|
+
|
|
208
|
+
<h2>คำสั่ง</h2>
|
|
209
|
+
<table>
|
|
210
|
+
<tr><th>คำสั่ง</th><th>คำà¸à¸˜à¸´à¸šà¸²à¸¢</th></tr>
|
|
211
|
+
<tr><td><code>/peer</code></td><td>เปิดเมนู interactive (share, join, discover, inbox)</td></tr>
|
|
212
|
+
<tr><td><code>/peer share</code></td><td>เริ่มประà¸à¸²à¸¨ instance นี้เป็น worker บน LAN</td></tr>
|
|
213
|
+
<tr><td><code>/peer stop</code></td><td>หยุดประà¸à¸²à¸¨à¹à¸¥à¸°à¸›à¸´à¸” peer server</td></tr>
|
|
214
|
+
<tr><td><code>/peer discover</code></td><td>สà¹à¸à¸™à¸«à¸² peers (file + UDP, timeout 3s)</td></tr>
|
|
215
|
+
<tr><td><code>/peer join <host> <port></code></td><td>เชื่à¸à¸¡à¸•่à¸à¸à¸±à¸š remote peer ด้วย host à¹à¸¥à¸° port</td></tr>
|
|
216
|
+
<tr><td><code>/peer name <ชื่à¸></code></td><td>à¸à¸³à¸«à¸™à¸”ชื่à¸à¸—ี่à¹à¸ªà¸”งสำหรับตัวเà¸à¸‡</td></tr>
|
|
217
|
+
<tr><td><code>/peer role <บทบาท></code></td><td>à¸à¸³à¸«à¸™à¸”บทบาท (builder, tester, deployer, ฯลฯ)</td></tr>
|
|
218
|
+
<tr><td><code>/peer inbox</code></td><td>ดูข้à¸à¸„วามà¹à¸¥à¸° todos ที่รà¸à¸à¸¢à¸¹à¹ˆà¸ˆà¸²à¸ peers</td></tr>
|
|
219
|
+
<tr><td><code>/peer disconnect <peer></code></td><td>ตัดà¸à¸²à¸£à¹€à¸Šà¸·à¹ˆà¸à¸¡à¸•่à¸à¸ˆà¸²à¸ peer ที่ระบุ</td></tr>
|
|
220
|
+
<tr><td><code>/peer todos</code></td><td>à¹à¸ªà¸”งรายà¸à¸²à¸£ task ทั้งหมดที่ได้รับ</td></tr>
|
|
221
|
+
<tr><td><code>/peer todo done <id></code></td><td>ทำเครื่à¸à¸‡à¸«à¸¡à¸²à¸¢ task ว่าเสร็จà¹à¸¥à¹‰à¸§</td></tr>
|
|
222
|
+
</table>
|
|
223
|
+
|
|
224
|
+
<h2>AI Tools</h2>
|
|
225
|
+
<p>เครื่à¸à¸‡à¸¡à¸·à¸à¸—ี่ AI agent ใช้เพื่à¸à¹‚ต้ตà¸à¸šà¸à¸±à¸šà¸£à¸°à¸šà¸š peer:</p>
|
|
226
|
+
<table>
|
|
227
|
+
<tr><th>Tool</th><th>คำà¸à¸˜à¸´à¸šà¸²à¸¢</th></tr>
|
|
228
|
+
<tr><td><code>peer_discover</code></td><td>สà¹à¸à¸™ LAN หา Clew workers</td></tr>
|
|
229
|
+
<tr><td><code>peer_join</code></td><td>เชื่à¸à¸¡à¸•่à¸à¸à¸±à¸š remote peer ผ่าน HTTP</td></tr>
|
|
230
|
+
<tr><td><code>peer_ping</code></td><td>ตรวจสà¸à¸šà¸§à¹ˆà¸² peer à¸à¸à¸™à¹„ลน์à¸à¸¢à¸¹à¹ˆà¸«à¸£à¸·à¸à¹„ม่ (GET /peer-info)</td></tr>
|
|
231
|
+
<tr><td><code>peer_info</code></td><td>ดึงข้à¸à¸¡à¸¹à¸¥ peer à¸à¸¢à¹ˆà¸²à¸‡à¸¥à¸°à¹€à¸à¸µà¸¢à¸”</td></tr>
|
|
232
|
+
<tr><td><code>peer_send_message</code></td><td>ส่งข้à¸à¸„วาม chat ไปที่ peer</td></tr>
|
|
233
|
+
<tr><td><code>peer_send_task</code></td><td>มà¸à¸šà¸«à¸¡à¸²à¸¢ task ให้ remote worker</td></tr>
|
|
234
|
+
<tr><td><code>peer_broadcast</code></td><td>ส่ง task ไปทุภpeer ที่เชื่à¸à¸¡à¸•่à¸à¸žà¸£à¹‰à¸à¸¡à¸à¸±à¸™</td></tr>
|
|
235
|
+
<tr><td><code>peer_run</code></td><td>รันคำสั่ง shell บน remote worker</td></tr>
|
|
236
|
+
<tr><td><code>peer_disconnect</code></td><td>ลบ peer à¸à¸à¸à¸ˆà¸²à¸à¸£à¸²à¸¢à¸à¸²à¸£à¹€à¸Šà¸·à¹ˆà¸à¸¡à¸•่à¸</td></tr>
|
|
237
|
+
<tr><td><code>peer_list_messages</code></td><td>à¹à¸ªà¸”งข้à¸à¸„วาม chat ทั้งหมดที่ได้รับ</td></tr>
|
|
238
|
+
<tr><td><code>peer_list_roles</code></td><td>à¹à¸ªà¸”งรายà¸à¸²à¸£ peers พร้à¸à¸¡à¸šà¸—บาท</td></tr>
|
|
239
|
+
<tr><td><code>peer_list_tasks</code></td><td>à¹à¸ªà¸”ง task ทั้งหมดà¹à¸¥à¸°à¸ªà¸–านะ</td></tr>
|
|
240
|
+
<tr><td><code>peer_set_name</code></td><td>à¸à¸³à¸«à¸™à¸”ชื่à¸à¸—ี่à¹à¸ªà¸”งให้ peer</td></tr>
|
|
241
|
+
<tr><td><code>peer_set_role</code></td><td>à¸à¸³à¸«à¸™à¸”บทบาทให้ peer</td></tr>
|
|
242
|
+
<tr><td><code>peer_share</code></td><td>เปิด/ปิด/เช็คสถานะà¸à¸²à¸£à¹à¸Šà¸£à¹Œ</td></tr>
|
|
243
|
+
</table>
|
|
244
|
+
|
|
245
|
+
<h2>à¸à¸²à¸£à¹€à¸Šà¸·à¹ˆà¸à¸¡à¸•่à¸à¸à¸±à¸š DAEMON</h2>
|
|
246
|
+
<p>Autonomous agent loop (<code>src/services/autonomous/agentLoop.ts</code>) เชื่à¸à¸¡à¸•่à¸à¸à¸±à¸š PeerServer เพื่à¸à¸£à¸±à¸š task จาภremote peers:</p>
|
|
247
|
+
<ul>
|
|
248
|
+
<li>ฟัง <code>/peer-todo</code> POST ตลà¸à¸” 24/7</li>
|
|
249
|
+
<li>จัดคิว task ที่ได้รับà¹à¸¥à¸°à¸›à¸£à¸°à¸¡à¸§à¸¥à¸œà¸¥à¹ƒà¸™ background workers</li>
|
|
250
|
+
<li>สูงสุด 3 workers พร้à¸à¸¡à¸à¸±à¸™; task timeout หลัง 30 นาที</li>
|
|
251
|
+
<li>ดูผลลัพธ์ผ่าน <code>/peer todos</code> à¹à¸¥à¸° <code>/tasks</code></li>
|
|
252
|
+
</ul>
|
|
253
|
+
|
|
254
|
+
<h2>ไฟล์ในระบบ</h2>
|
|
255
|
+
<table>
|
|
256
|
+
<tr><th>ไฟล์</th><th>หน้าที่</th></tr>
|
|
257
|
+
<tr><td><code>src/peer/PeerServer.ts</code></td><td>HTTP server พร้à¸à¸¡ endpoint /peer-info, /peer-msg, /peer-todo, /peer-exec</td></tr>
|
|
258
|
+
<tr><td><code>src/peer/PeerDiscovery.ts</code></td><td>ค้นหา peer ผ่านไฟล์ + UDP multicast บน LAN</td></tr>
|
|
259
|
+
<tr><td><code>src/peer/PeerStore.ts</code></td><td>Singleton registry ใน memory (peers, messages, todos, tags)</td></tr>
|
|
260
|
+
<tr><td><code>src/peer/types.ts</code></td><td>Shared types à¹à¸¥à¸°à¸„่าคงที่ขà¸à¸‡ protocol</td></tr>
|
|
261
|
+
<tr><td><code>src/commands/peer/</code></td><td>Interactive peer menu à¹à¸¥à¸° CLI commands</td></tr>
|
|
262
|
+
<tr><td><code>src/tools/Peer*Tool/</code></td><td>16 AI agent tools สำหรับ peer operations</td></tr>
|
|
263
|
+
<tr><td><code>src/services/autonomous/agentLoop.ts</code></td><td>Daemon integration: รับ remote tasks ผ่าน PeerServer</td></tr>
|
|
264
|
+
</table>
|
|
265
|
+
|
|
266
|
+
<footer class="footer">
|
|
267
|
+
<span>Clew Code v0.2.7 — Open Source</span>
|
|
268
|
+
<div class="footer-links">
|
|
269
|
+
<a href="https://github.com/ClewCode/ClewCode">GitHub</a>
|
|
270
|
+
<a href="https://github.com/ClewCode/ClewCode/issues">Issues</a>
|
|
271
|
+
</div>
|
|
272
|
+
</footer>
|
|
273
|
+
</main>
|
|
274
|
+
<nav class="toc-sidebar"></nav>
|
|
275
|
+
</div>
|
|
276
|
+
</div>
|
|
277
|
+
<script src="js/main.js"></script>
|
|
278
|
+
</body>
|
|
279
|
+
</html>
|
|
280
|
+
|