@nanobpm/agentic 0.1.0
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 +22 -0
- package/dist/blackboard/family.d.ts +40 -0
- package/dist/blackboard/family.js +151 -0
- package/dist/blackboard/index.d.ts +21 -0
- package/dist/blackboard/index.js +19 -0
- package/dist/blackboard/schema.d.ts +30 -0
- package/dist/blackboard/schema.js +42 -0
- package/dist/blackboard/store.d.ts +138 -0
- package/dist/blackboard/store.js +216 -0
- package/dist/blackboard/test-db.d.ts +5 -0
- package/dist/blackboard/test-db.js +42 -0
- package/dist/channel/auth.d.ts +41 -0
- package/dist/channel/auth.js +67 -0
- package/dist/channel/clock.d.ts +11 -0
- package/dist/channel/clock.js +4 -0
- package/dist/channel/connection.d.ts +75 -0
- package/dist/channel/connection.js +14 -0
- package/dist/channel/dispatch.d.ts +46 -0
- package/dist/channel/dispatch.js +86 -0
- package/dist/channel/hub.d.ts +78 -0
- package/dist/channel/hub.js +157 -0
- package/dist/channel/index.d.ts +27 -0
- package/dist/channel/index.js +20 -0
- package/dist/channel/registry.d.ts +68 -0
- package/dist/channel/registry.js +84 -0
- package/dist/channel/ws-transport.d.ts +23 -0
- package/dist/channel/ws-transport.js +178 -0
- package/dist/cockpit/boot.d.ts +68 -0
- package/dist/cockpit/boot.js +202 -0
- package/dist/cockpit/fake-dom.d.ts +37 -0
- package/dist/cockpit/fake-dom.js +73 -0
- package/dist/cockpit/index.d.ts +27 -0
- package/dist/cockpit/index.js +27 -0
- package/dist/cockpit/relay-client.d.ts +52 -0
- package/dist/cockpit/relay-client.js +192 -0
- package/dist/cockpit/render.d.ts +58 -0
- package/dist/cockpit/render.js +122 -0
- package/dist/cockpit/terminal-session.d.ts +95 -0
- package/dist/cockpit/terminal-session.js +123 -0
- package/dist/cockpit/view.d.ts +79 -0
- package/dist/cockpit/view.js +58 -0
- package/dist/demand/c8-rest.d.ts +77 -0
- package/dist/demand/c8-rest.js +123 -0
- package/dist/demand/index.d.ts +24 -0
- package/dist/demand/index.js +24 -0
- package/dist/demand/model.d.ts +68 -0
- package/dist/demand/model.js +118 -0
- package/dist/demand/taskdef.d.ts +40 -0
- package/dist/demand/taskdef.js +67 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.js +17 -0
- package/dist/presence/family.d.ts +40 -0
- package/dist/presence/family.js +166 -0
- package/dist/presence/index.d.ts +19 -0
- package/dist/presence/index.js +17 -0
- package/dist/presence/schema.d.ts +20 -0
- package/dist/presence/schema.js +32 -0
- package/dist/presence/store.d.ts +130 -0
- package/dist/presence/store.js +191 -0
- package/dist/presence/test-db.d.ts +5 -0
- package/dist/presence/test-db.js +42 -0
- package/dist/protocol/conformance/frames.d.ts +24 -0
- package/dist/protocol/conformance/frames.js +116 -0
- package/dist/protocol/conformance/index.d.ts +13 -0
- package/dist/protocol/conformance/index.js +13 -0
- package/dist/protocol/conformance/malformed.d.ts +14 -0
- package/dist/protocol/conformance/malformed.js +44 -0
- package/dist/protocol/conformance/tokens.d.ts +19 -0
- package/dist/protocol/conformance/tokens.js +49 -0
- package/dist/protocol/conformance/vocab.d.ts +23 -0
- package/dist/protocol/conformance/vocab.js +97 -0
- package/dist/protocol/families.d.ts +32 -0
- package/dist/protocol/families.js +45 -0
- package/dist/protocol/frame.d.ts +45 -0
- package/dist/protocol/frame.js +114 -0
- package/dist/protocol/hex.d.ts +7 -0
- package/dist/protocol/hex.js +26 -0
- package/dist/protocol/index.d.ts +23 -0
- package/dist/protocol/index.js +23 -0
- package/dist/protocol/lanes.d.ts +36 -0
- package/dist/protocol/lanes.js +40 -0
- package/dist/protocol/payloads.d.ts +64 -0
- package/dist/protocol/payloads.js +122 -0
- package/dist/protocol/token.d.ts +34 -0
- package/dist/protocol/token.js +81 -0
- package/dist/protocol/vocab/schema.d.ts +51 -0
- package/dist/protocol/vocab/schema.js +218 -0
- package/dist/relay/incarnation.d.ts +17 -0
- package/dist/relay/incarnation.js +50 -0
- package/dist/relay/index.d.ts +25 -0
- package/dist/relay/index.js +22 -0
- package/dist/relay/relay-family.d.ts +68 -0
- package/dist/relay/relay-family.js +272 -0
- package/dist/relay/ring.d.ts +49 -0
- package/dist/relay/ring.js +105 -0
- package/dist/relay/scheduler.d.ts +72 -0
- package/dist/relay/scheduler.js +180 -0
- package/dist/relay/validate.d.ts +29 -0
- package/dist/relay/validate.js +39 -0
- package/dist/transcript/index.d.ts +18 -0
- package/dist/transcript/index.js +17 -0
- package/dist/transcript/schema.d.ts +32 -0
- package/dist/transcript/schema.js +48 -0
- package/dist/transcript/store.d.ts +192 -0
- package/dist/transcript/store.js +347 -0
- package/dist/transcript/test-db.d.ts +5 -0
- package/dist/transcript/test-db.js +41 -0
- package/dist/vocab/core-vocab.d.ts +26 -0
- package/dist/vocab/core-vocab.js +67 -0
- package/dist/vocab/diversity.d.ts +78 -0
- package/dist/vocab/diversity.js +89 -0
- package/dist/vocab/index.d.ts +22 -0
- package/dist/vocab/index.js +22 -0
- package/dist/vocab/merge.d.ts +9 -0
- package/dist/vocab/merge.js +104 -0
- package/dist/vocab/requires.d.ts +49 -0
- package/dist/vocab/requires.js +107 -0
- package/dist/vocab/resolver.d.ts +62 -0
- package/dist/vocab/resolver.js +149 -0
- package/dist/vocab/serve.d.ts +39 -0
- package/dist/vocab/serve.js +36 -0
- package/package.json +108 -0
- package/page/cockpit.css +114 -0
- package/page/cockpit.page.json +33 -0
- package/page/embed.html +40 -0
- package/page/mount.js +78 -0
- package/page/standalone.html +43 -0
- package/src/blackboard/family.test.ts +280 -0
- package/src/blackboard/family.ts +208 -0
- package/src/blackboard/index.ts +42 -0
- package/src/blackboard/schema.test.ts +60 -0
- package/src/blackboard/schema.ts +44 -0
- package/src/blackboard/store.test.ts +189 -0
- package/src/blackboard/store.ts +331 -0
- package/src/blackboard/test-db.ts +47 -0
- package/src/channel/auth.test.ts +64 -0
- package/src/channel/auth.ts +101 -0
- package/src/channel/clock.ts +14 -0
- package/src/channel/connection.ts +77 -0
- package/src/channel/dispatch.test.ts +83 -0
- package/src/channel/dispatch.ts +102 -0
- package/src/channel/hub.test.ts +335 -0
- package/src/channel/hub.ts +222 -0
- package/src/channel/index.ts +55 -0
- package/src/channel/registry.test.ts +73 -0
- package/src/channel/registry.ts +137 -0
- package/src/channel/ws-transport.test.ts +234 -0
- package/src/channel/ws-transport.ts +212 -0
- package/src/cockpit/boot.test.ts +374 -0
- package/src/cockpit/boot.ts +280 -0
- package/src/cockpit/fake-dom.ts +90 -0
- package/src/cockpit/index.ts +63 -0
- package/src/cockpit/relay-client.test.ts +359 -0
- package/src/cockpit/relay-client.ts +234 -0
- package/src/cockpit/render.test.ts +149 -0
- package/src/cockpit/render.ts +194 -0
- package/src/cockpit/terminal-session.test.ts +252 -0
- package/src/cockpit/terminal-session.ts +194 -0
- package/src/cockpit/view.test.ts +117 -0
- package/src/cockpit/view.ts +140 -0
- package/src/demand/c8-rest.test.ts +140 -0
- package/src/demand/c8-rest.ts +167 -0
- package/src/demand/index.ts +42 -0
- package/src/demand/model.test.ts +197 -0
- package/src/demand/model.ts +183 -0
- package/src/demand/taskdef.test.ts +85 -0
- package/src/demand/taskdef.ts +78 -0
- package/src/index.ts +17 -0
- package/src/presence/family.test.ts +252 -0
- package/src/presence/family.ts +205 -0
- package/src/presence/index.ts +26 -0
- package/src/presence/schema.test.ts +53 -0
- package/src/presence/schema.ts +34 -0
- package/src/presence/store.test.ts +190 -0
- package/src/presence/store.ts +287 -0
- package/src/presence/test-db.test.ts +57 -0
- package/src/presence/test-db.ts +47 -0
- package/src/protocol/conformance/corpus.test.ts +66 -0
- package/src/protocol/conformance/frames.ts +142 -0
- package/src/protocol/conformance/index.ts +29 -0
- package/src/protocol/conformance/malformed.ts +59 -0
- package/src/protocol/conformance/tokens.ts +70 -0
- package/src/protocol/conformance/vocab.ts +122 -0
- package/src/protocol/families.ts +54 -0
- package/src/protocol/frame.test.ts +116 -0
- package/src/protocol/frame.ts +171 -0
- package/src/protocol/hex.ts +28 -0
- package/src/protocol/index.ts +84 -0
- package/src/protocol/lanes.test.ts +82 -0
- package/src/protocol/lanes.ts +54 -0
- package/src/protocol/payloads.test.ts +91 -0
- package/src/protocol/payloads.ts +201 -0
- package/src/protocol/token.test.ts +57 -0
- package/src/protocol/token.ts +123 -0
- package/src/protocol/vocab/schema.test.ts +67 -0
- package/src/protocol/vocab/schema.ts +281 -0
- package/src/relay/incarnation.test.ts +53 -0
- package/src/relay/incarnation.ts +54 -0
- package/src/relay/index.ts +34 -0
- package/src/relay/integration.test.ts +135 -0
- package/src/relay/relay-family.test.ts +236 -0
- package/src/relay/relay-family.ts +336 -0
- package/src/relay/ring.test.ts +138 -0
- package/src/relay/ring.ts +136 -0
- package/src/relay/scheduler.test.ts +233 -0
- package/src/relay/scheduler.ts +208 -0
- package/src/relay/validate.test.ts +43 -0
- package/src/relay/validate.ts +44 -0
- package/src/transcript/index.ts +33 -0
- package/src/transcript/integration.test.ts +108 -0
- package/src/transcript/schema.test.ts +69 -0
- package/src/transcript/schema.ts +51 -0
- package/src/transcript/store.test.ts +285 -0
- package/src/transcript/store.ts +530 -0
- package/src/transcript/test-db.ts +46 -0
- package/src/vocab/core-vocab.test.ts +34 -0
- package/src/vocab/core-vocab.ts +88 -0
- package/src/vocab/diversity.test.ts +153 -0
- package/src/vocab/diversity.ts +169 -0
- package/src/vocab/index.ts +55 -0
- package/src/vocab/merge.test.ts +73 -0
- package/src/vocab/merge.ts +117 -0
- package/src/vocab/requires.test.ts +69 -0
- package/src/vocab/requires.ts +155 -0
- package/src/vocab/resolver.test.ts +118 -0
- package/src/vocab/resolver.ts +187 -0
- package/src/vocab/serve.test.ts +64 -0
- package/src/vocab/serve.ts +66 -0
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The vocab resolver — S3's REGISTER→SERVE core.
|
|
3
|
+
*
|
|
4
|
+
* The versioned vocab artifact (S0 {@link VocabDocument}) is the ONE
|
|
5
|
+
* capability→token map; no map is ever baked into a worker. This resolver reads
|
|
6
|
+
* it and, given a declared enrolment capability, produces the deterministic
|
|
7
|
+
* SERVE token set the worker is entitled to fill — the `serve` reply to a
|
|
8
|
+
* `register` handshake.
|
|
9
|
+
*
|
|
10
|
+
* Token derivation walks `networks → subnetworks → roles`, joining segments into
|
|
11
|
+
* a routing token `network[.subnetwork…].role` (never a seat, never a
|
|
12
|
+
* capability). One exception encodes a BARE (network-less) role such as
|
|
13
|
+
* `decide`: a top-level role whose name equals its containing network and that
|
|
14
|
+
* has no subnetworks collapses to the single-segment token `role` — the only way
|
|
15
|
+
* the S0 network→role schema can express the bare tokens the grammar allows. So
|
|
16
|
+
* `networks.decide.roles.decide` serves the token `decide`.
|
|
17
|
+
*/
|
|
18
|
+
import { formatToken, parseToken, validateVocabDocument } from "../protocol/index.js";
|
|
19
|
+
import { parseRequiresList, RequiresParseError, satisfiesRequires } from "./requires.js";
|
|
20
|
+
/** Raised when the document handed to the resolver is not a valid vocab artifact. */
|
|
21
|
+
export class VocabDocumentError extends Error {
|
|
22
|
+
errors;
|
|
23
|
+
constructor(errors) {
|
|
24
|
+
super(`invalid vocab document: ${errors.map((e) => `${e.path}: ${e.message}`).join("; ")}`);
|
|
25
|
+
this.name = "VocabDocumentError";
|
|
26
|
+
this.errors = errors;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Parse a role's `requires` gate, tagging any failure with the routing token so a
|
|
31
|
+
* malformed predicate names the role that carries it (e.g. when merging author
|
|
32
|
+
* extensions) instead of surfacing a bare, context-free {@link RequiresParseError}.
|
|
33
|
+
*/
|
|
34
|
+
function parseRequires(token, requires) {
|
|
35
|
+
try {
|
|
36
|
+
return parseRequiresList(requires);
|
|
37
|
+
}
|
|
38
|
+
catch (error) {
|
|
39
|
+
if (error instanceof RequiresParseError) {
|
|
40
|
+
throw new VocabDocumentError([{ path: token, message: error.message }]);
|
|
41
|
+
}
|
|
42
|
+
throw error;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
function deriveToken(network, subnetworks, role) {
|
|
46
|
+
// A self-named top-level role denotes a rootless (network-less) role: the only
|
|
47
|
+
// way the network→role schema can express a single-segment token like `decide`.
|
|
48
|
+
if (subnetworks.length === 0 && network === role) {
|
|
49
|
+
return formatToken({ subnetworks: [], role });
|
|
50
|
+
}
|
|
51
|
+
return formatToken({ network, subnetworks: [...subnetworks], role });
|
|
52
|
+
}
|
|
53
|
+
function normaliseSeats(seats) {
|
|
54
|
+
if (seats === undefined)
|
|
55
|
+
return 1;
|
|
56
|
+
if (typeof seats === "number")
|
|
57
|
+
return seats;
|
|
58
|
+
return [...seats];
|
|
59
|
+
}
|
|
60
|
+
export class VocabResolver {
|
|
61
|
+
#roles;
|
|
62
|
+
#byToken;
|
|
63
|
+
#version;
|
|
64
|
+
/**
|
|
65
|
+
* Build a resolver over a vocab document. The document is re-validated against
|
|
66
|
+
* the S0 schema and every role's `requires` gate is parsed up front, so a
|
|
67
|
+
* malformed gate fails loudly at construction rather than silently at match.
|
|
68
|
+
*/
|
|
69
|
+
constructor(doc) {
|
|
70
|
+
const validation = validateVocabDocument(doc);
|
|
71
|
+
if (!validation.ok) {
|
|
72
|
+
throw new VocabDocumentError(validation.errors.map((e) => ({ path: e.path, message: e.message })));
|
|
73
|
+
}
|
|
74
|
+
const document = validation.value;
|
|
75
|
+
this.#version = document.version;
|
|
76
|
+
const roles = [];
|
|
77
|
+
const collect = (network, subnetworks, node) => {
|
|
78
|
+
if (node.roles !== undefined) {
|
|
79
|
+
for (const [roleName, role] of Object.entries(node.roles)) {
|
|
80
|
+
const token = deriveToken(network, subnetworks, roleName);
|
|
81
|
+
roles.push({
|
|
82
|
+
token,
|
|
83
|
+
...(subnetworks.length === 0 && network === roleName ? {} : { network }),
|
|
84
|
+
subnetworks: [...subnetworks],
|
|
85
|
+
role: roleName,
|
|
86
|
+
...(role.weight === undefined ? {} : { weight: role.weight }),
|
|
87
|
+
seats: normaliseSeats(role.seats),
|
|
88
|
+
seatsDistinctFamily: role.seatsDistinctFamily === true,
|
|
89
|
+
requires: parseRequires(token, role.requires),
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
if (node.subnetworks !== undefined) {
|
|
94
|
+
for (const [subName, sub] of Object.entries(node.subnetworks)) {
|
|
95
|
+
collect(network, [...subnetworks, subName], sub);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
for (const [networkName, network] of Object.entries(document.networks)) {
|
|
100
|
+
collect(networkName, [], network);
|
|
101
|
+
}
|
|
102
|
+
// Deterministic order + a token→role index; a duplicate token is a modelling
|
|
103
|
+
// error (two roles cannot claim one routing token).
|
|
104
|
+
roles.sort((a, b) => (a.token < b.token ? -1 : a.token > b.token ? 1 : 0));
|
|
105
|
+
const byToken = new Map();
|
|
106
|
+
for (const role of roles) {
|
|
107
|
+
if (byToken.has(role.token)) {
|
|
108
|
+
throw new VocabDocumentError([{ path: role.token, message: `duplicate routing token: ${role.token}` }]);
|
|
109
|
+
}
|
|
110
|
+
byToken.set(role.token, role);
|
|
111
|
+
}
|
|
112
|
+
this.#roles = roles;
|
|
113
|
+
this.#byToken = byToken;
|
|
114
|
+
}
|
|
115
|
+
/** The vocab artifact version this resolver was built from. */
|
|
116
|
+
get version() {
|
|
117
|
+
return this.#version;
|
|
118
|
+
}
|
|
119
|
+
/** Every role in the vocab, sorted by token. */
|
|
120
|
+
roles() {
|
|
121
|
+
return this.#roles;
|
|
122
|
+
}
|
|
123
|
+
/** Every leaf token in the vocab, sorted. */
|
|
124
|
+
tokens() {
|
|
125
|
+
return this.#roles.map((role) => role.token);
|
|
126
|
+
}
|
|
127
|
+
/** Look up a role by its routing token. */
|
|
128
|
+
roleForToken(token) {
|
|
129
|
+
// Normalise through the grammar so `planning.planner` and any equivalent
|
|
130
|
+
// spelling resolve identically.
|
|
131
|
+
let key;
|
|
132
|
+
try {
|
|
133
|
+
key = formatToken(parseToken(token));
|
|
134
|
+
}
|
|
135
|
+
catch {
|
|
136
|
+
return undefined;
|
|
137
|
+
}
|
|
138
|
+
return this.#byToken.get(key);
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Resolve a declared enrolment capability to its SERVE token set: every role
|
|
142
|
+
* whose `requires` gate the capability satisfies. The token list is sorted and
|
|
143
|
+
* de-duplicated, so the same capability always yields the same SERVE.
|
|
144
|
+
*/
|
|
145
|
+
resolve(capability) {
|
|
146
|
+
const matched = this.#roles.filter((role) => satisfiesRequires(role.requires, capability));
|
|
147
|
+
return { tokens: matched.map((role) => role.token), roles: matched };
|
|
148
|
+
}
|
|
149
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The REGISTER→SERVE handshake mechanics.
|
|
3
|
+
*
|
|
4
|
+
* S2 owns the `register` family on the hub (one family, one owning module — this
|
|
5
|
+
* slice does NOT claim it). S3 supplies the other half of the handshake: given a
|
|
6
|
+
* declared enrolment capability, resolve it (via {@link VocabResolver}) to its
|
|
7
|
+
* SERVE token set and emit a `serve` frame back to the worker.
|
|
8
|
+
*
|
|
9
|
+
* The `serve` reply rides the CONTROL lane — it is a control/facts message, so a
|
|
10
|
+
* bulk relay storm can never delay a worker learning which tokens it may serve
|
|
11
|
+
* (S0 invariant 5). The composition root wires this after presence register
|
|
12
|
+
* (e.g. from S2's register path or an app-level adapter); keeping it a pure,
|
|
13
|
+
* connection-agnostic helper is what lets it compose without editing S2.
|
|
14
|
+
*/
|
|
15
|
+
import type { Capability, Frame, ServePayload } from "../protocol/index.ts";
|
|
16
|
+
import type { Resolution, VocabResolver } from "./resolver.ts";
|
|
17
|
+
/** The minimal per-connection sink a serve reply needs — S1's `HubConnection.send`. */
|
|
18
|
+
export interface ServeSink {
|
|
19
|
+
send(frame: Frame): void;
|
|
20
|
+
}
|
|
21
|
+
/** Build the `serve` payload for an instance and its resolved token set. */
|
|
22
|
+
export declare function buildServePayload(instance: string, tokens: readonly string[]): ServePayload;
|
|
23
|
+
/**
|
|
24
|
+
* Build the `serve` frame carrying an instance's SERVE token set. Rides the
|
|
25
|
+
* control lane; `seq` defaults to 0 (the caller owns per-connection sequencing).
|
|
26
|
+
* The built payload is validated against the S0 `serve` contract, so a
|
|
27
|
+
* malformed token set fails loudly here rather than on the wire. `seq` is
|
|
28
|
+
* likewise checked against the wire's uint32 bound (the same {@link MAX_SEQ}
|
|
29
|
+
* `encodeFrame` enforces) so a bad sequence number fails here — at construction
|
|
30
|
+
* — rather than later during encoding.
|
|
31
|
+
*/
|
|
32
|
+
export declare function buildServeFrame(instance: string, tokens: readonly string[], seq?: number): Frame;
|
|
33
|
+
/**
|
|
34
|
+
* Resolve `capability` and send the resulting `serve` frame to `sink`. Returns
|
|
35
|
+
* the {@link Resolution} so the caller can also record the assigned tokens (e.g.
|
|
36
|
+
* for demand×supply or diversity correlation). This is the server side of
|
|
37
|
+
* `REGISTER {capability}` → `SERVE [leaf tokens]`.
|
|
38
|
+
*/
|
|
39
|
+
export declare function serveCapability(resolver: VocabResolver, sink: ServeSink, instance: string, capability: Capability, seq?: number): Resolution;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { MAX_SEQ, validatePayload } from "../protocol/index.js";
|
|
2
|
+
/** Build the `serve` payload for an instance and its resolved token set. */
|
|
3
|
+
export function buildServePayload(instance, tokens) {
|
|
4
|
+
return { instance, tokens: [...tokens] };
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Build the `serve` frame carrying an instance's SERVE token set. Rides the
|
|
8
|
+
* control lane; `seq` defaults to 0 (the caller owns per-connection sequencing).
|
|
9
|
+
* The built payload is validated against the S0 `serve` contract, so a
|
|
10
|
+
* malformed token set fails loudly here rather than on the wire. `seq` is
|
|
11
|
+
* likewise checked against the wire's uint32 bound (the same {@link MAX_SEQ}
|
|
12
|
+
* `encodeFrame` enforces) so a bad sequence number fails here — at construction
|
|
13
|
+
* — rather than later during encoding.
|
|
14
|
+
*/
|
|
15
|
+
export function buildServeFrame(instance, tokens, seq = 0) {
|
|
16
|
+
if (!Number.isInteger(seq) || seq < 0 || seq > MAX_SEQ) {
|
|
17
|
+
throw new Error(`refusing to build serve frame with invalid seq (must be a uint32): ${String(seq)}`);
|
|
18
|
+
}
|
|
19
|
+
const payload = buildServePayload(instance, tokens);
|
|
20
|
+
const check = validatePayload("serve", payload);
|
|
21
|
+
if (!check.ok) {
|
|
22
|
+
throw new Error(`refusing to build invalid serve frame: ${check.errors.map((e) => e.message).join("; ")}`);
|
|
23
|
+
}
|
|
24
|
+
return { lane: "control", family: "serve", seq, payload };
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Resolve `capability` and send the resulting `serve` frame to `sink`. Returns
|
|
28
|
+
* the {@link Resolution} so the caller can also record the assigned tokens (e.g.
|
|
29
|
+
* for demand×supply or diversity correlation). This is the server side of
|
|
30
|
+
* `REGISTER {capability}` → `SERVE [leaf tokens]`.
|
|
31
|
+
*/
|
|
32
|
+
export function serveCapability(resolver, sink, instance, capability, seq = 0) {
|
|
33
|
+
const resolution = resolver.resolve(capability);
|
|
34
|
+
sink.send(buildServeFrame(instance, resolution.tokens, seq));
|
|
35
|
+
return resolution;
|
|
36
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@nanobpm/agentic",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "The Nano agentic protocol (ADR 0056): one app-tier channel carrying agent presence/registry, demand×supply, a shared blackboard and live terminal relay — with the wire contract, channel/hub, family modules and the operator cockpit, as subpath exports.",
|
|
5
|
+
"license": "Apache-2.0",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"keywords": [
|
|
8
|
+
"nanobpm",
|
|
9
|
+
"agentic",
|
|
10
|
+
"protocol",
|
|
11
|
+
"channel",
|
|
12
|
+
"presence",
|
|
13
|
+
"demand",
|
|
14
|
+
"relay",
|
|
15
|
+
"transcript",
|
|
16
|
+
"blackboard",
|
|
17
|
+
"cockpit",
|
|
18
|
+
"conformance"
|
|
19
|
+
],
|
|
20
|
+
"publishConfig": {
|
|
21
|
+
"access": "public"
|
|
22
|
+
},
|
|
23
|
+
"repository": {
|
|
24
|
+
"type": "git",
|
|
25
|
+
"url": "git+https://github.com/nanobpm/nano-ide.git",
|
|
26
|
+
"directory": "packages/agentic"
|
|
27
|
+
},
|
|
28
|
+
"main": "./dist/index.js",
|
|
29
|
+
"types": "./dist/index.d.ts",
|
|
30
|
+
"exports": {
|
|
31
|
+
".": {
|
|
32
|
+
"types": "./dist/index.d.ts",
|
|
33
|
+
"default": "./dist/index.js"
|
|
34
|
+
},
|
|
35
|
+
"./source": "./src/index.ts",
|
|
36
|
+
"./protocol": {
|
|
37
|
+
"types": "./dist/protocol/index.d.ts",
|
|
38
|
+
"default": "./dist/protocol/index.js"
|
|
39
|
+
},
|
|
40
|
+
"./source/protocol": "./src/protocol/index.ts",
|
|
41
|
+
"./channel": {
|
|
42
|
+
"types": "./dist/channel/index.d.ts",
|
|
43
|
+
"default": "./dist/channel/index.js"
|
|
44
|
+
},
|
|
45
|
+
"./source/channel": "./src/channel/index.ts",
|
|
46
|
+
"./presence": {
|
|
47
|
+
"types": "./dist/presence/index.d.ts",
|
|
48
|
+
"default": "./dist/presence/index.js"
|
|
49
|
+
},
|
|
50
|
+
"./source/presence": "./src/presence/index.ts",
|
|
51
|
+
"./vocab": {
|
|
52
|
+
"types": "./dist/vocab/index.d.ts",
|
|
53
|
+
"default": "./dist/vocab/index.js"
|
|
54
|
+
},
|
|
55
|
+
"./source/vocab": "./src/vocab/index.ts",
|
|
56
|
+
"./demand": {
|
|
57
|
+
"types": "./dist/demand/index.d.ts",
|
|
58
|
+
"default": "./dist/demand/index.js"
|
|
59
|
+
},
|
|
60
|
+
"./source/demand": "./src/demand/index.ts",
|
|
61
|
+
"./relay": {
|
|
62
|
+
"types": "./dist/relay/index.d.ts",
|
|
63
|
+
"default": "./dist/relay/index.js"
|
|
64
|
+
},
|
|
65
|
+
"./source/relay": "./src/relay/index.ts",
|
|
66
|
+
"./transcript": {
|
|
67
|
+
"types": "./dist/transcript/index.d.ts",
|
|
68
|
+
"default": "./dist/transcript/index.js"
|
|
69
|
+
},
|
|
70
|
+
"./source/transcript": "./src/transcript/index.ts",
|
|
71
|
+
"./blackboard": {
|
|
72
|
+
"types": "./dist/blackboard/index.d.ts",
|
|
73
|
+
"default": "./dist/blackboard/index.js"
|
|
74
|
+
},
|
|
75
|
+
"./source/blackboard": "./src/blackboard/index.ts",
|
|
76
|
+
"./cockpit": {
|
|
77
|
+
"types": "./dist/cockpit/index.d.ts",
|
|
78
|
+
"default": "./dist/cockpit/index.js"
|
|
79
|
+
},
|
|
80
|
+
"./source/cockpit": "./src/cockpit/index.ts",
|
|
81
|
+
"./protocol/conformance": {
|
|
82
|
+
"types": "./dist/protocol/conformance/index.d.ts",
|
|
83
|
+
"default": "./dist/protocol/conformance/index.js"
|
|
84
|
+
},
|
|
85
|
+
"./source/protocol/conformance": "./src/protocol/conformance/index.ts"
|
|
86
|
+
},
|
|
87
|
+
"files": [
|
|
88
|
+
"dist",
|
|
89
|
+
"src",
|
|
90
|
+
"page",
|
|
91
|
+
"README.md"
|
|
92
|
+
],
|
|
93
|
+
"scripts": {
|
|
94
|
+
"build": "tsc -p tsconfig.build.json",
|
|
95
|
+
"prepack": "npm run build",
|
|
96
|
+
"typecheck": "tsc --noEmit -p tsconfig.json",
|
|
97
|
+
"test": "node --test --experimental-strip-types \"src/**/*.test.ts\"",
|
|
98
|
+
"test:conformance": "node --test --experimental-strip-types \"src/protocol/conformance/corpus.test.ts\""
|
|
99
|
+
},
|
|
100
|
+
"dependencies": {
|
|
101
|
+
"ws": "^8.18.3"
|
|
102
|
+
},
|
|
103
|
+
"devDependencies": {
|
|
104
|
+
"@types/node": "^22.20.1",
|
|
105
|
+
"@types/ws": "^8.5.12",
|
|
106
|
+
"typescript": "^5.9.3"
|
|
107
|
+
}
|
|
108
|
+
}
|
package/page/cockpit.css
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/* The cockpit stylesheet — a compact, phone-friendly dark cockpit shared by the
|
|
2
|
+
standalone shell and the console App-View embed, so the two render identically.
|
|
3
|
+
The three-state SLO lights key off `data-status` (green | amber | red). */
|
|
4
|
+
|
|
5
|
+
:root {
|
|
6
|
+
--cockpit-bg: #0b0f14;
|
|
7
|
+
--cockpit-panel: #131a22;
|
|
8
|
+
--cockpit-edge: #223041;
|
|
9
|
+
--cockpit-text: #e6edf3;
|
|
10
|
+
--cockpit-muted: #8b98a5;
|
|
11
|
+
--cockpit-green: #2ea043;
|
|
12
|
+
--cockpit-amber: #d29922;
|
|
13
|
+
--cockpit-red: #f85149;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
.cockpit-shell {
|
|
17
|
+
color: var(--cockpit-text);
|
|
18
|
+
background: var(--cockpit-bg);
|
|
19
|
+
font: 14px/1.4 ui-sans-serif, system-ui, sans-serif;
|
|
20
|
+
display: grid;
|
|
21
|
+
gap: 12px;
|
|
22
|
+
padding: 12px;
|
|
23
|
+
min-height: 100%;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
.cockpit-header {
|
|
27
|
+
display: flex;
|
|
28
|
+
flex-wrap: wrap;
|
|
29
|
+
align-items: center;
|
|
30
|
+
gap: 12px;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
.cockpit-title {
|
|
34
|
+
font-size: 16px;
|
|
35
|
+
margin: 0;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
.cockpit-status {
|
|
39
|
+
padding: 2px 8px;
|
|
40
|
+
border-radius: 999px;
|
|
41
|
+
border: 1px solid var(--cockpit-edge);
|
|
42
|
+
font-variant: small-caps;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
.cockpit-dot {
|
|
46
|
+
display: inline-block;
|
|
47
|
+
width: 10px;
|
|
48
|
+
height: 10px;
|
|
49
|
+
border-radius: 50%;
|
|
50
|
+
margin-right: 6px;
|
|
51
|
+
background: var(--cockpit-muted);
|
|
52
|
+
vertical-align: middle;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
[data-status="green"] > .cockpit-dot,
|
|
56
|
+
.cockpit-dot[data-status="green"] { background: var(--cockpit-green); }
|
|
57
|
+
[data-status="amber"] > .cockpit-dot,
|
|
58
|
+
.cockpit-dot[data-status="amber"] { background: var(--cockpit-amber); }
|
|
59
|
+
[data-status="red"] > .cockpit-dot,
|
|
60
|
+
.cockpit-dot[data-status="red"] { background: var(--cockpit-red); }
|
|
61
|
+
|
|
62
|
+
.cockpit-status[data-status="green"] { color: var(--cockpit-green); }
|
|
63
|
+
.cockpit-status[data-status="amber"] { color: var(--cockpit-amber); }
|
|
64
|
+
.cockpit-status[data-status="red"] { color: var(--cockpit-red); }
|
|
65
|
+
|
|
66
|
+
.cockpit-missing {
|
|
67
|
+
background: var(--cockpit-panel);
|
|
68
|
+
border: 1px solid var(--cockpit-edge);
|
|
69
|
+
border-radius: 8px;
|
|
70
|
+
padding: 10px;
|
|
71
|
+
}
|
|
72
|
+
.cockpit-missing[data-empty="true"] { opacity: 0.7; }
|
|
73
|
+
.cockpit-panel-title { font-size: 13px; margin: 0 0 8px; color: var(--cockpit-muted); }
|
|
74
|
+
.cockpit-light { display: flex; align-items: center; gap: 6px; padding: 2px 0; }
|
|
75
|
+
.cockpit-light-detail { color: var(--cockpit-muted); font-size: 12px; }
|
|
76
|
+
|
|
77
|
+
.cockpit-matrix { display: grid; gap: 10px; }
|
|
78
|
+
.cockpit-network {
|
|
79
|
+
background: var(--cockpit-panel);
|
|
80
|
+
border: 1px solid var(--cockpit-edge);
|
|
81
|
+
border-left: 3px solid var(--cockpit-muted);
|
|
82
|
+
border-radius: 8px;
|
|
83
|
+
padding: 10px;
|
|
84
|
+
}
|
|
85
|
+
.cockpit-network[data-status="green"] { border-left-color: var(--cockpit-green); }
|
|
86
|
+
.cockpit-network[data-status="red"] { border-left-color: var(--cockpit-red); }
|
|
87
|
+
.cockpit-network-head { display: flex; align-items: center; gap: 8px; margin-bottom: 6px; }
|
|
88
|
+
.cockpit-network-name { font-weight: 600; }
|
|
89
|
+
.cockpit-network-missing { color: var(--cockpit-red); font-size: 12px; }
|
|
90
|
+
|
|
91
|
+
.cockpit-tokens { width: 100%; border-collapse: collapse; }
|
|
92
|
+
.cockpit-th { text-align: left; color: var(--cockpit-muted); font-weight: 500; font-size: 12px; padding: 2px 6px; }
|
|
93
|
+
.cockpit-td { padding: 3px 6px; border-top: 1px solid var(--cockpit-edge); vertical-align: top; }
|
|
94
|
+
.cockpit-token[data-status="red"] .cockpit-token-name { color: var(--cockpit-red); }
|
|
95
|
+
.cockpit-workers { display: flex; flex-wrap: wrap; gap: 4px; }
|
|
96
|
+
.cockpit-worker {
|
|
97
|
+
background: #1c2733;
|
|
98
|
+
color: var(--cockpit-text);
|
|
99
|
+
border: 1px solid var(--cockpit-edge);
|
|
100
|
+
border-radius: 6px;
|
|
101
|
+
padding: 1px 8px;
|
|
102
|
+
cursor: pointer;
|
|
103
|
+
font: inherit;
|
|
104
|
+
}
|
|
105
|
+
.cockpit-worker:hover { border-color: var(--cockpit-green); }
|
|
106
|
+
|
|
107
|
+
.cockpit-terminal {
|
|
108
|
+
background: #000;
|
|
109
|
+
border: 1px solid var(--cockpit-edge);
|
|
110
|
+
border-radius: 8px;
|
|
111
|
+
padding: 8px;
|
|
112
|
+
}
|
|
113
|
+
.cockpit-terminal-host { min-height: 220px; }
|
|
114
|
+
.cockpit-nonagentic { color: var(--cockpit-muted); font-size: 12px; }
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": "1.0",
|
|
3
|
+
"title": "Cockpit",
|
|
4
|
+
"nodes": [
|
|
5
|
+
{
|
|
6
|
+
"type": "nav",
|
|
7
|
+
"id": "nav",
|
|
8
|
+
"props": {
|
|
9
|
+
"variant": "bar",
|
|
10
|
+
"title": "Agent cockpit",
|
|
11
|
+
"items": [{ "label": "Cockpit", "page": "cockpit" }]
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"type": "text",
|
|
16
|
+
"id": "intro",
|
|
17
|
+
"props": {
|
|
18
|
+
"text": "Live demand×supply across the agent networks, missing-agent lights, the diversity SLO, and a drill-into-a-worker live terminal over the relay. The same view renders embedded in the console (App View) and standalone on a phone.",
|
|
19
|
+
"variant": "sub"
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
"type": "appView",
|
|
24
|
+
"id": "cockpit",
|
|
25
|
+
"props": {
|
|
26
|
+
"title": "Agent networks",
|
|
27
|
+
"embed": "./embed.html",
|
|
28
|
+
"standalone": "./standalone.html",
|
|
29
|
+
"fill": true
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
]
|
|
33
|
+
}
|
package/page/embed.html
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
6
|
+
<title>Cockpit — App View embed</title>
|
|
7
|
+
<link rel="stylesheet" href="./cockpit.css" />
|
|
8
|
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@xterm/xterm@5/css/xterm.min.css" />
|
|
9
|
+
<style>
|
|
10
|
+
html, body { margin: 0; height: 100%; background: #0b0f14; }
|
|
11
|
+
</style>
|
|
12
|
+
<script type="importmap">
|
|
13
|
+
{
|
|
14
|
+
"imports": {
|
|
15
|
+
"@nanobpm/agentic/cockpit": "../dist/cockpit/index.js",
|
|
16
|
+
"@xterm/xterm": "https://cdn.jsdelivr.net/npm/@xterm/xterm@5/+esm"
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
</script>
|
|
20
|
+
</head>
|
|
21
|
+
<body>
|
|
22
|
+
<!--
|
|
23
|
+
Console App-View embed. The console loads this document into its App-View
|
|
24
|
+
surface (ADR 0057) and hands it a host element; we mount the SAME cockpit
|
|
25
|
+
via the SAME page/mount.js as the standalone shell — only the host and the
|
|
26
|
+
injected endpoints differ, so the page renders identically. When the console
|
|
27
|
+
injects endpoint config via `window.__NANO_APP_VIEW__`, it is honoured.
|
|
28
|
+
-->
|
|
29
|
+
<main id="cockpit-root"></main>
|
|
30
|
+
<script type="module">
|
|
31
|
+
import { mountCockpit } from "./mount.js";
|
|
32
|
+
|
|
33
|
+
const cfg = window.__NANO_APP_VIEW__ ?? {};
|
|
34
|
+
mountCockpit(cfg.host ?? document.getElementById("cockpit-root"), {
|
|
35
|
+
reportUrl: cfg.reportUrl,
|
|
36
|
+
relayUrl: cfg.relayUrl,
|
|
37
|
+
});
|
|
38
|
+
</script>
|
|
39
|
+
</body>
|
|
40
|
+
</html>
|
package/page/mount.js
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
// Browser adapter for @nanobpm/agentic/cockpit (S8).
|
|
2
|
+
//
|
|
3
|
+
// This is the ONE wiring both the standalone shell and the console App-View embed
|
|
4
|
+
// call — they differ only in the host element they pass, so the cockpit renders
|
|
5
|
+
// identically embedded and standalone. It supplies the browser capabilities the
|
|
6
|
+
// injection-based core (`bootCockpit`) needs: the real `document`, a `fetch`-based
|
|
7
|
+
// S4 demand report source, a `WebSocket` relay socket factory, and an xterm.js
|
|
8
|
+
// terminal sink. It is plain browser ESM — not part of the typechecked/linted src.
|
|
9
|
+
//
|
|
10
|
+
// Resolve `@nanobpm/agentic/cockpit` and `@xterm/xterm` via the host page's import
|
|
11
|
+
// map (see standalone.html / embed.html).
|
|
12
|
+
import { bootCockpit } from "@nanobpm/agentic/cockpit";
|
|
13
|
+
import { Terminal } from "@xterm/xterm";
|
|
14
|
+
|
|
15
|
+
/** An xterm.js-backed terminal sink mounted into `host`. */
|
|
16
|
+
function xtermSink(host) {
|
|
17
|
+
const term = new Terminal({ convertEol: true, fontFamily: "ui-monospace, monospace", fontSize: 13 });
|
|
18
|
+
term.open(host);
|
|
19
|
+
return { write: (chunk) => term.write(chunk), dispose: () => term.dispose() };
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** A WebSocket relay socket factory for the agentic channel at `url`. */
|
|
23
|
+
function relaySocketFactory(url) {
|
|
24
|
+
return () => {
|
|
25
|
+
const ws = new WebSocket(url);
|
|
26
|
+
ws.binaryType = "arraybuffer";
|
|
27
|
+
return {
|
|
28
|
+
send: (bytes) => ws.send(bytes),
|
|
29
|
+
close: () => ws.close(),
|
|
30
|
+
onMessage: (cb) => ws.addEventListener("message", (event) => cb(new Uint8Array(event.data))),
|
|
31
|
+
onOpen: (cb) => ws.addEventListener("open", () => cb()),
|
|
32
|
+
onClose: (cb) => ws.addEventListener("close", () => cb()),
|
|
33
|
+
};
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Mount the cockpit into `host` and start polling.
|
|
39
|
+
*
|
|
40
|
+
* @param {Element} host — where the cockpit renders (standalone: document.body; embedded: the App-View host).
|
|
41
|
+
* @param {object} [opts]
|
|
42
|
+
* @param {string} [opts.reportUrl] — the S4 demand×supply JSON endpoint the app serves.
|
|
43
|
+
* @param {string} [opts.relayUrl] — the agentic channel WebSocket URL (with auth token + capability query).
|
|
44
|
+
* @param {number} [opts.refreshMs] — poll interval (default 2000).
|
|
45
|
+
* @returns the CockpitHandle (call `.dispose()` to tear down).
|
|
46
|
+
*/
|
|
47
|
+
export function mountCockpit(host, opts = {}) {
|
|
48
|
+
if (host == null || typeof host.replaceChildren !== "function") {
|
|
49
|
+
throw new Error(
|
|
50
|
+
"mountCockpit(host): host must be a mounted DOM element (got " +
|
|
51
|
+
(host === null ? "null" : typeof host) +
|
|
52
|
+
"). Check that the cockpit root element exists before mounting.",
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
const reportUrl = opts.reportUrl ?? "/app/api/agentic/demand";
|
|
56
|
+
const relayUrl = opts.relayUrl ?? defaultRelayUrl();
|
|
57
|
+
const cockpit = bootCockpit({
|
|
58
|
+
host,
|
|
59
|
+
doc: document,
|
|
60
|
+
fetchReport: async () => {
|
|
61
|
+
const res = await fetch(reportUrl, { headers: { accept: "application/json" } });
|
|
62
|
+
if (!res.ok) throw new Error(`demand fetch failed: ${res.status}`);
|
|
63
|
+
return res.json();
|
|
64
|
+
},
|
|
65
|
+
connectRelay: relaySocketFactory(relayUrl),
|
|
66
|
+
createTerminal: xtermSink,
|
|
67
|
+
refreshMs: opts.refreshMs ?? 2000,
|
|
68
|
+
onError: (err) => console.error("[cockpit]", err),
|
|
69
|
+
});
|
|
70
|
+
cockpit.start();
|
|
71
|
+
return cockpit;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/** Derive the channel WebSocket URL from the current origin (path `/agentic`). */
|
|
75
|
+
function defaultRelayUrl() {
|
|
76
|
+
const proto = location.protocol === "https:" ? "wss:" : "ws:";
|
|
77
|
+
return `${proto}//${location.host}/agentic`;
|
|
78
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
|
|
6
|
+
<title>Agent networks — the cockpit</title>
|
|
7
|
+
<link rel="stylesheet" href="./cockpit.css" />
|
|
8
|
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@xterm/xterm@5/css/xterm.min.css" />
|
|
9
|
+
<style>
|
|
10
|
+
html, body { margin: 0; height: 100%; background: #0b0f14; }
|
|
11
|
+
</style>
|
|
12
|
+
<!--
|
|
13
|
+
Resolve the cockpit ESM + xterm.js. The cockpit is mapped RELATIVE to this
|
|
14
|
+
page (../dist/index.js) — exactly like ./mount.js and ./cockpit.css above —
|
|
15
|
+
so it resolves against whatever origin/version served the page (a CDN pin,
|
|
16
|
+
the Urban runtime, or a local checkout) with no hard-coded version to drift
|
|
17
|
+
out of sync with package.json. xterm.js stays a pinned third-party CDN dep.
|
|
18
|
+
The SAME page/mount.js the console App-View embed uses is loaded here.
|
|
19
|
+
-->
|
|
20
|
+
<script type="importmap">
|
|
21
|
+
{
|
|
22
|
+
"imports": {
|
|
23
|
+
"@nanobpm/agentic/cockpit": "../dist/cockpit/index.js",
|
|
24
|
+
"@xterm/xterm": "https://cdn.jsdelivr.net/npm/@xterm/xterm@5/+esm"
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
</script>
|
|
28
|
+
</head>
|
|
29
|
+
<body>
|
|
30
|
+
<main id="cockpit-root"></main>
|
|
31
|
+
<script type="module">
|
|
32
|
+
import { mountCockpit } from "./mount.js";
|
|
33
|
+
|
|
34
|
+
// Standalone: mount into the page body. Endpoints default to the current
|
|
35
|
+
// origin; override via ?report= and ?relay= for a remote app.
|
|
36
|
+
const params = new URLSearchParams(location.search);
|
|
37
|
+
mountCockpit(document.getElementById("cockpit-root"), {
|
|
38
|
+
reportUrl: params.get("report") ?? undefined,
|
|
39
|
+
relayUrl: params.get("relay") ?? undefined,
|
|
40
|
+
});
|
|
41
|
+
</script>
|
|
42
|
+
</body>
|
|
43
|
+
</html>
|