@scp-dev/broker 0.1.3 → 0.1.4
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/dist/index.d.ts +28 -115
- package/dist/index.js +94 -514
- package/package.json +3 -3
- package/src/client.ts +2 -2
- package/src/index.ts +3 -2
- package/src/judge-gate.ts +14 -545
- package/src/server.ts +51 -18
package/src/server.ts
CHANGED
|
@@ -26,7 +26,7 @@ import {
|
|
|
26
26
|
stripControl,
|
|
27
27
|
} from "@scp-dev/core"
|
|
28
28
|
import { FederatedCore } from "@scp-dev/hub"
|
|
29
|
-
import { type Bridge, type BridgeConfig, remoteSend, startBridge } from "./bridge"
|
|
29
|
+
import { type Bridge, type BridgeConfig, type BridgeHooks, remoteSend, startBridge } from "./bridge"
|
|
30
30
|
import { type Deliver, type JudgeClientFactory, JudgeGate } from "./judge-gate"
|
|
31
31
|
import { releaseBrokerLock, takeBrokerLock } from "./lock"
|
|
32
32
|
import { encodeLine, LineReader, LineWriter, MAX_LINE } from "./ndjson"
|
|
@@ -60,6 +60,8 @@ export interface BrokerOptions {
|
|
|
60
60
|
bridge?: BridgeConfig | undefined
|
|
61
61
|
/** reads the bridge configuration again for bridge_relink; one that appeared or names another hub or machine restarts the broker */
|
|
62
62
|
bridgeConfig?: (() => Promise<BridgeConfig | undefined>) | undefined
|
|
63
|
+
/** experimental: another link to other machines in place of the hub's, started only when `bridge` is unset */
|
|
64
|
+
link?: LinkFactory | undefined
|
|
63
65
|
/** how often presence that decays without a request (a hook-only endpoint) is checked for changes (default 60 s) */
|
|
64
66
|
presenceEveryMs?: number | undefined
|
|
65
67
|
/** where a judge model's key_env is read (default the broker's environment) */
|
|
@@ -68,6 +70,15 @@ export interface BrokerOptions {
|
|
|
68
70
|
judgeClient?: JudgeClientFactory | undefined
|
|
69
71
|
}
|
|
70
72
|
|
|
73
|
+
/** what a link gets from the broker: its core, the remote hooks a bridge gets, and a send as a local endpoint or a mirrored one through the judge */
|
|
74
|
+
export interface LinkContext {
|
|
75
|
+
core: FederatedCore
|
|
76
|
+
hooks: BridgeHooks
|
|
77
|
+
send: Deliver
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export type LinkFactory = (context: LinkContext) => Bridge
|
|
81
|
+
|
|
71
82
|
export interface Broker {
|
|
72
83
|
socket: string
|
|
73
84
|
core: FederatedCore
|
|
@@ -88,6 +99,8 @@ interface Connection {
|
|
|
88
99
|
tty?: boolean
|
|
89
100
|
/** the connection holds the human inbox */
|
|
90
101
|
human?: boolean
|
|
102
|
+
/** a human hello bound to the agent of this shell's lineage said a terminal was attached: the human at that agent's shell */
|
|
103
|
+
terminal?: boolean
|
|
91
104
|
/**
|
|
92
105
|
* the lineage endpoint of an observer, or of a hello bound to it: the connection sees and acts with that endpoint's view,
|
|
93
106
|
* never widening unless `own`, and never counts as connected
|
|
@@ -269,21 +282,17 @@ export async function startBroker(options: BrokerOptions): Promise<Broker> {
|
|
|
269
282
|
}
|
|
270
283
|
for (const id of known.keys()) if (!live.has(id)) known.delete(id)
|
|
271
284
|
}, options.presenceEveryMs ?? PRESENCE_MS)
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
loadPolicy()
|
|
281
|
-
return policyError
|
|
282
|
-
},
|
|
283
|
-
log,
|
|
284
|
-
},
|
|
285
|
-
})
|
|
285
|
+
const remoteHooks: BridgeHooks = {
|
|
286
|
+
wake,
|
|
287
|
+
fleet: notifyFleet,
|
|
288
|
+
policyError: () => {
|
|
289
|
+
loadPolicy()
|
|
290
|
+
return policyError
|
|
291
|
+
},
|
|
292
|
+
log,
|
|
286
293
|
}
|
|
294
|
+
if (options.bridge) bridge = startBridge({ ...options.bridge, core, onRemote: remoteHooks })
|
|
295
|
+
else if (options.link) bridge = options.link({ core, hooks: remoteHooks, send: (from, params, origin) => judge.send(from, params, origin, deliver) })
|
|
287
296
|
|
|
288
297
|
/** links the bridge again with the credential on disk; a configuration this broker cannot take without a restart stops it */
|
|
289
298
|
async function relink(): Promise<object> {
|
|
@@ -697,18 +706,24 @@ export async function startBroker(options: BrokerOptions): Promise<Broker> {
|
|
|
697
706
|
}
|
|
698
707
|
const join = params.join
|
|
699
708
|
if (join !== undefined && (typeof join !== "object" || join === null || Array.isArray(join)))
|
|
700
|
-
throw new RelayError("invalid_params", "join is {handle?}")
|
|
709
|
+
throw new RelayError("invalid_params", "join is {handle?, under?}")
|
|
710
|
+
const under = (join as { under?: unknown } | undefined)?.under
|
|
701
711
|
const before = new Map(core.list({ all: true }).map((view) => [view.id, view]))
|
|
702
712
|
const { endpoint, subagent, created } =
|
|
703
713
|
join === undefined
|
|
704
714
|
? core.claim((params.claim ?? {}) as Claim, { transient: true })
|
|
705
|
-
: {
|
|
715
|
+
: {
|
|
716
|
+
endpoint: core.joinSession((params.claim ?? {}) as Claim, (join as { handle?: unknown }).handle, under),
|
|
717
|
+
subagent: undefined,
|
|
718
|
+
created: undefined,
|
|
719
|
+
}
|
|
706
720
|
s.data.endpoint = endpoint.id
|
|
707
721
|
s.data.transient = true
|
|
708
722
|
const key = (params.claim as Claim | undefined)?.process
|
|
709
723
|
if (typeof key === "string") s.data.process = key
|
|
710
724
|
const was = before.get(endpoint.id)?.address
|
|
711
725
|
if (join !== undefined) membership(endpoint, core.sessionOf(endpoint.id), true, was?.slice(was.lastIndexOf("/") + 1))
|
|
726
|
+
if (under !== undefined && before.get(endpoint.id)?.parent !== endpoint.parent) bridge?.resync()
|
|
712
727
|
else if ((before.get(endpoint.id)?.presence ?? "offline") !== endpoint.presence) presenceChanged(endpoint)
|
|
713
728
|
return {
|
|
714
729
|
endpoint,
|
|
@@ -741,7 +756,10 @@ export async function startBroker(options: BrokerOptions): Promise<Broker> {
|
|
|
741
756
|
const claim = params.claim as Claim | undefined
|
|
742
757
|
if (params.human === true && params.resume === undefined && params.lineage !== undefined) {
|
|
743
758
|
const placed = core.placeLineage(params.lineage)
|
|
744
|
-
if (placed)
|
|
759
|
+
if (placed) {
|
|
760
|
+
s.data.terminal = params.tty === true
|
|
761
|
+
return bindLineage(s, placed, params)
|
|
762
|
+
}
|
|
745
763
|
} else if (params.human !== true && params.resume === undefined && claim?.process === undefined && typeof claim?.handle === "string") {
|
|
746
764
|
const placed = claim.lineage === undefined ? null : core.placeLineage(claim.lineage, claim.handle)
|
|
747
765
|
if (placed && (claim.grant === undefined || core.grantBinds(claim.grant, placed))) {
|
|
@@ -858,6 +876,21 @@ export async function startBroker(options: BrokerOptions): Promise<Broker> {
|
|
|
858
876
|
membership(renamed.endpoint, renamed.session, true, renamed.from)
|
|
859
877
|
return renamed.endpoint
|
|
860
878
|
}
|
|
879
|
+
case "adopt": {
|
|
880
|
+
const actor = actorOf(s)
|
|
881
|
+
const adopted = core.adopt(
|
|
882
|
+
actor,
|
|
883
|
+
{ endpoint: params.endpoint, ...(params.parent !== undefined ? { parent: params.parent } : {}) },
|
|
884
|
+
{ tty: actor.kind === "endpoint" && s.data.terminal === true },
|
|
885
|
+
)
|
|
886
|
+
bridge?.resync()
|
|
887
|
+
return adopted
|
|
888
|
+
}
|
|
889
|
+
case "unlink": {
|
|
890
|
+
const unlinked = core.unlink(actorOf(s), params.endpoint !== undefined ? { endpoint: params.endpoint } : {})
|
|
891
|
+
bridge?.resync()
|
|
892
|
+
return unlinked
|
|
893
|
+
}
|
|
861
894
|
case "leave": {
|
|
862
895
|
const left = core.leaveSession(actorOf(s), params.endpoint !== undefined ? { endpoint: params.endpoint } : {})
|
|
863
896
|
for (const c of connections) if (c.data.endpoint === left.endpoint.id && !c.data.transient && !c.data.lineage && c !== s) c.end()
|