super-dsh 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/README.md +9 -8
- package/agent-agy/cordis.patch.yml +8 -4
- package/agent-agy/dist/agent-preset-agy.d.ts +23 -43
- package/agent-agy/dist/agent-preset-agy.js +29 -166
- package/agent-agy/dist/agent.d.ts +2 -1
- package/agent-agy/dist/agent.js +3 -2
- package/agent-agy/dist/agy-sessions.d.ts +2 -1
- package/agent-agy/dist/agy-sessions.js +2 -11
- package/agent-agy/dist/index.js +13 -12
- package/agent-agy/dist/world-plugin.d.ts +2 -1
- package/agent-agy/dist/world-plugin.js +16 -21
- package/agent-agy/package.json +6 -6
- package/agent-claude/cordis.patch.yml +8 -4
- package/agent-claude/dist/agent-preset-claude.js +29 -166
- package/agent-claude/dist/agent.js +10 -10
- package/agent-claude/dist/claude-client.js +22 -3
- package/agent-claude/dist/claude-home.js +0 -8
- package/agent-claude/dist/index.js +12 -15
- package/agent-claude/dist/session-map.js +2 -5
- package/agent-claude/dist/world-plugin.js +3 -3
- package/agent-claude/package.json +9 -9
- package/agent-codex/README.md +8 -8
- package/agent-codex/cordis.patch.yml +9 -5
- package/agent-codex/dist/agent-preset-codex.js +29 -168
- package/agent-codex/dist/agent.js +17 -12
- package/agent-codex/dist/codex-store.js +0 -9
- package/agent-codex/dist/index.js +13 -16
- package/agent-codex/dist/session-map.js +2 -5
- package/agent-codex/dist/world-plugin.js +15 -20
- package/agent-codex/package.json +14 -11
- package/agent-hermes/README.md +9 -8
- package/agent-hermes/cordis.patch.yml +8 -4
- package/agent-hermes/dist/agent-preset-hermes.js +29 -168
- package/agent-hermes/dist/agent.js +33 -19
- package/agent-hermes/dist/hermes-client.js +5 -5
- package/agent-hermes/dist/hermes-store.js +2 -11
- package/agent-hermes/dist/index.js +13 -12
- package/agent-hermes/dist/world-plugin.js +16 -21
- package/agent-hermes/package.json +14 -11
- package/agent-omp/README.md +2 -2
- package/agent-omp/cordis.patch.yml +12 -7
- package/agent-omp/dist/agent-preset-omp.js +30 -169
- package/agent-omp/dist/agent.js +13 -5
- package/agent-omp/dist/index.js +12 -22
- package/agent-omp/dist/mobile/gesture.js +2 -2
- package/agent-omp/dist/mobile/zoom-guard.js +4 -4
- package/agent-omp/dist/mobile-boot.js +2 -2
- package/agent-omp/dist/omp-cli.js +1 -1
- package/agent-omp/dist/omp-disk-discovery.js +1 -1
- package/agent-omp/dist/protocol.js +1 -1
- package/agent-omp/dist/sidecar-client.js +1 -1
- package/agent-omp/dist/world-plugin.js +22 -19
- package/agent-omp/package.json +18 -14
- package/agent-omp/sidecar/main.ts +2 -2
- package/agent-pi/README.md +5 -5
- package/agent-pi/cordis.patch.yml +8 -4
- package/agent-pi/dist/agent-preset-pi.js +29 -166
- package/agent-pi/dist/agent.js +18 -12
- package/agent-pi/dist/index.js +14 -16
- package/agent-pi/dist/pi-home.js +2 -12
- package/agent-pi/dist/world-plugin.js +16 -21
- package/agent-pi/package.json +14 -11
- package/cordis.patch.yml +31 -21
- package/package.json +30 -38
- package/{agent-hub → superd-hub}/cordis.patch.yml +3 -3
- package/{agent-hub → superd-hub}/dist/carrier.d.ts +9 -3
- package/{agent-hub → superd-hub}/dist/carrier.js +29 -3
- package/{agent-hub → superd-hub}/dist/gateway.d.ts +2 -2
- package/{agent-hub → superd-hub}/dist/gateway.js +1 -1
- package/{agent-hub → superd-hub}/dist/index.d.ts +5 -3
- package/{agent-hub → superd-hub}/dist/index.js +5 -3
- package/{agent-hub → superd-hub}/dist/roster.d.ts +2 -2
- package/{agent-hub → superd-hub}/dist/rpc.js +1 -1
- package/{agent-hub → superd-hub}/dist/spawn-world.d.ts +27 -6
- package/superd-hub/dist/spawn-world.js +277 -0
- package/superd-hub/dist/system-head.d.ts +69 -0
- package/superd-hub/dist/system-head.js +194 -0
- package/superd-hub/dist/web-trust.d.ts +112 -0
- package/superd-hub/dist/web-trust.js +126 -0
- package/{agent-hub → superd-hub}/dist/world-entry.d.ts +2 -2
- package/{agent-hub → superd-hub}/dist/world-entry.js +4 -4
- package/{agent-hub → superd-hub}/dist/world-host.d.ts +1 -1
- package/{agent-hub → superd-hub}/dist/world-join.d.ts +2 -1
- package/{agent-hub → superd-hub}/dist/world-join.js +16 -21
- package/{agent-hub → superd-hub}/dist/world-mount.d.ts +14 -6
- package/{agent-hub → superd-hub}/dist/world-mount.js +28 -7
- package/{agent-hub → superd-hub}/dist/world-mux.d.ts +21 -6
- package/superd-hub/dist/world-mux.js +392 -0
- package/{agent-hub → superd-hub}/dist/world-provision.d.ts +20 -4
- package/superd-hub/dist/world-provision.js +196 -0
- package/{agent-hub → superd-hub}/dist/world-web-server.js +7 -1
- package/{agent-hub → superd-hub}/package.json +9 -16
- package/agent-hub/dist/spawn-world.js +0 -72
- package/agent-hub/dist/world-mux.js +0 -224
- package/agent-hub/dist/world-provision.js +0 -122
- package/agent-hub/lib/client/index.js +0 -242
- package/client/index.js +0 -242
- /package/{agent-hub → superd-hub}/dist/agent-roster.d.ts +0 -0
- /package/{agent-hub → superd-hub}/dist/agent-roster.js +0 -0
- /package/{agent-hub → superd-hub}/dist/client-shim.d.ts +0 -0
- /package/{agent-hub → superd-hub}/dist/client-shim.js +0 -0
- /package/{agent-hub → superd-hub}/dist/envelope.d.ts +0 -0
- /package/{agent-hub → superd-hub}/dist/envelope.js +0 -0
- /package/{agent-hub → superd-hub}/dist/index-pass.d.ts +0 -0
- /package/{agent-hub → superd-hub}/dist/index-pass.js +0 -0
- /package/{agent-hub → superd-hub}/dist/labels.d.ts +0 -0
- /package/{agent-hub → superd-hub}/dist/labels.js +0 -0
- /package/{agent-hub → superd-hub}/dist/ownership.d.ts +0 -0
- /package/{agent-hub → superd-hub}/dist/ownership.js +0 -0
- /package/{agent-hub → superd-hub}/dist/roster.js +0 -0
- /package/{agent-hub → superd-hub}/dist/rpc.d.ts +0 -0
- /package/{agent-hub → superd-hub}/dist/targets.d.ts +0 -0
- /package/{agent-hub → superd-hub}/dist/targets.js +0 -0
- /package/{agent-hub → superd-hub}/dist/world-host.js +0 -0
- /package/{agent-hub → superd-hub}/dist/world-web-server.d.ts +0 -0
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Protected system-surface head co-writer for bridge sessions (2026-09-24).
|
|
3
|
+
*
|
|
4
|
+
* dsh session format v4 (0.1.7 reader) constrains every `system/message`
|
|
5
|
+
* twice: it must sit INSIDE an open, matching turn+step (`requireStep`), and
|
|
6
|
+
* it must either BE the surface's first node (the protected head) or replace
|
|
7
|
+
* one ("system/message requires a protected first surface head"). Bridge
|
|
8
|
+
* adapters learn the runtime's real prompt only after the first user message
|
|
9
|
+
* is already on the surface, and most write the user message at turn level
|
|
10
|
+
* (before any step) — so every bridge-written log violated one rule or the
|
|
11
|
+
* other and became unresumable across restarts.
|
|
12
|
+
*
|
|
13
|
+
* The native loop never hits this because it opens the step before writing
|
|
14
|
+
* anything: `turn/start → step/start → system/message → user/message …`,
|
|
15
|
+
* and later prompt changes REPLACE the protected head (the operation
|
|
16
|
+
* compaction also uses). This module gives bridge adapters that posture with
|
|
17
|
+
* a one-line wrap:
|
|
18
|
+
*
|
|
19
|
+
* this.session = withSystemHead(session, () => createSystemMessage(""))
|
|
20
|
+
*
|
|
21
|
+
* The wrap is a transparent Proxy that tracks the open turn/step (exactly as
|
|
22
|
+
* the v4 reader does) and intervenes minimally:
|
|
23
|
+
*
|
|
24
|
+
* - the FIRST `user/message` append on a still-headless, still-empty
|
|
25
|
+
* surface first opens the turn's next step (when a turn is open but no
|
|
26
|
+
* step is) and appends a placeholder head; the adapters' real prompt
|
|
27
|
+
* later REPLACES it (replaced heads are shadowed out of derived model
|
|
28
|
+
* history, so the empty placeholder never reaches the model or the UI);
|
|
29
|
+
* - the adapter's own `step/start` for a step THIS wrap opened is dropped
|
|
30
|
+
* (the log already carries it — appending again would duplicate), and a
|
|
31
|
+
* wrap-opened step still open at `turn/end` is closed just before it;
|
|
32
|
+
* - `stampSystemPrompt(turn, step, message)` appends-or-replaces the
|
|
33
|
+
* protected head with the runtime's real prompt. A stamp arriving with
|
|
34
|
+
* no open step (an adapter bootstrapping before the first turn) is
|
|
35
|
+
* queued and flushed inside the next step the adapter opens;
|
|
36
|
+
* - everything else delegates unchanged.
|
|
37
|
+
*
|
|
38
|
+
* A surface that already starts with a user message and has no head (legacy
|
|
39
|
+
* logs) stays headless — valid — and is never stamped: appending a head
|
|
40
|
+
* there is exactly the corruption this module exists to prevent.
|
|
41
|
+
*/
|
|
42
|
+
/**
|
|
43
|
+
* The session slice the co-writer reads. Deliberately loose so the real
|
|
44
|
+
* (generic-typed) `Session.append` satisfies it; the system-head calls go
|
|
45
|
+
* through {@link SystemHeadAppend}, the shape this module itself issues.
|
|
46
|
+
*/
|
|
47
|
+
export interface HeadAwareSessionFace {
|
|
48
|
+
append(...args: never[]): unknown;
|
|
49
|
+
snapshotEvents(): ReadonlyArray<{
|
|
50
|
+
type: string;
|
|
51
|
+
seq: number;
|
|
52
|
+
surfaceOp?: unknown;
|
|
53
|
+
}>;
|
|
54
|
+
}
|
|
55
|
+
/** Builds the placeholder head message (adapters pass their createSystemMessage). */
|
|
56
|
+
export type HeadPlaceholder = () => unknown;
|
|
57
|
+
/** One co-written session: transparent delegation + head bookkeeping. */
|
|
58
|
+
export type WithSystemHead<S> = S & {
|
|
59
|
+
/** Append-or-replace the protected head with the runtime's system prompt. */
|
|
60
|
+
stampSystemPrompt(turn: number, step: number, message: unknown): void;
|
|
61
|
+
};
|
|
62
|
+
/**
|
|
63
|
+
* Wrap one bridge session with protected-head co-writing.
|
|
64
|
+
* @param session - the adapter's real Session.
|
|
65
|
+
* @param placeholder - builds the placeholder head message when the first
|
|
66
|
+
* user message arrives on an empty surface.
|
|
67
|
+
* @returns the session (transparently delegated) with `stampSystemPrompt`.
|
|
68
|
+
*/
|
|
69
|
+
export declare function withSystemHead<S extends HeadAwareSessionFace>(session: S, placeholder: HeadPlaceholder): WithSystemHead<S>;
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Protected system-surface head co-writer for bridge sessions (2026-09-24).
|
|
3
|
+
*
|
|
4
|
+
* dsh session format v4 (0.1.7 reader) constrains every `system/message`
|
|
5
|
+
* twice: it must sit INSIDE an open, matching turn+step (`requireStep`), and
|
|
6
|
+
* it must either BE the surface's first node (the protected head) or replace
|
|
7
|
+
* one ("system/message requires a protected first surface head"). Bridge
|
|
8
|
+
* adapters learn the runtime's real prompt only after the first user message
|
|
9
|
+
* is already on the surface, and most write the user message at turn level
|
|
10
|
+
* (before any step) — so every bridge-written log violated one rule or the
|
|
11
|
+
* other and became unresumable across restarts.
|
|
12
|
+
*
|
|
13
|
+
* The native loop never hits this because it opens the step before writing
|
|
14
|
+
* anything: `turn/start → step/start → system/message → user/message …`,
|
|
15
|
+
* and later prompt changes REPLACE the protected head (the operation
|
|
16
|
+
* compaction also uses). This module gives bridge adapters that posture with
|
|
17
|
+
* a one-line wrap:
|
|
18
|
+
*
|
|
19
|
+
* this.session = withSystemHead(session, () => createSystemMessage(""))
|
|
20
|
+
*
|
|
21
|
+
* The wrap is a transparent Proxy that tracks the open turn/step (exactly as
|
|
22
|
+
* the v4 reader does) and intervenes minimally:
|
|
23
|
+
*
|
|
24
|
+
* - the FIRST `user/message` append on a still-headless, still-empty
|
|
25
|
+
* surface first opens the turn's next step (when a turn is open but no
|
|
26
|
+
* step is) and appends a placeholder head; the adapters' real prompt
|
|
27
|
+
* later REPLACES it (replaced heads are shadowed out of derived model
|
|
28
|
+
* history, so the empty placeholder never reaches the model or the UI);
|
|
29
|
+
* - the adapter's own `step/start` for a step THIS wrap opened is dropped
|
|
30
|
+
* (the log already carries it — appending again would duplicate), and a
|
|
31
|
+
* wrap-opened step still open at `turn/end` is closed just before it;
|
|
32
|
+
* - `stampSystemPrompt(turn, step, message)` appends-or-replaces the
|
|
33
|
+
* protected head with the runtime's real prompt. A stamp arriving with
|
|
34
|
+
* no open step (an adapter bootstrapping before the first turn) is
|
|
35
|
+
* queued and flushed inside the next step the adapter opens;
|
|
36
|
+
* - everything else delegates unchanged.
|
|
37
|
+
*
|
|
38
|
+
* A surface that already starts with a user message and has no head (legacy
|
|
39
|
+
* logs) stays headless — valid — and is never stamped: appending a head
|
|
40
|
+
* there is exactly the corruption this module exists to prevent.
|
|
41
|
+
*/
|
|
42
|
+
/** Events that occupy a slot on the ordered message surface. */
|
|
43
|
+
const SURFACE_TYPES = new Set(['system/message', 'user/message', 'developer/message', 'assistant/message', 'tool/result']);
|
|
44
|
+
/** Seq of the live protected head: the LAST `system/message` on the surface (replacements shadow their source). */
|
|
45
|
+
function liveHeadSeq(events) {
|
|
46
|
+
let head;
|
|
47
|
+
for (const event of events) {
|
|
48
|
+
if (event.type === 'system/message' && event.surfaceOp !== undefined)
|
|
49
|
+
head = event.seq;
|
|
50
|
+
}
|
|
51
|
+
return head;
|
|
52
|
+
}
|
|
53
|
+
/** Whether any surface node exists yet. */
|
|
54
|
+
function surfaceIsEmpty(events) {
|
|
55
|
+
return !events.some(event => SURFACE_TYPES.has(event.type) && event.surfaceOp !== undefined);
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Wrap one bridge session with protected-head co-writing.
|
|
59
|
+
* @param session - the adapter's real Session.
|
|
60
|
+
* @param placeholder - builds the placeholder head message when the first
|
|
61
|
+
* user message arrives on an empty surface.
|
|
62
|
+
* @returns the session (transparently delegated) with `stampSystemPrompt`.
|
|
63
|
+
*/
|
|
64
|
+
export function withSystemHead(session, placeholder) {
|
|
65
|
+
// The one narrow this module performs: the loose face above is what makes
|
|
66
|
+
// the generic real Session assignable; every call below issues exactly the
|
|
67
|
+
// SystemHeadAppend shape.
|
|
68
|
+
const appendHead = session.append.bind(session);
|
|
69
|
+
const rawAppend = session.append.bind(session);
|
|
70
|
+
// Reader-mirrored turn/step state.
|
|
71
|
+
let turn = null;
|
|
72
|
+
let step = null;
|
|
73
|
+
let nextStep = 1;
|
|
74
|
+
let wrapOpenedStep = false;
|
|
75
|
+
let wrapClosedStep;
|
|
76
|
+
let headSeq;
|
|
77
|
+
let queuedStamp;
|
|
78
|
+
const openWrapStep = () => {
|
|
79
|
+
if (turn === null || step !== null)
|
|
80
|
+
return step !== null;
|
|
81
|
+
rawAppend('step/start', { turn, step: nextStep });
|
|
82
|
+
step = nextStep;
|
|
83
|
+
wrapOpenedStep = true;
|
|
84
|
+
return true;
|
|
85
|
+
};
|
|
86
|
+
const ensureHead = () => {
|
|
87
|
+
if (headSeq !== undefined)
|
|
88
|
+
return;
|
|
89
|
+
headSeq = liveHeadSeq(session.snapshotEvents());
|
|
90
|
+
if (headSeq !== undefined)
|
|
91
|
+
return;
|
|
92
|
+
if (!surfaceIsEmpty(session.snapshotEvents()))
|
|
93
|
+
return; // headless-but-valid legacy surface: never append here
|
|
94
|
+
if (!openWrapStep())
|
|
95
|
+
return; // no open turn: a head is impossible; stay headless
|
|
96
|
+
headSeq = appendHead('system/message', { turn: turn, step: step, message: placeholder() }, { surfaceOp: 'append' }).seq;
|
|
97
|
+
};
|
|
98
|
+
const stamp = (turnArg, stepArg, message) => {
|
|
99
|
+
if (step === null) {
|
|
100
|
+
// No open step (adapter bootstrapping before the first turn): queue and
|
|
101
|
+
// flush inside the step the adapter opens next.
|
|
102
|
+
queuedStamp = { turn: turnArg, step: stepArg, message };
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
if (headSeq === undefined) {
|
|
106
|
+
headSeq = liveHeadSeq(session.snapshotEvents());
|
|
107
|
+
if (headSeq === undefined && surfaceIsEmpty(session.snapshotEvents())) {
|
|
108
|
+
headSeq = appendHead('system/message', { turn: turnArg, step: stepArg, message }, { surfaceOp: 'append' }).seq;
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
if (headSeq === undefined) {
|
|
113
|
+
queuedStamp = { turn: turnArg, step: stepArg, message };
|
|
114
|
+
return; // headless legacy surface: stamping would corrupt it
|
|
115
|
+
}
|
|
116
|
+
headSeq = appendHead('system/message', { turn: turn ?? turnArg, step: step ?? stepArg, message }, { surfaceOp: { op: 'replace', startSeq: headSeq, endSeq: headSeq }, sourceEventSeqs: [headSeq] }).seq;
|
|
117
|
+
};
|
|
118
|
+
return new Proxy(session, {
|
|
119
|
+
get(target, property) {
|
|
120
|
+
if (property === 'stampSystemPrompt') {
|
|
121
|
+
return (turnArg, stepArg, message) => { stamp(turnArg, stepArg, message); };
|
|
122
|
+
}
|
|
123
|
+
if (property === 'append') {
|
|
124
|
+
return (type, data, opts) => {
|
|
125
|
+
if (type === 'turn/start') {
|
|
126
|
+
turn = data.turn;
|
|
127
|
+
nextStep = 1;
|
|
128
|
+
wrapOpenedStep = false;
|
|
129
|
+
wrapClosedStep = undefined;
|
|
130
|
+
}
|
|
131
|
+
else if (type === 'turn/end') {
|
|
132
|
+
// A step THIS wrap opened must not survive the turn boundary.
|
|
133
|
+
if (step !== null && wrapOpenedStep) {
|
|
134
|
+
rawAppend('step/end', { turn, step });
|
|
135
|
+
wrapClosedStep = step;
|
|
136
|
+
step = null;
|
|
137
|
+
nextStep += 1;
|
|
138
|
+
}
|
|
139
|
+
turn = null;
|
|
140
|
+
}
|
|
141
|
+
else if (type === 'step/start') {
|
|
142
|
+
const dataStep = data;
|
|
143
|
+
if (step !== null && wrapOpenedStep && dataStep.turn === turn && dataStep.step === step) {
|
|
144
|
+
// The adapter opened the step the wrap already wrote: drop the
|
|
145
|
+
// duplicate (the adapter's own counters stay consistent).
|
|
146
|
+
return { type, seq: session.snapshotEvents().length, time: Date.now(), data };
|
|
147
|
+
}
|
|
148
|
+
if (step === null && dataStep.step !== undefined)
|
|
149
|
+
step = dataStep.step;
|
|
150
|
+
wrapOpenedStep = false;
|
|
151
|
+
const applied = rawAppend(type, data);
|
|
152
|
+
if (queuedStamp !== undefined && step !== null) {
|
|
153
|
+
const pending = queuedStamp;
|
|
154
|
+
queuedStamp = undefined;
|
|
155
|
+
stamp(pending.turn, pending.step, pending.message);
|
|
156
|
+
}
|
|
157
|
+
return applied;
|
|
158
|
+
}
|
|
159
|
+
else if (type === 'step/end') {
|
|
160
|
+
const dataStep = data;
|
|
161
|
+
if (step === null && wrapClosedStep !== undefined && dataStep.step === wrapClosedStep) {
|
|
162
|
+
wrapClosedStep = undefined;
|
|
163
|
+
return { type, seq: session.snapshotEvents().length, time: Date.now(), data }; // the wrap already closed it
|
|
164
|
+
}
|
|
165
|
+
step = null;
|
|
166
|
+
if (dataStep.step !== undefined)
|
|
167
|
+
nextStep = dataStep.step + 1;
|
|
168
|
+
}
|
|
169
|
+
else if (type === 'user/message') {
|
|
170
|
+
ensureHead();
|
|
171
|
+
}
|
|
172
|
+
else if (type === 'system/message') {
|
|
173
|
+
// A direct adapter append (notices, bootstraps): give it the step
|
|
174
|
+
// context the reader demands — open the turn's next step when
|
|
175
|
+
// none is open and NORMALIZE the coordinates to that context
|
|
176
|
+
// (requireStep compares them for equality; a mismatched append
|
|
177
|
+
// is doomed otherwise). Surface ordering beyond the head rules
|
|
178
|
+
// stays the adapter's own responsibility.
|
|
179
|
+
if (step === null && turn !== null)
|
|
180
|
+
openWrapStep();
|
|
181
|
+
if (step !== null && turn !== null) {
|
|
182
|
+
const direct = data;
|
|
183
|
+
if (direct.turn !== turn || direct.step !== step)
|
|
184
|
+
data = { ...data, turn, step };
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
return Reflect.apply(target.append, target, [type, data, opts]);
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
const value = Reflect.get(target, property, target);
|
|
191
|
+
return typeof value === 'function' ? value.bind(target) : value;
|
|
192
|
+
},
|
|
193
|
+
});
|
|
194
|
+
}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Web page trust component — the browser-side twin of the connection row's
|
|
3
|
+
* server-side `trustedHosts` fence (ported from better-dsh `web-trust.ts`,
|
|
4
|
+
* authorities leg only).
|
|
5
|
+
*
|
|
6
|
+
* Why: when the Web UI is served on an operator-declared non-loopback
|
|
7
|
+
* authority (Caddy domain, LAN IP), the server side accepts the Host through
|
|
8
|
+
* `trustedHosts`, but the BROWSER-side connection client computes
|
|
9
|
+
* `ctx.connection.isLoopback` from `location.hostname` — non-loopback → the
|
|
10
|
+
* page degrades into the describe-mirror shape (Host settings persistence
|
|
11
|
+
* reports terminal `memory` unavailability). This component injects one
|
|
12
|
+
* inline `<head>` script through the webserver's public
|
|
13
|
+
* `webserver/index-inject` event (every index render re-emits; row data is
|
|
14
|
+
* read fresh at emit time, so a config reload needs no listener re-wiring):
|
|
15
|
+
* when the page's hostname is one the operator declared their own, it sets
|
|
16
|
+
* `window.__DSH_TRANSPORT__ = { ownsHost: true }` BEFORE any application
|
|
17
|
+
* bundle materializes — the connection client's only whole-repo consumer —
|
|
18
|
+
* restoring the upstream-documented shape for trusted authorities.
|
|
19
|
+
*
|
|
20
|
+
* The object deliberately carries NO fetch/openStream: the connection
|
|
21
|
+
* client's optional chaining then builds the default HTTP/WS transport, so
|
|
22
|
+
* the flag's only effect is the loopback verdict. An existing
|
|
23
|
+
* `__DSH_TRANSPORT__` (a worker shell that owns a real transport) is never
|
|
24
|
+
* overwritten.
|
|
25
|
+
*
|
|
26
|
+
* On/off (two independent levels, both standard):
|
|
27
|
+
* - row level: a profile layer with `- id: superd-web-trust / disabled: true`
|
|
28
|
+
* removes the component entirely;
|
|
29
|
+
* - config level: `trustedPageAuthorities: []` keeps the row mounted but
|
|
30
|
+
* injects nothing (fully inert).
|
|
31
|
+
*
|
|
32
|
+
* Composed on BOTH trees: ctx0 through the `super-dsh/web-trust` row (the
|
|
33
|
+
* composite bundle's insert list) and every world through the
|
|
34
|
+
* `@pgmi-builds/superd-hub/web-trust` row (world mount patches) — world pages
|
|
35
|
+
* emit `webserver/index-inject` on their own root, so a ctx0-only listener
|
|
36
|
+
* would never reach them.
|
|
37
|
+
*
|
|
38
|
+
* @module @pgmi-builds/superd-hub/web-trust
|
|
39
|
+
*/
|
|
40
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
41
|
+
import z from '@deepseek-ai/schemastery';
|
|
42
|
+
/**
|
|
43
|
+
* The schema-level default for `trustedPageAuthorities` (better-dsh v0.2.2a
|
|
44
|
+
* shape): derive from the trusted-hosts environment — the SAME single source
|
|
45
|
+
* the server-side fence legs read (`SUPERD_TRUSTED_HOSTS` on this line, upstream
|
|
46
|
+
* spelling `DSH_TRUSTED_HOSTS` honored first) — so ONE declaration (e.g. one
|
|
47
|
+
* `Environment=` line in the service unit) drives the server fence and the
|
|
48
|
+
* page leg with zero per-instance plugin config. Bare hostnames only:
|
|
49
|
+
* `location.hostname` carries no port, and {@link assertBareHostname} would
|
|
50
|
+
* reject a portful entry at boot, so portful/schematic entries are dropped
|
|
51
|
+
* here instead of thrown. Declared in the Config SCHEMA rather than the
|
|
52
|
+
* bundle patch row because a profile/home layer row with the same id
|
|
53
|
+
* whole-row-overrides the row's config object, while schema defaults fill
|
|
54
|
+
* per-key at plugin load and survive every overlay layer. An explicit
|
|
55
|
+
* configured value always wins.
|
|
56
|
+
*
|
|
57
|
+
* @param env - the raw trusted-hosts value (whitespace/comma separated).
|
|
58
|
+
* @returns the bare-hostname authorities to trust by default.
|
|
59
|
+
*/
|
|
60
|
+
export declare function deriveDefaultPageAuthorities(env: string | undefined): string[];
|
|
61
|
+
/** The `webserver/index-inject` row shape this component pushes. */
|
|
62
|
+
export interface BootScriptRow {
|
|
63
|
+
kind: 'script';
|
|
64
|
+
placement: 'head';
|
|
65
|
+
text: string;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Build the authorities-leg script. Pure: same config in, same script out —
|
|
69
|
+
* the unit tests pin the shape (authority match, no-overwrite guard,
|
|
70
|
+
* inert-when-empty, hostile-string escaping).
|
|
71
|
+
*
|
|
72
|
+
* @param trustedPageAuthorities - the operator-declared hostnames.
|
|
73
|
+
* @returns the script text, or `undefined` when the list is empty (inject
|
|
74
|
+
* nothing — the component is fully inert).
|
|
75
|
+
*/
|
|
76
|
+
export declare function buildTrustScript(trustedPageAuthorities: readonly string[] | undefined): string | undefined;
|
|
77
|
+
/** Row config schema. */
|
|
78
|
+
export interface TrustConfig {
|
|
79
|
+
/** Hostnames this operator declares their own devices' pages run on. */
|
|
80
|
+
trustedPageAuthorities?: readonly string[];
|
|
81
|
+
}
|
|
82
|
+
/** Row config schema (schema-level default derives from the trusted-hosts env). */
|
|
83
|
+
export declare const Config: z<Schemastery.ObjectS<NoInfer<{
|
|
84
|
+
trustedPageAuthorities: z<string[], string[], "defined">;
|
|
85
|
+
}>>, Schemastery.ObjectT<NoInfer<{
|
|
86
|
+
trustedPageAuthorities: z<string[], string[], "defined">;
|
|
87
|
+
}>>, "plain">;
|
|
88
|
+
/** Cordis plugin name. */
|
|
89
|
+
export declare const name = "superd-web-trust";
|
|
90
|
+
/**
|
|
91
|
+
* No static injects: `webServer` is composed conditionally inside
|
|
92
|
+
* {@link apply}, so the row loads (dormant) in compositions without one.
|
|
93
|
+
*/
|
|
94
|
+
export declare const inject: string[];
|
|
95
|
+
/**
|
|
96
|
+
* Mount the authorities-leg injection: one `webserver/index-inject` listener
|
|
97
|
+
* pushing the rendered row.
|
|
98
|
+
* @param ctx - host plugin context.
|
|
99
|
+
* @param config - the row config.
|
|
100
|
+
*/
|
|
101
|
+
export declare function apply(ctx: Context, config: TrustConfig | undefined): void;
|
|
102
|
+
declare const _default: {
|
|
103
|
+
name: string;
|
|
104
|
+
inject: string[];
|
|
105
|
+
Config: z<Schemastery.ObjectS<NoInfer<{
|
|
106
|
+
trustedPageAuthorities: z<string[], string[], "defined">;
|
|
107
|
+
}>>, Schemastery.ObjectT<NoInfer<{
|
|
108
|
+
trustedPageAuthorities: z<string[], string[], "defined">;
|
|
109
|
+
}>>, "plain">;
|
|
110
|
+
apply: typeof apply;
|
|
111
|
+
};
|
|
112
|
+
export default _default;
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Web page trust component — the browser-side twin of the connection row's
|
|
3
|
+
* server-side `trustedHosts` fence (ported from better-dsh `web-trust.ts`,
|
|
4
|
+
* authorities leg only).
|
|
5
|
+
*
|
|
6
|
+
* Why: when the Web UI is served on an operator-declared non-loopback
|
|
7
|
+
* authority (Caddy domain, LAN IP), the server side accepts the Host through
|
|
8
|
+
* `trustedHosts`, but the BROWSER-side connection client computes
|
|
9
|
+
* `ctx.connection.isLoopback` from `location.hostname` — non-loopback → the
|
|
10
|
+
* page degrades into the describe-mirror shape (Host settings persistence
|
|
11
|
+
* reports terminal `memory` unavailability). This component injects one
|
|
12
|
+
* inline `<head>` script through the webserver's public
|
|
13
|
+
* `webserver/index-inject` event (every index render re-emits; row data is
|
|
14
|
+
* read fresh at emit time, so a config reload needs no listener re-wiring):
|
|
15
|
+
* when the page's hostname is one the operator declared their own, it sets
|
|
16
|
+
* `window.__DSH_TRANSPORT__ = { ownsHost: true }` BEFORE any application
|
|
17
|
+
* bundle materializes — the connection client's only whole-repo consumer —
|
|
18
|
+
* restoring the upstream-documented shape for trusted authorities.
|
|
19
|
+
*
|
|
20
|
+
* The object deliberately carries NO fetch/openStream: the connection
|
|
21
|
+
* client's optional chaining then builds the default HTTP/WS transport, so
|
|
22
|
+
* the flag's only effect is the loopback verdict. An existing
|
|
23
|
+
* `__DSH_TRANSPORT__` (a worker shell that owns a real transport) is never
|
|
24
|
+
* overwritten.
|
|
25
|
+
*
|
|
26
|
+
* On/off (two independent levels, both standard):
|
|
27
|
+
* - row level: a profile layer with `- id: superd-web-trust / disabled: true`
|
|
28
|
+
* removes the component entirely;
|
|
29
|
+
* - config level: `trustedPageAuthorities: []` keeps the row mounted but
|
|
30
|
+
* injects nothing (fully inert).
|
|
31
|
+
*
|
|
32
|
+
* Composed on BOTH trees: ctx0 through the `super-dsh/web-trust` row (the
|
|
33
|
+
* composite bundle's insert list) and every world through the
|
|
34
|
+
* `@pgmi-builds/superd-hub/web-trust` row (world mount patches) — world pages
|
|
35
|
+
* emit `webserver/index-inject` on their own root, so a ctx0-only listener
|
|
36
|
+
* would never reach them.
|
|
37
|
+
*
|
|
38
|
+
* @module @pgmi-builds/superd-hub/web-trust
|
|
39
|
+
*/
|
|
40
|
+
import z from '@deepseek-ai/schemastery';
|
|
41
|
+
/**
|
|
42
|
+
* The schema-level default for `trustedPageAuthorities` (better-dsh v0.2.2a
|
|
43
|
+
* shape): derive from the trusted-hosts environment — the SAME single source
|
|
44
|
+
* the server-side fence legs read (`SUPERD_TRUSTED_HOSTS` on this line, upstream
|
|
45
|
+
* spelling `DSH_TRUSTED_HOSTS` honored first) — so ONE declaration (e.g. one
|
|
46
|
+
* `Environment=` line in the service unit) drives the server fence and the
|
|
47
|
+
* page leg with zero per-instance plugin config. Bare hostnames only:
|
|
48
|
+
* `location.hostname` carries no port, and {@link assertBareHostname} would
|
|
49
|
+
* reject a portful entry at boot, so portful/schematic entries are dropped
|
|
50
|
+
* here instead of thrown. Declared in the Config SCHEMA rather than the
|
|
51
|
+
* bundle patch row because a profile/home layer row with the same id
|
|
52
|
+
* whole-row-overrides the row's config object, while schema defaults fill
|
|
53
|
+
* per-key at plugin load and survive every overlay layer. An explicit
|
|
54
|
+
* configured value always wins.
|
|
55
|
+
*
|
|
56
|
+
* @param env - the raw trusted-hosts value (whitespace/comma separated).
|
|
57
|
+
* @returns the bare-hostname authorities to trust by default.
|
|
58
|
+
*/
|
|
59
|
+
export function deriveDefaultPageAuthorities(env) {
|
|
60
|
+
return (env ?? '').split(/[\s,]+/).filter(h => h.length > 0 && !/[:/@?#]/.test(h));
|
|
61
|
+
}
|
|
62
|
+
/** Assert one configured authority is a bare hostname (no port/path/scheme). */
|
|
63
|
+
function assertBareHostname(entry) {
|
|
64
|
+
if (entry.length === 0 || /[:/@?#]/.test(entry)) {
|
|
65
|
+
throw new Error(`superd-web-trust: trustedPageAuthorities entry ${JSON.stringify(entry)} is not a bare hostname `
|
|
66
|
+
+ '(location.hostname carries no port; an entry with port/path would never match)');
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
/** The IIFE framing (a throwing leg stays invisible on the page). */
|
|
70
|
+
function frame(body) {
|
|
71
|
+
return ['(function(){try{', ...body, '}catch(e){}})();'].join('');
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Build the authorities-leg script. Pure: same config in, same script out —
|
|
75
|
+
* the unit tests pin the shape (authority match, no-overwrite guard,
|
|
76
|
+
* inert-when-empty, hostile-string escaping).
|
|
77
|
+
*
|
|
78
|
+
* @param trustedPageAuthorities - the operator-declared hostnames.
|
|
79
|
+
* @returns the script text, or `undefined` when the list is empty (inject
|
|
80
|
+
* nothing — the component is fully inert).
|
|
81
|
+
*/
|
|
82
|
+
export function buildTrustScript(trustedPageAuthorities) {
|
|
83
|
+
const authorities = [...(trustedPageAuthorities ?? [])];
|
|
84
|
+
if (authorities.length === 0)
|
|
85
|
+
return undefined;
|
|
86
|
+
for (const entry of authorities)
|
|
87
|
+
assertBareHostname(entry);
|
|
88
|
+
return frame([
|
|
89
|
+
'if(!window.__DSH_TRANSPORT__){',
|
|
90
|
+
`var h=location.hostname,a=${JSON.stringify(authorities)};`,
|
|
91
|
+
'for(var i=0;i<a.length;i++)if(a[i]===h){window.__DSH_TRANSPORT__={ownsHost:true};break}',
|
|
92
|
+
'}',
|
|
93
|
+
]);
|
|
94
|
+
}
|
|
95
|
+
/** The trusted-hosts single source: upstream spelling first, this line's second. */
|
|
96
|
+
function trustedHostsEnv() {
|
|
97
|
+
return process.env.DSH_TRUSTED_HOSTS ?? process.env.SUPERD_TRUSTED_HOSTS;
|
|
98
|
+
}
|
|
99
|
+
/** Row config schema (schema-level default derives from the trusted-hosts env). */
|
|
100
|
+
export const Config = z.object({
|
|
101
|
+
trustedPageAuthorities: z.array(String).default(deriveDefaultPageAuthorities(trustedHostsEnv())),
|
|
102
|
+
});
|
|
103
|
+
/** Cordis plugin name. */
|
|
104
|
+
export const name = 'superd-web-trust';
|
|
105
|
+
/**
|
|
106
|
+
* No static injects: `webServer` is composed conditionally inside
|
|
107
|
+
* {@link apply}, so the row loads (dormant) in compositions without one.
|
|
108
|
+
*/
|
|
109
|
+
export const inject = [];
|
|
110
|
+
/**
|
|
111
|
+
* Mount the authorities-leg injection: one `webserver/index-inject` listener
|
|
112
|
+
* pushing the rendered row.
|
|
113
|
+
* @param ctx - host plugin context.
|
|
114
|
+
* @param config - the row config.
|
|
115
|
+
*/
|
|
116
|
+
export function apply(ctx, config) {
|
|
117
|
+
const authorities = config?.trustedPageAuthorities;
|
|
118
|
+
ctx.inject(['webServer'], () => {
|
|
119
|
+
ctx.on('webserver/index-inject', (table) => {
|
|
120
|
+
const text = buildTrustScript(authorities);
|
|
121
|
+
if (text !== undefined)
|
|
122
|
+
table.push({ kind: 'script', placement: 'head', text });
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
export default { name, inject, Config, apply };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* World-side mount plugin (AW-B DL1/DL3) — `@pgmi-builds/
|
|
2
|
+
* World-side mount plugin (AW-B DL1/DL3) — `@pgmi-builds/superd-hub/world`.
|
|
3
3
|
*
|
|
4
4
|
* Inserted into the WORLD root's composition (its own process-side root, no
|
|
5
5
|
* listener). It provides the virtual `webServer` for that root: every route the
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
*/
|
|
20
20
|
import type { Context } from '@deepseek-ai/cordis';
|
|
21
21
|
/** Stable Cordis plugin name. */
|
|
22
|
-
export declare const name = "
|
|
22
|
+
export declare const name = "superd-world-mount";
|
|
23
23
|
/** Row config: which world this root is. */
|
|
24
24
|
export interface Config {
|
|
25
25
|
key: string;
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
import { noteWorldServer, publishWorldFetch, takeHostMount } from './world-host.js';
|
|
2
2
|
import { WorldWebServer } from './world-web-server.js';
|
|
3
3
|
/** Stable Cordis plugin name. */
|
|
4
|
-
export const name = '
|
|
4
|
+
export const name = 'superd-world-mount';
|
|
5
5
|
/** Apply the world mount. @param ctx - the WORLD root context. @param config - `{ key }`. */
|
|
6
6
|
export function apply(ctx, config) {
|
|
7
7
|
const key = typeof config?.key === 'string' ? config.key : undefined;
|
|
8
8
|
if (key === undefined) {
|
|
9
|
-
ctx.logger?.warn?.('
|
|
9
|
+
ctx.logger?.warn?.('superd-world-mount: missing config.key — world not mounted');
|
|
10
10
|
return;
|
|
11
11
|
}
|
|
12
12
|
const mount = takeHostMount(key);
|
|
13
13
|
if (mount === undefined) {
|
|
14
|
-
ctx.logger?.warn?.(`
|
|
14
|
+
ctx.logger?.warn?.(`superd-world-mount: no host mount registered for key ${JSON.stringify(key)}`);
|
|
15
15
|
return;
|
|
16
16
|
}
|
|
17
17
|
const worldWebServer = new WorldWebServer(mount.labelPath, mount.real, {
|
|
@@ -46,6 +46,6 @@ export function apply(ctx, config) {
|
|
|
46
46
|
ctx.effect(() => () => {
|
|
47
47
|
worldWebServer.dispose();
|
|
48
48
|
dropWorldFetch?.();
|
|
49
|
-
}, '
|
|
49
|
+
}, 'superd-world-mount: virtual webServer');
|
|
50
50
|
}
|
|
51
51
|
export default apply;
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* A world runs as its OWN cordis ROOT (events are root-scoped — probe
|
|
5
5
|
* 2026-09-16), so ctx0 and the world cannot share service instances through
|
|
6
6
|
* the context tree. They DO share this module (the profile links
|
|
7
|
-
* `@pgmi-builds/
|
|
7
|
+
* `@pgmi-builds/superd-hub` at the same path), which is where the host hands the
|
|
8
8
|
* world everything it needs to mount itself:
|
|
9
9
|
*
|
|
10
10
|
* ctx0 hub → registerHostMount({ key, labelPath, real }) (before spawn)
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/** Stable Cordis plugin name. */
|
|
2
|
-
export declare const name = "
|
|
2
|
+
export declare const name = "superd-join";
|
|
3
3
|
/** Row config: which world to join, under which roster label. */
|
|
4
4
|
export interface Config {
|
|
5
5
|
key: string;
|
|
@@ -10,6 +10,7 @@ interface HostContext {
|
|
|
10
10
|
provide(name: string, value: unknown): void;
|
|
11
11
|
inject(deps: string[], callback: (ctx: unknown) => void): unknown;
|
|
12
12
|
effect?(callback: () => () => void, label?: string): unknown;
|
|
13
|
+
get(name: string): unknown;
|
|
13
14
|
}
|
|
14
15
|
/** Apply the join. @param ctx - the CTX0 root context. @param config - `{ key, label? }`. */
|
|
15
16
|
export declare function apply(ctx: HostContext, config?: Partial<Config>): void;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Hub-side world joiner (AW-B DL1–DL5) — `@pgmi-builds/
|
|
2
|
+
* Hub-side world joiner (AW-B DL1–DL5) — `@pgmi-builds/superd-hub/join`.
|
|
3
3
|
*
|
|
4
4
|
* Ruling R2 (2026-09-16): an adapter may ship STANDALONE only — joining the
|
|
5
|
-
*
|
|
5
|
+
* super-dsh hub is the HUB side's job. This plugin is that job, generic:
|
|
6
6
|
* the ctx0 composition inserts one row with `{ key }` and the hub does the
|
|
7
7
|
* spawn/roster/mount wiring the adapter's own world plugin would otherwise
|
|
8
8
|
* carry (the omp/codex adapters ship active per-adapter plugins; the claude
|
|
@@ -31,19 +31,17 @@ import { join } from 'node:path';
|
|
|
31
31
|
import { registerAgent, setReady } from './roster.js';
|
|
32
32
|
import { registerForeignTarget } from './targets.js';
|
|
33
33
|
import { spawnWorld } from './spawn-world.js';
|
|
34
|
-
import { provisionWorldProfile } from './world-provision.js';
|
|
34
|
+
import { provisionWorldProfile, resolveInstallAnchor } from './world-provision.js';
|
|
35
35
|
import { registerHostMount } from './world-host.js';
|
|
36
36
|
import { mountWorld } from './carrier.js';
|
|
37
37
|
import { worldMountPatches } from './world-mount.js';
|
|
38
38
|
/** Stable Cordis plugin name. */
|
|
39
|
-
export const name = '
|
|
40
|
-
const ANCHOR = process.env.SUPERD_DSH_ANCHOR
|
|
41
|
-
?? '/home/u1/.local/lib/node_modules/@deepseek-ai/dsh/package.json';
|
|
39
|
+
export const name = 'superd-join';
|
|
42
40
|
/** Apply the join. @param ctx - the CTX0 root context. @param config - `{ key, label? }`. */
|
|
43
41
|
export function apply(ctx, config) {
|
|
44
42
|
const key = typeof config?.key === 'string' ? config.key : undefined;
|
|
45
43
|
if (key === undefined || key === '' || key.includes('/')) {
|
|
46
|
-
ctx.provide('
|
|
44
|
+
ctx.provide('superd.world.join.error', `invalid config.key ${JSON.stringify(config?.key)}`);
|
|
47
45
|
return;
|
|
48
46
|
}
|
|
49
47
|
const label = typeof config?.label === 'string' && config.label !== '' ? config.label : key;
|
|
@@ -58,12 +56,10 @@ export function apply(ctx, config) {
|
|
|
58
56
|
const worldHome = join(root, 'agents', key);
|
|
59
57
|
mkdirSync(join(worldHome, 'profiles'), { recursive: true });
|
|
60
58
|
// Publish story: a foreign install has no line bootstrap, so the nested
|
|
61
|
-
// profile + @pgmi-builds links are provisioned HERE (idempotent
|
|
62
|
-
|
|
63
|
-
// overrides the resolved base).
|
|
64
|
-
const bareBase = provisionWorldProfile({
|
|
59
|
+
// profile + @pgmi-builds links are provisioned HERE (idempotent).
|
|
60
|
+
provisionWorldProfile({
|
|
65
61
|
key,
|
|
66
|
-
adapterPkg: '@pgmi-builds/agent-
|
|
62
|
+
adapterPkg: '@pgmi-builds/agent-claude',
|
|
67
63
|
worldHome,
|
|
68
64
|
});
|
|
69
65
|
const world = (async () => {
|
|
@@ -84,16 +80,15 @@ export function apply(ctx, config) {
|
|
|
84
80
|
authorize: (req, res) => connection.authorizeIndex(req, res),
|
|
85
81
|
});
|
|
86
82
|
const ctxW = await spawnWorld({
|
|
87
|
-
appName: `
|
|
83
|
+
appName: `superd-${key}`,
|
|
88
84
|
profileName: 'web',
|
|
89
|
-
installAnchor:
|
|
85
|
+
installAnchor: resolveInstallAnchor(ctx), // lazy: never at module scope — import must stay side-effect-free
|
|
90
86
|
home: worldHome,
|
|
91
87
|
dataHome: worldHome,
|
|
92
|
-
//
|
|
93
|
-
//
|
|
94
|
-
//
|
|
95
|
-
|
|
96
|
-
bareModuleBaseUrl: process.env.AW_BARE_BASE ?? bareBase ?? undefined,
|
|
88
|
+
// The host's live table carries this world: spawnWorld republishes a
|
|
89
|
+
// widened successor before boot imports anything (spec §2.1), and
|
|
90
|
+
// @pgmi-builds rows resolve from the profile-local links written above.
|
|
91
|
+
host: ctx,
|
|
97
92
|
extraPatches: worldMountPatches(key),
|
|
98
93
|
});
|
|
99
94
|
const gateway = ctxW.get('typertGateway');
|
|
@@ -109,7 +104,7 @@ export function apply(ctx, config) {
|
|
|
109
104
|
ctx.effect?.(() => () => {
|
|
110
105
|
mounted.dispose();
|
|
111
106
|
dropHostMount();
|
|
112
|
-
}, `
|
|
107
|
+
}, `superd-join: ${key} mount`);
|
|
113
108
|
return ctxW;
|
|
114
109
|
})().catch((cause) => {
|
|
115
110
|
// A failed world degrades to a not-ready roster row; a rejected provided
|
|
@@ -117,6 +112,6 @@ export function apply(ctx, config) {
|
|
|
117
112
|
console.error(`[super-dsh] ${key} world failed:`, cause);
|
|
118
113
|
return null;
|
|
119
114
|
});
|
|
120
|
-
ctx.provide(`
|
|
115
|
+
ctx.provide(`superd.world.${key}`, world);
|
|
121
116
|
}
|
|
122
117
|
export default apply;
|