@knowlearning/agents 0.3.16 → 0.3.18
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/agents/generic.js +42 -16
- package/agents/node.js +1 -1
- package/package.json +1 -1
package/agents/generic.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import MutableProxy from '../persistence/json.js'
|
|
2
2
|
|
|
3
|
+
const HEARTBEAT_TIMEOUT = 10000
|
|
4
|
+
|
|
3
5
|
// transform our custom path implementation to the standard JSONPatch path
|
|
4
6
|
function standardJSONPatch(patch) {
|
|
5
7
|
return patch.map(p => {
|
|
@@ -34,6 +36,8 @@ export default function Agent({ host, token, WebSocket, protocol='ws', uuid, fet
|
|
|
34
36
|
let failedConnections = 0
|
|
35
37
|
let mode = 'normal'
|
|
36
38
|
const environmentPromise = new Promise(r => resolveEnvironment = r)
|
|
39
|
+
let lastSentSI = -1
|
|
40
|
+
let lastHeartbeat
|
|
37
41
|
|
|
38
42
|
const sessionData = new MutableProxy({}, patch => queueMessage({scope: 'sessions', patch}))
|
|
39
43
|
|
|
@@ -44,6 +48,9 @@ export default function Agent({ host, token, WebSocket, protocol='ws', uuid, fet
|
|
|
44
48
|
patches: {}
|
|
45
49
|
}
|
|
46
50
|
|
|
51
|
+
log('INITIALIZING AGENT CONNECTION')
|
|
52
|
+
initWS()
|
|
53
|
+
|
|
47
54
|
function log() {
|
|
48
55
|
if (mode === 'debug') console.log(...arguments)
|
|
49
56
|
}
|
|
@@ -53,11 +60,6 @@ export default function Agent({ host, token, WebSocket, protocol='ws', uuid, fet
|
|
|
53
60
|
if (watcherIndex > -1) watchers[key].splice(watcherIndex, 1)
|
|
54
61
|
else console.warn('TRIED TO REMOVE WATCHER THAT DOES NOT EXIST')
|
|
55
62
|
}
|
|
56
|
-
|
|
57
|
-
log('INITIALIZING AGENT CONNECTION')
|
|
58
|
-
initWS()
|
|
59
|
-
|
|
60
|
-
let lastSentSI = -1
|
|
61
63
|
function flushMessageQueue() {
|
|
62
64
|
// TODO: probably want to make this loop async so we don't try and push more to
|
|
63
65
|
// a closed connection
|
|
@@ -78,16 +80,43 @@ export default function Agent({ host, token, WebSocket, protocol='ws', uuid, fet
|
|
|
78
80
|
flushMessageQueue()
|
|
79
81
|
}
|
|
80
82
|
|
|
83
|
+
function checkHeartbeat() {
|
|
84
|
+
clearTimeout(lastHeartbeat)
|
|
85
|
+
lastHeartbeat = setTimeout(
|
|
86
|
+
() => {
|
|
87
|
+
log('CLOSING DUE TO HEARTBEAT TIMEOUT')
|
|
88
|
+
restartConnection()
|
|
89
|
+
},
|
|
90
|
+
HEARTBEAT_TIMEOUT
|
|
91
|
+
)
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
async function restartConnection() {
|
|
95
|
+
if (authed) log(`CLOSED DOMAIN ${domain} USER ${user} SESSION ${session} CONNECTION TO SERVER ${server}`)
|
|
96
|
+
authed = false
|
|
97
|
+
ws.onmessage = () => {} // needs to be a no-op since a closing ws can still get messages
|
|
98
|
+
if (!disconnected) {
|
|
99
|
+
await new Promise(r => setTimeout(r, Math.min(1000, failedConnections * 100)))
|
|
100
|
+
failedConnections += 1
|
|
101
|
+
initWS() // TODO: don't do this if we are purposefully unloading...
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
81
105
|
function initWS() {
|
|
82
106
|
ws = new WebSocket(`${protocol}://${host}`)
|
|
83
107
|
|
|
108
|
+
let opened = false
|
|
84
109
|
ws.onopen = async () => {
|
|
110
|
+
opened = true
|
|
85
111
|
log('AUTHORIZING NEWLY OPENED WS FOR SESSION:', session)
|
|
86
112
|
failedConnections = 0
|
|
87
113
|
ws.send(JSON.stringify({ token: await token(), session }))
|
|
88
114
|
}
|
|
89
115
|
|
|
90
116
|
ws.onmessage = async ({ data }) => {
|
|
117
|
+
checkHeartbeat()
|
|
118
|
+
if (data.length === 0) return // heartbeat
|
|
119
|
+
|
|
91
120
|
try {
|
|
92
121
|
log('handling message', disconnected, authed)
|
|
93
122
|
const message = JSON.parse(data)
|
|
@@ -151,19 +180,16 @@ export default function Agent({ host, token, WebSocket, protocol='ws', uuid, fet
|
|
|
151
180
|
}
|
|
152
181
|
|
|
153
182
|
ws.onerror = async error => {
|
|
154
|
-
log('WS CONNECTION ERROR', error.message)
|
|
183
|
+
log('WS CONNECTION ERROR', error.message, opened)
|
|
184
|
+
if (!opened) restartConnection() // onclose won't trigger if never opened
|
|
155
185
|
}
|
|
156
186
|
|
|
157
|
-
ws.onclose = async
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
ws.onmessage = () => {} // needs to be a no-op since a closing ws can still get messages
|
|
161
|
-
if (!disconnected) {
|
|
162
|
-
await new Promise(r => setTimeout(r, Math.min(1000, failedConnections * 100)))
|
|
163
|
-
failedConnections += 1
|
|
164
|
-
initWS() // TODO: don't do this if we are purposefully unloading...
|
|
165
|
-
}
|
|
187
|
+
ws.onclose = async error => {
|
|
188
|
+
log('WS CLOSURE', error.message, opened)
|
|
189
|
+
restartConnection()
|
|
166
190
|
}
|
|
191
|
+
|
|
192
|
+
checkHeartbeat()
|
|
167
193
|
}
|
|
168
194
|
|
|
169
195
|
function environment() { return environmentPromise }
|
|
@@ -205,7 +231,7 @@ export default function Agent({ host, token, WebSocket, protocol='ws', uuid, fet
|
|
|
205
231
|
// POSSIBLE: wait for most recent interaction update response for key k that we sent...
|
|
206
232
|
await lastInteractionUpdateForWatchedScope[k]
|
|
207
233
|
// TODO: probably something like await updateMessageReceivedForLastInteractionWeSentForKey[k]
|
|
208
|
-
resolveState(states[k])
|
|
234
|
+
resolveState(structuredClone(await states[k]))
|
|
209
235
|
})
|
|
210
236
|
|
|
211
237
|
promise.watch = fn => {
|
package/agents/node.js
CHANGED