humanish 0.49.0 → 0.50.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 +52 -3
- package/dist/concurrent-shared-world-lab.d.ts +5 -0
- package/dist/concurrent-shared-world-lab.js +28 -0
- package/dist/concurrent-shared-world-lab.js.map +1 -1
- package/dist/cua-actor-lab.d.ts +7 -0
- package/dist/cua-actor-lab.js +46 -2
- package/dist/cua-actor-lab.js.map +1 -1
- package/dist/e2b-terminal-lab.d.ts +7 -0
- package/dist/e2b-terminal-lab.js +54 -0
- package/dist/e2b-terminal-lab.js.map +1 -1
- package/dist/init-templates.js +7 -2
- package/dist/init-templates.js.map +1 -1
- package/dist/lab-engine.d.ts +11 -0
- package/dist/lab-engine.js +18 -0
- package/dist/lab-engine.js.map +1 -1
- package/dist/oss-lab.d.ts +3 -0
- package/dist/oss-lab.js.map +1 -1
- package/dist/oss-meta-lab.d.ts +3 -0
- package/dist/oss-meta-lab.js +13 -0
- package/dist/oss-meta-lab.js.map +1 -1
- package/dist/program.d.ts +14 -0
- package/dist/program.js +127 -9
- package/dist/program.js.map +1 -1
- package/dist/run-detail.d.ts +59 -0
- package/dist/run-detail.js +108 -0
- package/dist/run-detail.js.map +1 -0
- package/dist/run-index.d.ts +71 -0
- package/dist/run-index.js +206 -0
- package/dist/run-index.js.map +1 -0
- package/dist/run-paths.js +16 -1
- package/dist/run-paths.js.map +1 -1
- package/dist/run-projection.d.ts +182 -0
- package/dist/run-projection.js +349 -0
- package/dist/run-projection.js.map +1 -0
- package/dist/run-status.d.ts +139 -0
- package/dist/run-status.js +218 -0
- package/dist/run-status.js.map +1 -0
- package/dist/run.d.ts +15 -0
- package/dist/run.js +91 -6
- package/dist/run.js.map +1 -1
- package/dist/scripted-browser-lab.d.ts +5 -0
- package/dist/scripted-browser-lab.js +27 -0
- package/dist/scripted-browser-lab.js.map +1 -1
- package/dist/shared-world-lab.d.ts +5 -0
- package/dist/shared-world-lab.js +27 -0
- package/dist/shared-world-lab.js.map +1 -1
- package/dist/tui-app.js +402 -0
- package/dist/tui-contract.d.ts +73 -0
- package/dist/tui-contract.js +32 -0
- package/dist/tui-contract.js.map +1 -0
- package/dist/tui-launch.d.ts +48 -0
- package/dist/tui-launch.js +159 -0
- package/dist/tui-launch.js.map +1 -0
- package/docs/contracts/run-bundle.md +8 -0
- package/docs/contracts/schemas.md +70 -1
- package/docs/goals/current.md +3 -2
- package/docs/ramp/README.md +1 -1
- package/package.json +8 -5
- package/skills/humanish/SKILL.md +19 -0
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import type { LabListResult } from "./labs.js";
|
|
2
|
+
import type { RunDetail } from "./run-detail.js";
|
|
3
|
+
import type { ReadRunIndexOptions, RunIndexResult } from "./run-index.js";
|
|
4
|
+
import type { LaunchRunOptions, LaunchRunResult } from "./tui-launch.js";
|
|
5
|
+
/** The humanish version string shown in the frame, so a screenshot in a bug report is datable. */
|
|
6
|
+
export interface TuiVersionInfo {
|
|
7
|
+
cli: string;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* What the surface may do to the project. Deliberately a small, explicit list rather than a handle
|
|
11
|
+
* to the whole library: the set of verbs a stakeholder surface can perform should be readable in
|
|
12
|
+
* one place, and anything absent here is something the TUI simply cannot do.
|
|
13
|
+
*/
|
|
14
|
+
export interface TuiCapabilities {
|
|
15
|
+
/** Read every run in the project, cheapest source first. */
|
|
16
|
+
readRunIndex(cwd: string, options?: ReadRunIndexOptions): Promise<RunIndexResult>;
|
|
17
|
+
/**
|
|
18
|
+
* The labs DECLARED in this project. Listed separately from run history because neither side is
|
|
19
|
+
* the whole truth: a fresh project has manifests and no runs, and a long-lived one has runs from
|
|
20
|
+
* manifests since renamed or deleted.
|
|
21
|
+
*/
|
|
22
|
+
listLabs(cwd: string): Promise<LabListResult>;
|
|
23
|
+
/**
|
|
24
|
+
* Start a run and return once it is running. The run is DETACHED: it outlives this surface, so
|
|
25
|
+
* quitting the TUI — or losing the connection it runs over — does not kill a study that costs
|
|
26
|
+
* real money. The surface then follows it through `status.json` like any other reader.
|
|
27
|
+
*/
|
|
28
|
+
startRun(options: Omit<LaunchRunOptions, "spawn" | "cliPath" | "now">): Promise<LaunchRunResult>;
|
|
29
|
+
/** The tail of a launch log: the only account of a run that died before writing evidence. */
|
|
30
|
+
readLaunchLog(logPath: string): Promise<string>;
|
|
31
|
+
/**
|
|
32
|
+
* Who is in ONE run and what they are thinking. Opens that run's bundle, which the index
|
|
33
|
+
* deliberately does not — affordable because it is asked only for the run being watched.
|
|
34
|
+
* `null` when the run has not written a bundle yet.
|
|
35
|
+
*/
|
|
36
|
+
readRunDetail(cwd: string, runId: string): Promise<RunDetail | null>;
|
|
37
|
+
}
|
|
38
|
+
export interface TuiOptions {
|
|
39
|
+
/** The project the surface is reading. Already resolved by the CLI. */
|
|
40
|
+
cwd: string;
|
|
41
|
+
version: TuiVersionInfo;
|
|
42
|
+
capabilities: TuiCapabilities;
|
|
43
|
+
/**
|
|
44
|
+
* Terminal streams, injected so a test can drive the surface without a real TTY. The CLI passes
|
|
45
|
+
* the real ones; both are known to be TTYs by the time this is called, because the command
|
|
46
|
+
* refuses to start otherwise.
|
|
47
|
+
*/
|
|
48
|
+
stdin: NodeJS.ReadStream;
|
|
49
|
+
stdout: NodeJS.WriteStream;
|
|
50
|
+
/** Test seam: render one frame and resolve, instead of waiting for the operator to quit. */
|
|
51
|
+
exitAfterFirstFrame?: boolean;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Start the surface. Resolves with the process exit code when the operator quits — the TUI owns the
|
|
55
|
+
* screen until then, so the CLI must not write to stdout while this is pending.
|
|
56
|
+
*/
|
|
57
|
+
export type StartTui = (options: TuiOptions) => Promise<number>;
|
|
58
|
+
/** The shape `dist/tui-app.js` exports. Asserted at the load boundary in program.ts. */
|
|
59
|
+
export interface TuiModule {
|
|
60
|
+
startTui: StartTui;
|
|
61
|
+
}
|
|
62
|
+
/** Node version the Ink runtime requires (ink@7 declares `engines.node >= 22`). */
|
|
63
|
+
export declare const TUI_MIN_NODE_MAJOR = 22;
|
|
64
|
+
/**
|
|
65
|
+
* Whether this runtime can host the surface. Kept here, beside the reason, so the CLI's refusal and
|
|
66
|
+
* `doctor`'s readiness row can never disagree about the answer.
|
|
67
|
+
*/
|
|
68
|
+
export declare function nodeSupportsTui(versionString?: string): boolean;
|
|
69
|
+
/**
|
|
70
|
+
* Where the bundle sits relative to the compiled CLI. Shared so the command that loads it and the
|
|
71
|
+
* readiness row that reports it can never look in different places.
|
|
72
|
+
*/
|
|
73
|
+
export declare function tuiBundleUrl(baseUrl: string): URL;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
// The boundary between the CLI and the terminal UI (#455).
|
|
2
|
+
//
|
|
3
|
+
// `humanish tui` loads a PRE-BUILT bundle (dist/tui-app.js) that contains Ink, React and the
|
|
4
|
+
// screens — and nothing else. Everything the surface needs to KNOW is passed across this interface
|
|
5
|
+
// by the CLI, which imports it from the same modules every other command uses.
|
|
6
|
+
//
|
|
7
|
+
// The reason for the seam: a terminal UI that reads the filesystem itself would become a second
|
|
8
|
+
// implementation of "what is a run, which lab does it belong to, is it alive" — one that ships
|
|
9
|
+
// minified, is invisible to the root test suite, and drifts from `humanish runs` the first time
|
|
10
|
+
// either side changes. Injection keeps exactly one implementation, already unit-tested, and leaves
|
|
11
|
+
// the bundle a view layer that can be reasoned about as one.
|
|
12
|
+
//
|
|
13
|
+
// It also makes the UI testable without a terminal: a test hands `startTui` a fake index and reads
|
|
14
|
+
// the frames back.
|
|
15
|
+
/** Node version the Ink runtime requires (ink@7 declares `engines.node >= 22`). */
|
|
16
|
+
export const TUI_MIN_NODE_MAJOR = 22;
|
|
17
|
+
/**
|
|
18
|
+
* Whether this runtime can host the surface. Kept here, beside the reason, so the CLI's refusal and
|
|
19
|
+
* `doctor`'s readiness row can never disagree about the answer.
|
|
20
|
+
*/
|
|
21
|
+
export function nodeSupportsTui(versionString = process.version) {
|
|
22
|
+
const major = Number.parseInt(versionString.replace(/^v/, "").split(".")[0] ?? "", 10);
|
|
23
|
+
return Number.isFinite(major) && major >= TUI_MIN_NODE_MAJOR;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Where the bundle sits relative to the compiled CLI. Shared so the command that loads it and the
|
|
27
|
+
* readiness row that reports it can never look in different places.
|
|
28
|
+
*/
|
|
29
|
+
export function tuiBundleUrl(baseUrl) {
|
|
30
|
+
return new URL("./tui-app.js", baseUrl);
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=tui-contract.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tui-contract.js","sourceRoot":"","sources":["../src/tui-contract.ts"],"names":[],"mappings":"AAAA,2DAA2D;AAC3D,EAAE;AACF,6FAA6F;AAC7F,mGAAmG;AACnG,+EAA+E;AAC/E,EAAE;AACF,gGAAgG;AAChG,+FAA+F;AAC/F,gGAAgG;AAChG,mGAAmG;AACnG,6DAA6D;AAC7D,EAAE;AACF,mGAAmG;AACnG,mBAAmB;AAqEnB,mFAAmF;AACnF,MAAM,CAAC,MAAM,kBAAkB,GAAG,EAAE,CAAC;AAErC;;;GAGG;AACH,MAAM,UAAU,eAAe,CAAC,gBAAwB,OAAO,CAAC,OAAO;IACrE,MAAM,KAAK,GAAG,MAAM,CAAC,QAAQ,CAAC,aAAa,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC;IACvF,OAAO,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,KAAK,IAAI,kBAAkB,CAAC;AAC/D,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,YAAY,CAAC,OAAe;IAC1C,OAAO,IAAI,GAAG,CAAC,cAAc,EAAE,OAAO,CAAC,CAAC;AAC1C,CAAC"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
export declare function isSafeLabHandle(value: string): boolean;
|
|
3
|
+
export interface LaunchRunOptions {
|
|
4
|
+
cwd: string;
|
|
5
|
+
/** The manifest handle (filename stem), as `humanish lab run` takes it. */
|
|
6
|
+
lab: string;
|
|
7
|
+
mode: "dry-run" | "live";
|
|
8
|
+
/** Injected in tests; defaults to the real spawn. */
|
|
9
|
+
spawn?: typeof spawn;
|
|
10
|
+
/** Injected in tests; defaults to the CLI beside this module. */
|
|
11
|
+
cliPath?: string;
|
|
12
|
+
env?: NodeJS.ProcessEnv;
|
|
13
|
+
/** Injected clock so a test can pin the log filename. */
|
|
14
|
+
now?: () => Date;
|
|
15
|
+
}
|
|
16
|
+
export interface LaunchedRun {
|
|
17
|
+
/** The spawned CLI's pid. The run's `status.json` stamps the same value. */
|
|
18
|
+
pid: number;
|
|
19
|
+
/**
|
|
20
|
+
* When the launch happened. A pid ALONE cannot identify a run: pids are recycled by the OS and a
|
|
21
|
+
* finished run keeps its pid in `status.json` forever, so a week-old record can carry the pid the
|
|
22
|
+
* kernel just handed this child. Anything matching on pid must also require the record to be
|
|
23
|
+
* newer than this.
|
|
24
|
+
*/
|
|
25
|
+
launchedAt: string;
|
|
26
|
+
/** Absolute path to the launch log; the only diagnosis when a run dies before writing evidence. */
|
|
27
|
+
logPath: string;
|
|
28
|
+
/** Exactly what was executed, so a failure can be reproduced by hand. */
|
|
29
|
+
command: readonly string[];
|
|
30
|
+
}
|
|
31
|
+
export type LaunchRunResult = {
|
|
32
|
+
ok: true;
|
|
33
|
+
run: LaunchedRun;
|
|
34
|
+
} | {
|
|
35
|
+
ok: false;
|
|
36
|
+
error: {
|
|
37
|
+
code: LaunchErrorCode;
|
|
38
|
+
message: string;
|
|
39
|
+
};
|
|
40
|
+
};
|
|
41
|
+
export type LaunchErrorCode = "HUMANISH_LAUNCH_INVALID_LAB" | "HUMANISH_LAUNCH_FAILED";
|
|
42
|
+
/**
|
|
43
|
+
* Start a run and return as soon as it is running. Never throws: a launch that cannot happen is a
|
|
44
|
+
* result the surface can render, not an exception that would tear down the screen.
|
|
45
|
+
*/
|
|
46
|
+
export declare function launchRun(options: LaunchRunOptions): Promise<LaunchRunResult>;
|
|
47
|
+
/** Read the tail of a launch log — the only account of a run that died before writing evidence. */
|
|
48
|
+
export declare function readLaunchLogTail(logPath: string, maxBytes?: number): Promise<string>;
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
// Starting a run from the terminal surface (#455).
|
|
2
|
+
//
|
|
3
|
+
// THE RUN MUST OUTLIVE THE SURFACE. A study can take minutes and costs real money, so a run
|
|
4
|
+
// started from the TUI cannot be a child of the TUI's event loop: closing the surface, or losing
|
|
5
|
+
// the SSH session it is running over, must not kill it. So this spawns the CLI the same way a
|
|
6
|
+
// person would type it — detached, in its own process group, with its output going to a file
|
|
7
|
+
// rather than to a terminal that may be about to disappear.
|
|
8
|
+
//
|
|
9
|
+
// The surface then learns what happened the same way any other reader does: from `status.json`.
|
|
10
|
+
// It does not hold a handle to the run, parse its stdout, or track it in memory. That is what
|
|
11
|
+
// makes the surface restartable — quit the TUI mid-run, reopen it, and the run is still there,
|
|
12
|
+
// because the filesystem was always the source of truth rather than a process handle.
|
|
13
|
+
import { spawn } from "node:child_process";
|
|
14
|
+
import { constants as fsConstants } from "node:fs";
|
|
15
|
+
import { open } from "node:fs/promises";
|
|
16
|
+
import path from "node:path";
|
|
17
|
+
import { fileURLToPath } from "node:url";
|
|
18
|
+
import { prepareManagedHumanishOutputDirectory } from "./selected-output-paths.js";
|
|
19
|
+
/**
|
|
20
|
+
* A lab handle is the manifest FILENAME, which is what `humanish lab run` resolves. Restricted to
|
|
21
|
+
* characters a manifest name can actually contain, and — the part that matters — never allowed to
|
|
22
|
+
* begin with `-`, because argv is positional: a lab called `--json` would otherwise be handed to
|
|
23
|
+
* the CLI as a flag. There is no shell involved, so this is the whole injection surface.
|
|
24
|
+
*
|
|
25
|
+
* A leading underscore IS allowed: `_wip.yaml` is an ordinary way to name a work-in-progress
|
|
26
|
+
* manifest, `humanish lab run _wip` resolves it, and refusing it here would leave the surface
|
|
27
|
+
* listing a lab it will not start. A leading dot stays out — that names a hidden file, not a lab.
|
|
28
|
+
*/
|
|
29
|
+
const SAFE_LAB_HANDLE = /^[A-Za-z0-9_][A-Za-z0-9._:-]*$/;
|
|
30
|
+
export function isSafeLabHandle(value) {
|
|
31
|
+
return SAFE_LAB_HANDLE.test(value) && value.length <= 128;
|
|
32
|
+
}
|
|
33
|
+
/** Where the CLI lives, relative to this compiled module. */
|
|
34
|
+
function defaultCliPath() {
|
|
35
|
+
return fileURLToPath(new URL("./cli.js", import.meta.url));
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Start a run and return as soon as it is running. Never throws: a launch that cannot happen is a
|
|
39
|
+
* result the surface can render, not an exception that would tear down the screen.
|
|
40
|
+
*/
|
|
41
|
+
export async function launchRun(options) {
|
|
42
|
+
if (!isSafeLabHandle(options.lab)) {
|
|
43
|
+
return {
|
|
44
|
+
ok: false,
|
|
45
|
+
error: {
|
|
46
|
+
code: "HUMANISH_LAUNCH_INVALID_LAB",
|
|
47
|
+
message: `"${options.lab}" is not a usable lab handle. Run it by path with \`humanish lab run <path>\` instead.`
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
const cwd = path.resolve(options.cwd);
|
|
52
|
+
const now = options.now ?? (() => new Date());
|
|
53
|
+
const spawnFn = options.spawn ?? spawn;
|
|
54
|
+
const launchedAt = now().toISOString();
|
|
55
|
+
let logPath;
|
|
56
|
+
let handle;
|
|
57
|
+
try {
|
|
58
|
+
// Contained under `.humanish/`, through the same guard every other output path uses, so a
|
|
59
|
+
// symlinked directory cannot redirect the log somewhere outside the project.
|
|
60
|
+
const logDir = await prepareManagedHumanishOutputDirectory(cwd, "launches");
|
|
61
|
+
const stamp = launchedAt.replace(/[:.]/g, "-");
|
|
62
|
+
logPath = path.join(logDir.physicalPath, `${stamp}-${options.lab}.log`);
|
|
63
|
+
// O_NOFOLLOW so a symlink planted at this path cannot redirect a run's output — which may carry
|
|
64
|
+
// provider error text — outside the project, and cannot defeat the 0600 mode by pointing at a
|
|
65
|
+
// file that already exists with looser permissions. O_CREAT|O_APPEND keeps ordinary reuse
|
|
66
|
+
// working; only a symlink is refused (ELOOP).
|
|
67
|
+
handle = await open(logPath, fsConstants.O_CREAT | fsConstants.O_WRONLY | fsConstants.O_APPEND | fsConstants.O_NOFOLLOW, 0o600);
|
|
68
|
+
}
|
|
69
|
+
catch (cause) {
|
|
70
|
+
return {
|
|
71
|
+
ok: false,
|
|
72
|
+
error: {
|
|
73
|
+
code: "HUMANISH_LAUNCH_FAILED",
|
|
74
|
+
message: `Could not open a launch log: ${cause instanceof Error ? cause.message : String(cause)}`
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
// `--json` because nothing reads this stream interactively; `--no-open` because the surface owns
|
|
79
|
+
// the operator's attention and must not have a browser thrown over it.
|
|
80
|
+
const args = [
|
|
81
|
+
options.cliPath ?? defaultCliPath(),
|
|
82
|
+
"lab",
|
|
83
|
+
"run",
|
|
84
|
+
"--cwd",
|
|
85
|
+
cwd,
|
|
86
|
+
"--json",
|
|
87
|
+
"--no-open",
|
|
88
|
+
...(options.mode === "dry-run" ? ["--dry-run"] : []),
|
|
89
|
+
// `--` ends option parsing, so the handle can only ever be read as the positional argument.
|
|
90
|
+
"--",
|
|
91
|
+
options.lab
|
|
92
|
+
];
|
|
93
|
+
const spawnOptions = {
|
|
94
|
+
cwd,
|
|
95
|
+
env: options.env ?? process.env,
|
|
96
|
+
// A new process group, so the SIGHUP that arrives when a terminal closes is not delivered here.
|
|
97
|
+
detached: true,
|
|
98
|
+
// stdin closed, output to the log: a detached process must never hold the terminal, and
|
|
99
|
+
// inheriting a pipe nobody reads is how a run blocks forever on a full buffer.
|
|
100
|
+
stdio: ["ignore", handle.fd, handle.fd]
|
|
101
|
+
};
|
|
102
|
+
try {
|
|
103
|
+
const child = spawnFn(process.execPath, args, spawnOptions);
|
|
104
|
+
// A spawn failure is delivered ASYNCHRONOUSLY as an 'error' event (EAGAIN, EMFILE, ENOMEM, a
|
|
105
|
+
// vanished node binary). An 'error' event with no listener is re-thrown by EventEmitter as an
|
|
106
|
+
// uncaught exception — which would tear down the whole surface, the one thing this module
|
|
107
|
+
// promises never to do. The run is already unref'd and unobserved, so recording it is all that
|
|
108
|
+
// is available; the operator learns about it from the launch log and the missing record.
|
|
109
|
+
child.on("error", () => {
|
|
110
|
+
// Deliberately empty: see above. The failure surfaces as a run that never reports in.
|
|
111
|
+
});
|
|
112
|
+
if (child.pid === undefined) {
|
|
113
|
+
await handle.close();
|
|
114
|
+
return {
|
|
115
|
+
ok: false,
|
|
116
|
+
error: { code: "HUMANISH_LAUNCH_FAILED", message: "The run process did not start." }
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
// Release the surface's hold: the parent can now exit whenever it likes and the run continues,
|
|
120
|
+
// reparented to init.
|
|
121
|
+
child.unref();
|
|
122
|
+
// The child owns the descriptor now.
|
|
123
|
+
await handle.close();
|
|
124
|
+
return {
|
|
125
|
+
ok: true,
|
|
126
|
+
run: { pid: child.pid, launchedAt, logPath, command: [process.execPath, ...args] }
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
catch (cause) {
|
|
130
|
+
await handle.close().catch(() => undefined);
|
|
131
|
+
return {
|
|
132
|
+
ok: false,
|
|
133
|
+
error: {
|
|
134
|
+
code: "HUMANISH_LAUNCH_FAILED",
|
|
135
|
+
message: cause instanceof Error ? cause.message : String(cause)
|
|
136
|
+
}
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
/** Read the tail of a launch log — the only account of a run that died before writing evidence. */
|
|
141
|
+
export async function readLaunchLogTail(logPath, maxBytes = 4_000) {
|
|
142
|
+
try {
|
|
143
|
+
const handle = await open(logPath, "r");
|
|
144
|
+
try {
|
|
145
|
+
const { size } = await handle.stat();
|
|
146
|
+
const start = Math.max(0, size - maxBytes);
|
|
147
|
+
const buffer = Buffer.alloc(Math.min(size, maxBytes));
|
|
148
|
+
await handle.read(buffer, 0, buffer.length, start);
|
|
149
|
+
return buffer.toString("utf8").trim();
|
|
150
|
+
}
|
|
151
|
+
finally {
|
|
152
|
+
await handle.close();
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
catch {
|
|
156
|
+
return "";
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
//# sourceMappingURL=tui-launch.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tui-launch.js","sourceRoot":"","sources":["../src/tui-launch.ts"],"names":[],"mappings":"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"}
|
|
@@ -21,6 +21,10 @@ runId: "<core run id>"
|
|
|
21
21
|
mode: "dry-run|live"
|
|
22
22
|
simCount: 1
|
|
23
23
|
createdAt: "<ISO timestamp>"
|
|
24
|
+
lab: # optional, additive: which manifest produced this run
|
|
25
|
+
id: "<lab id>"
|
|
26
|
+
path: "humanish/labs/<lab id>.yaml"
|
|
27
|
+
origin: "committed|ignored|explicit"
|
|
24
28
|
cwd: "[target-cwd]"
|
|
25
29
|
artifactRoot: ".humanish/runs/<run-id>"
|
|
26
30
|
source:
|
|
@@ -360,6 +364,10 @@ History entries use:
|
|
|
360
364
|
schema: humanish.run-history-entry.v1
|
|
361
365
|
runId: "<run-id>"
|
|
362
366
|
createdAt: "<ISO timestamp>"
|
|
367
|
+
lab: # optional, additive: which manifest produced this run
|
|
368
|
+
id: "<lab id>"
|
|
369
|
+
path: "humanish/labs/<lab id>.yaml"
|
|
370
|
+
origin: "committed|ignored|explicit"
|
|
363
371
|
mode: "dry-run|live"
|
|
364
372
|
path: ".humanish/runs/<run-id>"
|
|
365
373
|
```
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
Date: 2026-06-02 (current-state note updated 2026-07-14)
|
|
4
4
|
|
|
5
5
|
Status: reference map for the major contracts shipped through source version
|
|
6
|
-
`0.
|
|
6
|
+
`0.50.0`; it is not an exhaustive inventory of command/result envelopes. Exported types,
|
|
7
7
|
schema constants, parsers, and validators in `src/` are authoritative. Rows
|
|
8
8
|
marked "reserved" name layering intent only — no code emits or validates them
|
|
9
9
|
yet. Do not emit a reserved schema.
|
|
@@ -371,6 +371,14 @@ Core-owned fields:
|
|
|
371
371
|
- `artifacts`
|
|
372
372
|
- `review`
|
|
373
373
|
- `feedbackCandidates`
|
|
374
|
+
- `lab` (optional, additive): which manifest produced the run —
|
|
375
|
+
`{ id, path?, origin? }`, where `origin` is `committed` (humanish/labs),
|
|
376
|
+
`ignored` (a local overlay), or `explicit` (a path the operator passed).
|
|
377
|
+
Absent on bundles written before this contract and on library callers who
|
|
378
|
+
hand a `LabConfig` directly — the run is then honestly lab-less rather than
|
|
379
|
+
guessed. Readers wanting attribution for an older bundle may fall back to
|
|
380
|
+
`inferLegacyLabId`, which reads only the historical
|
|
381
|
+
`persona.source = "lab:<id>"` convention and nothing else.
|
|
374
382
|
- `subject` (optional, additive): structured subject provenance —
|
|
375
383
|
`{ source: clone | app-url | local-tree, repo?, commit?, archiveSha256?,
|
|
376
384
|
dirty?, envNames?, state }` where `state` is `{ provenance: seeded |
|
|
@@ -879,6 +887,67 @@ measure) and never grant a green pass (they surface as unmeasured). `verifyRun`
|
|
|
879
887
|
fails closed when a live bundle lacks the cost ledger or no-spend proof, when the
|
|
880
888
|
proof claims zero on a `null` line, or when known spend exceeds the declared cap.
|
|
881
889
|
|
|
890
|
+
## Run Status (identity + liveness index)
|
|
891
|
+
|
|
892
|
+
`humanish.run-status.v1` — `status.json`, written inside each run directory by
|
|
893
|
+
every backend at run start, refreshed on a fixed cadence while the run is
|
|
894
|
+
alive, and finalized when it ends: `{ schema, runId, state: running |
|
|
895
|
+
finished, mode, lab?, pid, startedAt, updatedAt, completedAt?, outcome? }`.
|
|
896
|
+
|
|
897
|
+
It answers two questions the filesystem could not answer before: **which lab**
|
|
898
|
+
a run belongs to, and **whether it is still alive** — including for runs an
|
|
899
|
+
agent launched (`lab run --json`) or that were detached, which previously wrote
|
|
900
|
+
nothing at all until they completed.
|
|
901
|
+
|
|
902
|
+
It is a DERIVED INDEX, not evidence. `run.json` remains the evidence-of-record;
|
|
903
|
+
`verify` never gates on `status.json`, nothing in it is a claim about what a
|
|
904
|
+
participant did, and when the two disagree the bundle wins and the record is
|
|
905
|
+
rebuildable from it. A `running` record whose `updatedAt` is older than three
|
|
906
|
+
touch intervals is INTERRUPTED, not alive — a dropped connection or a killed
|
|
907
|
+
terminal leaves exactly that shape, and reading it as interrupted is the honest
|
|
908
|
+
outcome (`classifyRunStatus` is the one shared definition). Fields are
|
|
909
|
+
public-safe by construction: no hostname and no user paths, because a run
|
|
910
|
+
directory may be shared.
|
|
911
|
+
|
|
912
|
+
## Run Index, Run Detail, And The Terminal Surface (#455)
|
|
913
|
+
|
|
914
|
+
Three derived projections that exist so a surface can list, classify and watch
|
|
915
|
+
runs without opening evidence for all of them. None is authoritative: `run.json`
|
|
916
|
+
remains the evidence-of-record, `verify` gates on none of these, and nothing in
|
|
917
|
+
them is a claim about what a participant did.
|
|
918
|
+
|
|
919
|
+
`humanish.run-index.v1` — the listing projection. One entry per run, read
|
|
920
|
+
cheapest-source-first: the `status.json` record, else the bundle, else the run
|
|
921
|
+
directory alone. `{ runId, derivedFrom: status | bundle | directory, liveness,
|
|
922
|
+
mode?, pid?, lab?, startedAt?, updatedAt?, completedAt?, verdict?,
|
|
923
|
+
participants?, estimatedCostUsd?, durationMs? }`. The point is cost: walking
|
|
924
|
+
every run tree and parsing every bundle measured 167ms on a 25-run project,
|
|
925
|
+
against 16ms cold and 2.8ms warm here, which is what makes a surface that
|
|
926
|
+
refreshes on a cadence affordable. `derivedFrom` is reported so a surprising row
|
|
927
|
+
can be traced to the file it came from. A run with receipts and no outcome is
|
|
928
|
+
`interrupted` — the shape a dropped connection leaves — and so is an
|
|
929
|
+
IN-PROGRESS bundle reached without a status record, because there is no
|
|
930
|
+
freshness to judge and "it started and nothing here says it finished" is the
|
|
931
|
+
honest reading.
|
|
932
|
+
|
|
933
|
+
`humanish.run-detail.v1` — the watching projection, for ONE run. Who is in it
|
|
934
|
+
and what they are thinking: `{ runId, participants: [{ id, label, personaId?,
|
|
935
|
+
traits, status?, completionReason?, thought?, turns?, actions?, thoughts?,
|
|
936
|
+
estimatedCostUsd? }], observerPath? }`. It reads the actor trace from
|
|
937
|
+
`stream.liveActor` while a run is in flight and `stream.actor` once it has
|
|
938
|
+
finished, preferring the live one, so a single screen renders a run the whole
|
|
939
|
+
way through. A reasoning item still being written is skipped rather than quoted
|
|
940
|
+
half-finished, and the text is carried verbatim — a surface may wrap it, nothing
|
|
941
|
+
paraphrases it. Unlike the index this DOES open the bundle, which is affordable
|
|
942
|
+
only because it is asked for the one run being watched.
|
|
943
|
+
|
|
944
|
+
`humanish.tui-result.v1` — what `humanish tui` emits when it refuses:
|
|
945
|
+
`{ schema, ok: false, error: { code, message } }` with `HUMANISH_TUI_REQUIRES_TTY`,
|
|
946
|
+
`HUMANISH_TUI_UNSUPPORTED_NODE`, or `HUMANISH_TUI_BUNDLE_MISSING`. Every other
|
|
947
|
+
command is built for an agent to drive; this one takes the screen and waits for
|
|
948
|
+
a person, so a non-interactive stdin or stdout fails closed naming the commands
|
|
949
|
+
that DO answer the question rather than rendering escape codes into a pipe.
|
|
950
|
+
|
|
882
951
|
## Run Cost Summary And Estimated Actor Cost
|
|
883
952
|
|
|
884
953
|
The computer-use (CUA) lane surfaces an ADVISORY, additive cost ESTIMATE. It is
|
package/docs/goals/current.md
CHANGED
|
@@ -29,7 +29,7 @@ study completed, reproduced, and produced a real accessibility finding via a
|
|
|
29
29
|
keyboard-first participant
|
|
30
30
|
([docs/goals/email-gated-signup/receipts/](email-gated-signup/receipts/)).
|
|
31
31
|
|
|
32
|
-
## Current Program Truth (source `0.
|
|
32
|
+
## Current Program Truth (source `0.50.0`)
|
|
33
33
|
|
|
34
34
|
The package source and repository implementation in this tree agree on these
|
|
35
35
|
points:
|
|
@@ -45,7 +45,8 @@ The immutable 2026-06-10 proof-roadmap packet is paired with a
|
|
|
45
45
|
| Subject sources/routes | Six declared sources: `this-repo`, `clone`, `app-url`, `local-app`, `terminal-product`, and `local-tree`; support is route-specific and `this-repo` remains dry-run-only | One centralized run/resource lifecycle boundary across all routes |
|
|
46
46
|
| Public proof | A legible four-persona Observer hero from a verified real public-application study (commit-pinned drawDB) shipped in the npm payload (`0.16.0`) | Coverage beyond a single studied subject; the stratified breadth panel remains unbuilt |
|
|
47
47
|
| OSS meta-lab | Dry-run contract and separate disposable smoke harness | Live meta-lab execution; disabled until repository instructions and actor credentials have an isolated boundary |
|
|
48
|
-
| Observer serving | `watch`/`observe` loopback servers plus `serve` — the run-library surface with loopback default, capability-link exposure, `share_ready`-gated open mode, and optional operator-run tunnel; streams never served remotely | A remote live-stream (`--live-streams`) design; a persistent capability-link store
|
|
48
|
+
| Observer serving | `watch`/`observe` loopback servers plus `serve` — the run-library surface with loopback default, capability-link exposure, `share_ready`-gated open mode, and optional operator-run tunnel; streams never served remotely | A remote live-stream (`--live-streams`) design; a persistent capability-link store |
|
|
49
|
+
| Stakeholder terminal surface | `humanish tui` (`0.50.0`): labs -> lab -> run, arrow-key navigation, and starting a dry or live run from the lab screen. The run is DETACHED and outlives the terminal — live-proven by killing the terminal 42s into a real run that then ran on for ~3.5 minutes and finished `pass` at $0.639751. The run screen leads with the participant and their recorded thinking, live-proven mid-flight against a real computer-use run. Ships as one bundled file loaded on demand; refuses a non-interactive stdin/stdout naming the JSON commands instead | Cancelling a run from the surface; per-user persisted config (#470); export (#471); aggregate stats (#472); agent-authored labs (#473); a reusable persona panel (#474) |
|
|
49
50
|
| Off-app comms | Vendor-neutral in-sandbox email/SMS catch, a minimal persona inbox surface, and digest-only `humanish.comms-thread.v1` evidence; wired into the computer-use and shared-world routes over both HTTP and SMTP; live-proven end to end on 2026-08-08 — a persona signed up for a public app, read the emailed link in its inbox, and reached the signed-in product (`docs/goals/email-gated-signup/receipts/signup-verify-live-2026-08-08.md`); the adopter-hosted / app-url ingress plane is wired on the CUA and concurrent external-public routes (#387/#380, 2026-08-11) | Real-provider delivery; a live adopter-hosted receipt |
|
|
50
51
|
|
|
51
52
|
Capability proof and adopter replacement are different gates. A deterministic
|
package/docs/ramp/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Status: public-safe contributor and agent ramp.
|
|
4
4
|
|
|
5
|
-
Package/source version in this tree: `0.
|
|
5
|
+
Package/source version in this tree: `0.50.0` (2026-08-20). The Observer is phone-usable as a stated requirement (observer/AGENTS.md); interactive primitives start from Base UI. The Observer renderer is the observer/ workspace artifact only; the legacy string-concat renderer was deleted at cutover (#426), and rollback is a version pin to 0.42.0. The containment boundary introduced in
|
|
6
6
|
`0.15.1` remains in force: managed run and output paths bind to validated
|
|
7
7
|
physical filesystem identities, and stored provider IDs are evidence, not
|
|
8
8
|
cleanup authority. The bundled OSS meta-lab is dry-run only until
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "humanish",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.50.0",
|
|
4
4
|
"description": "Open-source-safe CLI for persona simulation, observer review, and public-safe feedback drafts.",
|
|
5
5
|
"author": "Daniel G Wilson <daniel@danielgwilson.com>",
|
|
6
6
|
"keywords": [
|
|
@@ -49,8 +49,8 @@
|
|
|
49
49
|
"access": "public"
|
|
50
50
|
},
|
|
51
51
|
"scripts": {
|
|
52
|
-
"build": "tsc -p tsconfig.build.json && pnpm --filter humanish-observer build &&
|
|
53
|
-
"check": "pnpm typecheck && pnpm test && pnpm build",
|
|
52
|
+
"build": "tsc -p tsconfig.build.json && pnpm --filter humanish-observer build && pnpm --filter humanish-tui build && node scripts/finish-build.mjs",
|
|
53
|
+
"check": "pnpm typecheck && pnpm test && pnpm tui:test && pnpm build && pnpm tui:smoke",
|
|
54
54
|
"humanish": "tsx src/cli.ts",
|
|
55
55
|
"pack:dry-run": "npm pack --dry-run",
|
|
56
56
|
"prepack": "pnpm build",
|
|
@@ -58,7 +58,7 @@
|
|
|
58
58
|
"release:check": "pnpm check && pnpm public-surface:scan && pnpm skill:check && npm pack --dry-run",
|
|
59
59
|
"skill:check": "DISABLE_TELEMETRY=1 npx skills add . --list",
|
|
60
60
|
"test": "vitest run",
|
|
61
|
-
"typecheck": "tsc -p tsconfig.json --noEmit && pnpm --filter humanish-observer typecheck",
|
|
61
|
+
"typecheck": "tsc -p tsconfig.json --noEmit && pnpm --filter humanish-observer typecheck && pnpm --filter humanish-tui typecheck",
|
|
62
62
|
"humanish:doctor": "pnpm humanish -- doctor",
|
|
63
63
|
"humanish:run": "pnpm humanish -- run --dry-run",
|
|
64
64
|
"humanish:watch": "pnpm humanish -- watch",
|
|
@@ -69,7 +69,10 @@
|
|
|
69
69
|
"humanish:lab:list": "pnpm humanish -- lab list",
|
|
70
70
|
"humanish:lab:oss": "pnpm humanish -- lab run oss",
|
|
71
71
|
"humanish:lab:oss:ci": "pnpm humanish -- lab run oss --dry-run --json --no-open",
|
|
72
|
-
"humanish:lab:oss:smoke": "pnpm humanish -- lab run oss-smoke"
|
|
72
|
+
"humanish:lab:oss:smoke": "pnpm humanish -- lab run oss-smoke",
|
|
73
|
+
"tui:typecheck": "pnpm --filter humanish-tui typecheck",
|
|
74
|
+
"tui:smoke": "node scripts/tui-smoke.mjs",
|
|
75
|
+
"tui:test": "pnpm --filter humanish-tui test"
|
|
73
76
|
},
|
|
74
77
|
"repository": {
|
|
75
78
|
"type": "git",
|
package/skills/humanish/SKILL.md
CHANGED
|
@@ -21,6 +21,25 @@ assume broad inherited job env is safe. Stop before live provider spend,
|
|
|
21
21
|
hosted execution, deploys, public tunnels, or GitHub mutation unless the user
|
|
22
22
|
explicitly approves that exact action.
|
|
23
23
|
|
|
24
|
+
## Not For You: `humanish tui`
|
|
25
|
+
|
|
26
|
+
`humanish tui` is a human-only surface. It takes over the terminal and waits for
|
|
27
|
+
keystrokes, so it will block you and produce nothing you can read. It refuses a
|
|
28
|
+
non-interactive stdin or stdout with `HUMANISH_TUI_REQUIRES_TTY` rather than
|
|
29
|
+
rendering escape codes into your transcript — but do not invoke it at all.
|
|
30
|
+
|
|
31
|
+
Everything it shows has a machine-readable equivalent, which is what you want:
|
|
32
|
+
|
|
33
|
+
| Instead of the TUI | Use |
|
|
34
|
+
| --- | --- |
|
|
35
|
+
| browsing labs | `npx humanish lab list --json` |
|
|
36
|
+
| browsing runs | `npx humanish runs --json` |
|
|
37
|
+
| starting a run | `npx humanish lab run <lab> --json --no-open` |
|
|
38
|
+
| a run's outcome | `npx humanish review --run <id> --json` |
|
|
39
|
+
|
|
40
|
+
If a human asks you to "open the TUI", tell them the command to type; do not run
|
|
41
|
+
it on their behalf.
|
|
42
|
+
|
|
24
43
|
## Setup Workflow
|
|
25
44
|
|
|
26
45
|
1. Inspect public target-repo files only: `package.json`, docs, route/app
|