super-dsh 0.1.4 → 0.1.5
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 +6 -7
- package/agent-agy/dist/world-plugin.js +18 -33
- package/agent-codex/dist/world-plugin.js +18 -33
- package/agent-hermes/dist/world-plugin.js +18 -34
- package/agent-omp/dist/world-plugin.js +16 -32
- package/agent-pi/dist/world-plugin.js +18 -33
- package/client/index.js +2086 -0
- package/cordis.patch.yml +20 -7
- package/package.json +13 -3
- package/superd-hub/dist/agent-roster.d.ts +3 -1
- package/superd-hub/dist/agent-roster.js +4 -2
- package/superd-hub/dist/carrier.d.ts +41 -28
- package/superd-hub/dist/carrier.js +148 -202
- package/superd-hub/dist/fusion-transport.d.ts +148 -0
- package/superd-hub/dist/fusion-transport.js +397 -0
- package/superd-hub/dist/gateway.d.ts +7 -2
- package/superd-hub/dist/gateway.js +28 -2
- package/superd-hub/dist/hub-route.d.ts +74 -0
- package/superd-hub/dist/hub-route.js +188 -0
- package/superd-hub/dist/index.d.ts +10 -4
- package/superd-hub/dist/index.js +10 -4
- package/superd-hub/dist/roster.d.ts +2 -2
- package/superd-hub/dist/spawn-world.js +5 -3
- package/superd-hub/dist/switch-core.d.ts +630 -0
- package/superd-hub/dist/switch-core.js +734 -0
- package/superd-hub/dist/web-trust.d.ts +5 -5
- package/superd-hub/dist/web-trust.js +5 -5
- package/superd-hub/dist/world-entry.d.ts +20 -13
- package/superd-hub/dist/world-entry.js +15 -19
- package/superd-hub/dist/world-host.d.ts +15 -20
- package/superd-hub/dist/world-host.js +25 -4
- package/superd-hub/dist/world-join.js +14 -29
- package/superd-hub/dist/world-mount.d.ts +48 -26
- package/superd-hub/dist/world-mount.js +59 -27
- package/superd-hub/dist/world-provision.js +11 -4
- package/superd-hub/dist/world-web-server.d.ts +57 -106
- package/superd-hub/dist/world-web-server.js +61 -216
- package/superd-hub/lib/client/index.js +2086 -0
- package/superd-hub/package.json +12 -0
- package/agent-agy/dist/agent-preset-projection.d.ts +0 -31
- package/agent-agy/dist/agent-preset-projection.js +0 -33
- package/agent-claude/dist/agent-preset-projection.js +0 -30
- package/agent-codex/dist/agent-preset-projection.js +0 -37
- package/agent-codex/dist/app-home.js +0 -65
- package/agent-codex/dist/spike.js +0 -46
- package/agent-hermes/dist/agent-preset-projection.js +0 -34
- package/agent-omp/dist/agent-preset-projection.js +0 -36
- package/agent-omp/dist/app-home.js +0 -86
- package/agent-pi/dist/agent-preset-projection.js +0 -29
- package/superd-hub/dist/client-shim.d.ts +0 -28
- package/superd-hub/dist/client-shim.js +0 -251
- package/superd-hub/dist/index-pass.d.ts +0 -22
- package/superd-hub/dist/index-pass.js +0 -47
- package/superd-hub/dist/labels.d.ts +0 -45
- package/superd-hub/dist/labels.js +0 -83
|
@@ -1,25 +1,35 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Per-world virtual webServer (AW-B DL3
|
|
2
|
+
* Per-world virtual webServer (AW-B DL3; path-separation T-C silent sink →
|
|
3
|
+
* T-D registration-only shell).
|
|
3
4
|
*
|
|
4
|
-
* A world's plugins
|
|
5
|
-
* `/
|
|
6
|
-
*
|
|
7
|
-
*
|
|
5
|
+
* A world's plugins still declare ANY path they like (`/api`, `/plugins`,
|
|
6
|
+
* `/whatever-name-i-like`), and the world root still needs a `webServer`
|
|
7
|
+
* provider for them to bind (the `modules` row injects it; `worldMountPatches`
|
|
8
|
+
* pins that inject). It used to translate every registration onto ctx0's real
|
|
9
|
+
* webServer as `/<label>+path` — the per-label page family. T-C (2026-09-26,
|
|
10
|
+
* user ruling "strip off the per agent url path … no need the url path
|
|
11
|
+
* fallback (we have git history)") severed that: every route a world plugin
|
|
12
|
+
* declares is accepted and SWALLOWED — no `/<label>/…` route ever lands on
|
|
13
|
+
* ctx0's real webServer. T-D (2026-09-26, "disable their webui build, just
|
|
14
|
+
* like disable their webserver") then disabled the world webui rows
|
|
15
|
+
* (`web-runtime`/`client-hmr`, see `worldMountPatches`) and retired the whole
|
|
16
|
+
* index face with them: no `renderIndex`, no taps, no index injections, no
|
|
17
|
+
* client mount shim — nothing renders a world page anywhere, and the mount
|
|
18
|
+
* HTML pass (`index-pass.ts`) plus the shim retired with the last consumer.
|
|
8
19
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
20
|
+
* What REMAINS and why (the data-plane coupling the guardrail protects):
|
|
21
|
+
* - the `webServer` provider itself: world-tree rows (`modules`, adapter
|
|
22
|
+
* rows) inject/bind it; removing the provider would pend them and fail
|
|
23
|
+
* the world boot;
|
|
24
|
+
* - `stripLabelFromUrl`/`RealWebServerFace`: the `/hub` family
|
|
25
|
+
* (`hub-route.ts`) types ctx0's real server and strips the `/hub` prefix
|
|
26
|
+
* with the same URL rule;
|
|
27
|
+
* - the neutral `port`/`host` getters: the shape every remaining reader
|
|
28
|
+
* sees (a world owns no socket).
|
|
11
29
|
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
* - two worlds may both own `/api` (they become `/omp/api`, `/codex/api`);
|
|
15
|
-
* - ctx0 only ever sees the label namespaces, so a world cannot collide with
|
|
16
|
-
* ctx0's own routes.
|
|
17
|
-
*
|
|
18
|
-
* Index rows stay world-local: this instance owns its taps and emits nothing
|
|
19
|
-
* on the shared tree (worlds run as their own cordis ROOTS, whose event buses
|
|
20
|
-
* are isolated — probe 2026-09-16: emit on root B never reaches root A).
|
|
30
|
+
* The RPC channel is hub-owned: the `/hub` family (`hub-route.ts`) is the
|
|
31
|
+
* sole foreign entry.
|
|
21
32
|
*/
|
|
22
|
-
import { type IndexInjection } from '@deepseek-ai/dsh-host-webserver';
|
|
23
33
|
/** Route match kind (mirrors the upstream contract). */
|
|
24
34
|
export type WebRouteKind = 'exact' | 'prefix';
|
|
25
35
|
/** The slice of node:http we touch (kept structural for testability). */
|
|
@@ -43,7 +53,11 @@ export interface WorldWebUpgradeRoute {
|
|
|
43
53
|
path: string;
|
|
44
54
|
handler: (req: WorldRequest, socket: unknown, head: Buffer) => void | Promise<void>;
|
|
45
55
|
}
|
|
46
|
-
/**
|
|
56
|
+
/**
|
|
57
|
+
* The real (ctx0) webServer surface — since T-C no longer reachable from a
|
|
58
|
+
* world mount. The type stays for the `/hub` family (`hub-route.ts`), which
|
|
59
|
+
* registers on ctx0's real webServer directly.
|
|
60
|
+
*/
|
|
47
61
|
export interface RealWebServerFace {
|
|
48
62
|
register(route: {
|
|
49
63
|
kind: WebRouteKind;
|
|
@@ -59,104 +73,41 @@ export interface RealWebServerFace {
|
|
|
59
73
|
readonly port?: number;
|
|
60
74
|
readonly host?: string;
|
|
61
75
|
}
|
|
62
|
-
/** Options: how this world collects its own index rows. */
|
|
63
|
-
export interface WorldWebServerOptions {
|
|
64
|
-
/**
|
|
65
|
-
* Push the world's index injection rows. The world plugin supplies a
|
|
66
|
-
* function that emits on the WORLD root (never on ctx0's tree).
|
|
67
|
-
*/
|
|
68
|
-
onIndexInject?(table: IndexInjection[]): void;
|
|
69
|
-
/**
|
|
70
|
-
* ctx0's index authorization, applied to every route this mount forwards
|
|
71
|
-
* (pages, assets, fallback): the SAME gate native uses, so one cookie opens
|
|
72
|
-
* everything under the origin. The world owns no listener and the browser
|
|
73
|
-
* reaches it only through this mount, so authentication is decided HERE, in
|
|
74
|
-
* ctx0's auth domain — never by the world's own browser-auth gate. A false
|
|
75
|
-
* return means the authorizer already wrote the response (401 / 303).
|
|
76
|
-
*/
|
|
77
|
-
authorize?(req: WorldRequest, res: WorldResponse): boolean;
|
|
78
|
-
}
|
|
79
76
|
/** Remove the mount label from `req.url`, preserving the trailing query. */
|
|
80
77
|
export declare function stripLabelFromUrl(url: string | undefined, labelPath: string): string;
|
|
81
|
-
/**
|
|
78
|
+
/**
|
|
79
|
+
* One world's web surface — a registration-only shell: it accepts every
|
|
80
|
+
* registration a world plugin makes (so the tree boots and composes exactly
|
|
81
|
+
* as before) and forwards none of them anywhere; nothing is stored, nothing
|
|
82
|
+
* is served (T-C), and nothing renders (T-D).
|
|
83
|
+
*/
|
|
82
84
|
export declare class WorldWebServer {
|
|
83
85
|
readonly labelPath: string;
|
|
84
|
-
private readonly real;
|
|
85
|
-
private readonly options;
|
|
86
|
-
private readonly disposers;
|
|
87
|
-
private readonly taps;
|
|
88
|
-
/**
|
|
89
|
-
* @param labelPath - mount root, e.g. `/omp` (no trailing slash).
|
|
90
|
-
* @param real - the ctx0 webServer this world's routes forward into.
|
|
91
|
-
* @param options - world-local index row source.
|
|
92
|
-
*/
|
|
93
|
-
constructor(labelPath: string, real: RealWebServerFace, options?: WorldWebServerOptions);
|
|
94
|
-
/** The ctx0 listener port (read-through; the world owns no socket). */
|
|
95
|
-
get port(): number;
|
|
96
|
-
/** The ctx0 bind host (read-through). */
|
|
97
|
-
get host(): string;
|
|
98
86
|
/**
|
|
99
|
-
*
|
|
100
|
-
*
|
|
101
|
-
* domain, so the world's own connection/gateway registrations are swallowed
|
|
102
|
-
* here (returning a no-op disposer) instead of colliding on the real table.
|
|
103
|
-
* Non-envelope GET/HEAD traffic on `/<label>/api` still reaches the world's
|
|
104
|
-
* exact Fetch routes: `world-entry` publishes the world connection's shared
|
|
105
|
-
* `/api` fetch handler through `world-host`, and the carrier forwards into
|
|
106
|
-
* it AFTER ctx0's fence — the world's own browser-auth gate is never in the
|
|
107
|
-
* path (a different tree, a different secret).
|
|
87
|
+
* @param labelPath - mount root, e.g. `/omp` (no trailing slash) — the
|
|
88
|
+
* world's identity record; no page is ever served under it.
|
|
108
89
|
*/
|
|
109
|
-
|
|
90
|
+
constructor(labelPath: string);
|
|
110
91
|
/**
|
|
111
|
-
*
|
|
112
|
-
*
|
|
113
|
-
*
|
|
114
|
-
* in-ctx through this mount, and its cookie secret belongs to a different
|
|
115
|
-
* tree (every runtime is nested with its own home). So the decision is made
|
|
116
|
-
* here, with ctx0's connection: the same cookie that authenticated `<label>`
|
|
117
|
-
* in ctx0 authorizes its pages and assets.
|
|
92
|
+
* The ctx0 listener facts (read-through retired with the mount): worlds
|
|
93
|
+
* keep the same neutral defaults the disconnected read-through always
|
|
94
|
+
* fell back to — a world owns no socket and no page anyone can hit.
|
|
118
95
|
*/
|
|
119
|
-
|
|
120
|
-
/**
|
|
121
|
-
|
|
122
|
-
/** Register a world upgrade route (verbatim exact path; hub-owned RPC paths are swallowed). */
|
|
123
|
-
registerUpgrade(route: WorldWebUpgradeRoute): () => void;
|
|
124
|
-
/**
|
|
125
|
-
* Claim the world's SPA fallback: the ctx0 fallback seat is already owned by
|
|
126
|
-
* native frontend-static, so the world's fallback lives as a PREFIX route on
|
|
127
|
-
* the mount path (`/<label>`), which also keeps it inside the label namespace.
|
|
128
|
-
*/
|
|
129
|
-
registerFallback(handler: (req: WorldRequest, res: WorldResponse) => void | Promise<void>): () => void;
|
|
96
|
+
get port(): number;
|
|
97
|
+
/** The neutral bind host (see {@link WorldWebServer.port}). */
|
|
98
|
+
get host(): string;
|
|
130
99
|
/**
|
|
131
|
-
*
|
|
132
|
-
*
|
|
133
|
-
*
|
|
134
|
-
* `renderIndex`, and the FIRST `<base>` wins in a document, so the mount
|
|
135
|
-
* base is re-pinned on the bytes actually sent;
|
|
136
|
-
* 2. the agent roster is injected LAST — its values are the mount paths of
|
|
137
|
-
* OTHER worlds (`/`, `/omp/`, `/codex/`), which the mount pass above
|
|
138
|
-
* would otherwise re-root under this world's label.
|
|
100
|
+
* Accept one world route — and swallow it (T-C): pages have no road to
|
|
101
|
+
* ctx0. The disposer is a no-op, matching the swallowed-registration
|
|
102
|
+
* contract the `/api` family always had.
|
|
139
103
|
*/
|
|
140
|
-
|
|
141
|
-
/**
|
|
142
|
-
|
|
143
|
-
/** Apply this world's taps in registration order. */
|
|
144
|
-
applyIndexTaps(html: string): string;
|
|
145
|
-
/** Collect this world's structured rows (supplied by the world root). */
|
|
146
|
-
collectIndexInjections(): IndexInjection[];
|
|
104
|
+
register(_route: WorldWebRoute): () => void;
|
|
105
|
+
/** Accept one world upgrade route — and swallow it (see {@link register}). */
|
|
106
|
+
registerUpgrade(_route: WorldWebUpgradeRoute): () => void;
|
|
147
107
|
/**
|
|
148
|
-
*
|
|
149
|
-
*
|
|
150
|
-
*
|
|
151
|
-
*
|
|
152
|
-
* The agent roster is injected LAST, after the mount pass: its values are
|
|
153
|
-
* mount paths of OTHER worlds (`/`, `/omp/`, `/codex/`), and the pass would
|
|
154
|
-
* happily re-root them under this world's label.
|
|
108
|
+
* Accept the world's SPA fallback claim — and swallow it (see
|
|
109
|
+
* {@link register}): the ctx0 fallback seat was always native
|
|
110
|
+
* frontend-static's alone.
|
|
155
111
|
*/
|
|
156
|
-
|
|
157
|
-
/** Unregister every translated route (world unloaded). */
|
|
158
|
-
dispose(): void;
|
|
159
|
-
/** Wrap a plugin handler so it sees the world-relative URL. */
|
|
160
|
-
private strip;
|
|
161
|
-
private track;
|
|
112
|
+
registerFallback(_handler: (req: WorldRequest, res: WorldResponse) => void | Promise<void>): () => void;
|
|
162
113
|
}
|
|
@@ -1,34 +1,35 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Per-world virtual webServer (AW-B DL3
|
|
2
|
+
* Per-world virtual webServer (AW-B DL3; path-separation T-C silent sink →
|
|
3
|
+
* T-D registration-only shell).
|
|
3
4
|
*
|
|
4
|
-
* A world's plugins
|
|
5
|
-
* `/
|
|
6
|
-
*
|
|
7
|
-
*
|
|
5
|
+
* A world's plugins still declare ANY path they like (`/api`, `/plugins`,
|
|
6
|
+
* `/whatever-name-i-like`), and the world root still needs a `webServer`
|
|
7
|
+
* provider for them to bind (the `modules` row injects it; `worldMountPatches`
|
|
8
|
+
* pins that inject). It used to translate every registration onto ctx0's real
|
|
9
|
+
* webServer as `/<label>+path` — the per-label page family. T-C (2026-09-26,
|
|
10
|
+
* user ruling "strip off the per agent url path … no need the url path
|
|
11
|
+
* fallback (we have git history)") severed that: every route a world plugin
|
|
12
|
+
* declares is accepted and SWALLOWED — no `/<label>/…` route ever lands on
|
|
13
|
+
* ctx0's real webServer. T-D (2026-09-26, "disable their webui build, just
|
|
14
|
+
* like disable their webserver") then disabled the world webui rows
|
|
15
|
+
* (`web-runtime`/`client-hmr`, see `worldMountPatches`) and retired the whole
|
|
16
|
+
* index face with them: no `renderIndex`, no taps, no index injections, no
|
|
17
|
+
* client mount shim — nothing renders a world page anywhere, and the mount
|
|
18
|
+
* HTML pass (`index-pass.ts`) plus the shim retired with the last consumer.
|
|
8
19
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
20
|
+
* What REMAINS and why (the data-plane coupling the guardrail protects):
|
|
21
|
+
* - the `webServer` provider itself: world-tree rows (`modules`, adapter
|
|
22
|
+
* rows) inject/bind it; removing the provider would pend them and fail
|
|
23
|
+
* the world boot;
|
|
24
|
+
* - `stripLabelFromUrl`/`RealWebServerFace`: the `/hub` family
|
|
25
|
+
* (`hub-route.ts`) types ctx0's real server and strips the `/hub` prefix
|
|
26
|
+
* with the same URL rule;
|
|
27
|
+
* - the neutral `port`/`host` getters: the shape every remaining reader
|
|
28
|
+
* sees (a world owns no socket).
|
|
11
29
|
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
* - two worlds may both own `/api` (they become `/omp/api`, `/codex/api`);
|
|
15
|
-
* - ctx0 only ever sees the label namespaces, so a world cannot collide with
|
|
16
|
-
* ctx0's own routes.
|
|
17
|
-
*
|
|
18
|
-
* Index rows stay world-local: this instance owns its taps and emits nothing
|
|
19
|
-
* on the shared tree (worlds run as their own cordis ROOTS, whose event buses
|
|
20
|
-
* are isolated — probe 2026-09-16: emit on root B never reaches root A).
|
|
30
|
+
* The RPC channel is hub-owned: the `/hub` family (`hub-route.ts`) is the
|
|
31
|
+
* sole foreign entry.
|
|
21
32
|
*/
|
|
22
|
-
import { renderIndexInjections } from '@deepseek-ai/dsh-host-webserver';
|
|
23
|
-
import { rewriteIndexHtml } from './index-pass.js';
|
|
24
|
-
import { renderClientShim } from './client-shim.js';
|
|
25
|
-
import { agentRosterRow } from './agent-roster.js';
|
|
26
|
-
function translatePath(labelPath, path) {
|
|
27
|
-
if (typeof path !== 'string' || !path.startsWith('/')) {
|
|
28
|
-
throw new Error(`world-web-server: route path must start with "/", got ${JSON.stringify(path)}`);
|
|
29
|
-
}
|
|
30
|
-
return path === '/' ? labelPath : `${labelPath}${path}`;
|
|
31
|
-
}
|
|
32
33
|
/** Remove the mount label from `req.url`, preserving the trailing query. */
|
|
33
34
|
export function stripLabelFromUrl(url, labelPath) {
|
|
34
35
|
const raw = typeof url === 'string' && url !== '' ? url : '/';
|
|
@@ -41,214 +42,58 @@ export function stripLabelFromUrl(url, labelPath) {
|
|
|
41
42
|
return `${path.slice(labelPath.length)}${rest}`;
|
|
42
43
|
return raw;
|
|
43
44
|
}
|
|
44
|
-
/**
|
|
45
|
+
/** No-op registration disposer (the swallowed-route shape). */
|
|
46
|
+
function swallowed() {
|
|
47
|
+
return () => { };
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* One world's web surface — a registration-only shell: it accepts every
|
|
51
|
+
* registration a world plugin makes (so the tree boots and composes exactly
|
|
52
|
+
* as before) and forwards none of them anywhere; nothing is stored, nothing
|
|
53
|
+
* is served (T-C), and nothing renders (T-D).
|
|
54
|
+
*/
|
|
45
55
|
export class WorldWebServer {
|
|
46
56
|
labelPath;
|
|
47
|
-
real;
|
|
48
|
-
options;
|
|
49
|
-
disposers = [];
|
|
50
|
-
taps = [];
|
|
51
57
|
/**
|
|
52
|
-
* @param labelPath - mount root, e.g. `/omp` (no trailing slash)
|
|
53
|
-
*
|
|
54
|
-
* @param options - world-local index row source.
|
|
58
|
+
* @param labelPath - mount root, e.g. `/omp` (no trailing slash) — the
|
|
59
|
+
* world's identity record; no page is ever served under it.
|
|
55
60
|
*/
|
|
56
|
-
constructor(labelPath
|
|
61
|
+
constructor(labelPath) {
|
|
57
62
|
this.labelPath = labelPath;
|
|
58
|
-
this.real = real;
|
|
59
|
-
this.options = options;
|
|
60
63
|
if (typeof labelPath !== 'string' || !labelPath.startsWith('/') || labelPath === '/' || labelPath.endsWith('/')) {
|
|
61
64
|
throw new Error(`world-web-server: labelPath must start with "/" and carry no trailing slash, got ${JSON.stringify(labelPath)}`);
|
|
62
65
|
}
|
|
63
66
|
}
|
|
64
|
-
/** The ctx0 listener port (read-through; the world owns no socket). */
|
|
65
|
-
get port() {
|
|
66
|
-
return this.real.port ?? 0;
|
|
67
|
-
}
|
|
68
|
-
/** The ctx0 bind host (read-through). */
|
|
69
|
-
get host() {
|
|
70
|
-
return this.real.host ?? '127.0.0.1';
|
|
71
|
-
}
|
|
72
|
-
/**
|
|
73
|
-
* The RPC channel belongs to the HUB, not to the world: the hub's carrier
|
|
74
|
-
* answers `/<label>/api/*` and `/<label>/api/remote.mux` with ctx0's auth
|
|
75
|
-
* domain, so the world's own connection/gateway registrations are swallowed
|
|
76
|
-
* here (returning a no-op disposer) instead of colliding on the real table.
|
|
77
|
-
* Non-envelope GET/HEAD traffic on `/<label>/api` still reaches the world's
|
|
78
|
-
* exact Fetch routes: `world-entry` publishes the world connection's shared
|
|
79
|
-
* `/api` fetch handler through `world-host`, and the carrier forwards into
|
|
80
|
-
* it AFTER ctx0's fence — the world's own browser-auth gate is never in the
|
|
81
|
-
* path (a different tree, a different secret).
|
|
82
|
-
*/
|
|
83
|
-
static hubOwned(path) {
|
|
84
|
-
return path === '/api' || path.startsWith('/api/');
|
|
85
|
-
}
|
|
86
67
|
/**
|
|
87
|
-
*
|
|
88
|
-
*
|
|
89
|
-
*
|
|
90
|
-
* in-ctx through this mount, and its cookie secret belongs to a different
|
|
91
|
-
* tree (every runtime is nested with its own home). So the decision is made
|
|
92
|
-
* here, with ctx0's connection: the same cookie that authenticated `<label>`
|
|
93
|
-
* in ctx0 authorizes its pages and assets.
|
|
68
|
+
* The ctx0 listener facts (read-through retired with the mount): worlds
|
|
69
|
+
* keep the same neutral defaults the disconnected read-through always
|
|
70
|
+
* fell back to — a world owns no socket and no page anyone can hit.
|
|
94
71
|
*/
|
|
95
|
-
|
|
96
|
-
return
|
|
97
|
-
const res = rest[0];
|
|
98
|
-
// false => the authorizer already answered (401, or token→cookie 303).
|
|
99
|
-
if (res !== undefined && this.options.authorize !== undefined
|
|
100
|
-
&& !this.options.authorize(req, res))
|
|
101
|
-
return;
|
|
102
|
-
return handler(req, ...rest);
|
|
103
|
-
};
|
|
104
|
-
}
|
|
105
|
-
/** Register a world route; the real table stores its translated path. */
|
|
106
|
-
register(route) {
|
|
107
|
-
if (WorldWebServer.hubOwned(route.path))
|
|
108
|
-
return () => { };
|
|
109
|
-
return this.track(this.real.register({
|
|
110
|
-
kind: route.kind,
|
|
111
|
-
path: translatePath(this.labelPath, route.path),
|
|
112
|
-
handler: this.strip(this.gate(route.handler)),
|
|
113
|
-
}));
|
|
114
|
-
}
|
|
115
|
-
/** Register a world upgrade route (verbatim exact path; hub-owned RPC paths are swallowed). */
|
|
116
|
-
registerUpgrade(route) {
|
|
117
|
-
if (WorldWebServer.hubOwned(route.path))
|
|
118
|
-
return () => { };
|
|
119
|
-
return this.track(this.real.registerUpgrade({
|
|
120
|
-
path: translatePath(this.labelPath, route.path),
|
|
121
|
-
handler: this.strip(route.handler),
|
|
122
|
-
}));
|
|
72
|
+
get port() {
|
|
73
|
+
return 0;
|
|
123
74
|
}
|
|
124
|
-
/**
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
* the mount path (`/<label>`), which also keeps it inside the label namespace.
|
|
128
|
-
*/
|
|
129
|
-
registerFallback(handler) {
|
|
130
|
-
// The world's index arrives HERE (its own frontend-static claims the
|
|
131
|
-
// fallback seat), so this is the route the mount must authenticate.
|
|
132
|
-
const gated = this.gate((req, ...rest) => handler(req, rest[0]));
|
|
133
|
-
return this.track(this.real.register({
|
|
134
|
-
kind: 'prefix',
|
|
135
|
-
path: this.labelPath,
|
|
136
|
-
handler: this.htmlRewrite(this.strip(gated)),
|
|
137
|
-
}));
|
|
75
|
+
/** The neutral bind host (see {@link WorldWebServer.port}). */
|
|
76
|
+
get host() {
|
|
77
|
+
return '127.0.0.1';
|
|
138
78
|
}
|
|
139
79
|
/**
|
|
140
|
-
*
|
|
141
|
-
*
|
|
142
|
-
*
|
|
143
|
-
* `renderIndex`, and the FIRST `<base>` wins in a document, so the mount
|
|
144
|
-
* base is re-pinned on the bytes actually sent;
|
|
145
|
-
* 2. the agent roster is injected LAST — its values are the mount paths of
|
|
146
|
-
* OTHER worlds (`/`, `/omp/`, `/codex/`), which the mount pass above
|
|
147
|
-
* would otherwise re-root under this world's label.
|
|
80
|
+
* Accept one world route — and swallow it (T-C): pages have no road to
|
|
81
|
+
* ctx0. The disposer is a no-op, matching the swallowed-registration
|
|
82
|
+
* contract the `/api` family always had.
|
|
148
83
|
*/
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
return (req, ...rest) => {
|
|
152
|
-
const res = rest[0];
|
|
153
|
-
const originalEnd = res?.end?.bind(res);
|
|
154
|
-
if (res !== undefined && originalEnd !== undefined) {
|
|
155
|
-
res.end = ((chunk, ...tail) => {
|
|
156
|
-
if (typeof chunk === 'string' || Buffer.isBuffer(chunk)) {
|
|
157
|
-
const text = typeof chunk === 'string' ? chunk : chunk.toString('utf8');
|
|
158
|
-
if (/<head[\s>]/i.test(text) && /<base\b/i.test(text)) {
|
|
159
|
-
const mounted = rewriteIndexHtml(text, labelPath);
|
|
160
|
-
// A world that composes the hub row emits its own roster row during
|
|
161
|
-
// renderIndex — i.e. BEFORE this mount pass, so its paths were
|
|
162
|
-
// already re-rooted. Drop every earlier copy and inject the one
|
|
163
|
-
// this pass has just built from unrewritten values.
|
|
164
|
-
const stale = /<script>globalThis\["__DSH_AGENT_ROSTER__"\][^<]*<\/script>/g;
|
|
165
|
-
const cleaned = mounted.replace(stale, '');
|
|
166
|
-
return originalEnd(renderIndexInjections(cleaned, [agentRosterRow()]), ...tail);
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
return originalEnd(chunk, ...tail);
|
|
170
|
-
});
|
|
171
|
-
}
|
|
172
|
-
return handler(req, ...rest);
|
|
173
|
-
};
|
|
84
|
+
register(_route) {
|
|
85
|
+
return swallowed();
|
|
174
86
|
}
|
|
175
|
-
/**
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
return () => {
|
|
179
|
-
const at = this.taps.indexOf(transform);
|
|
180
|
-
if (at !== -1)
|
|
181
|
-
this.taps.splice(at, 1);
|
|
182
|
-
};
|
|
183
|
-
}
|
|
184
|
-
/** Apply this world's taps in registration order. */
|
|
185
|
-
applyIndexTaps(html) {
|
|
186
|
-
let out = html;
|
|
187
|
-
for (const transform of this.taps)
|
|
188
|
-
out = transform(out);
|
|
189
|
-
return out;
|
|
190
|
-
}
|
|
191
|
-
/** Collect this world's structured rows (supplied by the world root). */
|
|
192
|
-
collectIndexInjections() {
|
|
193
|
-
const table = [];
|
|
194
|
-
this.options.onIndexInject?.(table);
|
|
195
|
-
return table;
|
|
87
|
+
/** Accept one world upgrade route — and swallow it (see {@link register}). */
|
|
88
|
+
registerUpgrade(_route) {
|
|
89
|
+
return swallowed();
|
|
196
90
|
}
|
|
197
91
|
/**
|
|
198
|
-
*
|
|
199
|
-
*
|
|
200
|
-
*
|
|
201
|
-
*
|
|
202
|
-
* The agent roster is injected LAST, after the mount pass: its values are
|
|
203
|
-
* mount paths of OTHER worlds (`/`, `/omp/`, `/codex/`), and the pass would
|
|
204
|
-
* happily re-root them under this world's label.
|
|
92
|
+
* Accept the world's SPA fallback claim — and swallow it (see
|
|
93
|
+
* {@link register}): the ctx0 fallback seat was always native
|
|
94
|
+
* frontend-static's alone.
|
|
205
95
|
*/
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
// to own the transport global before anything else materializes — the
|
|
209
|
-
// page-trust component's `window.__DSH_TRANSPORT__ = { ownsHost: true }`
|
|
210
|
-
// guards on absence, while the shim MERGES into an existing object
|
|
211
|
-
// (`globalThis.__DSH_TRANSPORT__ = globalThis.__DSH_TRANSPORT__ || {}`).
|
|
212
|
-
// Shim-first would create the object and silently eat the verdict.
|
|
213
|
-
const rows = [
|
|
214
|
-
// No hub-invented widget here: the world composes the hub's own client
|
|
215
|
-
// plugin (see worldMountPatches), so its selector IS the native one.
|
|
216
|
-
...this.collectIndexInjections(),
|
|
217
|
-
{ kind: 'script', placement: 'head', text: renderClientShim(this.labelPath) },
|
|
218
|
-
];
|
|
219
|
-
const rendered = this.applyIndexTaps(renderIndexInjections(html, rows));
|
|
220
|
-
// The roster rides the outbound pass (htmlRewrite), not this one: the mount
|
|
221
|
-
// rewrite above would re-root other worlds' paths under this label.
|
|
222
|
-
return rewriteIndexHtml(rendered, this.labelPath);
|
|
223
|
-
}
|
|
224
|
-
/** Unregister every translated route (world unloaded). */
|
|
225
|
-
dispose() {
|
|
226
|
-
for (const dispose of this.disposers.splice(0).reverse()) {
|
|
227
|
-
try {
|
|
228
|
-
dispose();
|
|
229
|
-
}
|
|
230
|
-
catch {
|
|
231
|
-
// release must never throw during teardown
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
this.taps.length = 0;
|
|
235
|
-
}
|
|
236
|
-
/** Wrap a plugin handler so it sees the world-relative URL. */
|
|
237
|
-
strip(handler) {
|
|
238
|
-
const labelPath = this.labelPath;
|
|
239
|
-
return (req, ...rest) => {
|
|
240
|
-
req.url = stripLabelFromUrl(req.url, labelPath);
|
|
241
|
-
return handler(req, ...rest);
|
|
242
|
-
};
|
|
243
|
-
}
|
|
244
|
-
track(dispose) {
|
|
245
|
-
const wrapped = () => {
|
|
246
|
-
const at = this.disposers.indexOf(wrapped);
|
|
247
|
-
if (at !== -1)
|
|
248
|
-
this.disposers.splice(at, 1);
|
|
249
|
-
dispose();
|
|
250
|
-
};
|
|
251
|
-
this.disposers.push(wrapped);
|
|
252
|
-
return wrapped;
|
|
96
|
+
registerFallback(_handler) {
|
|
97
|
+
return swallowed();
|
|
253
98
|
}
|
|
254
99
|
}
|