dsh-device-fleet 0.1.0-dev.5 → 0.1.0-dev.6
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/package.json +1 -1
- package/src/host/store.js +27 -1
- package/src/host/tools.js +2 -1
- package/src/net/fleet-node.js +8 -0
- package/src/plugin/index.js +14 -3
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-device-fleet",
|
|
3
|
-
"version": "0.1.0-dev.
|
|
3
|
+
"version": "0.1.0-dev.6",
|
|
4
4
|
"description": "DeepSeek Harness device fleet: enroll devices, direct-call remote tools, shared Rooms with lazy-woken agents, and delegated subagents across devices.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/plugin/index.js",
|
package/src/host/store.js
CHANGED
|
@@ -50,6 +50,7 @@ export function loadState({ dshHome, dataDir, name = 'device' } = {}) {
|
|
|
50
50
|
|
|
51
51
|
const roster = readJson(path.join(dir, 'roster.json'), [])
|
|
52
52
|
const config = readJson(path.join(dir, 'config.json'), {})
|
|
53
|
+
const peerHints = readJson(path.join(dir, 'peer-hints.json'), [])
|
|
53
54
|
let enrollment = readJson(path.join(dir, 'enrollment.json'), null)
|
|
54
55
|
let pendingEnrollment = readJson(path.join(dir, 'enrollment.pending.json'), null)
|
|
55
56
|
|
|
@@ -75,7 +76,32 @@ export function loadState({ dshHome, dataDir, name = 'device' } = {}) {
|
|
|
75
76
|
}
|
|
76
77
|
}
|
|
77
78
|
|
|
78
|
-
return {
|
|
79
|
+
return {
|
|
80
|
+
dataDir: dir,
|
|
81
|
+
identity,
|
|
82
|
+
fleet,
|
|
83
|
+
roster: Array.isArray(roster) ? roster : [],
|
|
84
|
+
config,
|
|
85
|
+
enrollment,
|
|
86
|
+
pendingEnrollment,
|
|
87
|
+
peerHints: Array.isArray(peerHints) ? peerHints : [],
|
|
88
|
+
createdFleet,
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/** Persist one manually supplied peer address (from fleet_connect) for reconnects. */
|
|
93
|
+
export function savePeerHint(state, hint) {
|
|
94
|
+
if (!hint || typeof hint.host !== 'string' || !Number.isInteger(hint.port) || hint.port <= 0) return
|
|
95
|
+
const entry = {
|
|
96
|
+
host: hint.host,
|
|
97
|
+
port: hint.port,
|
|
98
|
+
...(typeof hint.deviceId === 'string' ? { deviceId: hint.deviceId } : {}),
|
|
99
|
+
}
|
|
100
|
+
const hints = Array.isArray(state.peerHints) ? state.peerHints : []
|
|
101
|
+
const exists = hints.some((item) => item.host === entry.host && item.port === entry.port && item.deviceId === entry.deviceId)
|
|
102
|
+
if (!exists) hints.push(entry)
|
|
103
|
+
state.peerHints = hints
|
|
104
|
+
writeJsonAtomic(path.join(state.dataDir, 'peer-hints.json'), hints)
|
|
79
105
|
}
|
|
80
106
|
|
|
81
107
|
/** Queue an invite for the next plugin load (the running node keeps its fleet). */
|
package/src/host/tools.js
CHANGED
|
@@ -22,7 +22,7 @@ function fail(error) {
|
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
/** Pure handlers; wrapped into ToolDefinitions by `createFleetToolDefinitions`. */
|
|
25
|
-
export function createFleetHandlers({ node, client = new DirectCallClient({ node }), pairing = null }) {
|
|
25
|
+
export function createFleetHandlers({ node, client = new DirectCallClient({ node }), pairing = null, onPeerHint = null }) {
|
|
26
26
|
return {
|
|
27
27
|
async fleet_status() {
|
|
28
28
|
if (typeof node.status !== 'function') return { text: 'Error: [unsupported] status is unavailable' }
|
|
@@ -34,6 +34,7 @@ export function createFleetHandlers({ node, client = new DirectCallClient({ node
|
|
|
34
34
|
try {
|
|
35
35
|
node.addPeerHint?.({ host, port })
|
|
36
36
|
const peer = await node.connectAddress(host, port)
|
|
37
|
+
await onPeerHint?.({ host, port })
|
|
37
38
|
return { text: `connected to ${peer.deviceId} (${peer.name ?? 'device'}) at ${host}:${port}` }
|
|
38
39
|
} catch (error) {
|
|
39
40
|
return fail(error)
|
package/src/net/fleet-node.js
CHANGED
|
@@ -281,6 +281,14 @@ export class FleetNode extends EventEmitter {
|
|
|
281
281
|
if (current === channel) this.channels.delete(channel.peer.deviceId)
|
|
282
282
|
this.rejectPendingForChannel(channel, Object.assign(new Error('peer connection closed'), { code: 'peer-offline' }))
|
|
283
283
|
this.emit('peer-disconnected', { deviceId: channel.peer?.deviceId, via })
|
|
284
|
+
// Recover automatically: the peer may have restarted or moved; retry the
|
|
285
|
+
// known addresses instead of waiting for the next process start.
|
|
286
|
+
if (!this.closed && this.channels.size === 0 && this.peerHints.length > 0) {
|
|
287
|
+
clearTimeout(this.hintTimer)
|
|
288
|
+
this.hintTimer = null
|
|
289
|
+
this.hintAttempt = 0
|
|
290
|
+
this.scheduleHintConnects()
|
|
291
|
+
}
|
|
284
292
|
})
|
|
285
293
|
}
|
|
286
294
|
|
package/src/plugin/index.js
CHANGED
|
@@ -17,7 +17,7 @@ import { DelegationHost } from '../host/delegation.js'
|
|
|
17
17
|
import { ExecutionSlot, DEFAULT_SHELL_TOOL } from '../host/execution-slot.js'
|
|
18
18
|
import { ToolPolicy } from '../host/tool-policy.js'
|
|
19
19
|
import { createFleetHandlers, createFleetToolDefinitions } from '../host/tools.js'
|
|
20
|
-
import { accountMatches, createInvite, loadState, saveRoster, writePendingEnrollment } from '../host/store.js'
|
|
20
|
+
import { accountMatches, createInvite, loadState, savePeerHint, saveRoster, writePendingEnrollment } from '../host/store.js'
|
|
21
21
|
|
|
22
22
|
export const name = 'device-fleet'
|
|
23
23
|
|
|
@@ -147,7 +147,7 @@ export function apply(ctx, config) {
|
|
|
147
147
|
discovery: config.discovery ? true : false,
|
|
148
148
|
relay: config.relay ?? state.enrollment?.relay ?? null,
|
|
149
149
|
roster: state.roster,
|
|
150
|
-
peerHints: state.enrollment?.endpoints ?? [],
|
|
150
|
+
peerHints: [...(state.enrollment?.endpoints ?? []), ...(state.peerHints ?? [])],
|
|
151
151
|
pluginVersion,
|
|
152
152
|
dshVersion,
|
|
153
153
|
capabilities: CAPABILITIES,
|
|
@@ -268,7 +268,18 @@ export function apply(ctx, config) {
|
|
|
268
268
|
writePendingEnrollment(state, code)
|
|
269
269
|
},
|
|
270
270
|
}
|
|
271
|
-
const handlers = createFleetHandlers({
|
|
271
|
+
const handlers = createFleetHandlers({
|
|
272
|
+
node,
|
|
273
|
+
client,
|
|
274
|
+
pairing,
|
|
275
|
+
onPeerHint: async (hint) => {
|
|
276
|
+
try {
|
|
277
|
+
savePeerHint(state, hint)
|
|
278
|
+
} catch (error) {
|
|
279
|
+
logger.warn?.(`[device-fleet] cannot persist peer hint: ${String(error?.message ?? error)}`)
|
|
280
|
+
}
|
|
281
|
+
},
|
|
282
|
+
})
|
|
272
283
|
const definitions = createFleetToolDefinitions({ defineTool, handlers })
|
|
273
284
|
const wrapped = withManual(definitions.map((definition) => ({ ...definition, execute: safe(definition.execute) })))
|
|
274
285
|
for (const definition of wrapped) ctx.tools.register(definition)
|