humanish 0.139.1 → 0.141.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/CHANGELOG.md +23 -0
- package/README.md +5 -4
- package/dist/actors/codex/restricted-participant.js +1 -1
- package/dist/actors/computer-use/external-handoff.d.ts +22 -0
- package/dist/actors/computer-use/external-handoff.js +83 -0
- package/dist/actors/computer-use/loop/observation.d.ts +7 -0
- package/dist/actors/computer-use/loop/observation.js +17 -1
- package/dist/actors/computer-use/loop/types.d.ts +6 -0
- package/dist/analysis/analysis-limits.d.ts +6 -0
- package/dist/analysis/analysis-limits.js +6 -0
- package/dist/analysis/analysis-lock.d.ts +8 -0
- package/dist/analysis/analysis-lock.js +129 -0
- package/dist/analysis/automatic-completion.js +31 -0
- package/dist/analysis/automatic-config.d.ts +15 -6
- package/dist/analysis/automatic-config.js +25 -9
- package/dist/analysis/execute.js +1 -1
- package/dist/analysis/job.d.ts +1 -0
- package/dist/analysis/job.js +6 -2
- package/dist/analysis/recording-ended.d.ts +16 -0
- package/dist/analysis/recording-ended.js +29 -0
- package/dist/analysis/service.d.ts +1 -3
- package/dist/analysis/service.js +23 -50
- package/dist/analysis/source.d.ts +1 -1
- package/dist/browser-control/protocol.d.ts +8 -0
- package/dist/browser-control/protocol.js +21 -0
- package/dist/cli/commands/analysis-signals.d.ts +10 -4
- package/dist/cli/commands/analysis-signals.js +28 -6
- package/dist/cli/commands/runs.js +8 -2
- package/dist/cli/commands/study-route-computer-use.js +4 -5
- package/dist/cli/commands/study-route-scripted.js +5 -6
- package/dist/cli/commands/study-route-shared-world.js +4 -5
- package/dist/cli/commands/study-route-terminal.js +5 -6
- package/dist/cli/commands/study-run.js +1 -1
- package/dist/cli/doctor.d.ts +2 -0
- package/dist/cli/doctor.js +3 -0
- package/dist/cli/findings.d.ts +40 -14
- package/dist/cli/findings.js +117 -35
- package/dist/cli/io.d.ts +5 -1
- package/dist/cli/io.js +12 -2
- package/dist/cli/run-findings.d.ts +19 -0
- package/dist/cli/run-findings.js +106 -0
- package/dist/evidence/browser-hygiene.d.ts +12 -1
- package/dist/evidence/browser-hygiene.js +45 -2
- package/dist/feedback/export-bundle.js +1 -1
- package/dist/guest/bootstrap.d.ts +4 -2
- package/dist/guest/bootstrap.js +15 -7
- package/dist/guest/display-config.d.ts +31 -0
- package/dist/guest/display-config.js +35 -0
- package/dist/guest/external-links.d.ts +14 -0
- package/dist/guest/external-links.js +48 -0
- package/dist/guest/runtime-desktop.d.ts +9 -3
- package/dist/guest/runtime-desktop.js +64 -27
- package/dist/guest/runtime.d.ts +2 -1
- package/dist/guest/runtime.js +1 -1
- package/dist/guest-runtime-main.js +2 -1
- package/dist/observer/data.d.ts +4 -0
- package/dist/observer/run-routes.js +12 -3
- package/dist/observer-app.html +2 -2
- package/dist/routes/computer-use/e2b-desktop/desktop.js +27 -17
- package/dist/routes/computer-use/e2b-desktop/fidelity.js +10 -17
- package/dist/routes/computer-use/e2b-desktop/teardown.js +1 -1
- package/dist/routes/computer-use/in-process-desktop.js +2 -2
- package/dist/routes/computer-use/live-phase.js +1 -1
- package/dist/routes/computer-use/local-vm.js +34 -1
- package/dist/routes/computer-use/plan.js +1 -1
- package/dist/routes/computer-use/setup.js +2 -2
- package/dist/routes/scripted/plan.js +1 -1
- package/dist/routes/shared-world/plan.js +1 -1
- package/dist/routes/shared-world/route.js +1 -1
- package/dist/routes/terminal/plan.js +1 -1
- package/dist/run/display.d.ts +6 -1
- package/dist/run/display.js +11 -0
- package/dist/run/host-suspension.js +4 -1
- package/dist/run/preflight-receipts.d.ts +3 -12
- package/dist/run/preflight-receipts.js +7 -67
- package/dist/run/process-owner.d.ts +28 -0
- package/dist/run/process-owner.js +83 -0
- package/dist/run/projection.js +1 -1
- package/dist/run/reclaim.d.ts +6 -0
- package/dist/run/reclaim.js +32 -2
- package/dist/run/route-shell.js +2 -2
- package/dist/run/run-index.d.ts +3 -1
- package/dist/run/run-index.js +23 -11
- package/dist/run/run.d.ts +16 -5
- package/dist/run/run.js +25 -10
- package/dist/run/status.d.ts +57 -2
- package/dist/run/status.js +87 -1
- package/dist/run/stored-runs.d.ts +3 -0
- package/dist/run/stored-runs.js +9 -4
- package/dist/study/doctor.js +11 -6
- package/dist/study/init-templates.js +9 -3
- package/dist/study/mobile-emulation.d.ts +27 -0
- package/dist/study/mobile-emulation.js +60 -0
- package/dist/study/plan.js +1 -1
- package/dist/study/preflight-result.js +1 -1
- package/dist/study/run-study-events.d.ts +7 -1
- package/dist/study/summary.js +1 -1
- package/dist/study/warnings.js +5 -2
- package/dist/substrates/e2b/cdp-probe.d.ts +2 -6
- package/dist/substrates/e2b/desktop-browser.js +3 -1
- package/dist/substrates/e2b/desktop-cdp.d.ts +0 -2
- package/dist/substrates/e2b/desktop-cdp.js +0 -2
- package/dist/substrates/e2b/desktop-executor.d.ts +7 -0
- package/dist/substrates/e2b/desktop-executor.js +3 -0
- package/dist/substrates/e2b/desktop-handoff.d.ts +22 -0
- package/dist/substrates/e2b/desktop-handoff.js +86 -0
- package/dist/substrates/local/firecracker-desktop.d.ts +5 -0
- package/dist/substrates/local/firecracker-desktop.js +7 -1
- package/dist/substrates/local/runtime-config.d.ts +12 -0
- package/dist/substrates/local/runtime-config.js +55 -13
- package/dist/substrates/local/runtime-release.js +8 -8
- package/dist/substrates/local/runtime.js +1 -0
- package/dist/tui-app.js +115 -115
- package/docs/architecture/observer.md +7 -3
- package/docs/contracts/schemas.md +24 -3
- package/docs/contracts/study-analysis.md +15 -9
- package/docs/principles/invariants-and-defaults.md +1 -1
- package/docs/product/automatic-analysis.md +75 -18
- package/package.json +1 -1
- package/skills/humanish/SKILL.md +153 -31
package/dist/cli/findings.d.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import type { Command } from "commander";
|
|
2
|
-
import type { AutomaticAnalysisOutcome } from "../analysis/job.js";
|
|
3
1
|
import type { AnalysisArtifact, LoadedAnalysis } from "../analysis/types.js";
|
|
4
|
-
import { type
|
|
2
|
+
import { type RunAnalysisPhase } from "../run/status.js";
|
|
3
|
+
import { type RunStudyProvenance } from "../run/study-provenance.js";
|
|
5
4
|
export declare const ANALYSIS_FINDINGS_SCHEMA = "humanish.analysis-findings.v1";
|
|
6
5
|
type Finding = NonNullable<AnalysisArtifact["result"]>["findings"][number];
|
|
7
6
|
type DesignFinding = NonNullable<NonNullable<AnalysisArtifact["result"]>["designFindings"]>[number];
|
|
@@ -116,6 +115,35 @@ interface FindingsSource {
|
|
|
116
115
|
analysisOffIn?: string;
|
|
117
116
|
/** For a dry run, the study a live run can start from, read when the view is built. */
|
|
118
117
|
liveStudy?: LiveStudy;
|
|
118
|
+
/** The run's study, from its bundle. */
|
|
119
|
+
study?: Pick<RunStudyProvenance, "id" | "path">;
|
|
120
|
+
/**
|
|
121
|
+
* Whether Codex is signed in with a ChatGPT account on this machine, read only for an analysis
|
|
122
|
+
* skipped for a missing OpenAI key, whose next step it picks.
|
|
123
|
+
*/
|
|
124
|
+
codexSignedIn?: boolean;
|
|
125
|
+
/**
|
|
126
|
+
* Where the run is before its findings, from status.json: its participants still running, or
|
|
127
|
+
* the automatic analysis its own process started. Undefined once neither applies.
|
|
128
|
+
*/
|
|
129
|
+
progress?: {
|
|
130
|
+
stage: "participants";
|
|
131
|
+
}
|
|
132
|
+
/** The run ended while a participant stream was still running: nothing complete to analyze. */
|
|
133
|
+
| {
|
|
134
|
+
stage: "cut-off";
|
|
135
|
+
running: number;
|
|
136
|
+
streams: number;
|
|
137
|
+
} | {
|
|
138
|
+
stage: "analysis";
|
|
139
|
+
phase: RunAnalysisPhase;
|
|
140
|
+
/** status.json's last heartbeat. */
|
|
141
|
+
updatedAt: string;
|
|
142
|
+
/** The analyst the run's study gets, read only for an analysis whose process stopped. */
|
|
143
|
+
provider?: string;
|
|
144
|
+
};
|
|
145
|
+
/** When the view was built, for how long an analysis has run. */
|
|
146
|
+
nowMs?: number;
|
|
119
147
|
}
|
|
120
148
|
/** A study `run` can start live after a dry run. */
|
|
121
149
|
interface LiveStudy {
|
|
@@ -126,24 +154,22 @@ interface LiveStudy {
|
|
|
126
154
|
/** The study file to set `mode: live` in first, when the file still starts as a dry run. */
|
|
127
155
|
setLiveIn?: string;
|
|
128
156
|
}
|
|
157
|
+
/** ` --cwd <dir>`, or "" when `cwd` is the current directory. */
|
|
158
|
+
export declare function cwdFlag(cwd: string): string;
|
|
129
159
|
/** The findings view of a run's selected analysis, or why it has none. */
|
|
130
160
|
export declare function analysisFindings(source: FindingsSource): AnalysisFindings;
|
|
131
161
|
/**
|
|
132
162
|
* The run's bundle mode and its selected analysis: `analysisId`, or the version `analyze show`
|
|
133
163
|
* defaults to. Null when the run is missing or its storage is unsafe.
|
|
134
164
|
*/
|
|
135
|
-
export declare function readRunAnalysis(cwd: string, run: string, analysisId?: string): Promise<FindingsSource | null>;
|
|
165
|
+
export declare function readRunAnalysis(cwd: string, run: string, analysisId?: string, deps?: FindingsDeps): Promise<FindingsSource | null>;
|
|
166
|
+
/** What reading a run's findings needs from this machine; tests replace it. */
|
|
167
|
+
export interface FindingsDeps {
|
|
168
|
+
/** Whether Codex is signed in with a ChatGPT account. */
|
|
169
|
+
codexSignedIn?: () => Promise<boolean>;
|
|
170
|
+
}
|
|
136
171
|
/** The view of a run that could not be found. */
|
|
137
172
|
export declare function missingRunFindings(cwd: string, run: string): AnalysisFindings;
|
|
138
173
|
/** The findings of a run's selected analysis, or why it has none. */
|
|
139
|
-
export declare function readRunFindings(cwd: string, run: string, analysisId?: string): Promise<AnalysisFindings>;
|
|
140
|
-
/**
|
|
141
|
-
* The end of a live run's human output, after its `analysis:` line: the findings of the analysis
|
|
142
|
-
* the run just completed. Prints nothing for --json, or when the run's analysis did not complete,
|
|
143
|
-
* since that line already says why.
|
|
144
|
-
*/
|
|
145
|
-
export declare function writeRunFindings(command: Command, io: CliIo, cwd: string, result: {
|
|
146
|
-
runId?: string;
|
|
147
|
-
automaticAnalysis?: AutomaticAnalysisOutcome;
|
|
148
|
-
}): Promise<void>;
|
|
174
|
+
export declare function readRunFindings(cwd: string, run: string, analysisId?: string, deps?: FindingsDeps): Promise<AnalysisFindings>;
|
|
149
175
|
export {};
|
package/dist/cli/findings.js
CHANGED
|
@@ -1,26 +1,30 @@
|
|
|
1
|
-
// A run's analysis findings for `review`, `analyze show` and the end of a live `run
|
|
2
|
-
// `humanish.analysis-findings.v1` projection, read from the run.
|
|
1
|
+
// A run's analysis findings for `review`, `analyze show` and the end of a live `run`
|
|
2
|
+
// (run-findings.ts): the `humanish.analysis-findings.v1` projection, read from the run.
|
|
3
|
+
// findings-text.ts prints it. The
|
|
3
4
|
// analysis artifact stays the record; this reads the version `analyze show` selects and never
|
|
4
5
|
// starts an analysis.
|
|
5
6
|
import path from "node:path";
|
|
6
7
|
import { costRefusal } from "../analysis/admission.js";
|
|
7
|
-
import { DEFAULT_ANALYSIS_MAX_COST_USD } from "../analysis/automatic-config.js";
|
|
8
|
+
import { DEFAULT_ANALYSIS_MAX_COST_USD, resolveAutomaticAnalysis, } from "../analysis/automatic-config.js";
|
|
9
|
+
import { ANALYSIS_USUALLY_TAKES } from "../analysis/analysis-limits.js";
|
|
8
10
|
import { bySeverity } from "../analysis/finding-lead.js";
|
|
9
11
|
import { loadAnalysis } from "../analysis/load.js";
|
|
10
12
|
import { projectShareCheckedAnalysis } from "../analysis/sharing.js";
|
|
11
|
-
import { loadRunBundlePrepared, resolveRunPath } from "../run/locate.js";
|
|
13
|
+
import { loadRunBundlePrepared, readRunJsonIfExists, resolveRunPath, runJsonValue, } from "../run/locate.js";
|
|
14
|
+
import { classifyRunStatus, isRunStatusRecord, RUN_STATUS_FILE, runAnalysisPhase, } from "../run/status.js";
|
|
15
|
+
import { TERMINAL_SIMULATION_STATUSES } from "../run/streams.js";
|
|
16
|
+
import { formatDuration } from "../run/projection.js";
|
|
12
17
|
import { resolvePhysicalCwd, runIdOf } from "../run/paths.js";
|
|
13
18
|
import { studyProvenanceOf } from "../run/study-provenance.js";
|
|
14
19
|
import { resolveStudyManifest } from "../study/discover.js";
|
|
15
20
|
import { routeOf } from "../study/routing.js";
|
|
16
21
|
import { plural } from "../run/text.js";
|
|
17
22
|
import { shellQuote } from "../substrates/shell.js";
|
|
18
|
-
import { analysisOutcomeText
|
|
19
|
-
import { formatFindingsSummary } from "./findings-text.js";
|
|
23
|
+
import { analysisOutcomeText } from "./io.js";
|
|
20
24
|
import { cli } from "./invocation.js";
|
|
21
25
|
export const ANALYSIS_FINDINGS_SCHEMA = "humanish.analysis-findings.v1";
|
|
22
26
|
/** ` --cwd <dir>`, or "" when `cwd` is the current directory. */
|
|
23
|
-
function cwdFlag(cwd) {
|
|
27
|
+
export function cwdFlag(cwd) {
|
|
24
28
|
if (path.resolve(cwd) === process.cwd())
|
|
25
29
|
return "";
|
|
26
30
|
return ` --cwd ${/^[\w@%+=:,./-]+$/.test(cwd) ? cwd : shellQuote(cwd)}`;
|
|
@@ -152,6 +156,69 @@ function dryRunFindings(source) {
|
|
|
152
156
|
: `set mode: live in ${live.setLiveIn}, then run the next command.`;
|
|
153
157
|
return withoutFindings(source, "dry_run", null, `${opening} Findings come from live runs: ${howLive}`, cli(`run ${live.run}${source.cwdFlag}`));
|
|
154
158
|
}
|
|
159
|
+
/**
|
|
160
|
+
* An analysis skipped because OPENAI_API_KEY is not set. With Codex signed in, the next step is the
|
|
161
|
+
* Codex account analyst, named as an opt-in: the study chose the OpenAI analyst, and switching
|
|
162
|
+
* the account that pays for it is the operator's choice.
|
|
163
|
+
*/
|
|
164
|
+
function missingKeyFindings(source, reason) {
|
|
165
|
+
const opening = "The automatic analysis was skipped because OPENAI_API_KEY is not set.";
|
|
166
|
+
if (source.codexSignedIn !== true)
|
|
167
|
+
return withoutFindings(source, "skipped", reason, `${opening} Set it with ${cli("keys set openai")}, then run the next command.`, analyzeCommand(source, undefined));
|
|
168
|
+
const file = source.study?.path === undefined ? "the study file" : source.study.path;
|
|
169
|
+
return withoutFindings(source, "skipped", reason, `${opening} Codex is signed in on this machine, so its ChatGPT account can analyze the run instead, sending the evidence to OpenAI under that account. The next command does it once; review.analysis.provider: codex in ${file} makes it this study's analyst.`, analyzeCommand(source, "codex"));
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* The view of a run whose findings are not written yet: participants still running, an analysis
|
|
173
|
+
* running, or an analysis whose process stopped before it finished. Undefined otherwise.
|
|
174
|
+
*/
|
|
175
|
+
function pendingFindings(source, job) {
|
|
176
|
+
const again = cli(`review --run ${source.runId}${source.cwdFlag}`);
|
|
177
|
+
const progress = source.progress;
|
|
178
|
+
if (progress?.stage === "participants" && job === undefined)
|
|
179
|
+
return withoutFindings(source, "running", null, source.analysisOffIn === undefined
|
|
180
|
+
? "Participants are still running in this run, so no analysis has started. Automatic analysis starts when they finish, and the findings follow it."
|
|
181
|
+
: "Participants are still running in this run. Analysis is turned off in its study, so none starts when they finish.", again);
|
|
182
|
+
if (progress?.stage === "cut-off" && job === undefined)
|
|
183
|
+
return withoutFindings(source, "skipped", "ANALYSIS_RUN_INCOMPLETE", `This run ended while ${progress.running} of ${progress.streams} participant streams were still running, so its recording is incomplete and no analysis ran. Run the study again with the next command.`, cli(`run ${source.study?.id ?? "<study>"}${source.cwdFlag}`));
|
|
184
|
+
const phase = progress?.stage === "analysis" ? progress.phase : undefined;
|
|
185
|
+
const jobPending = job?.state === "queued" || job?.state === "running";
|
|
186
|
+
if (jobPending || (job === undefined && phase?.state === "running")) {
|
|
187
|
+
const since = phase === undefined
|
|
188
|
+
? ""
|
|
189
|
+
: ` It started at ${phase.startedAt}, ${formatDuration(Math.max(0, (source.nowMs ?? Date.now()) - Date.parse(phase.startedAt)))} ago.`;
|
|
190
|
+
return withoutFindings(source, "running", null, `The automatic analysis is running.${since} Findings usually take ${ANALYSIS_USUALLY_TAKES}; check again then.`, again);
|
|
191
|
+
}
|
|
192
|
+
const stopped = (job?.state === "unknown" && job.reason === "AUTOMATIC_ANALYSIS_STOPPED") ||
|
|
193
|
+
(job === undefined && phase?.state === "stopped");
|
|
194
|
+
if (!stopped)
|
|
195
|
+
return undefined;
|
|
196
|
+
const at = job?.updatedAt ?? (progress?.stage === "analysis" ? progress.updatedAt : undefined);
|
|
197
|
+
return withoutFindings(source, "failed", "AUTOMATIC_ANALYSIS_STOPPED", `The automatic analysis stopped before it wrote findings: the process running it stopped updating its record${at === undefined ? "" : ` at ${at}`}, most likely because it ended. Analyze the run again with the next command.`, analyzeCommand(source, progress?.stage === "analysis" ? progress.provider : undefined));
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* A run that ended, by a signal, a process that left without publishing, or a heartbeat that
|
|
201
|
+
* stopped, while some participant stream was still running. Undefined for any other run.
|
|
202
|
+
*/
|
|
203
|
+
function cutOff(bundle, record, nowMs) {
|
|
204
|
+
if (bundle === undefined || bundle.outcome?.state === "finished")
|
|
205
|
+
return undefined;
|
|
206
|
+
if (bundle.outcome === undefined &&
|
|
207
|
+
(record === null || classifyRunStatus(record, nowMs) !== "interrupted"))
|
|
208
|
+
return undefined;
|
|
209
|
+
const running = bundle.streams.filter((stream) => !TERMINAL_SIMULATION_STATUSES.has(stream.status) || stream.liveActor !== undefined).length;
|
|
210
|
+
return running === 0 ? undefined : { stage: "cut-off", running, streams: bundle.streams.length };
|
|
211
|
+
}
|
|
212
|
+
/** The analyst the run's study gets, when its file still names the same study. */
|
|
213
|
+
async function studyAnalyst(cwd, study) {
|
|
214
|
+
if (study?.path === undefined)
|
|
215
|
+
return undefined;
|
|
216
|
+
const resolved = await resolveStudyManifest(cwd, study.path).catch(() => null);
|
|
217
|
+
if (!resolved?.ok || resolved.config.id !== study.id)
|
|
218
|
+
return undefined;
|
|
219
|
+
const analysis = resolveAutomaticAnalysis(resolved.config.review?.analysis, resolved.config.actor);
|
|
220
|
+
return analysis.ok ? analysis.config?.provider : undefined;
|
|
221
|
+
}
|
|
155
222
|
/** The findings view of a run's selected analysis, or why it has none. */
|
|
156
223
|
export function analysisFindings(source) {
|
|
157
224
|
if (source.mode !== undefined && source.mode !== "live")
|
|
@@ -188,8 +255,9 @@ export function analysisFindings(source) {
|
|
|
188
255
|
if (loaded.warnings.includes("ANALYSIS_SENSITIVE_TEXT_QUARANTINED"))
|
|
189
256
|
return withoutFindings(source, "unavailable", "ANALYSIS_SENSITIVE_TEXT_QUARANTINED", `The analysis text matched a sensitive-text pattern, so it is not printed. ${cli(`analyze show --run ${source.runId}${source.cwdFlag} --json`)} prints the raw record.`, null);
|
|
190
257
|
const job = loaded.automatic;
|
|
191
|
-
|
|
192
|
-
|
|
258
|
+
const pending = pendingFindings(source, job);
|
|
259
|
+
if (pending !== undefined)
|
|
260
|
+
return pending;
|
|
193
261
|
if (analysis && analysis.result === null)
|
|
194
262
|
return withoutFindings(source, "failed", analysis.error, `Analysis attempt ${analysis.id} ${analysis.status === "cancelled" ? "was cancelled" : "failed"}${analysis.error ? ` (${analysis.error})` : ""} and left no findings.`, analyzeCommand(source, analysis.provider));
|
|
195
263
|
if (job?.state === "failed" || job?.state === "cancelled") {
|
|
@@ -200,6 +268,8 @@ export function analysisFindings(source) {
|
|
|
200
268
|
const refusal = costRefusal(job.admission, `--run ${source.runId}${source.cwdFlag}`, cli);
|
|
201
269
|
return withoutFindings(source, "skipped", job.reason, `The automatic analysis was refused before it started. ${refusal.text}`, refusal.command);
|
|
202
270
|
}
|
|
271
|
+
if (job?.state === "skipped" && job.reason === "AUTOMATIC_ANALYSIS_KEY_MISSING")
|
|
272
|
+
return missingKeyFindings(source, job.reason);
|
|
203
273
|
if (job?.state === "skipped") {
|
|
204
274
|
const refused = job.reason === "AUTOMATIC_ANALYSIS_ADMISSION_REFUSED";
|
|
205
275
|
return withoutFindings(source, "skipped", job.reason, `The automatic analysis was ${analysisOutcomeText(job)}.${refused ? " The dry run says why, and for a cost refusal names the --max-cost that admits it." : ""}`, job.reason === "AUTOMATIC_ANALYSIS_NO_PARTICIPANT_EVIDENCE"
|
|
@@ -253,7 +323,7 @@ async function liveStudyAfterDryRun(cwd, study) {
|
|
|
253
323
|
* The run's bundle mode and its selected analysis: `analysisId`, or the version `analyze show`
|
|
254
324
|
* defaults to. Null when the run is missing or its storage is unsafe.
|
|
255
325
|
*/
|
|
256
|
-
export async function readRunAnalysis(cwd, run, analysisId) {
|
|
326
|
+
export async function readRunAnalysis(cwd, run, analysisId, deps = {}) {
|
|
257
327
|
const physical = await resolvePhysicalCwd(cwd);
|
|
258
328
|
const prepared = await resolveRunPath(physical, run).catch(() => null);
|
|
259
329
|
if (!prepared)
|
|
@@ -263,16 +333,50 @@ export async function readRunAnalysis(cwd, run, analysisId) {
|
|
|
263
333
|
const study = bundle === null ? undefined : studyProvenanceOf(bundle.bundle);
|
|
264
334
|
const analysisOffIn = mode === "live" ? await studyAnalysisOff(physical, study) : undefined;
|
|
265
335
|
const liveStudy = mode !== undefined && mode !== "live" ? await liveStudyAfterDryRun(physical, study) : undefined;
|
|
336
|
+
const loaded = await loadAnalysis(prepared, analysisId);
|
|
337
|
+
const nowMs = Date.now();
|
|
338
|
+
const status = runJsonValue(await readRunJsonIfExists(prepared, RUN_STATUS_FILE));
|
|
339
|
+
const record = isRunStatusRecord(status) && status.runId === runIdOf(prepared) ? status : null;
|
|
340
|
+
const phase = record === null ? undefined : runAnalysisPhase(record, nowMs);
|
|
341
|
+
const stopped = phase?.state === "stopped" || loaded.automatic?.reason === "AUTOMATIC_ANALYSIS_STOPPED";
|
|
342
|
+
const provider = stopped ? await studyAnalyst(physical, study) : undefined;
|
|
343
|
+
const progress = phase !== undefined && record !== null
|
|
344
|
+
? {
|
|
345
|
+
stage: "analysis",
|
|
346
|
+
phase,
|
|
347
|
+
updatedAt: record.updatedAt,
|
|
348
|
+
...(provider === undefined ? {} : { provider }),
|
|
349
|
+
}
|
|
350
|
+
: bundle?.bundle.outcome === undefined &&
|
|
351
|
+
record !== null &&
|
|
352
|
+
classifyRunStatus(record, nowMs) === "running"
|
|
353
|
+
? { stage: "participants" }
|
|
354
|
+
: cutOff(bundle?.bundle, record, nowMs);
|
|
355
|
+
const codexSignedIn = loaded.automatic?.reason === "AUTOMATIC_ANALYSIS_KEY_MISSING"
|
|
356
|
+
? await (deps.codexSignedIn ?? codexSignedInHere)().catch(() => false)
|
|
357
|
+
: undefined;
|
|
266
358
|
return {
|
|
267
359
|
runId: runIdOf(prepared),
|
|
268
360
|
mode,
|
|
269
361
|
runRoot: prepared.relativeRunRoot,
|
|
270
|
-
loaded
|
|
362
|
+
loaded,
|
|
271
363
|
cwdFlag: cwdFlag(cwd),
|
|
272
364
|
...(analysisOffIn === undefined ? {} : { analysisOffIn }),
|
|
273
365
|
...(liveStudy === undefined ? {} : { liveStudy }),
|
|
366
|
+
...(study === undefined
|
|
367
|
+
? {}
|
|
368
|
+
: { study: { id: study.id, ...(study.path === undefined ? {} : { path: study.path }) } }),
|
|
369
|
+
...(codexSignedIn === undefined ? {} : { codexSignedIn }),
|
|
370
|
+
...(progress === undefined ? {} : { progress }),
|
|
371
|
+
nowMs,
|
|
274
372
|
};
|
|
275
373
|
}
|
|
374
|
+
async function codexSignedInHere() {
|
|
375
|
+
const { detectLocalAgents } = await import("../actors/local-agent/cli.js");
|
|
376
|
+
const agents = await detectLocalAgents({ only: "codex" });
|
|
377
|
+
// The Codex analyst refuses an API-key login, so only a ChatGPT login counts.
|
|
378
|
+
return agents.some((agent) => agent.authStatus === "authenticated" && agent.billing === "account-unknown");
|
|
379
|
+
}
|
|
276
380
|
/** The view of a run that could not be found. */
|
|
277
381
|
export function missingRunFindings(cwd, run) {
|
|
278
382
|
const loaded = {
|
|
@@ -285,29 +389,7 @@ export function missingRunFindings(cwd, run) {
|
|
|
285
389
|
return withoutFindings({ runId: run, mode: undefined, runRoot: "", loaded, cwdFlag: flag }, "unavailable", "ANALYSIS_RUN_NOT_FOUND", `Run ${run} was not found or its storage is unsafe.`, cli(`runs${flag}`));
|
|
286
390
|
}
|
|
287
391
|
/** The findings of a run's selected analysis, or why it has none. */
|
|
288
|
-
export async function readRunFindings(cwd, run, analysisId) {
|
|
289
|
-
const source = await readRunAnalysis(cwd, run, analysisId);
|
|
392
|
+
export async function readRunFindings(cwd, run, analysisId, deps = {}) {
|
|
393
|
+
const source = await readRunAnalysis(cwd, run, analysisId, deps);
|
|
290
394
|
return source ? analysisFindings(source) : missingRunFindings(cwd, run);
|
|
291
395
|
}
|
|
292
|
-
/**
|
|
293
|
-
* The end of a live run's human output, after its `analysis:` line: the findings of the analysis
|
|
294
|
-
* the run just completed. Prints nothing for --json, or when the run's analysis did not complete,
|
|
295
|
-
* since that line already says why.
|
|
296
|
-
*/
|
|
297
|
-
export async function writeRunFindings(command, io, cwd, result) {
|
|
298
|
-
const outcome = result.automaticAnalysis;
|
|
299
|
-
const analysisId = outcome?.result?.analysisId;
|
|
300
|
-
if (wantsJson(command) ||
|
|
301
|
-
result.runId === undefined ||
|
|
302
|
-
analysisId === undefined ||
|
|
303
|
-
(outcome?.state !== "complete" && outcome?.state !== "partial"))
|
|
304
|
-
return;
|
|
305
|
-
try {
|
|
306
|
-
const view = await readRunFindings(cwd, result.runId, analysisId);
|
|
307
|
-
const full = cli(`review --run ${view.runId}${cwdFlag(cwd)}`);
|
|
308
|
-
io.writeOut(`${formatFindingsSummary(view, full).join("\n")}\n`);
|
|
309
|
-
}
|
|
310
|
-
catch {
|
|
311
|
-
// The run's own result is already printed; review reads the same record later.
|
|
312
|
-
}
|
|
313
|
-
}
|
package/dist/cli/io.d.ts
CHANGED
|
@@ -113,7 +113,11 @@ export declare function messageOutput(value: {
|
|
|
113
113
|
export declare function humanError(error: CliError | undefined): HumanOutput;
|
|
114
114
|
/** Prints a human formatter's output: text to stdout, an error to stderr through formatCliError. */
|
|
115
115
|
export declare function writeHuman(command: Command, io: CliIo, human: HumanOutput): void;
|
|
116
|
-
|
|
116
|
+
/**
|
|
117
|
+
* Prints a result: JSON for --json, else the human formatter's text and, for a run, its analysis
|
|
118
|
+
* line. `analysisLines` replaces that line, for a caller that has read what the analysis left.
|
|
119
|
+
*/
|
|
120
|
+
export declare function writeResult<T>(command: Command, io: CliIo, result: T, formatHuman: (result: T) => HumanOutput, analysisLines?: readonly string[]): void;
|
|
117
121
|
export declare function noteRunFacts(command: Command, facts: TelemetryProperties): void;
|
|
118
122
|
/** Exported for tests: the facts writeResult read off a command's result document. */
|
|
119
123
|
export declare function runFactsFor(command: Command): TelemetryProperties;
|
package/dist/cli/io.js
CHANGED
|
@@ -178,7 +178,11 @@ export function writeHuman(command, io, human) {
|
|
|
178
178
|
if (human.error)
|
|
179
179
|
io.writeErr(formatCliError(commandLabel(command), human.error));
|
|
180
180
|
}
|
|
181
|
-
|
|
181
|
+
/**
|
|
182
|
+
* Prints a result: JSON for --json, else the human formatter's text and, for a run, its analysis
|
|
183
|
+
* line. `analysisLines` replaces that line, for a caller that has read what the analysis left.
|
|
184
|
+
*/
|
|
185
|
+
export function writeResult(command, io, result, formatHuman, analysisLines) {
|
|
182
186
|
const output = automaticAnalysisEnvelope(result);
|
|
183
187
|
if (wantsJson(command)) {
|
|
184
188
|
// Queued warnings are on stderr already; a JSON caller gets them in warnings[] as well.
|
|
@@ -186,9 +190,14 @@ export function writeResult(command, io, result, formatHuman) {
|
|
|
186
190
|
}
|
|
187
191
|
else {
|
|
188
192
|
writeHuman(command, io, formatHuman(output));
|
|
193
|
+
if (analysisLines !== undefined) {
|
|
194
|
+
for (const line of analysisLines)
|
|
195
|
+
io.writeOut(`${line}\n`);
|
|
196
|
+
}
|
|
197
|
+
else if (
|
|
189
198
|
// A run refused before it was created has no analysis to report, so its output ends with the
|
|
190
199
|
// refusal. Its JSON keeps the skip record.
|
|
191
|
-
|
|
200
|
+
output !== null &&
|
|
192
201
|
typeof output === "object" &&
|
|
193
202
|
"automaticAnalysis" in output &&
|
|
194
203
|
output.runId !== "not-created") {
|
|
@@ -306,6 +315,7 @@ const ANALYSIS_REASON_TEXT = {
|
|
|
306
315
|
AUTOMATIC_ANALYSIS_CANCELLATION_UNAVAILABLE: "outcome unknown: a cancellation could not be recorded",
|
|
307
316
|
AUTOMATIC_ANALYSIS_STORAGE_UNAVAILABLE: "outcome unknown: analysis storage is unavailable",
|
|
308
317
|
AUTOMATIC_ANALYSIS_OUTCOME_UNKNOWN: "outcome unknown",
|
|
318
|
+
AUTOMATIC_ANALYSIS_STOPPED: "outcome unknown: the process running it stopped before it finished",
|
|
309
319
|
};
|
|
310
320
|
export function analysisOutcomeText(analysis) {
|
|
311
321
|
if (analysis.reason === null)
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { Command } from "commander";
|
|
2
|
+
import type { AutomaticAnalysisOutcome } from "../analysis/job.js";
|
|
3
|
+
import { type FindingsDeps } from "./findings.js";
|
|
4
|
+
import { type CliIo, type HumanOutput } from "./io.js";
|
|
5
|
+
interface RunResultShape {
|
|
6
|
+
runId?: string;
|
|
7
|
+
automaticAnalysis?: AutomaticAnalysisOutcome;
|
|
8
|
+
observer?: {
|
|
9
|
+
observerPath?: string;
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Writes a finished run's result. After the route's own output it says what the automatic
|
|
14
|
+
* analysis did and the command a person runs next; when the analysis left findings, the top ones.
|
|
15
|
+
* `--json` carries the same message and command on `automaticAnalysis` and the findings summary
|
|
16
|
+
* as `findings`. A run with no analysis record is written as writeResult writes it.
|
|
17
|
+
*/
|
|
18
|
+
export declare function writeRunResult<T extends RunResultShape>(command: Command, io: CliIo, cwd: string, result: T, formatHuman: (result: T) => HumanOutput, deps?: FindingsDeps): Promise<void>;
|
|
19
|
+
export {};
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
// The end of a live run's output: what its automatic analysis left, read from the run once and
|
|
2
|
+
// written both ways. The human output gets the analysis line, then the top findings or the command
|
|
3
|
+
// that gets them; `--json` gets the same message and command on `automaticAnalysis`, and a bounded
|
|
4
|
+
// `findings` summary when the analysis left findings.
|
|
5
|
+
import { cwdFlag, readRunFindings } from "./findings.js";
|
|
6
|
+
import { formatFindingsSummary } from "./findings-text.js";
|
|
7
|
+
import { cli } from "./invocation.js";
|
|
8
|
+
import { analysisOutcomeText, wantsJson, writeResult } from "./io.js";
|
|
9
|
+
/** At most this many findings go in `run --json`; `review --json` has every one. */
|
|
10
|
+
const TOP_FINDINGS = 5;
|
|
11
|
+
function findingsSummary(view, review, observerPath) {
|
|
12
|
+
return {
|
|
13
|
+
analysisId: view.analysisId,
|
|
14
|
+
status: view.status,
|
|
15
|
+
count: view.findings.length,
|
|
16
|
+
designFindingCount: view.designFindings === null ? null : view.designFindings.length,
|
|
17
|
+
top: view.findings.slice(0, TOP_FINDINGS).map((finding) => ({
|
|
18
|
+
id: finding.id,
|
|
19
|
+
title: finding.title,
|
|
20
|
+
headline: finding.headline,
|
|
21
|
+
impact: finding.impact,
|
|
22
|
+
confidence: finding.confidence,
|
|
23
|
+
participants: finding.affected.map((participant) => participant.streamId),
|
|
24
|
+
})),
|
|
25
|
+
review,
|
|
26
|
+
observer: observerPath === undefined ? null : `${observerPath}#/report`,
|
|
27
|
+
path: view.path,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
/** What the run's analysis left: the message, the command, the human lines and any findings. */
|
|
31
|
+
async function readRunReport(cwd, result, deps) {
|
|
32
|
+
const outcome = result.automaticAnalysis;
|
|
33
|
+
if (outcome === undefined || result.runId === undefined || result.runId === "not-created")
|
|
34
|
+
return undefined;
|
|
35
|
+
const outcomeLine = `analysis: ${analysisOutcomeText(outcome)}`;
|
|
36
|
+
const plain = {
|
|
37
|
+
message: `The automatic analysis was ${analysisOutcomeText(outcome)}.`,
|
|
38
|
+
nextCommand: null,
|
|
39
|
+
lines: [outcomeLine],
|
|
40
|
+
};
|
|
41
|
+
// A dry run left no evidence, and its own output names the live run.
|
|
42
|
+
if (outcome.reason === "AUTOMATIC_ANALYSIS_DRY_RUN")
|
|
43
|
+
return plain;
|
|
44
|
+
const analysisId = outcome.result?.analysisId;
|
|
45
|
+
const finished = outcome.state === "complete" || outcome.state === "partial";
|
|
46
|
+
let view;
|
|
47
|
+
try {
|
|
48
|
+
view = await readRunFindings(cwd, result.runId, finished ? analysisId : undefined, deps);
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
return plain;
|
|
52
|
+
}
|
|
53
|
+
const rejected = outcome.result?.rejectedOutputPath;
|
|
54
|
+
const rejectedLine = rejected
|
|
55
|
+
? [
|
|
56
|
+
`analysis: ${outcome.result?.error?.code ?? "rejected"}; the rejected output is kept locally at ${rejected}`,
|
|
57
|
+
]
|
|
58
|
+
: [];
|
|
59
|
+
const review = cli(`review --run ${view.runId}${cwdFlag(cwd)}`);
|
|
60
|
+
if (finished && (view.state === "ready" || view.state === "stale"))
|
|
61
|
+
return {
|
|
62
|
+
message: view.message,
|
|
63
|
+
nextCommand: review,
|
|
64
|
+
lines: [outcomeLine, ...rejectedLine, ...formatFindingsSummary(view, review)],
|
|
65
|
+
findings: findingsSummary(view, review, result.observer?.observerPath),
|
|
66
|
+
};
|
|
67
|
+
// The line reads "analysis: skipped because ...", as it does without the view.
|
|
68
|
+
const said = view.message.replace(/^The automatic analysis (?:was )?/, "");
|
|
69
|
+
return {
|
|
70
|
+
message: view.message,
|
|
71
|
+
nextCommand: view.next,
|
|
72
|
+
lines: [
|
|
73
|
+
`analysis: ${said}`,
|
|
74
|
+
...rejectedLine,
|
|
75
|
+
...(view.next === null ? [] : [`next: ${view.next}`]),
|
|
76
|
+
],
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Writes a finished run's result. After the route's own output it says what the automatic
|
|
81
|
+
* analysis did and the command a person runs next; when the analysis left findings, the top ones.
|
|
82
|
+
* `--json` carries the same message and command on `automaticAnalysis` and the findings summary
|
|
83
|
+
* as `findings`. A run with no analysis record is written as writeResult writes it.
|
|
84
|
+
*/
|
|
85
|
+
export async function writeRunResult(command, io, cwd, result, formatHuman, deps = {}) {
|
|
86
|
+
const report = await readRunReport(cwd, result, deps);
|
|
87
|
+
if (report === undefined || result.automaticAnalysis === undefined) {
|
|
88
|
+
writeResult(command, io, result, formatHuman);
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
if (!wantsJson(command)) {
|
|
92
|
+
writeResult(command, io, result, formatHuman, report.lines);
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
writeResult(command, io, {
|
|
96
|
+
...result,
|
|
97
|
+
automaticAnalysis: {
|
|
98
|
+
...result.automaticAnalysis,
|
|
99
|
+
message: report.message,
|
|
100
|
+
nextCommand: report.nextCommand,
|
|
101
|
+
},
|
|
102
|
+
...(report.findings === undefined ? {} : { findings: report.findings }),
|
|
103
|
+
},
|
|
104
|
+
// JSON output never calls the human formatter.
|
|
105
|
+
() => "");
|
|
106
|
+
}
|
|
@@ -4,4 +4,15 @@
|
|
|
4
4
|
* place so browser-backed routes can prefer product pixels over browser UI.
|
|
5
5
|
*/
|
|
6
6
|
export declare const CHROMIUM_EVIDENCE_HYGIENE_FLAGS: readonly ["--disable-background-networking", "--disable-component-update", "--disable-default-apps", "--disable-domain-reliability", "--disable-extensions", "--disable-sync", "--no-default-browser-check", "--no-first-run", "--password-store=basic", "--use-mock-keychain"];
|
|
7
|
-
|
|
7
|
+
/**
|
|
8
|
+
* The profile preference that lets a web `targetUrl`'s origin open the external app schemes
|
|
9
|
+
* without Chrome's prompt: the "always allow" choice Chrome stores per origin, which needs no root
|
|
10
|
+
* and no managed policy. Links from other origins still prompt. Undefined for any other target.
|
|
11
|
+
*/
|
|
12
|
+
export declare function externalAppPreferences(targetUrl: string | undefined): {
|
|
13
|
+
protocol_handler: {
|
|
14
|
+
allowed_origin_protocol_pairs: Record<string, Record<string, true>>;
|
|
15
|
+
};
|
|
16
|
+
} | undefined;
|
|
17
|
+
/** The profile's Preferences file, with externalAppPreferences for a web `targetUrl`. */
|
|
18
|
+
export declare function chromiumEvidenceProfilePreferencesJson(targetUrl?: string): string;
|
|
@@ -37,6 +37,49 @@ const CHROMIUM_EVIDENCE_PROFILE_PREFERENCES = {
|
|
|
37
37
|
password_manager_enabled: false,
|
|
38
38
|
},
|
|
39
39
|
};
|
|
40
|
-
|
|
41
|
-
|
|
40
|
+
/**
|
|
41
|
+
* Schemes whose links Chrome hands to another app after asking "Open xdg-open?". The desktop's
|
|
42
|
+
* stand-ins answer them (src/substrates/e2b/desktop-handoff.ts), so the prompt is browser chrome
|
|
43
|
+
* a person on their own computer would not see for that app. Chrome never asks for mailto.
|
|
44
|
+
*/
|
|
45
|
+
const EXTERNAL_APP_SCHEMES = [
|
|
46
|
+
"callto",
|
|
47
|
+
"facetime",
|
|
48
|
+
"facetime-audio",
|
|
49
|
+
"geo",
|
|
50
|
+
"maps",
|
|
51
|
+
"mms",
|
|
52
|
+
"msteams",
|
|
53
|
+
"sip",
|
|
54
|
+
"skype",
|
|
55
|
+
"slack",
|
|
56
|
+
"sms",
|
|
57
|
+
"tel",
|
|
58
|
+
"webcal",
|
|
59
|
+
"whatsapp",
|
|
60
|
+
"zoommtg",
|
|
61
|
+
];
|
|
62
|
+
/**
|
|
63
|
+
* The profile preference that lets a web `targetUrl`'s origin open the external app schemes
|
|
64
|
+
* without Chrome's prompt: the "always allow" choice Chrome stores per origin, which needs no root
|
|
65
|
+
* and no managed policy. Links from other origins still prompt. Undefined for any other target.
|
|
66
|
+
*/
|
|
67
|
+
export function externalAppPreferences(targetUrl) {
|
|
68
|
+
const target = targetUrl === undefined ? null : URL.parse(targetUrl);
|
|
69
|
+
if (target === null || !/^https?:$/.test(target.protocol))
|
|
70
|
+
return undefined;
|
|
71
|
+
return {
|
|
72
|
+
protocol_handler: {
|
|
73
|
+
allowed_origin_protocol_pairs: {
|
|
74
|
+
[target.origin]: Object.fromEntries(EXTERNAL_APP_SCHEMES.map((scheme) => [scheme, true])),
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
/** The profile's Preferences file, with externalAppPreferences for a web `targetUrl`. */
|
|
80
|
+
export function chromiumEvidenceProfilePreferencesJson(targetUrl) {
|
|
81
|
+
return JSON.stringify({
|
|
82
|
+
...CHROMIUM_EVIDENCE_PROFILE_PREFERENCES,
|
|
83
|
+
...externalAppPreferences(targetUrl),
|
|
84
|
+
});
|
|
42
85
|
}
|
|
@@ -47,7 +47,7 @@ function omittedReason(relative) {
|
|
|
47
47
|
return "automatic execution intent and liveness do not transfer to a derivative";
|
|
48
48
|
if (isAnalysisRecordPath(relative))
|
|
49
49
|
return "analysis records bind original evidence hashes; reanalyze the derivative";
|
|
50
|
-
if (relative.startsWith(".analysis-lock
|
|
50
|
+
if (relative.startsWith(".analysis-lock"))
|
|
51
51
|
return "local analysis execution lock";
|
|
52
52
|
return (OMITTED.get(relative) ??
|
|
53
53
|
(["feedback/draft.json", "feedback/issue.md"].includes(relative)
|
|
@@ -3,6 +3,7 @@ import type { Duplex } from "node:stream";
|
|
|
3
3
|
import { type BrowserControlIdentity } from "../browser-control/protocol.js";
|
|
4
4
|
import { createBrowserControlClient } from "../browser-control/client.js";
|
|
5
5
|
import { type GuestMediaConfig } from "./media-config.js";
|
|
6
|
+
import { type GuestDisplayConfig } from "./display-config.js";
|
|
6
7
|
export declare const GUEST_BOOTSTRAP_VERSION = 1;
|
|
7
8
|
export declare const GUEST_BOOTSTRAP_LIMITS: Readonly<{
|
|
8
9
|
bytes: number;
|
|
@@ -18,7 +19,7 @@ export declare const GUEST_BOOTSTRAP_LIMITS: Readonly<{
|
|
|
18
19
|
/** Same entry authority as the local desktop's opaque app-port forward. */
|
|
19
20
|
export declare function validateGuestInitialUrl(value: string): string;
|
|
20
21
|
export declare function guestReadyTimeoutMs(initialUrl?: string): number;
|
|
21
|
-
export declare function encodeGuestBootstrap(identity: BrowserControlIdentity, ready?: boolean, initialUrl?: string, media?: GuestMediaConfig, recording?: DesktopRecordingConfig): Buffer;
|
|
22
|
+
export declare function encodeGuestBootstrap(identity: BrowserControlIdentity, ready?: boolean, initialUrl?: string, media?: GuestMediaConfig, recording?: DesktopRecordingConfig, display?: GuestDisplayConfig): Buffer;
|
|
22
23
|
export declare function parseGuestBootstrap(bytes: Buffer, revision: string, ready?: boolean): BrowserControlIdentity;
|
|
23
24
|
/** Own the first frame and continue refusing early bytes until synchronous handoff. */
|
|
24
25
|
export declare class GuestBootstrapReader {
|
|
@@ -31,6 +32,7 @@ export declare class GuestBootstrapReader {
|
|
|
31
32
|
initialUrl: string | undefined;
|
|
32
33
|
media: GuestMediaConfig | undefined;
|
|
33
34
|
recording: DesktopRecordingConfig | undefined;
|
|
35
|
+
display: GuestDisplayConfig | undefined;
|
|
34
36
|
private resolve;
|
|
35
37
|
private reject;
|
|
36
38
|
private readonly header;
|
|
@@ -52,4 +54,4 @@ export declare class GuestBootstrapReader {
|
|
|
52
54
|
close(): void;
|
|
53
55
|
}
|
|
54
56
|
/** Fixed preface on an already acquired Firecracker stream; no path discovery/retry. */
|
|
55
|
-
export declare function connectGuestBootstrap(stream: Duplex, identity: BrowserControlIdentity, signal: AbortSignal, initialUrl?: string, media?: GuestMediaConfig, recording?: DesktopRecordingConfig): Promise<ReturnType<typeof createBrowserControlClient>>;
|
|
57
|
+
export declare function connectGuestBootstrap(stream: Duplex, identity: BrowserControlIdentity, signal: AbortSignal, initialUrl?: string, media?: GuestMediaConfig, recording?: DesktopRecordingConfig, display?: GuestDisplayConfig): Promise<ReturnType<typeof createBrowserControlClient>>;
|