humanish 0.15.3 → 0.17.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 +49 -6
- package/dist/concurrent-shared-world-lab.js +20 -7
- package/dist/concurrent-shared-world-lab.js.map +1 -1
- package/dist/cua-actor-lab.d.ts +101 -4
- package/dist/cua-actor-lab.js +456 -77
- package/dist/cua-actor-lab.js.map +1 -1
- package/dist/device-presets.d.ts +4 -4
- package/dist/device-presets.js +5 -5
- package/dist/device-presets.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js.map +1 -1
- package/dist/lab-config.d.ts +2 -2
- package/dist/observer-assets.js +92 -15
- package/dist/observer-assets.js.map +1 -1
- package/dist/observer-auth.d.ts +21 -0
- package/dist/observer-auth.js +92 -0
- package/dist/observer-auth.js.map +1 -0
- package/dist/observer-library.d.ts +19 -0
- package/dist/observer-library.js +189 -0
- package/dist/observer-library.js.map +1 -0
- package/dist/observer-serve.d.ts +111 -0
- package/dist/observer-serve.js +369 -0
- package/dist/observer-serve.js.map +1 -0
- package/dist/observer.d.ts +31 -0
- package/dist/observer.js +10 -5
- package/dist/observer.js.map +1 -1
- package/dist/oss-meta-lab.js +2 -4
- package/dist/oss-meta-lab.js.map +1 -1
- package/dist/program.js +242 -0
- package/dist/program.js.map +1 -1
- package/dist/run.d.ts +38 -0
- package/dist/run.js +71 -1
- package/dist/run.js.map +1 -1
- package/dist/serve-tunnel.d.ts +16 -0
- package/dist/serve-tunnel.js +104 -0
- package/dist/serve-tunnel.js.map +1 -0
- package/dist/shared-world-lab.d.ts +20 -1
- package/dist/shared-world-lab.js +197 -23
- package/dist/shared-world-lab.js.map +1 -1
- package/docs/architecture/observer.md +7 -0
- package/docs/architecture/serve.md +140 -0
- package/docs/assets/humanish-drawdb-hero.png +0 -0
- package/docs/contracts/run-bundle.md +19 -0
- package/docs/contracts/schemas.md +23 -1
- package/docs/goals/current.md +4 -3
- package/docs/principles/invariants-and-defaults.md +1 -0
- package/docs/ramp/README.md +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import type { IncomingMessage, ServerResponse } from "node:http";
|
|
2
|
+
import type { PinnedDirectory } from "./observer.js";
|
|
3
|
+
import type { ServeSessionStore } from "./observer-auth.js";
|
|
4
|
+
import type { LibraryHistory } from "./observer-library.js";
|
|
5
|
+
import { verifyRun } from "./run.js";
|
|
6
|
+
export declare const SERVE_SCHEMA = "humanish.serve-result.v1";
|
|
7
|
+
export type ServeMode = "loopback" | "capability-link" | "share-safe-open";
|
|
8
|
+
export type ServeErrorCode = "HUMANISH_INVALID_PORT" | "HUMANISH_SERVE_INVALID_TTL" | "HUMANISH_SERVE_OPTION_CONFLICT" | "HUMANISH_SERVE_TUNNEL_REQUIRES_EXPOSE" | "HUMANISH_SERVE_EXPOSE_REQUIRES_ORIGIN" | "HUMANISH_SERVE_OPEN_REQUIRES_SAFE" | "HUMANISH_SERVE_TUNNEL_NOT_FOUND" | "HUMANISH_SERVE_TUNNEL_START_FAILED" | "HUMANISH_RUN_NOT_FOUND" | "HUMANISH_SERVE_RUN_NOT_SHAREABLE";
|
|
9
|
+
export interface ServeResult {
|
|
10
|
+
schema: typeof SERVE_SCHEMA;
|
|
11
|
+
ok: boolean;
|
|
12
|
+
cwd: string;
|
|
13
|
+
mode: ServeMode;
|
|
14
|
+
safe: boolean;
|
|
15
|
+
host: "127.0.0.1";
|
|
16
|
+
port?: number;
|
|
17
|
+
url?: string;
|
|
18
|
+
capabilityUrl?: string;
|
|
19
|
+
publicUrl?: string;
|
|
20
|
+
publicCapabilityUrl?: string;
|
|
21
|
+
tunnel?: {
|
|
22
|
+
provider: "ngrok";
|
|
23
|
+
url: string;
|
|
24
|
+
};
|
|
25
|
+
ttlMinutes?: number;
|
|
26
|
+
runsListed: number;
|
|
27
|
+
shareReadyCount?: number;
|
|
28
|
+
entryRunId?: string;
|
|
29
|
+
opened?: boolean;
|
|
30
|
+
openCommand?: string;
|
|
31
|
+
warnings: string[];
|
|
32
|
+
error?: {
|
|
33
|
+
code: ServeErrorCode;
|
|
34
|
+
message: string;
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
export interface ServeControlPlane {
|
|
38
|
+
startRun?(request: {
|
|
39
|
+
labId: string;
|
|
40
|
+
dryRun: boolean;
|
|
41
|
+
}): Promise<{
|
|
42
|
+
accepted: boolean;
|
|
43
|
+
runId?: string;
|
|
44
|
+
}>;
|
|
45
|
+
}
|
|
46
|
+
export interface ShareSafetyAdmission {
|
|
47
|
+
admit(runId: string): Promise<boolean>;
|
|
48
|
+
}
|
|
49
|
+
export declare function createShareSafetyAdmission(cwd: string, options?: {
|
|
50
|
+
verifyImpl?: typeof verifyRun;
|
|
51
|
+
ttlMs?: number;
|
|
52
|
+
now?: () => number;
|
|
53
|
+
}): ShareSafetyAdmission;
|
|
54
|
+
export declare function buildServeSecurityHeaders(): Record<string, string>;
|
|
55
|
+
export declare function hostAllowed(hostHeader: string | undefined, allowlist: ReadonlySet<string>): boolean;
|
|
56
|
+
export declare function parsePublicOrigin(value: string): {
|
|
57
|
+
origin: string;
|
|
58
|
+
host: string;
|
|
59
|
+
scheme: "http" | "https";
|
|
60
|
+
} | null;
|
|
61
|
+
export interface ServeAuthContext {
|
|
62
|
+
tokenDigest: Buffer;
|
|
63
|
+
sessions: ServeSessionStore;
|
|
64
|
+
ttlSeconds: number;
|
|
65
|
+
publicScheme?: "http" | "https";
|
|
66
|
+
}
|
|
67
|
+
export interface ServeRequestHandlerOptions {
|
|
68
|
+
proofRoot: PinnedDirectory;
|
|
69
|
+
safe: boolean;
|
|
70
|
+
admit: (runId: string) => Promise<boolean>;
|
|
71
|
+
auth?: ServeAuthContext | null;
|
|
72
|
+
hostAllowlist: ReadonlySet<string>;
|
|
73
|
+
entryRunId?: string;
|
|
74
|
+
renderLibrary: (history: LibraryHistory) => string;
|
|
75
|
+
controlPlane?: ServeControlPlane;
|
|
76
|
+
}
|
|
77
|
+
export declare function createServeRequestHandler(options: ServeRequestHandlerOptions): (request: IncomingMessage, response: ServerResponse) => Promise<void>;
|
|
78
|
+
export interface ServeLibraryOptions {
|
|
79
|
+
port: number;
|
|
80
|
+
safe: boolean;
|
|
81
|
+
expose: boolean;
|
|
82
|
+
authMode: "link" | "none";
|
|
83
|
+
ttlMinutes: number;
|
|
84
|
+
publicOrigin?: string;
|
|
85
|
+
entryRunId?: string;
|
|
86
|
+
controlPlane?: ServeControlPlane;
|
|
87
|
+
verifyImpl?: typeof verifyRun;
|
|
88
|
+
now?: () => number;
|
|
89
|
+
}
|
|
90
|
+
export interface ServeLibraryServer {
|
|
91
|
+
url: string;
|
|
92
|
+
port: number;
|
|
93
|
+
mode: ServeMode;
|
|
94
|
+
capabilityToken?: string;
|
|
95
|
+
runsListed: number;
|
|
96
|
+
shareReadyCount?: number;
|
|
97
|
+
entryRunId?: string;
|
|
98
|
+
addPublicOrigin(origin: string): void;
|
|
99
|
+
close(): Promise<void>;
|
|
100
|
+
}
|
|
101
|
+
export type ServeLibraryStart = {
|
|
102
|
+
ok: true;
|
|
103
|
+
server: ServeLibraryServer;
|
|
104
|
+
} | {
|
|
105
|
+
ok: false;
|
|
106
|
+
error: {
|
|
107
|
+
code: ServeErrorCode;
|
|
108
|
+
message: string;
|
|
109
|
+
};
|
|
110
|
+
};
|
|
111
|
+
export declare function serveObserverLibrary(cwdInput: string, options: ServeLibraryOptions): Promise<ServeLibraryStart>;
|
|
@@ -0,0 +1,369 @@
|
|
|
1
|
+
import { createServer } from "node:http";
|
|
2
|
+
import { lstat } from "node:fs/promises";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { buildHistoryIndex, matchRunRoute, pinDirectChildDirectory, pinDirectory, serveRunPath } from "./observer.js";
|
|
5
|
+
import { buildSessionCookie, createServeSessionStore, mintServeToken, sha256Digest, verifyTokenDigest } from "./observer-auth.js";
|
|
6
|
+
import { renderLibraryHtml } from "./observer-library.js";
|
|
7
|
+
import { listRuns, verifyRun } from "./run.js";
|
|
8
|
+
export const SERVE_SCHEMA = "humanish.serve-result.v1";
|
|
9
|
+
export function createShareSafetyAdmission(cwd, options = {}) {
|
|
10
|
+
const verifyImpl = options.verifyImpl ?? verifyRun;
|
|
11
|
+
const now = options.now ?? (() => Date.now());
|
|
12
|
+
// verifyRun scans the ENTIRE run tree, but the cache key is run.json's stat
|
|
13
|
+
// identity, so an out-of-band edit to a scanned artifact that leaves run.json
|
|
14
|
+
// untouched would otherwise keep a stale share_ready verdict. A short TTL
|
|
15
|
+
// bounds that window: any mutation is re-scanned within ttlMs even when
|
|
16
|
+
// run.json never changes. Defense in depth on top of pre-persist redaction.
|
|
17
|
+
const ttlMs = options.ttlMs ?? 30_000;
|
|
18
|
+
const cache = new Map();
|
|
19
|
+
const bundleIdentity = async (runId) => {
|
|
20
|
+
try {
|
|
21
|
+
const stats = await lstat(path.join(cwd, ".humanish", "runs", runId, "run.json"), { bigint: true });
|
|
22
|
+
if (!stats.isFile()) {
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
return `${stats.dev}:${stats.ino}:${stats.mtimeNs}:${stats.size}`;
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
return {
|
|
32
|
+
async admit(runId) {
|
|
33
|
+
const identity = await bundleIdentity(runId);
|
|
34
|
+
if (!identity) {
|
|
35
|
+
cache.delete(runId);
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
const cached = cache.get(runId);
|
|
39
|
+
if (cached && cached.identity === identity && now() - cached.verifiedAt < ttlMs) {
|
|
40
|
+
return cached.admitted;
|
|
41
|
+
}
|
|
42
|
+
const admitted = verifyImpl(cwd, runId)
|
|
43
|
+
.then((verified) => verified.ok === true && verified.shareSafety.status === "share_ready")
|
|
44
|
+
.catch(() => false);
|
|
45
|
+
cache.set(runId, { identity, verifiedAt: now(), admitted });
|
|
46
|
+
return admitted;
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
export function buildServeSecurityHeaders() {
|
|
51
|
+
return {
|
|
52
|
+
"cache-control": "no-store",
|
|
53
|
+
"referrer-policy": "no-referrer",
|
|
54
|
+
"x-content-type-options": "nosniff",
|
|
55
|
+
"x-frame-options": "DENY",
|
|
56
|
+
"x-robots-tag": "noindex, nofollow"
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
export function hostAllowed(hostHeader, allowlist) {
|
|
60
|
+
return typeof hostHeader === "string" && allowlist.has(hostHeader.trim().toLowerCase());
|
|
61
|
+
}
|
|
62
|
+
export function parsePublicOrigin(value) {
|
|
63
|
+
let parsed;
|
|
64
|
+
try {
|
|
65
|
+
parsed = new URL(value);
|
|
66
|
+
}
|
|
67
|
+
catch {
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
if ((parsed.pathname !== "/" && parsed.pathname !== "") || parsed.search || parsed.hash || !parsed.host) {
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
return {
|
|
77
|
+
origin: parsed.origin,
|
|
78
|
+
host: parsed.host.toLowerCase(),
|
|
79
|
+
scheme: parsed.protocol === "https:" ? "https" : "http"
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
const UNAUTHORIZED_BODY = "humanish serve: capability link required";
|
|
83
|
+
export function createServeRequestHandler(options) {
|
|
84
|
+
return async (request, response) => {
|
|
85
|
+
try {
|
|
86
|
+
for (const [name, value] of Object.entries(buildServeSecurityHeaders())) {
|
|
87
|
+
response.setHeader(name, value);
|
|
88
|
+
}
|
|
89
|
+
const method = request.method ?? "GET";
|
|
90
|
+
if (method !== "GET" && method !== "HEAD") {
|
|
91
|
+
writeText(response, 405, "Method Not Allowed");
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
if (!hostAllowed(request.headers.host, options.hostAllowlist)) {
|
|
95
|
+
writeText(response, 421, "Misdirected Request");
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
const url = new URL(request.url ?? "/", `http://${request.headers.host ?? "127.0.0.1"}`);
|
|
99
|
+
if (options.auth) {
|
|
100
|
+
const mintMatch = url.pathname.match(/^\/_humanish\/auth\/([^/]+)$/);
|
|
101
|
+
if (mintMatch) {
|
|
102
|
+
let candidate = "";
|
|
103
|
+
try {
|
|
104
|
+
candidate = decodeURIComponent(mintMatch[1] ?? "");
|
|
105
|
+
}
|
|
106
|
+
catch {
|
|
107
|
+
candidate = "";
|
|
108
|
+
}
|
|
109
|
+
if (candidate && verifyTokenDigest(candidate, options.auth.tokenDigest)) {
|
|
110
|
+
const { cookieValue } = options.auth.sessions.mint();
|
|
111
|
+
const secure = options.auth.publicScheme === "https";
|
|
112
|
+
response.setHeader("set-cookie", buildSessionCookie(cookieValue, { ttlSeconds: options.auth.ttlSeconds, secure }));
|
|
113
|
+
response.writeHead(302, { location: "/" });
|
|
114
|
+
response.end();
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
writeText(response, 401, UNAUTHORIZED_BODY);
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
// Every route below requires a valid session, regardless of source
|
|
121
|
+
// address: the tunnel agent connects from 127.0.0.1, so a loopback-peer
|
|
122
|
+
// trust shortcut would disable auth in exactly the deployment it protects.
|
|
123
|
+
if (!options.auth.sessions.validate(request.headers.cookie)) {
|
|
124
|
+
writeText(response, 401, UNAUTHORIZED_BODY);
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
else if (url.pathname.startsWith("/_humanish/auth/")) {
|
|
129
|
+
writeText(response, 404, "Not found");
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
if (url.pathname === "/_humanish/api" || url.pathname.startsWith("/_humanish/api/")) {
|
|
133
|
+
writeText(response, 501, `${JSON.stringify({
|
|
134
|
+
error: {
|
|
135
|
+
code: "HUMANISH_SERVE_CONTROL_PLANE_DISABLED",
|
|
136
|
+
message: "control plane not enabled in this version"
|
|
137
|
+
}
|
|
138
|
+
}, null, 2)}\n`, "application/json; charset=utf-8");
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
if (url.pathname === "/") {
|
|
142
|
+
if (options.entryRunId) {
|
|
143
|
+
response.writeHead(302, {
|
|
144
|
+
location: `/_humanish/runs/${encodeURIComponent(options.entryRunId)}/observer/index.html`
|
|
145
|
+
});
|
|
146
|
+
response.end();
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
const history = await loadFilteredHistory(options);
|
|
150
|
+
writeText(response, 200, options.renderLibrary(history), "text/html; charset=utf-8");
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
if (url.pathname === "/_humanish/history.json") {
|
|
154
|
+
const history = await loadFilteredHistory(options);
|
|
155
|
+
writeText(response, 200, JSON.stringify(history, null, 2), "application/json; charset=utf-8");
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
if (url.pathname.startsWith("/_humanish/runs/")) {
|
|
159
|
+
const runRoute = matchRunRoute(url.pathname);
|
|
160
|
+
if (!runRoute) {
|
|
161
|
+
writeText(response, 404, "Run not found");
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
if (options.safe && !(await options.admit(runRoute.runId))) {
|
|
165
|
+
// Byte-identical to the nonexistent-run 404: no existence oracle.
|
|
166
|
+
writeText(response, 404, "Run not found");
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
const targetRoot = await pinDirectChildDirectory(options.proofRoot, runRoute.runId);
|
|
170
|
+
if (!targetRoot) {
|
|
171
|
+
writeText(response, 404, "Run not found");
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
// The explicit empty runtimeStreamUrls keeps a future refactor from
|
|
175
|
+
// reintroducing auth-keyed stream injection on the serve surface.
|
|
176
|
+
await serveRunPath(targetRoot, runRoute.relativePath || "observer/index.html", response, []);
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
writeText(response, 404, "Not found");
|
|
180
|
+
}
|
|
181
|
+
catch {
|
|
182
|
+
writeText(response, 500, "Observer request failed");
|
|
183
|
+
}
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
async function loadFilteredHistory(options) {
|
|
187
|
+
const history = await buildHistoryIndex(options.proofRoot);
|
|
188
|
+
if (!options.safe) {
|
|
189
|
+
return history;
|
|
190
|
+
}
|
|
191
|
+
const admissions = await Promise.all(history.runs.map(async (run) => ((await options.admit(run.runId)) === true ? run : null)));
|
|
192
|
+
const runs = admissions.filter((run) => run !== null);
|
|
193
|
+
const latestRunId = runs.some((run) => run.runId === history.latestRunId)
|
|
194
|
+
? history.latestRunId
|
|
195
|
+
: runs[0]?.runId ?? null;
|
|
196
|
+
return { latestRunId, runs };
|
|
197
|
+
}
|
|
198
|
+
export async function serveObserverLibrary(cwdInput, options) {
|
|
199
|
+
const cwd = path.resolve(cwdInput);
|
|
200
|
+
const verifyImpl = options.verifyImpl ?? verifyRun;
|
|
201
|
+
let proofRoot;
|
|
202
|
+
try {
|
|
203
|
+
proofRoot = await pinDirectory(path.join(cwd, ".humanish", "runs"));
|
|
204
|
+
}
|
|
205
|
+
catch {
|
|
206
|
+
return {
|
|
207
|
+
ok: false,
|
|
208
|
+
error: {
|
|
209
|
+
code: "HUMANISH_RUN_NOT_FOUND",
|
|
210
|
+
message: "No run library found under .humanish/runs. Run `humanish watch` to create the first run."
|
|
211
|
+
}
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
const admission = createShareSafetyAdmission(cwd, {
|
|
215
|
+
verifyImpl,
|
|
216
|
+
...(options.now ? { now: options.now } : {})
|
|
217
|
+
});
|
|
218
|
+
const mode = options.expose
|
|
219
|
+
? options.authMode === "link" ? "capability-link" : "share-safe-open"
|
|
220
|
+
: "loopback";
|
|
221
|
+
let entryRunId;
|
|
222
|
+
if (options.entryRunId) {
|
|
223
|
+
const resolved = options.entryRunId === "latest"
|
|
224
|
+
? (await listRuns(cwd)).latest ?? null
|
|
225
|
+
: options.entryRunId;
|
|
226
|
+
const pinned = resolved ? await pinDirectChildDirectory(proofRoot, resolved) : null;
|
|
227
|
+
if (!resolved || !pinned) {
|
|
228
|
+
return {
|
|
229
|
+
ok: false,
|
|
230
|
+
error: { code: "HUMANISH_RUN_NOT_FOUND", message: `Run not found: ${options.entryRunId}` }
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
if (options.safe && !(await admission.admit(resolved))) {
|
|
234
|
+
const verified = await verifyImpl(cwd, resolved).catch(() => null);
|
|
235
|
+
const status = verified?.shareSafety.status ?? "unverifiable";
|
|
236
|
+
const reasons = verified?.shareSafety.reasons.map((reason) => reason.code).join(", ") || "VERIFY_FAILED";
|
|
237
|
+
return {
|
|
238
|
+
ok: false,
|
|
239
|
+
error: {
|
|
240
|
+
code: "HUMANISH_SERVE_RUN_NOT_SHAREABLE",
|
|
241
|
+
message: `Run ${resolved} is not share_ready (shareSafety: ${status}; reasons: ${reasons}); --safe refuses to serve it.`
|
|
242
|
+
}
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
entryRunId = resolved;
|
|
246
|
+
}
|
|
247
|
+
// Counts are computed over the UNCAPPED run list, not the 80-item history
|
|
248
|
+
// index: a capability link (non-safe) grants access to every run by direct
|
|
249
|
+
// URL, and share-safe modes admit every share_ready run per request, so a
|
|
250
|
+
// count capped at 80 would understate exactly what the exposure warning
|
|
251
|
+
// claims. runsListed feeds the operator's declared-friction warning.
|
|
252
|
+
const allRuns = (await listRuns(cwd)).runs;
|
|
253
|
+
let runsListed;
|
|
254
|
+
let shareReadyCount;
|
|
255
|
+
if (options.safe) {
|
|
256
|
+
const admitted = await Promise.all(allRuns.map((run) => admission.admit(run.runId)));
|
|
257
|
+
shareReadyCount = admitted.filter(Boolean).length;
|
|
258
|
+
runsListed = shareReadyCount;
|
|
259
|
+
}
|
|
260
|
+
else {
|
|
261
|
+
runsListed = allRuns.length;
|
|
262
|
+
}
|
|
263
|
+
const declaredOrigin = options.publicOrigin ? parsePublicOrigin(options.publicOrigin) : null;
|
|
264
|
+
let auth = null;
|
|
265
|
+
let capabilityToken;
|
|
266
|
+
if (options.expose && options.authMode === "link") {
|
|
267
|
+
capabilityToken = mintServeToken();
|
|
268
|
+
auth = {
|
|
269
|
+
tokenDigest: sha256Digest(capabilityToken),
|
|
270
|
+
sessions: createServeSessionStore({
|
|
271
|
+
ttlMs: options.ttlMinutes * 60_000,
|
|
272
|
+
...(options.now ? { now: options.now } : {})
|
|
273
|
+
}),
|
|
274
|
+
ttlSeconds: options.ttlMinutes * 60,
|
|
275
|
+
...(declaredOrigin ? { publicScheme: declaredOrigin.scheme } : {})
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
const hostAllowlist = new Set();
|
|
279
|
+
const handler = createServeRequestHandler({
|
|
280
|
+
proofRoot,
|
|
281
|
+
safe: options.safe,
|
|
282
|
+
admit: (runId) => admission.admit(runId),
|
|
283
|
+
auth,
|
|
284
|
+
hostAllowlist,
|
|
285
|
+
...(entryRunId ? { entryRunId } : {}),
|
|
286
|
+
renderLibrary: (history) => renderLibraryHtml(history, {
|
|
287
|
+
mode,
|
|
288
|
+
safe: options.safe,
|
|
289
|
+
capabilities: { actions: false }
|
|
290
|
+
}),
|
|
291
|
+
...(options.controlPlane ? { controlPlane: options.controlPlane } : {})
|
|
292
|
+
});
|
|
293
|
+
const server = createServer((request, response) => {
|
|
294
|
+
void handler(request, response);
|
|
295
|
+
});
|
|
296
|
+
const port = await listen(server, options.port);
|
|
297
|
+
hostAllowlist.add(`127.0.0.1:${port}`);
|
|
298
|
+
hostAllowlist.add(`localhost:${port}`);
|
|
299
|
+
hostAllowlist.add(`[::1]:${port}`);
|
|
300
|
+
if (declaredOrigin) {
|
|
301
|
+
hostAllowlist.add(declaredOrigin.host);
|
|
302
|
+
}
|
|
303
|
+
return {
|
|
304
|
+
ok: true,
|
|
305
|
+
server: {
|
|
306
|
+
url: `http://127.0.0.1:${port}/`,
|
|
307
|
+
port,
|
|
308
|
+
mode,
|
|
309
|
+
...(capabilityToken ? { capabilityToken } : {}),
|
|
310
|
+
runsListed,
|
|
311
|
+
...(shareReadyCount !== undefined ? { shareReadyCount } : {}),
|
|
312
|
+
...(entryRunId ? { entryRunId } : {}),
|
|
313
|
+
// A tunnel's public origin is only known after the tunnel starts (post
|
|
314
|
+
// bind); this lets the caller declare it, extending the Host allowlist and
|
|
315
|
+
// — for an https origin like every ngrok tunnel — marking cookies Secure.
|
|
316
|
+
addPublicOrigin(origin) {
|
|
317
|
+
const parsed = parsePublicOrigin(origin);
|
|
318
|
+
if (!parsed) {
|
|
319
|
+
return;
|
|
320
|
+
}
|
|
321
|
+
hostAllowlist.add(parsed.host);
|
|
322
|
+
if (auth) {
|
|
323
|
+
auth.publicScheme = parsed.scheme;
|
|
324
|
+
}
|
|
325
|
+
},
|
|
326
|
+
close: async () => {
|
|
327
|
+
auth?.sessions.revokeAll();
|
|
328
|
+
const closed = closeServer(server);
|
|
329
|
+
// Keep-alive sockets would otherwise keep close() pending past the point
|
|
330
|
+
// the operator believes Ctrl-C tore the server down.
|
|
331
|
+
server.closeAllConnections?.();
|
|
332
|
+
await closed;
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
};
|
|
336
|
+
}
|
|
337
|
+
function writeText(response, status, body, contentType = "text/plain; charset=utf-8") {
|
|
338
|
+
response.writeHead(status, { "content-type": contentType });
|
|
339
|
+
response.end(body);
|
|
340
|
+
}
|
|
341
|
+
function listen(server, port) {
|
|
342
|
+
return new Promise((resolve, reject) => {
|
|
343
|
+
server.once("error", reject);
|
|
344
|
+
// The serve surface never binds beyond loopback; exposure only ever happens
|
|
345
|
+
// through a tunnel or proxy forwarding to this port.
|
|
346
|
+
server.listen(port, "127.0.0.1", () => {
|
|
347
|
+
server.off("error", reject);
|
|
348
|
+
const address = server.address();
|
|
349
|
+
if (!address || typeof address === "string") {
|
|
350
|
+
reject(new Error("Serve library server did not bind to a TCP port."));
|
|
351
|
+
return;
|
|
352
|
+
}
|
|
353
|
+
resolve(address.port);
|
|
354
|
+
});
|
|
355
|
+
});
|
|
356
|
+
}
|
|
357
|
+
function closeServer(server) {
|
|
358
|
+
return new Promise((resolve, reject) => {
|
|
359
|
+
server.close((error) => {
|
|
360
|
+
if (error) {
|
|
361
|
+
reject(error);
|
|
362
|
+
}
|
|
363
|
+
else {
|
|
364
|
+
resolve();
|
|
365
|
+
}
|
|
366
|
+
});
|
|
367
|
+
});
|
|
368
|
+
}
|
|
369
|
+
//# sourceMappingURL=observer-serve.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"observer-serve.js","sourceRoot":"","sources":["../src/observer-serve.ts"],"names":[],"mappings":"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"}
|
package/dist/observer.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type ServerResponse } from "node:http";
|
|
1
2
|
export declare const OBSERVER_SCHEMA = "humanish.observer-result.v1";
|
|
2
3
|
export interface ObserverResult {
|
|
3
4
|
schema: typeof OBSERVER_SCHEMA;
|
|
@@ -36,9 +37,39 @@ export interface ObserverRuntimeStreamUrl {
|
|
|
36
37
|
streamId: string;
|
|
37
38
|
url: string;
|
|
38
39
|
}
|
|
40
|
+
/** internal: consumed by observer-serve */
|
|
41
|
+
export interface PinnedDirectory {
|
|
42
|
+
readonly birthtimeNs: bigint;
|
|
43
|
+
readonly dev: bigint;
|
|
44
|
+
readonly ino: bigint;
|
|
45
|
+
readonly physicalPath: string;
|
|
46
|
+
}
|
|
39
47
|
export declare function attachObserverRuntimeStreamUrls(result: ObserverResult, streams: ObserverRuntimeStreamUrl[]): void;
|
|
40
48
|
export declare function renderObserver(cwdInput: string, runInput: string, options?: ObserverOptions): Promise<ObserverResult>;
|
|
41
49
|
export declare function serveObserver(result: ObserverResult, options?: ObserverServeOptions): Promise<ObserverServer>;
|
|
50
|
+
/** internal: consumed by observer-serve */
|
|
51
|
+
export declare function serveRunPath(runRoot: PinnedDirectory, relativePath: string, response: ServerResponse, runtimeStreamUrls?: ObserverRuntimeStreamUrl[]): Promise<void>;
|
|
52
|
+
/** internal: consumed by observer-serve */
|
|
53
|
+
export declare function buildHistoryIndex(proofRoot: PinnedDirectory): Promise<{
|
|
54
|
+
latestRunId: string | null;
|
|
55
|
+
runs: Array<{
|
|
56
|
+
runId: string;
|
|
57
|
+
createdAt: string | null;
|
|
58
|
+
mode: string | null;
|
|
59
|
+
href: string;
|
|
60
|
+
status: string;
|
|
61
|
+
streamCount: number;
|
|
62
|
+
}>;
|
|
63
|
+
}>;
|
|
64
|
+
/** internal: consumed by observer-serve */
|
|
65
|
+
export declare function matchRunRoute(pathname: string): {
|
|
66
|
+
runId: string;
|
|
67
|
+
relativePath: string;
|
|
68
|
+
} | null;
|
|
69
|
+
/** internal: consumed by observer-serve */
|
|
70
|
+
export declare function pinDirectory(directoryInput: string): Promise<PinnedDirectory>;
|
|
71
|
+
/** internal: consumed by observer-serve */
|
|
72
|
+
export declare function pinDirectChildDirectory(root: PinnedDirectory, name: string): Promise<PinnedDirectory | null>;
|
|
42
73
|
export declare function openTarget(target: string): {
|
|
43
74
|
opened: boolean;
|
|
44
75
|
command?: string;
|
package/dist/observer.js
CHANGED
|
@@ -219,7 +219,8 @@ function renderObserverHtml(data) {
|
|
|
219
219
|
</html>
|
|
220
220
|
`;
|
|
221
221
|
}
|
|
222
|
-
|
|
222
|
+
/** internal: consumed by observer-serve */
|
|
223
|
+
export async function serveRunPath(runRoot, relativePath, response, runtimeStreamUrls = []) {
|
|
223
224
|
const root = runRoot.physicalPath;
|
|
224
225
|
const cleanedRelativePath = relativePath === "" ? "observer/index.html" : relativePath;
|
|
225
226
|
const filePath = path.resolve(root, cleanedRelativePath);
|
|
@@ -309,7 +310,8 @@ function withRuntimeStreamUrls(data, runtimeStreamUrls) {
|
|
|
309
310
|
})
|
|
310
311
|
};
|
|
311
312
|
}
|
|
312
|
-
|
|
313
|
+
/** internal: consumed by observer-serve */
|
|
314
|
+
export async function buildHistoryIndex(proofRoot) {
|
|
313
315
|
await assertPinnedDirectory(proofRoot);
|
|
314
316
|
const physicalCwd = path.dirname(path.dirname(proofRoot.physicalPath));
|
|
315
317
|
const listed = await listRuns(physicalCwd);
|
|
@@ -331,7 +333,8 @@ async function buildHistoryIndex(proofRoot) {
|
|
|
331
333
|
runs
|
|
332
334
|
};
|
|
333
335
|
}
|
|
334
|
-
|
|
336
|
+
/** internal: consumed by observer-serve */
|
|
337
|
+
export function matchRunRoute(pathname) {
|
|
335
338
|
const match = pathname.match(/^\/_humanish\/runs\/([^/]+)(?:\/(.*))?$/);
|
|
336
339
|
if (!match)
|
|
337
340
|
return null;
|
|
@@ -414,7 +417,8 @@ async function inspectContainedRegularFile(root, filePath) {
|
|
|
414
417
|
return null;
|
|
415
418
|
return fileIdentity;
|
|
416
419
|
}
|
|
417
|
-
|
|
420
|
+
/** internal: consumed by observer-serve */
|
|
421
|
+
export async function pinDirectory(directoryInput) {
|
|
418
422
|
const physicalPath = await realpath(path.resolve(directoryInput));
|
|
419
423
|
const stats = await lstat(physicalPath, { bigint: true });
|
|
420
424
|
if (stats.isSymbolicLink() || !stats.isDirectory()) {
|
|
@@ -422,7 +426,8 @@ async function pinDirectory(directoryInput) {
|
|
|
422
426
|
}
|
|
423
427
|
return Object.freeze({ birthtimeNs: stats.birthtimeNs, dev: stats.dev, ino: stats.ino, physicalPath });
|
|
424
428
|
}
|
|
425
|
-
|
|
429
|
+
/** internal: consumed by observer-serve */
|
|
430
|
+
export async function pinDirectChildDirectory(root, name) {
|
|
426
431
|
if (!isSafeRunIdSegment(name))
|
|
427
432
|
return null;
|
|
428
433
|
try {
|