humanish 0.84.0 → 0.85.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/dist/cua-actor-lab.js +3 -2
- package/dist/cua-actor-lab.js.map +1 -1
- package/dist/export.js +17 -10
- package/dist/export.js.map +1 -1
- package/dist/feedback-proof.d.ts +8 -0
- package/dist/feedback-proof.js +44 -0
- package/dist/feedback-proof.js.map +1 -0
- package/dist/feedback.js +6 -5
- package/dist/feedback.js.map +1 -1
- package/dist/observer-app.html +55 -9
- package/dist/observer-data.d.ts +12 -0
- package/dist/observer-data.js +8 -0
- package/dist/observer-data.js.map +1 -1
- package/dist/observer-library.d.ts +2 -0
- package/dist/observer-library.js.map +1 -1
- package/dist/observer-static.js +52 -1
- package/dist/observer-static.js.map +1 -1
- package/dist/observer.d.ts +5 -0
- package/dist/observer.js +90 -34
- package/dist/observer.js.map +1 -1
- package/dist/oss-meta-lab.js +6 -5
- package/dist/oss-meta-lab.js.map +1 -1
- package/dist/program.js +48 -47
- package/dist/program.js.map +1 -1
- package/dist/serve-http.d.ts +4 -0
- package/dist/serve-http.js +9 -1
- package/dist/serve-http.js.map +1 -1
- package/dist/tui-actions.d.ts +12 -7
- package/dist/tui-actions.js +72 -56
- package/dist/tui-actions.js.map +1 -1
- package/dist/tui-app.js +1 -1
- package/dist/tui-contract.d.ts +1 -1
- package/docs/architecture/observer-review.md +130 -0
- package/docs/architecture/observer.md +88 -2
- package/docs/contracts/feedback.md +9 -0
- package/docs/contracts/schemas.md +1 -1
- package/docs/goals/current.md +6 -2
- package/docs/ramp/README.md +1 -1
- package/docs/release/0.84.1-portable-feedback.md +20 -0
- package/docs/release/0.85.0-observer-review.md +56 -0
- package/package.json +4 -2
package/dist/serve-http.d.ts
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
export type ServeMode = "loopback" | "exposed" | "share-safe-open";
|
|
2
2
|
export declare function buildServeSecurityHeaders(): Record<string, string>;
|
|
3
|
+
/** Saved artifacts may contain active HTML, SVG or XML. Give raw documents an
|
|
4
|
+
* opaque origin even on direct navigation, while preserving self-contained scripts.
|
|
5
|
+
* Generated Observer pages use buildServeSecurityHeaders instead. */
|
|
6
|
+
export declare function buildArtifactSecurityHeaders(): Record<string, string>;
|
|
3
7
|
export declare function hostAllowed(hostHeader: string | undefined, allowlist: ReadonlySet<string>): boolean;
|
|
4
8
|
export declare function parsePublicOrigin(value: string): {
|
|
5
9
|
origin: string;
|
package/dist/serve-http.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// Shared HTTP hardening primitives for the serve surfaces. Extracted here so BOTH the live
|
|
2
|
-
// Observer server (src/observer.ts
|
|
2
|
+
// Observer server (src/observer.ts) and the run-library server
|
|
3
3
|
// (src/observer-serve.ts) can enforce the identical Host allowlist + security-header posture
|
|
4
4
|
// without a module cycle. This file imports nothing from the serve modules.
|
|
5
5
|
export function buildServeSecurityHeaders() {
|
|
@@ -8,9 +8,17 @@ export function buildServeSecurityHeaders() {
|
|
|
8
8
|
"referrer-policy": "no-referrer",
|
|
9
9
|
"x-content-type-options": "nosniff",
|
|
10
10
|
"x-frame-options": "DENY",
|
|
11
|
+
"content-security-policy": "frame-ancestors 'none'",
|
|
11
12
|
"x-robots-tag": "noindex, nofollow"
|
|
12
13
|
};
|
|
13
14
|
}
|
|
15
|
+
/** Saved artifacts may contain active HTML, SVG or XML. Give raw documents an
|
|
16
|
+
* opaque origin even on direct navigation, while preserving self-contained scripts.
|
|
17
|
+
* Generated Observer pages use buildServeSecurityHeaders instead. */
|
|
18
|
+
export function buildArtifactSecurityHeaders() {
|
|
19
|
+
const headers = buildServeSecurityHeaders();
|
|
20
|
+
return { ...headers, "content-security-policy": `${headers["content-security-policy"]}; sandbox allow-scripts` };
|
|
21
|
+
}
|
|
14
22
|
export function hostAllowed(hostHeader, allowlist) {
|
|
15
23
|
return typeof hostHeader === "string" && allowlist.has(hostHeader.trim().toLowerCase());
|
|
16
24
|
}
|
package/dist/serve-http.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"serve-http.js","sourceRoot":"","sources":["../src/serve-http.ts"],"names":[],"mappings":"AAAA,2FAA2F;AAC3F
|
|
1
|
+
{"version":3,"file":"serve-http.js","sourceRoot":"","sources":["../src/serve-http.ts"],"names":[],"mappings":"AAAA,2FAA2F;AAC3F,+DAA+D;AAC/D,6FAA6F;AAC7F,4EAA4E;AAS5E,MAAM,UAAU,yBAAyB;IACvC,OAAO;QACL,eAAe,EAAE,UAAU;QAC3B,iBAAiB,EAAE,aAAa;QAChC,wBAAwB,EAAE,SAAS;QACnC,iBAAiB,EAAE,MAAM;QACzB,yBAAyB,EAAE,wBAAwB;QACnD,cAAc,EAAE,mBAAmB;KACpC,CAAC;AACJ,CAAC;AAED;;qEAEqE;AACrE,MAAM,UAAU,4BAA4B;IAC1C,MAAM,OAAO,GAAG,yBAAyB,EAAE,CAAC;IAC5C,OAAO,EAAE,GAAG,OAAO,EAAE,yBAAyB,EAAE,GAAG,OAAO,CAAC,yBAAyB,CAAC,yBAAyB,EAAE,CAAC;AACnH,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,UAA8B,EAAE,SAA8B;IACxF,OAAO,OAAO,UAAU,KAAK,QAAQ,IAAI,SAAS,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,CAAC;AAC1F,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,KAAa;IAC7C,IAAI,MAAW,CAAC;IAChB,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC;IAC1B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;IACD,IAAI,MAAM,CAAC,QAAQ,KAAK,OAAO,IAAI,MAAM,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;QAChE,OAAO,IAAI,CAAC;IACd,CAAC;IACD,IAAI,CAAC,MAAM,CAAC,QAAQ,KAAK,GAAG,IAAI,MAAM,CAAC,QAAQ,KAAK,EAAE,CAAC,IAAI,MAAM,CAAC,MAAM,IAAI,MAAM,CAAC,IAAI,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;QACxG,OAAO,IAAI,CAAC;IACd,CAAC;IACD,OAAO;QACL,MAAM,EAAE,MAAM,CAAC,MAAM;QACrB,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,WAAW,EAAE;QAC/B,MAAM,EAAE,MAAM,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM;KACxD,CAAC;AACJ,CAAC"}
|
package/dist/tui-actions.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { openTarget } from "./observer.js";
|
|
1
2
|
export declare const TUI_ACTION_SCHEMA = "humanish.tui-action.v1";
|
|
2
3
|
export interface TuiActionResult {
|
|
3
4
|
schema: typeof TUI_ACTION_SCHEMA;
|
|
@@ -5,15 +6,19 @@ export interface TuiActionResult {
|
|
|
5
6
|
/** What to tell the operator. Always set: an action that appears to do nothing is a bug. */
|
|
6
7
|
message: string;
|
|
7
8
|
}
|
|
9
|
+
/** A TUI owns one evidence server, shared by its open browser tabs and closed when it exits. */
|
|
10
|
+
export interface TuiObserverSession {
|
|
11
|
+
open(cwd: string, observerPath: string): Promise<TuiActionResult>;
|
|
12
|
+
close(): Promise<void>;
|
|
13
|
+
}
|
|
8
14
|
/**
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* SOFTLY and usefully: over SSH there is no desktop to open anything, which is not an error — it
|
|
13
|
-
* just means the answer is the path, so the operator can forward the port, scp the file, or open it
|
|
14
|
-
* on the machine it lives on.
|
|
15
|
+
* The library server projects the latest contained bundle on each browser poll. It can follow
|
|
16
|
+
* captures written by another process without starting a study or recovering desktop credentials.
|
|
17
|
+
* Keeping it in this process avoids detached viewers accumulating each time Open Observer is used.
|
|
15
18
|
*/
|
|
16
|
-
export declare function
|
|
19
|
+
export declare function createTuiObserverSession(cwdInput: string, options?: {
|
|
20
|
+
openTarget?: typeof openTarget;
|
|
21
|
+
}): TuiObserverSession;
|
|
17
22
|
/**
|
|
18
23
|
* Stop a run that is still going.
|
|
19
24
|
*
|
package/dist/tui-actions.js
CHANGED
|
@@ -4,68 +4,84 @@
|
|
|
4
4
|
// nothing is worse than no action — so this ships the two that are genuinely implementable today
|
|
5
5
|
// and nothing else. `Share…` waits for the export contract (#471) rather than appearing as a
|
|
6
6
|
// control that fails.
|
|
7
|
-
import {
|
|
8
|
-
import { access, readFile } from "node:fs/promises";
|
|
7
|
+
import { readFile } from "node:fs/promises";
|
|
9
8
|
import path from "node:path";
|
|
10
9
|
import { RUN_STATUS_FILE, isRunStatusRecord } from "./run-status.js";
|
|
11
10
|
import { resolveRunPath } from "./run.js";
|
|
11
|
+
import { bindExistingRunArtifactPaths, isSafeRunIdSegment } from "./run-paths.js";
|
|
12
|
+
import { openTarget } from "./observer.js";
|
|
13
|
+
import { serveObserverLibrary } from "./observer-serve.js";
|
|
12
14
|
export const TUI_ACTION_SCHEMA = "humanish.tui-action.v1";
|
|
13
|
-
/** The platform's "open this file" command. */
|
|
14
|
-
function opener() {
|
|
15
|
-
if (process.platform === "darwin")
|
|
16
|
-
return { command: "open", args: [] };
|
|
17
|
-
if (process.platform === "win32")
|
|
18
|
-
return { command: "cmd", args: ["/c", "start", ""] };
|
|
19
|
-
if (process.platform === "linux")
|
|
20
|
-
return { command: "xdg-open", args: [] };
|
|
21
|
-
return null;
|
|
22
|
-
}
|
|
23
15
|
/**
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
* SOFTLY and usefully: over SSH there is no desktop to open anything, which is not an error — it
|
|
28
|
-
* just means the answer is the path, so the operator can forward the port, scp the file, or open it
|
|
29
|
-
* on the machine it lives on.
|
|
16
|
+
* The library server projects the latest contained bundle on each browser poll. It can follow
|
|
17
|
+
* captures written by another process without starting a study or recovering desktop credentials.
|
|
18
|
+
* Keeping it in this process avoids detached viewers accumulating each time Open Observer is used.
|
|
30
19
|
*/
|
|
31
|
-
export
|
|
32
|
-
const
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
20
|
+
export function createTuiObserverSession(cwdInput, options = {}) {
|
|
21
|
+
const cwd = path.resolve(cwdInput);
|
|
22
|
+
const open = options.openTarget ?? openTarget;
|
|
23
|
+
let serverPromise;
|
|
24
|
+
let closed = false;
|
|
25
|
+
let closePromise;
|
|
26
|
+
const result = (ok, message) => ({ schema: TUI_ACTION_SCHEMA, ok, message });
|
|
27
|
+
return {
|
|
28
|
+
async open(targetCwd, observerPath) {
|
|
29
|
+
if (closed)
|
|
30
|
+
return result(false, "this terminal session has closed — reopen humanish tui to view the run");
|
|
31
|
+
if (path.resolve(targetCwd) !== cwd)
|
|
32
|
+
return result(false, "Observer can only open runs from this terminal session's project");
|
|
33
|
+
// The capability takes the run-card path, not an arbitrary file, URL, or second project.
|
|
34
|
+
// Validate the exact shape before using its id, then enforce the existing physical storage
|
|
35
|
+
// boundary (including descendant symlinks). An index.html need not exist during an active
|
|
36
|
+
// run: the HTTP server renders it from the contained run.json or observer-data.json.
|
|
37
|
+
const relative = path.relative(cwd, path.resolve(cwd, observerPath));
|
|
38
|
+
const segments = relative.split(path.sep);
|
|
39
|
+
const runId = segments[2];
|
|
40
|
+
if (segments.length !== 5 || segments[0] !== ".humanish" || segments[1] !== "runs"
|
|
41
|
+
|| !runId || !isSafeRunIdSegment(runId) || segments[3] !== "observer" || segments[4] !== "index.html") {
|
|
42
|
+
return result(false, "Observer requires a run under this project's .humanish/runs directory");
|
|
43
|
+
}
|
|
44
|
+
try {
|
|
45
|
+
await bindExistingRunArtifactPaths(cwd, runId);
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
return result(false, "this run's evidence directory is missing or unsafe to open");
|
|
49
|
+
}
|
|
50
|
+
if (closed)
|
|
51
|
+
return result(false, "this terminal session has closed — reopen humanish tui to view the run");
|
|
52
|
+
try {
|
|
53
|
+
// Share the pending start too: two quick selections must not create two listeners.
|
|
54
|
+
serverPromise ??= serveObserverLibrary(cwd, {
|
|
55
|
+
port: 0, safe: false, expose: false, edgeAuthed: false
|
|
56
|
+
}).then((started) => {
|
|
57
|
+
if (!started.ok)
|
|
58
|
+
throw new Error(started.error.message);
|
|
59
|
+
return started.server;
|
|
60
|
+
}).catch((error) => {
|
|
61
|
+
serverPromise = undefined;
|
|
62
|
+
throw error;
|
|
63
|
+
});
|
|
64
|
+
const server = await serverPromise;
|
|
65
|
+
if (closed)
|
|
66
|
+
return result(false, "this terminal session has closed — reopen humanish tui to view the run");
|
|
67
|
+
const url = new URL(`_humanish/runs/${encodeURIComponent(runId)}/observer/index.html`, server.url).href;
|
|
68
|
+
const opened = open(url);
|
|
69
|
+
return result(true, `${url} — follows saved captures; keep this TUI open.${opened.warning ? ` ${opened.warning}` : " If no browser appeared, open this URL on this machine or forward its port over SSH."}`);
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
return result(false, "Observer could not start — try humanish observe --run with this run's id in another terminal");
|
|
73
|
+
}
|
|
74
|
+
},
|
|
75
|
+
close() {
|
|
76
|
+
closed = true;
|
|
77
|
+
closePromise ??= (async () => {
|
|
78
|
+
// A close racing startup still awaits and releases the listener it owns.
|
|
79
|
+
const server = await serverPromise?.catch(() => undefined);
|
|
80
|
+
await server?.close();
|
|
81
|
+
})();
|
|
82
|
+
return closePromise;
|
|
83
|
+
}
|
|
84
|
+
};
|
|
69
85
|
}
|
|
70
86
|
/**
|
|
71
87
|
* Stop a run that is still going.
|
package/dist/tui-actions.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tui-actions.js","sourceRoot":"","sources":["../src/tui-actions.ts"],"names":[],"mappings":"AAAA,iDAAiD;AACjD,EAAE;AACF,mGAAmG;AACnG,iGAAiG;AACjG,6FAA6F;AAC7F,sBAAsB;AAEtB,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"tui-actions.js","sourceRoot":"","sources":["../src/tui-actions.ts"],"names":[],"mappings":"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"}
|
package/dist/tui-app.js
CHANGED
|
@@ -286,7 +286,7 @@ Read about how to prevent this error on https://github.com/vadimdemedes/ink/#isr
|
|
|
286
286
|
`)E.name="enter";else if(n===" ")E.name="tab";else if(n==="\b"||n==="\x1B\b")E.name="backspace",E.meta=n.charAt(0)==="\x1B";else if(n==="\x7F"||n==="\x1B\x7F")E.name="backspace",E.meta=n.charAt(0)==="\x1B";else if(n==="\x1B"||n==="\x1B\x1B")E.name="escape",E.meta=n.length===2;else if(n===" "||n==="\x1B ")E.name="space",E.meta=n.length===2;else if(n.length===1&&n<="")E.name=String.fromCharCode(n.charCodeAt(0)+97-1),E.ctrl=!0;else if(n.length===1&&n>="0"&&n<="9")E.name="number";else if(n.length===1&&n>="a"&&n<="z")E.name=n;else if(n.length===1&&n>="A"&&n<="Z")E.name=n.toLowerCase(),E.shift=!0;else if(i=vH.exec(n))E.name=i[1].toLowerCase(),E.meta=!0,E.shift=/^[A-Z]$/.test(i[1]);else if(i=DH.exec(n)){let m=[...n];m[0]==="\x1B"&&m[1]==="\x1B"&&(E.meta=!0);let h=[i[1],i[2],i[4],i[6]].filter(Boolean).join(""),Q=(i[3]||i[5]||1)-1;E.ctrl=!!(Q&4),E.meta=E.meta||!!(Q&10),E.shift=!!(Q&1),E.code=h,E.name=I2[h]??"",E.shift=RH(h)||E.shift,E.ctrl=NH(h)||E.ctrl}return E},"parseKeypress"),R2=UH;var N2=xt(Ue(),1);var _0=u(()=>(0,N2.useContext)(th),"useStdinContext");var HH=u((n,i={})=>{let{setRawMode:o,internal_exitOnCtrlC:c,internal_eventEmitter:E}=_0();(0,Xm.useEffect)(()=>{if(i.isActive!==!1)return o(!0),()=>{o(!1)}},[i.isActive,o]);let m=(0,Xm.useEffectEvent)(h=>{let Q=R2(h),y={upArrow:Q.name==="up",downArrow:Q.name==="down",leftArrow:Q.name==="left",rightArrow:Q.name==="right",pageDown:Q.name==="pagedown",pageUp:Q.name==="pageup",home:Q.name==="home",end:Q.name==="end",return:Q.name==="return",escape:Q.name==="escape",ctrl:Q.ctrl,shift:Q.shift,tab:Q.name==="tab",backspace:Q.name==="backspace",delete:Q.name==="delete",meta:Q.meta,super:Q.super??!1,hyper:Q.hyper??!1,capsLock:Q.capsLock??!1,numLock:Q.numLock??!1,eventType:Q.eventType},N;Q.isKittyProtocol?Q.isPrintable?N=Q.text??Q.name:Q.ctrl&&Q.name.length===1?N=Q.name:N="":Q.ctrl?N=Q.name??"":N=Q.sequence,!Q.isKittyProtocol&&Q2.includes(Q.name)&&(N=""),N.startsWith("\x1B")&&(N=N.slice(1)),N.length===1&&/[A-Z]/.test(N)&&(y.shift=!0),!(N==="c"&&y.ctrl&&c)&&Oa.discreteUpdates(()=>{n(N,y)})});(0,Xm.useEffect)(()=>{if(i.isActive!==!1)return E.on("input",m),()=>{E.removeListener("input",m)}},[i.isActive,E])},"useInput"),M0=HH;var b2=xt(Ue(),1);var T2=xt(Ue(),1);var OH=u(()=>(0,T2.useContext)($B),"useApp"),U0=OH;var x2=xt(Ue(),1);var GH=u(()=>(0,x2.useContext)(eh),"useStdout"),rh=GH;var wH=xt(Ue(),1);var H0=xt(Ue(),1);var zH=xt(Ue(),1);var YH=xt(Ue(),1);var sh=xt(Ue(),1);var Zm=xt(Ue(),1);var _2=xt(Ue(),1);var ch=xt(Ue(),1);var JH=/utf-?8/i;function Rc(n=process.env){if(process.platform==="win32")return!0;let i=n.LC_ALL??n.LC_CTYPE??n.LANG;return i===void 0||i.trim().length===0?!1:JH.test(i)}u(Rc,"terminalRendersUnicode");var KH=new Map([["\u2014","--"],["\u2013","-"],["\u2019","'"],["\u2018","'"],["\u201C",'"'],["\u201D",'"'],["\u2026","..."],["\xB7","-"],["\u276F",">"],["\u2039","<"],["\u25B8",">"],["\u2713","+"],["\u2691","!"],["\u23CE","Enter"],["\u2191","^"],["\u2193","v"],["\u2190","<-"],["\u2192","->"],["\xD7","x"],["\u2248","~"],["\u2717","x"]]);function M2(n,i=process.env){if(Rc(i))return n;let o="";for(let c of n){if(c.codePointAt(0)<128){o+=c;continue}o+=KH.get(c)??"?"}return o}u(M2,"forTerminal");var Xe=xt(Ue(),1);var be={accent:"#93a2ff",ok:"#7dc9a0",warn:"#d9a441",bad:"#e0796b"};var Di=xt(ha(),1);function G2({columns:n}){let i=[["\u2191 \u2193 \xB7 k j","move the cursor"],["\u23CE \xB7 \u2192","open what the cursor is on, or run the action it names"],["esc \xB7 \u2190","back \u2014 and it cancels an armed confirmation first"],["g \xB7 G","jump to the top, jump to the bottom"],["?","these keys"],["q","quit \u2014 a run you started keeps going without this window"]];return(0,Di.jsxs)(ht,{flexDirection:"column",width:n,children:[(0,Di.jsx)(rt,{color:be.accent,bold:!0,children:"Keys"}),(0,Di.jsx)(ht,{marginTop:1,flexDirection:"column",children:i.map(([o,c])=>(0,Di.jsxs)(ht,{children:[(0,Di.jsx)(ht,{width:16,children:(0,Di.jsx)(rt,{bold:!0,children:o})}),(0,Di.jsx)(rt,{dimColor:!0,children:c})]},o))}),(0,Di.jsxs)(ht,{marginTop:1,flexDirection:"column",children:[(0,Di.jsx)(rt,{dimColor:!0,children:"Starting a run asks twice when it spends money: the first \u23CE arms and"}),(0,Di.jsx)(rt,{dimColor:!0,children:"restates the cost, the second commits. A dry run never asks."})]})]})}u(G2,"HelpScreen");function jH(n){let i=new Map,o=[];for(let E of n){let m=E.lab?.id;if(m===void 0){o.push(E);continue}let h=i.get(m);h===void 0?i.set(m,[E]):h.push(E)}let c=[...i.entries()].map(([E,m])=>{let h=m.filter(Q=>Q.liveness==="running");return{labId:E,runs:m.length,live:h.length,...m[0]===void 0?{}:{latest:m[0]},liveRuns:h}});return c.sort((E,m)=>E.live!==m.live?m.live-E.live:Eh(m.latest)-Eh(E.latest)),{labs:c,unattributed:o}}u(jH,"groupRunsByLab");function Eh(n){if(n===void 0)return 0;for(let i of[n.completedAt,n.updatedAt,n.startedAt]){if(i===void 0)continue;let o=Date.parse(i);if(Number.isFinite(o))return o}return 0}u(Eh,"recencyOf");function w2(n,i){let c=(i===void 0?n:n.filter(Q=>Q.mode===i)).filter(Q=>Q.liveness==="finished"),E=c.map(Q=>Q.durationMs).filter(Q=>typeof Q=="number"&&Number.isFinite(Q)&&Q>=0).sort((Q,y)=>Q-y),m=c.map(Q=>Q.estimatedCostUsd).filter(Q=>typeof Q=="number"&&Number.isFinite(Q)).sort((Q,y)=>Q-y),h=c.filter(Q=>Q.estimatedCostUsd===null||Q.estimatedCostUsd===void 0).length;return{sample:c.length,...E.length===0?{}:{medianDurationMs:L2(E),durationRangeMs:{min:E[0],max:E[E.length-1]}},...m.length===0?{}:{medianCostUsd:L2(m)},costUnknown:h}}u(w2,"expectationFor");function L2(n){let i=Math.floor(n.length/2);return n.length%2===1?n[i]:(n[i-1]+n[i])/2}u(L2,"median");function Wm(n){if(n.sample===0)return"no runs yet";let i=[];if(n.durationRangeMs!==void 0){let{min:m,max:h}=n.durationRangeMs;i.push(m===h?Ri(m):`${Ri(m)}\u2013${Ri(h)}`)}n.medianCostUsd!==void 0&&i.push(`~$${n.medianCostUsd.toFixed(2)} median`);let o=`${n.sample} run${n.sample===1?"":"s"}`,c=n.sample>0&&n.costUnknown===n.sample,E=n.costUnknown===0?"":c?", none priced":`, ${n.costUnknown} unpriced`;return i.length===0?c?`${o}, nothing recorded`:`${o}${E}, nothing timed`:`${i.join(" \xB7 ")} \xB7 ${o}${E}`}u(Wm,"expectationLine");function dh(n){return n.runs===0?n.declared?"never run":"no runs":n.liveExpectation.sample>0?Wm(n.liveExpectation):`${n.runs} ${n.runs===1?"run":"runs"}, none live`}u(dh,"labSummaryLine");function Ri(n){let i=Math.max(0,Math.round(n/1e3));if(i<60)return`${i}s`;let o=Math.floor(i/60);if(o<60)return i%60===0?`${o}m`:`${o}m ${i%60}s`;let c=Math.floor(o/60);return o%60===0?`${c}h`:`${c}h ${o%60}m`}u(Ri,"formatDuration");function cE(n,i){let o=Math.max(8,Math.floor(i.width)),c=Math.max(1,Math.floor(i.maxLines)),E=n.replace(/\*\*([^*]+)\*\*/g,"$1").replace(/\s*\n+\s*/g," ").replace(/\s{2,}/g," ").trim();if(E==="")return{lines:[],truncated:!1};let m=E.split(" "),h=[],Q="",y=0;for(;y<m.length;y+=1){let x=m[y],w=Q===""?x:`${Q} ${x}`;if(w.length<=o){Q=w;continue}if(Q!==""&&h.push(Q),Q=x.length<=o?x:`${x.slice(0,o-1)}\u2026`,x.length>o&&(h.push(Q),Q=""),h.length===c)break}h.length<c&&Q!==""&&(h.push(Q),y=m.length);let N=y<m.length;if(N&&h.length>0){let x=h[h.length-1];h[h.length-1]=x.length+1<=o?`${x}\u2026`:`${x.slice(0,o-1)}\u2026`}return{lines:h,truncated:N}}u(cE,"normalizeThought");function fE(n){let i=Math.max(0,Math.floor(n.total)),o=Math.max(1,Math.floor(n.viewport));if(i<=o)return{start:0,end:i};let c=Math.min(Math.max(0,Math.floor(n.selected)),i-1),E=Math.min(Math.max(0,Math.floor(n.margin??1)),Math.floor((o-1)/2)),m=Math.min(Math.max(0,c-E),i-o);return c>=m+o-E&&(m=Math.min(c-o+1+E,i-o)),m=Math.max(0,Math.min(m,i-o)),{start:m,end:m+o}}u(fE,"listWindow");function z2(n){return(n.split("/").pop()??n).replace(/\.(ya?ml)$/i,"")}u(z2,"labNameFromPath");function Y2(n,i){let{labs:o,unattributed:c}=jH(i),E=new Map(o.map(w=>[w.labId,w])),m=new Map;for(let w of i){let b=w.lab?.id;if(b===void 0)continue;let L=m.get(b);L===void 0?m.set(b,[w]):L.push(w)}let h=new Map;for(let w of n)h.set(w.id,(h.get(w.id)??0)+1);let Q=u((w,b,L)=>{let j=E.get(w),q=b?.path;return{key:q??`id:${w}`,labId:w,name:q===void 0?w:z2(q),...b?.title===void 0?{}:{title:b.title},...q===void 0?{}:{path:q},...b?.origin===void 0?{}:{origin:b.origin},declared:L,label:b?.title??(q===void 0?w:z2(q)),...b?.description===void 0?{}:{description:b.description},sharesIdWith:Math.max(0,(h.get(w)??0)-1),runs:j?.runs??0,live:j?.live??0,...j?.latest===void 0?{}:{latest:j.latest},liveRuns:j?.liveRuns??[],expectation:w2(m.get(w)??[]),liveExpectation:w2(m.get(w)??[],"live")}},"build"),y=new Set(n.map(w=>w.id)),N=[...n.map(w=>Q(w.id,w,!0)),...o.filter(w=>!y.has(w.labId)).map(w=>Q(w.labId,void 0,!1))];kH(N);let x=u(w=>w.label,"label");return N.sort((w,b)=>{if(w.live!==b.live)return b.live-w.live;let L=u(q=>q.runs>0?0:q.declared?1:2,"rank");if(L(w)!==L(b))return L(w)-L(b);if(w.runs>0&&b.runs>0){let q=Eh(b.latest)-Eh(w.latest);if(q!==0)return q}let j=x(w).localeCompare(x(b));return j!==0?j:w.key.localeCompare(b.key)}),{rows:N,unattributed:c}}u(Y2,"labRows");function kH(n){let i=u(E=>{let m=new Map;for(let h of E)m.set(h,(m.get(h)??0)+1);return m},"count"),o=i(n.map(E=>E.title??E.name)),c=i(n.map(E=>E.name));for(let E of n){let m=E.title??E.name;if((o.get(m)??0)===1){E.label=m;continue}E.label=(c.get(E.name)??0)===1?E.name:E.path??E.name}}u(kH,"assignLabels");function G0(n){switch(n.name){case"labs":return"labs";case"all-runs":return"all-runs";case"lab":return`lab:${n.labKey}`;default:return`run:${n.runId}`}}u(G0,"screenKey");function J2(){return{stack:[{name:"labs"}],selection:{},quit:!1}}u(J2,"initialNav");function Pm(n){return n.stack[n.stack.length-1]??{name:"labs"}}u(Pm,"currentScreen");function K2(n){return n.selection[G0(Pm(n))]??0}u(K2,"selectedIndex");function F2(n,i){switch(i.type){case"enter":return{...n,stack:[...n.stack,i.screen]};case"back":return n.stack.length<=1?{...n,quit:!0}:{...n,stack:n.stack.slice(0,-1)};case"move":{if(i.total<=0)return n;let o=G0(Pm(n)),c=n.selection[o]??0,E=Math.min(Math.max(0,c+i.delta),i.total-1);return E===c?n:{...n,selection:{...n.selection,[o]:E}}}case"select":{if(i.total<=0)return n;let o=G0(Pm(n)),c=Math.min(Math.max(0,i.index),i.total-1);return{...n,selection:{...n.selection,[o]:c}}}default:return{...n,quit:!0}}}u(F2,"navigate");function rA(n){return n===void 0?{}:{color:n}}u(rA,"color");var gu=xt(ha(),1),VH=96;function mh(n){return Math.max(20,Math.min(VH,n))}u(mh,"contentWidth");function q2({columns:n,context:i,breadcrumb:o,hints:c,children:E}){let m=mh(n);return(0,gu.jsxs)(ht,{flexDirection:"column",width:m,children:[(0,gu.jsxs)(ht,{width:m,children:[(0,gu.jsx)(rt,{bold:!0,children:"human(ish)"}),(0,gu.jsx)(ht,{flexGrow:1}),i===void 0?null:(0,gu.jsx)(rt,{dimColor:!0,wrap:"truncate-start",children:i})]}),o===void 0?null:(0,gu.jsx)(rt,{dimColor:!0,wrap:"truncate-start",children:o}),(0,gu.jsx)(ht,{marginTop:1,flexDirection:"column",children:E}),(0,gu.jsx)(ht,{marginTop:1,children:(0,gu.jsx)(rt,{dimColor:!0,children:c})})]})}u(q2,"Frame");var w0=Rc()?["\u280B","\u2819","\u2839","\u2838","\u283C","\u2834","\u2826","\u2827","\u2807","\u280F"]:["|","/","-","\\","|","/","-","\\","|","/"];function $m(n){return w0[Math.abs(Math.floor(n))%w0.length]??w0[0]}u($m,"spinnerFrame");function Ni(n){return n?Rc()?"\u276F":">":" "}u(Ni,"gutter");function tp(n){if(n.liveness==="running")return $m(n.tick??0);let i=Rc();return n.liveness==="interrupted"||n.verdict==="fail"?i?"\u2691":"!":n.verdict===void 0?i?"\xB7":"-":i?"\u2713":"+"}u(tp,"verdictGlyph");function XH(n){if(n.liveness==="running")return be.ok;if(n.liveness==="interrupted")return be.warn;if(n.verdict==="fail")return be.bad}u(XH,"verdictColor");var gE=u(n=>rA(XH(n)),"glyphColor");function jr(n,i){let o=Math.max(4,Math.floor(i));return n.length<=o?n:`${n.slice(0,o-1)}\u2026`}u(jr,"fitLabelToWidth");var Dn=xt(ha(),1);function V2({runs:n,details:i,labels:o,expected:c,selected:E,columns:m,viewport:h,tick:Q,now:y}){if(n.length===0)return(0,Dn.jsxs)(ht,{flexDirection:"column",children:[(0,Dn.jsx)(rt,{children:"nobody is working right now"}),(0,Dn.jsx)(rt,{dimColor:!0,children:"start a run from any lab and it appears here"})]});let N=fE({total:n.length,selected:E,viewport:Math.max(2,h-6)}),x=n[Math.min(Math.max(0,E),n.length-1)],w=x===void 0?void 0:i.get(x.runId),b=w?.participants[0],L=b?.thought===void 0?void 0:cE(b.thought.text,{width:Math.max(16,m-2),maxLines:2});return(0,Dn.jsxs)(ht,{flexDirection:"column",children:[n.slice(N.start,N.end).map((j,q)=>(0,Dn.jsx)(ZH,{run:j,who:j2(i.get(j.runId))??"starting\u2026",lab:o.get(j.runId)??j.lab?.id??"",expectedMs:c.get(j.lab?.id??""),active:N.start+q===E,columns:m,tick:Q,now:y},j.runId)),L===void 0?null:(0,Dn.jsxs)(ht,{marginTop:1,flexDirection:"column",children:[L.lines.map((j,q)=>(0,Dn.jsxs)(ht,{width:m,children:[(0,Dn.jsx)(ht,{width:2,flexShrink:0,children:(0,Dn.jsx)(rt,{color:be.accent,children:"\u258C"})}),(0,Dn.jsxs)(rt,{dimColor:!0,wrap:"truncate-end",children:[q===0?`"${j}`:j,q===L.lines.length-1?'"':""]})]},q)),(0,Dn.jsxs)(ht,{width:m,children:[(0,Dn.jsx)(ht,{width:2,flexShrink:0,children:(0,Dn.jsx)(rt,{children:" "})}),(0,Dn.jsx)(rt,{dimColor:!0,wrap:"truncate-end",children:jr([j2(w),o.get(x?.runId??"")].filter(Boolean).join(" \xB7 "),Math.max(10,m-2))})]})]}),(0,Dn.jsx)(ht,{marginTop:1,children:(0,Dn.jsx)(rt,{dimColor:!0,children:WH(n,i)})})]})}u(V2,"AllRunsScreen");function j2(n){let i=n?.participants[0];return i?.personaId??i?.label}u(j2,"personaOf");function ZH({run:n,who:i,lab:o,expectedMs:c,active:E,columns:m,tick:h,now:Q}){let y=n.startedAt===void 0?Number.NaN:Date.parse(n.startedAt),N=Number.isFinite(y)?k2(Q-y):"",x=c===void 0?N:`${N} / ~${k2(c)}`,w=Math.max(10,Math.floor((m-x.length-6)*.5)),b=Math.max(8,m-x.length-w-6);return(0,Dn.jsxs)(ht,{width:m,children:[(0,Dn.jsxs)(rt,{...rA(E?be.accent:void 0),bold:E,children:[Ni(E)," "]}),(0,Dn.jsxs)(rt,{color:be.ok,children:[$m(h)," "]}),(0,Dn.jsx)(ht,{width:w,children:(0,Dn.jsx)(rt,{...rA(E?be.accent:void 0),bold:E,wrap:"truncate-end",children:jr(i,w)})}),(0,Dn.jsx)(ht,{width:b,children:(0,Dn.jsx)(rt,{dimColor:!0,wrap:"truncate-end",children:jr(o,b)})}),(0,Dn.jsx)(ht,{flexGrow:1}),(0,Dn.jsx)(rt,{dimColor:!0,children:x})]})}u(ZH,"RunRow");function WH(n,i){let o=0,c=0;for(let h of n){let Q=h.estimatedCostUsd??i.get(h.runId)?.participants[0]?.estimatedCostUsd;typeof Q=="number"&&(o+=Q,c+=1)}if(c===0)return"no spend recorded yet \u2014 a live run prices itself as it goes";let E=n.length-c,m=E===0?"":` \xB7 ${E} not priced yet`;return`spend ~$${o.toFixed(2)} so far across ${c} of ${n.length}${m}`}u(WH,"spendLine");function k2(n){if(!Number.isFinite(n)||n<0)return"0:00";let i=Math.floor(n/1e3),o=Math.floor(i/60);return o<60?`${o}:${String(i%60).padStart(2,"0")}`:Ri(n)}u(k2,"clockOf");var ee=xt(ha(),1);function ph(n,i){return[...i?[{kind:"start",mode:"dry-run"},{kind:"start",mode:"live"}]:[],...n.map(c=>({kind:"run",run:c}))]}u(ph,"labItems");function P2(n){let{row:i,summary:o,runs:c,selected:E,columns:m,viewport:h,now:Q,tick:y,canStart:N}=n,x=ph(c,N);return(0,ee.jsxs)(ht,{flexDirection:"column",children:[o?.description===void 0?null:(0,ee.jsx)(rt,{wrap:"truncate-end",children:AO(o.description)}),o?.subject===void 0?null:(0,ee.jsx)(rt,{dimColor:!0,wrap:"truncate-end",children:[o.subject,o.participants,o.model,o.reasoningEffort===void 0?void 0:`${o.reasoningEffort} effort`].filter(Boolean).join(" \xB7 ")}),(0,ee.jsxs)(ht,{width:m,children:[(0,ee.jsxs)(rt,{dimColor:!0,wrap:"truncate-end",children:[dh(i),nO(o)]}),(0,ee.jsx)(ht,{flexGrow:1}),o?.keysReady===void 0?null:(0,ee.jsx)(rt,{...rA(o.keysReady?be.ok:be.warn),children:o.keysReady?"keys \u2713":"keys \u2717"})]}),o?.keysReady===!1?(0,ee.jsxs)(ht,{flexDirection:"column",width:m,children:[(0,ee.jsxs)(rt,{...rA(be.warn),children:[o.missingKeys?.join(", ")," not found"]}),(0,ee.jsxs)(rt,{dimColor:!0,children:[" ","humanish keys set openai"," ","\u2014 stores them for every project"]}),(0,ee.jsxs)(rt,{dimColor:!0,children:[" ","humanish tui --env-file .env"," ","\u2014 or just this session"]})]}):null,n.launchNote===void 0?null:(0,ee.jsx)(ht,{marginTop:1,children:(0,ee.jsx)(rt,{dimColor:!0,children:n.launchNote})}),n.launchError===void 0?null:(0,ee.jsx)(ht,{marginTop:1,children:(0,ee.jsx)(rt,{color:be.bad,children:n.launchError})}),N?(0,ee.jsxs)(ht,{marginTop:1,flexDirection:"column",children:[(0,ee.jsx)(Z2,{...n,mode:"dry-run",active:X2(x,E)==="dry-run"}),(0,ee.jsx)(Z2,{...n,mode:"live",active:X2(x,E)==="live"}),n.confirming==="live"?(0,ee.jsx)(ht,{marginTop:1,children:(0,ee.jsxs)(rt,{color:be.warn,children:[" ","start a live run? ",Wm(n.row.liveExpectation)," \xB7 \u23CE confirm \xB7 esc cancel"]})}):null]}):null,i.declared?null:(0,ee.jsx)(ht,{marginTop:1,children:(0,ee.jsx)(rt,{color:be.warn,children:"no manifest here \u2014 renamed, deleted, or run from elsewhere"})}),i.sharesIdWith>0?(0,ee.jsxs)(rt,{color:be.warn,children:[i.sharesIdWith+1,' manifests declare "',i.labId,'" \u2014 these runs are shared between them']}):null,(0,ee.jsxs)(ht,{marginTop:1,flexDirection:"column",children:[(0,ee.jsx)(rt,{dimColor:!0,children:"Runs"}),c.length===0?(0,ee.jsx)(rt,{dimColor:!0,children:" none yet"}):(0,ee.jsx)(PH,{runs:c,liveDetail:n.liveDetail,selected:E-ph([],N).length,columns:m,viewport:Math.max(2,h-6),now:Q,tick:y,expectedMs:i.liveExpectation.medianDurationMs})]})]})}u(P2,"LabScreen");function X2(n,i){let o=n[i];return o?.kind==="start"?o.mode:void 0}u(X2,"activeStart");function Z2({mode:n,confirming:i,active:o,columns:c,row:E,summary:m}){let h=n==="live",Q=h&&m?.keysReady===!1,y=h?be.warn:be.accent;return(0,ee.jsxs)(ht,{width:c,children:[(0,ee.jsxs)(rt,{...rA(o?y:void 0),bold:o,dimColor:Q&&!o,children:[Ni(o)," ",h?"Start a LIVE run":"Start a dry run"]}),(0,ee.jsx)(ht,{flexGrow:1}),(0,ee.jsx)(rt,{dimColor:!Q,...rA(Q?be.warn:void 0),children:h?Q?`${Wm(E.liveExpectation)} \xB7 needs keys`:Wm(E.liveExpectation):"free \xB7 no keys, no spend"})]})}u(Z2,"StartRow");function PH({runs:n,liveDetail:i,selected:o,columns:c,viewport:E,now:m,tick:h,expectedMs:Q}){let y=fE({total:n.length,selected:o,viewport:E});return(0,ee.jsxs)(ht,{flexDirection:"column",children:[y.start>0?(0,ee.jsxs)(rt,{dimColor:!0,children:[" \u2191 ",y.start," more"]}):null,n.slice(y.start,y.end).map((N,x)=>N.liveness==="running"?(0,ee.jsx)($H,{run:N,detail:i,active:y.start+x===o,columns:c,now:m,tick:h,expectedMs:Q},N.runId):(0,ee.jsx)(eO,{run:N,active:y.start+x===o,columns:c},N.runId)),y.end<n.length?(0,ee.jsxs)(rt,{dimColor:!0,children:[" \u2193 ",n.length-y.end," more"]}):null]})}u(PH,"RunList");function $H({run:n,detail:i,active:o,columns:c,now:E,tick:m,expectedMs:h}){let Q=i?.participants[0],y=n.startedAt===void 0?Number.NaN:Date.parse(n.startedAt),N=Number.isFinite(y)?W2(E-y):void 0,x=h===void 0?"":` of ~${W2(h)}`,w=Q?.thought===void 0?void 0:cE(Q.thought.text,{width:Math.max(16,c-6),maxLines:3});return(0,ee.jsxs)(ht,{flexDirection:"column",children:[(0,ee.jsxs)(ht,{width:c,children:[(0,ee.jsxs)(rt,{...rA(o?be.accent:void 0),bold:o,children:[Ni(o)," "]}),(0,ee.jsxs)(rt,{color:be.ok,children:[tp({liveness:"running",tick:m})," "]}),(0,ee.jsx)(rt,{...rA(o?be.accent:void 0),bold:o,wrap:"truncate-end",children:Q?.personaId??Q?.label??"starting\u2026"}),(0,ee.jsx)(ht,{flexGrow:1}),(0,ee.jsx)(rt,{dimColor:!0,children:N===void 0?"":`${N}${x}`})]}),w===void 0?null:(0,ee.jsx)(ht,{flexDirection:"column",marginLeft:4,children:w.lines.map((b,L)=>(0,ee.jsxs)(rt,{dimColor:L>0,children:[L===0?`"${b}`:b,L===w.lines.length-1?'"':""]},L))}),Q===void 0?null:(0,ee.jsx)(ht,{width:c,children:(0,ee.jsxs)(rt,{dimColor:!0,children:[" ",tO(Q)]})})]})}u($H,"LiveRun");function tO(n){let i=[];return n.actions!==void 0&&i.push(`${n.actions} actions`),n.thoughts!==void 0&&i.push(`${n.thoughts} thoughts`),n.turns!==void 0&&i.push(`${n.turns} turns`),i.length===0?"working\u2026":i.join(" \xB7 ")}u(tO,"activityLine");function eO({run:n,active:i,columns:o}){let c=aO(n.completedAt??n.startedAt),E=n.participants===void 0?n.verdict??"no verdict":`${n.participants.reachedGoal}/${n.participants.total} reached the goal`,m=n.estimatedCostUsd===void 0||n.estimatedCostUsd===null?"":` \xB7 ~$${n.estimatedCostUsd.toFixed(2)}`,h=[c,E].filter(Boolean).join(" \xB7 ")+m;return(0,ee.jsxs)(ht,{width:o,children:[(0,ee.jsxs)(rt,{...rA(i?be.accent:void 0),bold:i,children:[Ni(i)," "]}),(0,ee.jsxs)(rt,{...gE(n),children:[tp(n)," "]}),(0,ee.jsx)(rt,{...rA(i?be.accent:void 0),bold:i,wrap:"truncate-end",children:h})]})}u(eO,"PastRun");function nO(n){let i=n?.caps.laneUsd,o=n?.caps.studyUsd;if(i===void 0&&o===void 0)return"";let c=[];return i!==void 0&&c.push(`$${i} lane`),o!==void 0&&c.push(`$${o} study`),` \xB7 caps ${c.join(" / ")}`}u(nO,"capsLine");function AO(n){let i=n.replace(/\s+/g," ").trim(),o=i.indexOf(". ");return o===-1?i:i.slice(0,o+1)}u(AO,"firstSentence");function aO(n){if(n===void 0)return"";let i=Date.parse(n);if(!Number.isFinite(i))return"";let o=new Date(i),c=u(E=>String(E).padStart(2,"0"),"pad");return`${c(o.getUTCMonth()+1)}-${c(o.getUTCDate())} ${c(o.getUTCHours())}:${c(o.getUTCMinutes())}`}u(aO,"shortDate");function W2(n){if(!Number.isFinite(n)||n<0)return"0:00";let i=Math.floor(n/1e3),o=Math.floor(i/60);return o<60?`${o}:${String(i%60).padStart(2,"0")}`:Ri(n)}u(W2,"clockOf");var sA=xt(ha(),1);function Ch(n,i){return n.liveness==="interrupted"?i?.observerPath===void 0?["reclaim"]:["reclaim","observer"]:n.liveness==="running"?["stop"]:i?.observerPath===void 0?["again"]:["observer","again"]}u(Ch,"runActions");function lO(n){switch(n){case"observer":return"Open in Observer";case"again":return"Run again";case"stop":return"Stop this run";default:return"Reclaim \u2014 stop sandboxes, keep evidence"}}u(lO,"actionLabel");function $2({run:n,detail:i,columns:o,selected:c,tick:E,now:m,actionNote:h}){let Q=i?.participants[0],y=Ch(n,i),N=n.liveness==="interrupted";return(0,sA.jsxs)(ht,{flexDirection:"column",children:[(0,sA.jsx)(ht,{children:(0,sA.jsxs)(rt,{...gE(n),bold:!0,children:[tp({...n,tick:E})," ",iO(n,Q)]})}),N?(0,sA.jsx)(oO,{run:n,detail:i,participant:Q,now:m,columns:o}):(0,sA.jsx)(uO,{run:n,participant:Q,columns:o}),y.length===0?null:(0,sA.jsx)(ht,{marginTop:1,flexDirection:"column",children:y.map((x,w)=>(0,sA.jsx)(ht,{children:(0,sA.jsxs)(rt,{...rA(w===c?be.accent:void 0),bold:w===c,children:[Ni(w===c)," ",jr(lO(x),Math.max(10,o-3))]})},x))}),h===void 0?null:(0,sA.jsx)(ht,{marginTop:1,children:(0,sA.jsx)(rt,{dimColor:!0,wrap:"truncate-end",children:h})})]})}u($2,"RunScreen");function iO(n,i){if(n.liveness==="interrupted")return"interrupted \u2014 no outcome recorded";if(n.liveness==="running"){let E=i?.personaId??i?.label;return E===void 0?"starting\u2026":`${E} is working`}let o=n.participants;if(o===void 0)return n.verdict??"finished, no verdict recorded";let c=o.reportedFriction===void 0||o.reportedFriction===0?"":` \xB7 ${o.reportedFriction} reported friction`;return`${o.reachedGoal}/${o.total} reached the goal${c}`}u(iO,"headline");function uO({run:n,participant:i,columns:o}){let c=i?.thought===void 0?void 0:cE(i.thought.text,{width:Math.max(16,o),maxLines:3}),E=[n.durationMs===void 0?void 0:Ri(n.durationMs),i?.turns===void 0?void 0:`${i.turns} turns`,i?.thoughts===void 0?void 0:`${i.thoughts} thoughts`].filter(Boolean).join(" \xB7 ");return(0,sA.jsxs)(ht,{flexDirection:"column",children:[c===void 0?null:(0,sA.jsx)(ht,{marginTop:1,flexDirection:"column",children:c.lines.map((m,h)=>(0,sA.jsxs)(rt,{children:[h===0?`"${m}`:m,h===c.lines.length-1?'"':""]},h))}),(0,sA.jsxs)(ht,{marginTop:1,flexDirection:"column",children:[E===""?null:(0,sA.jsx)(rt,{dimColor:!0,children:E}),(0,sA.jsx)(rt,{dimColor:!0,children:tN(n,i)})]})]})}u(uO,"FinishedFacts");function oO({run:n,detail:i,participant:o,now:c,columns:E}){let m=n.startedAt===void 0?Number.NaN:Date.parse(n.startedAt),h=n.updatedAt===void 0?Number.NaN:c-Date.parse(n.updatedAt),Q=[o?.thoughts===void 0?void 0:`${o.thoughts} thoughts`,o?.actions===void 0?void 0:`${o.actions} actions`].filter(Boolean).join(" \xB7 ");return(0,sA.jsxs)(ht,{marginTop:1,flexDirection:"column",width:E,children:[(0,sA.jsxs)(rt,{dimColor:!0,children:[Number.isFinite(m)?`started ${rO(m)}`:"start time not recorded",Number.isFinite(h)?` \xB7 quiet ${Ri(h)}`:"",Q===""?"":` \xB7 ${Q} captured`]}),(0,sA.jsxs)(ht,{children:[(0,sA.jsx)(rt,{dimColor:!0,children:n.estimatedCostUsd===void 0&&o?.estimatedCostUsd===void 0?"cost unknown \u2014 it ended before pricing itself":tN(n,o)}),(0,sA.jsx)(rt,{color:be.warn,children:" sandboxes may still be running"})]})]})}u(oO,"InterruptedFacts");function tN(n,i){let o=n.estimatedCostUsd===void 0?i?.estimatedCostUsd:n.estimatedCostUsd;return o===void 0?"cost not recorded":o===null?"cost declared absent":`~$${o.toFixed(2)} estimated`}u(tN,"costLine");function rO(n){let i=new Date(n),o=u(c=>String(c).padStart(2,"0"),"pad");return`${o(i.getUTCHours())}:${o(i.getUTCMinutes())}`}u(rO,"clockTime");var eN=xt(Ue(),1);var Je=xt(ha(),1);function nN({rows:n,selected:i,columns:o,viewport:c,unattributed:E,tick:m,liveParticipants:h,now:Q,initialized:y,peerSelected:N,liveTotal:x,initArmed:w,actionNote:b}){if(n.length===0)return y?(0,Je.jsxs)(ht,{flexDirection:"column",children:[(0,Je.jsx)(rt,{children:"no labs here yet"}),(0,Je.jsxs)(ht,{marginTop:1,flexDirection:"column",children:[(0,Je.jsx)(rt,{dimColor:!0,children:"a lab is a study: who to send, to what, and what counts as done."}),(0,Je.jsx)(rt,{dimColor:!0,children:"write one in humanish/labs/, or `humanish init` for three to start from."})]})]}):(0,Je.jsxs)(ht,{flexDirection:"column",children:[(0,Je.jsx)(rt,{children:"this directory is not a humanish project"}),(0,Je.jsxs)(ht,{marginTop:1,flexDirection:"column",children:[(0,Je.jsx)(rt,{dimColor:!0,children:"humanish studies live in a project: a humanish/ directory of labs and"}),(0,Je.jsx)(rt,{dimColor:!0,children:"personas beside the app they study."})]}),(0,Je.jsxs)(ht,{marginTop:1,children:[(0,Je.jsxs)(rt,{...rA(be.accent),bold:!0,children:[Ni(!0)," Set up humanish here"]}),(0,Je.jsx)(ht,{flexGrow:1}),(0,Je.jsx)(rt,{dimColor:!0,children:w?"\u23CE again to confirm \xB7 esc cancel":"writes humanish/ and updates package.json"})]}),b===void 0?null:(0,Je.jsx)(ht,{marginTop:1,children:(0,Je.jsx)(rt,{dimColor:!0,children:b})})]});let L=fE({total:n.length,selected:i,viewport:c});return(0,Je.jsxs)(ht,{flexDirection:"column",children:[L.start>0?(0,Je.jsxs)(rt,{dimColor:!0,children:[" \u2191 ",L.start," more"]}):null,n.slice(L.start,L.end).map((j,q)=>{let F=L.start+q,ft=F>0?n[F-1]:void 0,_t=j.runs===0&&ft!==void 0&&ft.runs>0;return(0,Je.jsxs)(eN.default.Fragment,{children:[_t?(0,Je.jsx)(ht,{marginTop:1,children:(0,Je.jsxs)(rt,{dimColor:!0,children:[" ","not yet run"]})}):null,(0,Je.jsx)(cO,{row:j,columns:o,active:F===i,tick:m,liveParticipants:h,now:Q})]},j.key)}),L.end<n.length?(0,Je.jsxs)(rt,{dimColor:!0,children:[" \u2193 ",n.length-L.end," more"]}):null,(0,Je.jsxs)(ht,{marginTop:1,children:[(0,Je.jsxs)(rt,{...rA(N?be.accent:void 0),bold:N,dimColor:!N,children:[Ni(N)," All runs"]}),x>0?(0,Je.jsxs)(rt,{color:be.ok,children:[" ",x," working"]}):null]}),E>0?(0,Je.jsx)(ht,{marginTop:1,children:(0,Je.jsxs)(rt,{dimColor:!0,children:[E," ",E===1?"run":"runs"," with no lab"]})}):null,(0,Je.jsxs)(ht,{marginTop:1,width:o,children:[(0,Je.jsx)(rt,{...rA(be.accent),children:"\u25B8"}),(0,Je.jsx)(rt,{dimColor:!0,wrap:"truncate-end",children:` ${sO(n[i],N)}`})]})]})}u(nN,"LabsScreen");function sO(n,i){if(i)return"every run in this project, newest first \u2014 across every lab";if(n===void 0)return"";let o=n.description?.split(/(?<=[.!?])\s/)[0]?.trim();return o!==void 0&&o.length>0?o:"no description in the manifest"}u(sO,"describe");function cO({row:n,columns:i,active:o,tick:c,liveParticipants:E,now:m}){let h=n.liveRuns[0],Q=h===void 0?dh(n):fO(n,h,E.get(h.runId),m),y=Math.max(10,Math.floor(i/2)-2),N=Q.length<=y?Q:gO(n),x=Math.max(8,i-N.length-6);return(0,Je.jsxs)(ht,{width:i,children:[(0,Je.jsxs)(rt,{...rA(o?be.accent:void 0),bold:o,children:[Ni(o)," "]}),(0,Je.jsxs)(rt,{...gE({liveness:h===void 0?"finished":"running"}),children:[h===void 0?" ":$m(c)," "]}),(0,Je.jsx)(rt,{...rA(o?be.accent:void 0),bold:o,wrap:"truncate-end",children:jr(n.label,x)}),(0,Je.jsx)(ht,{flexGrow:1}),(0,Je.jsx)(rt,{...rA(h===void 0?void 0:be.ok),dimColor:h===void 0,children:N})]})}u(cO,"LabRowView");function fO(n,i,o,c){let E=i.startedAt===void 0?Number.NaN:Date.parse(i.startedAt),m=Number.isFinite(E)?EO(c-E):void 0,h=o??"starting\u2026",Q=n.live>1?` +${n.live-1}`:"";return m===void 0?`${h}${Q}`:`${h} \xB7 ${m}${Q}`}u(fO,"liveStatus");function gO(n){return n.live>0?`${n.live} running`:n.runs===0?n.declared?"never run":"no runs":`${n.runs} runs`}u(gO,"fallbackStatus");function EO(n){if(!Number.isFinite(n)||n<0)return"0:00";let i=Math.floor(n/1e3),o=Math.floor(i/60),c=i%60;return o<60?`${o}:${String(c).padStart(2,"0")}`:Ri(n)}u(EO,"clockOf");var Bh=xt(Ue(),1);function AN(){let{stdout:n}=rh(),i=u(()=>({columns:Math.max(20,n.columns||80),rows:Math.max(6,n.rows||24)}),"read"),[o,c]=(0,Bh.useState)(i);return(0,Bh.useEffect)(()=>{let E=u(()=>{c(h=>{let Q=i();return Q.columns===h.columns&&Q.rows===h.rows?h:Q})},"update");n.on("resize",E);let m=setTimeout(E,0);return()=>{n.off("resize",E),clearTimeout(m)}},[n]),o}u(AN,"useTerminalSize");var bi=xt(ha(),1),dO=6,aN=2e3,mO=5e3,pO=100,L0=400,CO=120;function uN({options:n,onReady:i,now:o,tick:c}){let{exit:E}=U0(),m=AN(),[h,Q]=(0,Xe.useReducer)(F2,void 0,J2),[y,N]=(0,Xe.useState)(void 0),[x,w]=(0,Xe.useState)(void 0),[b,L]=(0,Xe.useState)(void 0),[j,q]=(0,Xe.useState)(void 0),[F,ft]=(0,Xe.useState)(void 0),[_t,Vt]=(0,Xe.useState)(void 0),[Gt,pe]=(0,Xe.useState)(void 0),[Ce,Yt]=(0,Xe.useState)(void 0),[wt,pt]=(0,Xe.useState)(void 0),[Lt,ce]=(0,Xe.useState)(!1),[Dt,rn]=(0,Xe.useState)(void 0),[He,ct]=(0,Xe.useState)(0),Mt=c??He,[Jt,Nt]=(0,Xe.useState)(void 0),[ue,sn]=(0,Xe.useState)(new Map),tt=(0,Xe.useMemo)(()=>n.capabilities.readProjectState(n.cwd),[n]),Ot=o??Date.now(),oe=(0,Xe.useRef)(void 0),ie=(0,Xe.useRef)({name:"labs"});(0,Xe.useEffect)(()=>{let Bt=!1,Kt=u(async()=>{try{let[$t,Ke]=await Promise.all([n.capabilities.readRunIndex(n.cwd),n.capabilities.listLabs(n.cwd)]);if(Bt)return;w(void 0),N(lN($t,Ke.labs))}catch($t){if(Bt)return;w($t instanceof Error?$t.message:String($t))}},"read");Kt();let It=setInterval(()=>{Kt()},aN);return It.unref?.(),()=>{Bt=!0,clearInterval(It)}},[n]),(0,Xe.useEffect)(()=>{(y!==void 0||x!==void 0)&&i?.()},[y,x,i]);let kt=Pm(h),fn=K2(h),fe=iN(kt,y);(0,Xe.useEffect)(()=>{ie.current=kt;let Bt=yO(kt,y,fn);Bt!==void 0&&(oe.current=Bt)},[kt,y,fn]),(0,Xe.useEffect)(()=>{if(y===void 0)return;let Bt=oe.current;if(Bt===void 0)return;let Kt=SO(kt,y,Bt);Kt>=0&&Kt!==fn&&Q({type:"select",index:Kt,total:iN(kt,y)})},[y,kt]);let Oe=(0,Xe.useCallback)(async(Bt,Kt)=>{if(Kt==="live"&&b!=="live"){L("live"),Vt(Date.now());return}if(Kt==="live"&&_t!==void 0&&Date.now()-_t<L0)return;L(void 0),Vt(void 0),q(void 0),ft({labKey:Bt.key,text:`starting ${Bt.name}\u2026`});let It=await n.capabilities.startRun({cwd:n.cwd,lab:Bt.name,mode:Kt});if(!It.ok){ft(void 0),q({labKey:Bt.key,text:It.error.message});return}let $t=Date.parse(It.run.launchedAt),Ke=u(On=>{if(On.pid!==It.run.pid)return!1;let GA=On.startedAt===void 0?Number.NaN:Date.parse(On.startedAt);return!Number.isFinite(GA)||!Number.isFinite($t)?!1:GA>=$t-1e3},"isOurs"),he=Date.now()+mO;for(;;){let On=await n.capabilities.readRunIndex(n.cwd),GA=On.runs.find(Ke);if(GA!==void 0){let Ia=await n.capabilities.listLabs(n.cwd);N(lN(On,Ia.labs)),ft(void 0),ie.current.name==="lab"&&ie.current.labKey===Bt.key&&Q({type:"enter",screen:{name:"run",labId:Bt.labId,runId:GA.runId}});return}if(Date.now()>=he)break;await new Promise(Ia=>setTimeout(Ia,pO))}let un=await n.capabilities.readLaunchLog(It.run.logPath);ft(void 0),q({labKey:Bt.key,text:un===""?`${Bt.name} started (pid ${It.run.pid}) but has not reported in. Check ${It.run.logPath}.`:`${Bt.name} did not report in. Its log ends:
|
|
287
287
|
${un.split(`
|
|
288
288
|
`).slice(-3).join(`
|
|
289
|
-
`)}`})},[b,_t,n]),Pt=(0,Xe.useCallback)(async(Bt,Kt)=>{if(Kt==="stop"){if(wt===void 0){pt(Date.now()),Yt("stop this run? \u23CE again to confirm \xB7 esc cancel");return}if(Date.now()-wt<L0)return;pt(void 0),Yt("stopping\u2026");let he=await n.capabilities.stopRun(n.cwd,Bt.runId);Yt(he.message);return}if(Kt==="observer"){let he=Gt?.observerPath;if(he===void 0){Yt("this run has no Observer artifact on disk");return}Yt("opening\u2026");let un=await n.capabilities.openObserver(n.cwd,he);Yt(un.message);return}if(Kt==="reclaim"){Yt("reclaiming \u2014 stopping sandboxes, keeping evidence\u2026");let he=await n.capabilities.reclaimRun(n.cwd,Bt.runId);Yt(he.ok?`reclaimed ${he.receiptCount} recorded resource${he.receiptCount===1?"":"s"}`:`could not reclaim: ${he.error?.message??"unknown"}`);return}let It=Bt.lab?.id,$t=It===void 0?void 0:y?.rows.find(he=>he.labId===It);if($t===void 0||!$t.declared){Yt("cannot run this again \u2014 its lab has no manifest here any more");return}Yt(`starting ${$t.name}\u2026`);let Ke=await n.capabilities.startRun({cwd:n.cwd,lab:$t.name,mode:Bt.mode==="live"?"live":"dry-run"});Yt(Ke.ok?`started ${$t.name} (pid ${Ke.run.pid})`:Ke.error.message)},[Gt,n,y,wt]);M0((0,Xe.useCallback)((Bt,Kt)=>{if(Lt){ce(!1),Bt==="q"&&E();return}if(Bt==="?"){ce(!0);return}if(Bt==="q"){E();return}if(Bt==="g"||Bt==="G"){b!==void 0&&(L(void 0),Vt(void 0)),Q({type:"move",delta:Bt==="g"?-fe:fe,total:fe});return}if(Kt.upArrow||Bt==="k"||Kt.downArrow||Bt==="j"){b!==void 0&&(L(void 0),Vt(void 0)),Q({type:"move",delta:Kt.upArrow||Bt==="k"?-1:1,total:fe});return}if(Kt.escape||Kt.leftArrow){if(b!==void 0){L(void 0),Vt(void 0);return}if(wt!==void 0){pt(void 0),Yt(void 0);return}if(Dt!==void 0){rn(void 0);return}Q({type:"back"});return}if(Kt.return||Kt.rightArrow){if(kt.name==="labs"&&tt.initialized===!1){if(Dt===void 0){rn(Date.now());return}if(Date.now()-Dt<L0)return;rn(void 0),(async()=>{let $t=await n.capabilities.initProject(n.cwd);Yt($t.message)})();return}if(kt.name==="run"&&y!==void 0){let $t=y.runsById.get(kt.runId);if($t!==void 0){let Ke=Ch($t,Gt)[fn];if(Ke!==void 0){Pt($t,Ke);return}}}if(kt.name==="lab"&&y!==void 0){let{row:$t,items:Ke}=EE(y,kt.labKey),he=Ke[fn];if($t!==void 0&&he?.kind==="start"){Oe($t,he.mode);return}}let It=vO(kt,y,fn);It!==void 0&&Q({type:"enter",screen:It})}},[E,fe,kt,y,fn,b,Oe,Gt,Pt,wt,Lt,Dt,tt,n])),(0,Xe.useEffect)(()=>{h.quit&&E()},[h.quit,E]);let Ee=kt.name==="run"?kt.runId:void 0;(0,Xe.useEffect)(()=>{if(Yt(void 0),pt(void 0),Ee===void 0){pe(void 0);return}let Bt=!1;pe(void 0);let Kt=u(async()=>{try{let $t=await n.capabilities.readRunDetail(n.cwd,Ee);Bt||pe($t)}catch{Bt||pe(null)}},"read");Kt();let It=setInterval(()=>{Kt()},aN);return It.unref?.(),()=>{Bt=!0,clearInterval(It)}},[Ee,n]),(0,Xe.useEffect)(()=>{if(c!==void 0)return;let Bt=setInterval(()=>ct(Kt=>Kt+1),CO);return Bt.unref?.(),()=>clearInterval(Bt)},[c]);let tA=(y?.rows??[]).flatMap(Bt=>Bt.liveRuns.map(Kt=>Kt.runId)).join(",");(0,Xe.useEffect)(()=>{if(tA===""){sn(new Map);return}let Bt=!1;return(async()=>{let Kt=tA.split(","),It=await Promise.all(Kt.map(async $t=>{let Ke=await n.capabilities.readRunDetail(n.cwd,$t).catch(()=>null);return Ke===null?null:[$t,Ke]}));Bt||sn(new Map(It.filter($t=>$t!==null)))})(),()=>{Bt=!0}},[tA,n,Mt]);let CA=kt.name==="lab"?kt.labKey:void 0,vA=CA===void 0?void 0:y?.rows.find(Bt=>Bt.key===CA)?.name;(0,Xe.useEffect)(()=>{if(vA===void 0){Nt(void 0);return}let Bt=!1;return Nt(void 0),(async()=>{let Kt=await n.capabilities.readLabSummary(n.cwd,vA,{checkKeys:!0}).catch(()=>null);Bt||Nt(Kt)})(),()=>{Bt=!0}},[vA,n]);let Be=Math.max(1,m.rows-dO),Ve=(0,Xe.useMemo)(()=>Lt?(0,bi.jsx)(G2,{columns:mh(m.columns)}):x!==void 0?(0,bi.jsxs)(rt,{color:be.bad,children:["could not read this project: ",x]}):y===void 0?(0,bi.jsx)(rt,{dimColor:!0,children:"reading project\u2026"}):DO({screen:kt,data:y,selected:fn,columns:mh(m.columns),viewport:Be,now:Ot,confirming:b,launchError:j,launchNote:F,detail:Gt,summary:Jt,liveDetails:ue,tick:Mt,initialized:tt.initialized,actionNote:Ce,initArmed:Dt!==void 0}),[Lt,x,y,kt,fn,m.columns,Be,Ot,b,j,F,Gt,Jt,ue,Mt,tt,Ce]);return(0,bi.jsx)(q2,{columns:m.columns,context:BO(kt,y,n),breadcrumb:hO(kt,y),hints:Lt?"any key returns q quit":IO(kt,y,fn,b,tt.initialized),children:Ve})}u(uN,"App");function BO(n,i,o){let c=o.cwd.split("/").filter(Boolean).pop();if(n.name==="run"&&i!==void 0){let m=i.runsById.get(n.runId),h=n.runId.split("-").pop()??n.runId;return[m?.lab?.id,h].filter(Boolean).join(" \xB7 ")}if(i===void 0)return c;let E=Nc(i).length;return E===0?c:`${c} \xB7 ${E} participant${E===1?"":"s"} working`}u(BO,"contextLine");function hO(n,i){if(n.name==="labs")return;if(n.name==="all-runs")return"\u2039 labs / all runs";if(n.name==="lab")return`\u2039 labs / ${i?.rows.find(E=>E.key===n.labKey)?.name??n.labKey}`;let o=n.labId;return o===void 0?"\u2039 labs / run":`\u2039 labs / ${o} / run`}u(hO,"breadcrumbOf");function IO(n,i,o,c,E){let m="\u2191\u2193 move";switch(n.name){case"labs":return(i?.rows.length??0)>0?`${m} \u23CE open ? keys q quit`:E===!1?"\u23CE set up humanish here ? keys q quit":"? keys q quit";case"lab":{if(c!==void 0)return"\u21B5 confirm \xB7 esc cancel";let Q=(i===void 0?void 0:EE(i,n.labKey).items[o])?.kind==="start"?"\u23CE start":"\u23CE open run";return`${m} ${Q} esc back ? keys q quit`}case"all-runs":return`${m} \u23CE open run esc back ? keys q quit`;default:{let h=i===void 0?void 0:i.runsById.get(n.name==="run"?n.runId:"");return h!==void 0&&Ch(h,void 0).length>0?`${m} \u23CE select esc back ? keys q quit`:"esc back ? keys q quit"}}}u(IO,"keyHints");function lN(n,i){let{rows:o,unattributed:c}=Y2(i.map(h=>({id:h.id,...h.title===void 0?{}:{title:h.title},...h.description===void 0?{}:{description:h.description},path:h.path,origin:h.origin})),n.runs),E=new Map,m=new Map;for(let h of n.runs){m.set(h.runId,h);let Q=h.lab?.id;if(Q===void 0)continue;let y=E.get(Q);y===void 0?E.set(Q,[h]):y.push(h)}return{rows:o,unattributed:c,runsByLab:E,runsById:m,unreadable:n.unreadable}}u(lN,"project");function Nc(n){let i=new Set,o=[];for(let c of n.rows.flatMap(E=>E.liveRuns))i.has(c.runId)||(i.add(c.runId),o.push(c));return o}u(Nc,"liveRunsOf");function QO(n,i){return i===void 0?"":n.rows.find(o=>o.labId===i)?.label??i}u(QO,"labelForLab");function EE(n,i){let o=n.rows.find(c=>c.key===i);return o===void 0?{items:[]}:{row:o,items:ph(n.runsByLab.get(o.labId)??[],o.declared)}}u(EE,"itemsForLab");function iN(n,i){if(i===void 0)return 0;switch(n.name){case"labs":return i.rows.length+1;case"all-runs":return Nc(i).length;case"lab":return EE(i,n.labKey).items.length;default:return 0}}u(iN,"countRows");function yO(n,i,o){if(i!==void 0){if(n.name==="labs")return i.rows[o]?.key??"peer:all-runs";if(n.name==="all-runs")return Nc(i)[o]?.runId;if(n.name==="lab"){let c=EE(i,n.labKey).items[o];return c===void 0?void 0:c.kind==="start"
|
|
289
|
+
`)}`})},[b,_t,n]),Pt=(0,Xe.useCallback)(async(Bt,Kt)=>{if(Kt==="stop"){if(wt===void 0){pt(Date.now()),Yt("stop this run? \u23CE again to confirm \xB7 esc cancel");return}if(Date.now()-wt<L0)return;pt(void 0),Yt("stopping\u2026");let he=await n.capabilities.stopRun(n.cwd,Bt.runId);Yt(he.message);return}if(Kt==="observer"){let he=Gt?.observerPath;if(he===void 0){Yt("this run has no Observer artifact on disk");return}Yt("opening\u2026");let un=await n.capabilities.openObserver(n.cwd,he);Yt(un.message);return}if(Kt==="reclaim"){Yt("reclaiming \u2014 stopping sandboxes, keeping evidence\u2026");let he=await n.capabilities.reclaimRun(n.cwd,Bt.runId);Yt(he.ok?`reclaimed ${he.receiptCount} recorded resource${he.receiptCount===1?"":"s"}`:`could not reclaim: ${he.error?.message??"unknown"}`);return}let It=Bt.lab?.id,$t=It===void 0?void 0:y?.rows.find(he=>he.labId===It);if($t===void 0||!$t.declared){Yt("cannot run this again \u2014 its lab has no manifest here any more");return}Yt(`starting ${$t.name}\u2026`);let Ke=await n.capabilities.startRun({cwd:n.cwd,lab:$t.name,mode:Bt.mode==="live"?"live":"dry-run"});Yt(Ke.ok?`started ${$t.name} (pid ${Ke.run.pid})`:Ke.error.message)},[Gt,n,y,wt]);M0((0,Xe.useCallback)((Bt,Kt)=>{if(Lt){ce(!1),Bt==="q"&&E();return}if(Bt==="?"){ce(!0);return}if(Bt==="q"){E();return}if(Bt==="g"||Bt==="G"){b!==void 0&&(L(void 0),Vt(void 0)),Q({type:"move",delta:Bt==="g"?-fe:fe,total:fe});return}if(Kt.upArrow||Bt==="k"||Kt.downArrow||Bt==="j"){b!==void 0&&(L(void 0),Vt(void 0)),Q({type:"move",delta:Kt.upArrow||Bt==="k"?-1:1,total:fe});return}if(Kt.escape||Kt.leftArrow){if(b!==void 0){L(void 0),Vt(void 0);return}if(wt!==void 0){pt(void 0),Yt(void 0);return}if(Dt!==void 0){rn(void 0);return}Q({type:"back"});return}if(Kt.return||Kt.rightArrow){if(kt.name==="labs"&&tt.initialized===!1){if(Dt===void 0){rn(Date.now());return}if(Date.now()-Dt<L0)return;rn(void 0),(async()=>{let $t=await n.capabilities.initProject(n.cwd);Yt($t.message)})();return}if(kt.name==="run"&&y!==void 0){let $t=y.runsById.get(kt.runId);if($t!==void 0){let Ke=Ch($t,Gt)[fn];if(Ke!==void 0){Pt($t,Ke);return}}}if(kt.name==="lab"&&y!==void 0){let{row:$t,items:Ke}=EE(y,kt.labKey),he=Ke[fn];if($t!==void 0&&he?.kind==="start"){Oe($t,he.mode);return}}let It=vO(kt,y,fn);It!==void 0&&Q({type:"enter",screen:It})}},[E,fe,kt,y,fn,b,Oe,Gt,Pt,wt,Lt,Dt,tt,n])),(0,Xe.useEffect)(()=>{h.quit&&E()},[h.quit,E]);let Ee=kt.name==="run"?kt.runId:void 0;(0,Xe.useEffect)(()=>{if(Yt(void 0),pt(void 0),Ee===void 0){pe(void 0);return}let Bt=!1;pe(void 0);let Kt=u(async()=>{try{let $t=await n.capabilities.readRunDetail(n.cwd,Ee);Bt||pe($t)}catch{Bt||pe(null)}},"read");Kt();let It=setInterval(()=>{Kt()},aN);return It.unref?.(),()=>{Bt=!0,clearInterval(It)}},[Ee,n]),(0,Xe.useEffect)(()=>{if(c!==void 0)return;let Bt=setInterval(()=>ct(Kt=>Kt+1),CO);return Bt.unref?.(),()=>clearInterval(Bt)},[c]);let tA=(y?.rows??[]).flatMap(Bt=>Bt.liveRuns.map(Kt=>Kt.runId)).join(",");(0,Xe.useEffect)(()=>{if(tA===""){sn(new Map);return}let Bt=!1;return(async()=>{let Kt=tA.split(","),It=await Promise.all(Kt.map(async $t=>{let Ke=await n.capabilities.readRunDetail(n.cwd,$t).catch(()=>null);return Ke===null?null:[$t,Ke]}));Bt||sn(new Map(It.filter($t=>$t!==null)))})(),()=>{Bt=!0}},[tA,n,Mt]);let CA=kt.name==="lab"?kt.labKey:void 0,vA=CA===void 0?void 0:y?.rows.find(Bt=>Bt.key===CA)?.name;(0,Xe.useEffect)(()=>{if(vA===void 0){Nt(void 0);return}let Bt=!1;return Nt(void 0),(async()=>{let Kt=await n.capabilities.readLabSummary(n.cwd,vA,{checkKeys:!0}).catch(()=>null);Bt||Nt(Kt)})(),()=>{Bt=!0}},[vA,n]);let Be=Math.max(1,m.rows-dO),Ve=(0,Xe.useMemo)(()=>Lt?(0,bi.jsx)(G2,{columns:mh(m.columns)}):x!==void 0?(0,bi.jsxs)(rt,{color:be.bad,children:["could not read this project: ",x]}):y===void 0?(0,bi.jsx)(rt,{dimColor:!0,children:"reading project\u2026"}):DO({screen:kt,data:y,selected:fn,columns:mh(m.columns),viewport:Be,now:Ot,confirming:b,launchError:j,launchNote:F,detail:Gt,summary:Jt,liveDetails:ue,tick:Mt,initialized:tt.initialized,actionNote:Ce,initArmed:Dt!==void 0}),[Lt,x,y,kt,fn,m.columns,Be,Ot,b,j,F,Gt,Jt,ue,Mt,tt,Ce]);return(0,bi.jsx)(q2,{columns:m.columns,context:BO(kt,y,n),breadcrumb:hO(kt,y),hints:Lt?"any key returns q quit":IO(kt,y,fn,b,tt.initialized),children:Ve})}u(uN,"App");function BO(n,i,o){let c=o.cwd.split("/").filter(Boolean).pop();if(n.name==="run"&&i!==void 0){let m=i.runsById.get(n.runId),h=n.runId.split("-").pop()??n.runId;return[m?.lab?.id,h].filter(Boolean).join(" \xB7 ")}if(i===void 0)return c;let E=Nc(i).length;return E===0?c:`${c} \xB7 ${E} participant${E===1?"":"s"} working`}u(BO,"contextLine");function hO(n,i){if(n.name==="labs")return;if(n.name==="all-runs")return"\u2039 labs / all runs";if(n.name==="lab")return`\u2039 labs / ${i?.rows.find(E=>E.key===n.labKey)?.name??n.labKey}`;let o=n.labId;return o===void 0?"\u2039 labs / run":`\u2039 labs / ${o} / run`}u(hO,"breadcrumbOf");function IO(n,i,o,c,E){let m="\u2191\u2193 move";switch(n.name){case"labs":return(i?.rows.length??0)>0?`${m} \u23CE open ? keys q quit`:E===!1?"\u23CE set up humanish here ? keys q quit":"? keys q quit";case"lab":{if(c!==void 0)return"\u21B5 confirm \xB7 esc cancel";let Q=(i===void 0?void 0:EE(i,n.labKey).items[o])?.kind==="start"?"\u23CE start":"\u23CE open run";return`${m} ${Q} esc back ? keys q quit`}case"all-runs":return`${m} \u23CE open run esc back ? keys q quit`;default:{let h=i===void 0?void 0:i.runsById.get(n.name==="run"?n.runId:"");return h!==void 0&&Ch(h,void 0).length>0?`${m} \u23CE select esc back ? keys q quit`:"esc back ? keys q quit"}}}u(IO,"keyHints");function lN(n,i){let{rows:o,unattributed:c}=Y2(i.map(h=>({id:h.id,...h.title===void 0?{}:{title:h.title},...h.description===void 0?{}:{description:h.description},path:h.path,origin:h.origin})),n.runs),E=new Map,m=new Map;for(let h of n.runs){m.set(h.runId,h);let Q=h.lab?.id;if(Q===void 0)continue;let y=E.get(Q);y===void 0?E.set(Q,[h]):y.push(h)}return{rows:o,unattributed:c,runsByLab:E,runsById:m,unreadable:n.unreadable}}u(lN,"project");function Nc(n){let i=new Set,o=[];for(let c of n.rows.flatMap(E=>E.liveRuns))i.has(c.runId)||(i.add(c.runId),o.push(c));return o}u(Nc,"liveRunsOf");function QO(n,i){return i===void 0?"":n.rows.find(o=>o.labId===i)?.label??i}u(QO,"labelForLab");function EE(n,i){let o=n.rows.find(c=>c.key===i);return o===void 0?{items:[]}:{row:o,items:ph(n.runsByLab.get(o.labId)??[],o.declared)}}u(EE,"itemsForLab");function iN(n,i){if(i===void 0)return 0;switch(n.name){case"labs":return i.rows.length+1;case"all-runs":return Nc(i).length;case"lab":return EE(i,n.labKey).items.length;default:return 0}}u(iN,"countRows");function yO(n,i,o){if(i!==void 0){if(n.name==="labs")return i.rows[o]?.key??"peer:all-runs";if(n.name==="all-runs")return Nc(i)[o]?.runId;if(n.name==="lab"){let c=EE(i,n.labKey).items[o];return c===void 0?void 0:c.kind==="start"?`start:${c.mode}`:`run:${c.run.runId}`}}}u(yO,"identityOf");function SO(n,i,o){return n.name==="labs"?o==="peer:all-runs"?i.rows.length:i.rows.findIndex(c=>c.key===o):n.name==="all-runs"?Nc(i).findIndex(c=>c.runId===o):n.name==="lab"?EE(i,n.labKey).items.findIndex(c=>c.kind==="start"?o===`start:${c.mode}`:`run:${c.run.runId}`===o):-1}u(SO,"indexOfIdentity");function vO(n,i,o){if(i!==void 0){if(n.name==="labs"){let c=i.rows[o];return c===void 0?o===i.rows.length?{name:"all-runs"}:void 0:{name:"lab",labKey:c.key}}if(n.name==="all-runs"){let c=Nc(i)[o];return c===void 0?void 0:{name:"run",...c.lab?.id===void 0?{}:{labId:c.lab.id},runId:c.runId}}if(n.name==="lab"){let{row:c,items:E}=EE(i,n.labKey),m=E[o];return c===void 0||m===void 0||m.kind!=="run"?void 0:{name:"run",labId:c.labId,runId:m.run.runId}}}}u(vO,"openSelected");function DO(n){let{screen:i,data:o,selected:c,columns:E,viewport:m,now:h,confirming:Q,launchError:y,launchNote:N,detail:x}=n,{summary:w,liveDetails:b,tick:L,initialized:j,actionNote:q}=n;if(i.name==="labs")return(0,bi.jsx)(nN,{rows:o.rows,selected:c,columns:E,viewport:m,unattributed:o.unattributed.length,tick:L,initialized:j,peerSelected:c===o.rows.length,liveTotal:Nc(o).length,...n.initArmed===!0?{initArmed:!0}:{},...n.actionNote===void 0?{}:{actionNote:n.actionNote},liveParticipants:new Map([...b.entries()].map(([ft,_t])=>{let Vt=_t.participants[0]?.personaId??_t.participants[0]?.label;return Vt===void 0?null:[ft,Vt]}).filter(ft=>ft!==null)),now:h});if(i.name==="lab"){let ft=o.rows.find(_t=>_t.key===i.labKey);return ft===void 0?(0,bi.jsx)(rt,{color:be.warn,children:"that lab is no longer in this project"}):(0,bi.jsx)(P2,{row:ft,summary:w,runs:o.runsByLab.get(ft.labId)??[],liveDetail:b.get(ft.liveRuns[0]?.runId??""),selected:c,columns:E,viewport:m,now:h,tick:L,canStart:ft.declared,confirming:Q,launchError:y?.labKey===ft.key?y.text:void 0,launchNote:N?.labKey===ft.key?N.text:void 0})}if(i.name==="all-runs"){let ft=Nc(o);return(0,bi.jsx)(V2,{runs:ft,details:b,labels:new Map(ft.map(_t=>[_t.runId,QO(o,_t.lab?.id)])),expected:new Map(o.rows.map(_t=>_t.liveExpectation.medianDurationMs===void 0?null:[_t.labId,_t.liveExpectation.medianDurationMs]).filter(_t=>_t!==null)),selected:c,columns:E,viewport:m,tick:L,now:h})}let F=o.runsById.get(i.runId);return F===void 0?(0,bi.jsx)(rt,{color:be.warn,children:"that run is no longer on disk"}):(0,bi.jsx)($2,{run:F,detail:x,columns:E,viewport:m,selected:c,tick:L,now:h,actionNote:q})}u(DO,"renderScreen");var oN=xt(ha(),1);function RO(n){if(Rc())return n;let i=Object.create(n);return i.write=((o,...c)=>n.write(typeof o=="string"?M2(o):o,...c)),i}u(RO,"encodeFor");var f7=u(async n=>{let i=u(()=>{},"ready"),o=new Promise(m=>{i=m}),c=RO(n.stdout),E=T0((0,oN.jsx)(uN,{options:n,onReady:i}),{stdin:n.stdin,stdout:c,patchConsole:!1,exitOnCtrlC:!0,interactive:!0});n.exitAfterFirstFrame===!0&&(await o,await new Promise(m=>setImmediate(m)),E.unmount());try{return await E.waitUntilExit(),0}catch(m){return E.unmount(),n.stdout.write(`humanish tui exited: ${m instanceof Error?m.message:String(m)}
|
|
290
290
|
`),1}},"startTui");export{f7 as startTui};
|
|
291
291
|
/*! Bundled license information:
|
|
292
292
|
|
package/dist/tui-contract.d.ts
CHANGED
|
@@ -47,7 +47,7 @@ export interface TuiCapabilities {
|
|
|
47
47
|
/** Whether this directory is a humanish project — an empty project and a wrong directory are
|
|
48
48
|
* different problems and must not share a screen. */
|
|
49
49
|
readProjectState(cwd: string): TuiProjectState;
|
|
50
|
-
/**
|
|
50
|
+
/** Open the selected run in the session-owned local evidence server; closes when the TUI exits. */
|
|
51
51
|
openObserver(cwd: string, observerPath: string): Promise<TuiActionResult>;
|
|
52
52
|
/** Stop the sandboxes an interrupted run left behind, keeping its evidence. */
|
|
53
53
|
reclaimRun(cwd: string, runId: string): Promise<ReclaimResult>;
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
# Watching and reviewing evidence
|
|
2
|
+
|
|
3
|
+
Observer shows a study's participants, their recorded screens, and the events
|
|
4
|
+
that explain those screens. The grid, player and exported HTML use the same
|
|
5
|
+
current renderer. Screens fit their actual aspect ratio; a portrait capture is
|
|
6
|
+
never cropped to fill a landscape tile.
|
|
7
|
+
|
|
8
|
+
## Choose the right entry point
|
|
9
|
+
|
|
10
|
+
`humanish watch <lab>` starts a study and keeps its viewer attached. The process
|
|
11
|
+
that owns a hosted desktop can offer its live stream while it runs. `--no-open`
|
|
12
|
+
keeps that attachment without launching a browser. `--json` is the machine-result
|
|
13
|
+
path and does not keep an interactive stream server attached.
|
|
14
|
+
|
|
15
|
+
`humanish observe --run <id>` opens an existing study without starting another.
|
|
16
|
+
The TUI's **Open Observer** action does the same through its session-owned
|
|
17
|
+
loopback library. These entry points follow saved captures; they cannot recover
|
|
18
|
+
live desktop credentials owned by another process. Keep the owning CLI or TUI
|
|
19
|
+
open while using its viewer. Exported HTML is an independent offline recording.
|
|
20
|
+
|
|
21
|
+
## Read status without guessing
|
|
22
|
+
|
|
23
|
+
The top band separates the run's current status from evidence freshness. A
|
|
24
|
+
finished study never uses its execution mode (`live`, as opposed to `dry-run`)
|
|
25
|
+
as a claim that it is still running. When updates fail, the last usable evidence
|
|
26
|
+
stays visible, the band reports the failure, and **Retry** requests another
|
|
27
|
+
snapshot. A stale process heartbeat does not prove that the process died.
|
|
28
|
+
|
|
29
|
+
A player's **Live desktop** label identifies what it displays. An iframe loading
|
|
30
|
+
is not a connection-health signal: the provider owns that connection. **Reload
|
|
31
|
+
stream** reloads the desktop viewer without restarting a participant. **Latest capture**
|
|
32
|
+
follows the latest saved screenshot without a desktop stream. **Replay** means
|
|
33
|
+
the researcher has chosen a recorded moment. Offline exports say so explicitly.
|
|
34
|
+
|
|
35
|
+
Freshness uses recorded activity and capture timestamps, not the time the server
|
|
36
|
+
reserialized the same data. Browser tabs poll less frequently while hidden and
|
|
37
|
+
refresh when visible again. Requests are serialized, bounded by a timeout and
|
|
38
|
+
cancelled when the viewer closes.
|
|
39
|
+
|
|
40
|
+
## Work through a study
|
|
41
|
+
|
|
42
|
+
Previews share a height and wrap into rows; desktop cards are wider and portrait
|
|
43
|
+
cards narrower. Complete screens remain contained when the available width is
|
|
44
|
+
smaller than a preview. Sparse rows keep their chosen size. Open **View and filter
|
|
45
|
+
participants** in the top right to search, change **Preview size**, or enter
|
|
46
|
+
**Monitor** for a larger evidence area. **Exit monitor** stays beside the run status.
|
|
47
|
+
Open a card's **Participant details** to **Pin** it at the beginning. Ordering changes only
|
|
48
|
+
when the researcher changes it. Large studies have reachable participant pages.
|
|
49
|
+
At most four desktop previews attach in the grid; allocation prioritizes screens
|
|
50
|
+
that are actually visible, with the hovered or keyboard-focused participant taking
|
|
51
|
+
priority. Other cards show their latest saved capture.
|
|
52
|
+
|
|
53
|
+
Each card follows its screen's aspect ratio without a minimum width that adds
|
|
54
|
+
portrait gutters. Its 44px caption holds the participant name, a short preview
|
|
55
|
+
source (**Live**, **Capture**, or **Snapshot**) or finished outcome, and a stable
|
|
56
|
+
**Participant details** button. Labels and controls never cover captured pixels.
|
|
57
|
+
Details contains labeled **Pin** and **Compare** toggles, the full source and
|
|
58
|
+
freshness, final messages, recorded notices, duration and dimensions. Historical
|
|
59
|
+
notices do not turn the details control into a warning; exceptional outcomes
|
|
60
|
+
remain in the caption. Comparison permits three participants and explains the
|
|
61
|
+
limit while keeping selected participants removable.
|
|
62
|
+
|
|
63
|
+
Icon buttons use Lucide glyphs, accessible names, hover/focus hints and visible
|
|
64
|
+
keyboard focus. Touch targets expand to 44px. Popovers have explicit close buttons;
|
|
65
|
+
Escape dismisses a hint without navigating the player. These conventions follow
|
|
66
|
+
[Carbon icon usage](https://carbondesignsystem.com/elements/icons/usage/),
|
|
67
|
+
[ARIA toggle-button semantics](https://www.w3.org/WAI/ARIA/apg/patterns/button/),
|
|
68
|
+
and [WCAG target sizing](https://www.w3.org/WAI/WCAG22/Understanding/target-size-minimum.html).
|
|
69
|
+
The bookmark icon in the top bar opens **Saved moments**; recalling an available
|
|
70
|
+
moment closes the popover, while an unavailable moment leaves the error visible.
|
|
71
|
+
|
|
72
|
+
Opening a participant provides elapsed-time seeking, the original frame number,
|
|
73
|
+
playback speed, **Fit**, **Actual size**, zoom/pan and fullscreen. The inspector can
|
|
74
|
+
be hidden or resized. Click markers use the recorded desktop coordinate system
|
|
75
|
+
and the image's rendered rectangle, including at zoomed sizes.
|
|
76
|
+
|
|
77
|
+
Arrow keys, mouse controls, same-participant links, browser navigation and reload
|
|
78
|
+
agree about the selected moment. Seeking leaves live mode. Pausing on the newest
|
|
79
|
+
frame stays paused when new evidence arrives; **Go live** explicitly resumes
|
|
80
|
+
following. A missing addressed moment is reported instead of silently replaced.
|
|
81
|
+
|
|
82
|
+
Screenshot playback is sparse evidence, not a video recording. Capture gaps stay
|
|
83
|
+
visible. **Skip waits** compresses intervals associated with recorded waits and
|
|
84
|
+
reports the skipped duration. **Next action**, **Next finding**, activity filters
|
|
85
|
+
and grouped waits make long traces easier to inspect without deleting events.
|
|
86
|
+
Older unstamped recordings use labeled estimated pacing. Long feeds, filmstrips,
|
|
87
|
+
terminal output and event lists stay bounded while earlier/later content remains
|
|
88
|
+
reachable.
|
|
89
|
+
|
|
90
|
+
**Copy moment link** creates a frame address; denied clipboard access exposes a
|
|
91
|
+
selectable link. **Original frame** opens/downloads the captured raster. **Saved
|
|
92
|
+
moments** keeps bounded run/participant/frame identifiers in this browser. It does
|
|
93
|
+
not save credentials, publish annotations or change the evidence bundle. Storage
|
|
94
|
+
failure leaves the controls usable for the current visit.
|
|
95
|
+
|
|
96
|
+
## Compare recorded evidence
|
|
97
|
+
|
|
98
|
+
Select participants and open **Compare selected**. Capture-time alignment chooses
|
|
99
|
+
the latest frame at or before the cursor, never a future frame. Before/after
|
|
100
|
+
coverage and each frame's age are explicit. Clock differences between desktops
|
|
101
|
+
remain an uncertainty; matching timestamps alone do not establish causality.
|
|
102
|
+
|
|
103
|
+
Elapsed-time alignment starts each participant at its first capture and compares
|
|
104
|
+
progress rather than simultaneous events. Missing or descending timestamps cannot
|
|
105
|
+
be used as a shared clock. Another run can be loaded from the available library
|
|
106
|
+
as recorded evidence. The address retains the chosen runs, participants, clock
|
|
107
|
+
and cursor so reopening it restores the comparison.
|
|
108
|
+
|
|
109
|
+
## Boundaries and checks
|
|
110
|
+
|
|
111
|
+
Only the attached process can grant a desktop iframe its own origin for provider
|
|
112
|
+
module loading. Stored grants are removed, exports contain no runtime grants,
|
|
113
|
+
and every local viewer response refuses to be framed. Generic imported embeds
|
|
114
|
+
retain an opaque sandbox. Artifact and history links cannot introduce arbitrary
|
|
115
|
+
URL schemes or traverse outside their evidence roots.
|
|
116
|
+
|
|
117
|
+
`pnpm observer:browser:proof` exercises generated synthetic evidence in the
|
|
118
|
+
production artifact. `pnpm observer:iframe:proof`, after `pnpm build`, checks the
|
|
119
|
+
actual serving boundary against direct, redirected and scripted iframe attacks.
|
|
120
|
+
CI retains their screenshots and JSON receipts. These Chromium checks do not
|
|
121
|
+
claim physical-device or non-Chromium coverage. Actual provider checks and their
|
|
122
|
+
resource-cleanup receipts are separate acceptance evidence.
|
|
123
|
+
|
|
124
|
+
Interaction references rechecked September 8, 2026:
|
|
125
|
+
[Base UI popover](https://base-ui.com/react/components/popover),
|
|
126
|
+
[React state structure](https://react.dev/learn/choosing-the-state-structure),
|
|
127
|
+
[WAI-ARIA tabs](https://www.w3.org/WAI/ARIA/apg/patterns/tabs/),
|
|
128
|
+
[iframe sandboxing](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/iframe),
|
|
129
|
+
[Playwright Trace Viewer](https://playwright.dev/docs/trace-viewer), and
|
|
130
|
+
[PostHog recording controls](https://posthog.com/docs/session-replay/how-to-watch-recordings).
|