humanish 0.110.0 → 0.110.2
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/ARCHITECTURE.md +15 -12
- package/CHANGELOG.md +26 -0
- package/CONTEXT.md +8 -6
- package/README.md +103 -53
- package/dist/actors/computer-use/loop/ending.js +4 -4
- package/dist/actors/local-agent/claude-participant.d.ts +74 -0
- package/dist/actors/local-agent/claude-participant.js +243 -0
- package/dist/actors/local-agent/claude-session.d.ts +16 -3
- package/dist/actors/local-agent/claude-session.js +64 -23
- package/dist/actors/local-agent/cli.d.ts +17 -0
- package/dist/actors/local-agent/cli.js +111 -24
- package/dist/actors/local-agent/readiness.d.ts +2 -2
- package/dist/actors/local-agent/readiness.js +20 -12
- package/dist/cli/commands/analyze.js +21 -5
- package/dist/cli/commands/observe.js +15 -2
- package/dist/cli/commands/run-signals.d.ts +2 -0
- package/dist/cli/commands/run-signals.js +106 -45
- package/dist/cli/commands/runs.d.ts +5 -0
- package/dist/cli/commands/runs.js +113 -74
- package/dist/cli/commands/setup.js +23 -14
- package/dist/cli/commands/study-route-computer-use.js +9 -3
- package/dist/cli/commands/study-route-open.js +1 -0
- package/dist/cli/commands/study-route-preview.js +1 -0
- package/dist/cli/commands/study-route-scripted.js +2 -0
- package/dist/cli/commands/study-route-shared-world.js +6 -2
- package/dist/cli/commands/study-route-terminal.js +2 -0
- package/dist/cli/commands/watch.js +14 -4
- package/dist/cli/doctor.d.ts +4 -1
- package/dist/cli/doctor.js +51 -10
- package/dist/cli/findings.d.ts +103 -0
- package/dist/cli/findings.js +317 -0
- package/dist/cli/first-run-path.d.ts +32 -11
- package/dist/cli/first-run-path.js +113 -69
- package/dist/cli/invocation.d.ts +7 -0
- package/dist/cli/invocation.js +32 -0
- package/dist/cli/io.d.ts +3 -1
- package/dist/cli/io.js +8 -15
- package/dist/cli/observer-follow.d.ts +20 -0
- package/dist/cli/observer-follow.js +36 -1
- package/dist/cli/orientation.js +16 -14
- package/dist/cli/product-sentence.d.ts +3 -3
- package/dist/cli/product-sentence.js +3 -3
- package/dist/cli/version.d.ts +1 -0
- package/dist/cli/version.js +11 -0
- package/dist/evidence/encoded-text.d.ts +9 -6
- package/dist/evidence/encoded-text.js +84 -19
- package/dist/evidence/redaction.d.ts +2 -0
- package/dist/evidence/redaction.js +123 -21
- package/dist/feedback/export-bundle.js +2 -2
- package/dist/keys/env-file.d.ts +4 -0
- package/dist/keys/env-file.js +17 -0
- package/dist/keys/key-resolution.js +6 -5
- package/dist/observer/data.d.ts +14 -1
- package/dist/observer/data.js +19 -9
- package/dist/observer/exposure.js +1 -1
- package/dist/observer/library.d.ts +4 -0
- package/dist/observer/library.js +21 -17
- package/dist/observer/render.js +10 -3
- package/dist/observer/run-routes.d.ts +2 -0
- package/dist/observer/run-routes.js +51 -35
- package/dist/observer/serve.js +2 -1
- package/dist/observer-app.html +3 -3
- package/dist/routes/computer-use/bundle.js +3 -1
- package/dist/routes/computer-use/fanout-records.js +1 -1
- package/dist/routes/computer-use/participant-execution.js +3 -1
- package/dist/routes/computer-use/participant-model.js +5 -2
- package/dist/routes/computer-use/plan.js +9 -4
- package/dist/routes/computer-use/preflight.js +1 -1
- package/dist/routes/computer-use/result.js +17 -28
- package/dist/routes/computer-use/single-bundle.js +7 -1
- package/dist/routes/scripted/bundle.js +4 -2
- package/dist/routes/scripted/plan.js +2 -2
- package/dist/routes/scripted/result.d.ts +3 -5
- package/dist/routes/scripted/result.js +16 -22
- package/dist/routes/scripted/scenario.js +4 -4
- package/dist/routes/shared-world/bundle-records.js +6 -1
- package/dist/routes/shared-world/bundle.js +2 -0
- package/dist/routes/shared-world/plan.js +6 -2
- package/dist/routes/shared-world/result.js +22 -22
- package/dist/routes/terminal/bundle.js +6 -3
- package/dist/routes/terminal/credentials.d.ts +5 -5
- package/dist/routes/terminal/credentials.js +14 -6
- package/dist/routes/terminal/dry-run.js +8 -12
- package/dist/routes/terminal/ledger.d.ts +3 -3
- package/dist/routes/terminal/ledger.js +6 -6
- package/dist/routes/terminal/live-finish.js +15 -13
- package/dist/routes/terminal/plan.js +12 -4
- package/dist/routes/terminal/result.d.ts +6 -7
- package/dist/routes/terminal/result.js +6 -22
- package/dist/routes/terminal/route.js +5 -4
- package/dist/routes/terminal/runtime-auth.d.ts +6 -0
- package/dist/routes/terminal/runtime-auth.js +5 -0
- package/dist/routes/terminal/session.js +1 -1
- package/dist/run/bundle-shape.d.ts +3 -1
- package/dist/run/bundle-shape.js +25 -1
- package/dist/run/bundle.d.ts +24 -0
- package/dist/run/display.d.ts +59 -0
- package/dist/run/display.js +106 -0
- package/dist/run/dry-run.js +13 -8
- package/dist/run/judge.d.ts +3 -2
- package/dist/run/judge.js +12 -1
- package/dist/run/local-only-files.d.ts +13 -0
- package/dist/run/local-only-files.js +35 -0
- package/dist/run/pricing.js +1 -1
- package/dist/run/projection.d.ts +0 -2
- package/dist/run/projection.js +0 -11
- package/dist/run/reclaim-outcomes.d.ts +89 -0
- package/dist/run/reclaim-outcomes.js +213 -0
- package/dist/run/reclaim.d.ts +23 -17
- package/dist/run/reclaim.js +213 -114
- package/dist/run/results.d.ts +10 -20
- package/dist/run/results.js +2 -2
- package/dist/run/run-index.d.ts +11 -1
- package/dist/run/run-index.js +11 -0
- package/dist/run/run-not-found.js +3 -2
- package/dist/run/run.d.ts +34 -18
- package/dist/run/run.js +166 -67
- package/dist/run/sandbox-creates.d.ts +59 -0
- package/dist/run/sandbox-creates.js +140 -0
- package/dist/run/sandbox-receipts.d.ts +20 -5
- package/dist/run/sandbox-receipts.js +56 -7
- package/dist/run/stats.d.ts +5 -0
- package/dist/run/stats.js +8 -4
- package/dist/run/status.d.ts +6 -9
- package/dist/run/status.js +7 -23
- package/dist/run/stored-runs.d.ts +5 -14
- package/dist/run/stored-runs.js +31 -146
- package/dist/run/streams.d.ts +3 -2
- package/dist/study/agents-section.d.ts +21 -0
- package/dist/study/agents-section.js +86 -0
- package/dist/study/doctor.d.ts +1 -1
- package/dist/study/doctor.js +8 -4
- package/dist/study/init-templates.js +0 -95
- package/dist/study/init.js +45 -37
- package/dist/study/keys.d.ts +12 -0
- package/dist/study/keys.js +32 -0
- package/dist/study/local-browser-host.d.ts +11 -0
- package/dist/study/local-browser-host.js +42 -0
- package/dist/study/parse/actors.js +9 -1
- package/dist/study/parse/subject.js +12 -0
- package/dist/study/parse/url-credentials.d.ts +9 -0
- package/dist/study/parse/url-credentials.js +50 -0
- package/dist/study/persona.d.ts +4 -0
- package/dist/study/persona.js +4 -2
- package/dist/study/preflight-probes.js +2 -1
- package/dist/study/types.d.ts +8 -6
- package/dist/study/url-credentials.d.ts +6 -0
- package/dist/study/url-credentials.js +34 -0
- package/dist/substrates/e2b/peer-install.d.ts +51 -0
- package/dist/substrates/e2b/peer-install.js +161 -0
- package/dist/substrates/e2b/sandbox.d.ts +34 -6
- package/dist/substrates/e2b/sandbox.js +141 -30
- package/dist/substrates/e2b/sdk.d.ts +33 -0
- package/dist/substrates/e2b/sdk.js +21 -21
- package/dist/substrates/local/firecracker-desktop.js +3 -2
- package/dist/substrates/local/runtime-host.js +5 -4
- package/dist/substrates/local/runtime.js +5 -4
- package/dist/substrates/shell.d.ts +2 -0
- package/dist/substrates/shell.js +4 -0
- package/dist/tui/contract.js +5 -4
- package/dist/tui-app.js +133 -133
- package/dist/verify/liveness.d.ts +19 -3
- package/dist/verify/liveness.js +47 -25
- package/dist/verify/verify.d.ts +3 -0
- package/dist/verify/verify.js +8 -3
- package/docs/architecture/actor-contract.md +2 -1
- package/docs/architecture/observer.md +22 -7
- package/docs/architecture/task-protocol-support.md +13 -13
- package/docs/architecture/terminal-product-route.md +15 -13
- package/docs/contracts/run-bundle.md +95 -10
- package/docs/contracts/schemas.md +44 -26
- package/docs/contracts/study-analysis.md +45 -0
- package/docs/contracts/study-costs.md +8 -0
- package/docs/decisions/0001-run-bundle-is-the-source-of-truth.md +3 -0
- package/docs/decisions/0003-contained-paths-and-cleanup-authority.md +9 -3
- package/docs/principles/invariants-and-defaults.md +11 -8
- package/package.json +6 -3
- package/skills/humanish/SKILL.md +38 -20
|
@@ -33,84 +33,111 @@ function preferredAgent(env) {
|
|
|
33
33
|
export function starterLocalAgentFor(env) {
|
|
34
34
|
return starterActorFor(env) === "local-agent" ? preferredAgent(env)?.id : undefined;
|
|
35
35
|
}
|
|
36
|
-
/**
|
|
37
|
-
* How each hint invokes the CLI. \`npx humanish\` works from a project install and from an npx cache;
|
|
38
|
-
* a dev-dependency install puts no \`humanish\` on \`PATH\`, so a bare name can reach a stale global one.
|
|
39
|
-
*/
|
|
40
|
-
const HUMANISH = "npx humanish";
|
|
41
|
-
/** Hosts that run local browser studies: Linux x64 and Apple Silicon Macs. */
|
|
36
|
+
/** Host shapes the local browser route is built for: Linux x64 and Apple Silicon Macs. */
|
|
42
37
|
export function supportsLocalBrowser(platform, arch) {
|
|
43
38
|
return (platform === "linux" && arch === "x64") || (platform === "darwin" && arch === "arm64");
|
|
44
39
|
}
|
|
45
40
|
/**
|
|
46
|
-
* The next
|
|
47
|
-
*
|
|
41
|
+
* The next two or three commands, in order: the dry run, the hosted try-live study when keys or a
|
|
42
|
+
* signed-in agent allow it, and the local browser study where this host passed its quick checks.
|
|
43
|
+
* Deliberately short: a list of twelve options is the same as no guidance, and the reader here has
|
|
44
|
+
* just been handed a directory of new files.
|
|
48
45
|
*/
|
|
49
46
|
export function firstRunSteps(env) {
|
|
47
|
+
const humanish = env.humanish ?? "npx humanish";
|
|
50
48
|
const steps = [
|
|
51
49
|
{
|
|
52
|
-
command: `${
|
|
50
|
+
command: `${humanish} run first-run`,
|
|
53
51
|
why: "a dry run: no browser or model runs, no keys, no spend",
|
|
54
52
|
},
|
|
55
53
|
];
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
why: `check the local browser study (${prerequisites}); no resources or quota used. Run \`${HUMANISH} runtime setup\` to prepare it, then start your app and run \`${HUMANISH} run local-browser\`; no E2B or model API key`,
|
|
63
|
-
});
|
|
64
|
-
return steps;
|
|
54
|
+
const local = localBrowserStep(env, humanish);
|
|
55
|
+
const hostedRunnable = env.hasE2bKey && (env.hasProviderKey || env.localAgents.length > 0);
|
|
56
|
+
if (hostedRunnable) {
|
|
57
|
+
steps.push(hostedStep(env, humanish, ""));
|
|
58
|
+
if (local !== undefined)
|
|
59
|
+
steps.push(local);
|
|
65
60
|
}
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
: " Local browsers are unavailable on this host; they support Linux x64 or M3-or-newer Apple Silicon Macs.";
|
|
69
|
-
if (!env.hasE2bKey) {
|
|
70
|
-
steps.push({
|
|
71
|
-
command: `${HUMANISH} keys set e2b`,
|
|
72
|
-
why: `the hosted starter needs an E2B desktop.${localUnavailable}`,
|
|
73
|
-
});
|
|
74
|
-
return steps;
|
|
61
|
+
else if (local !== undefined) {
|
|
62
|
+
steps.push(local);
|
|
75
63
|
}
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
? "your provider key"
|
|
79
|
-
: `${preferredAgent(env)?.label} (already signed in, so no API key is needed)`;
|
|
80
|
-
// Everything the step needs, in one line. Splitting it across two commands means the second
|
|
81
|
-
// one fails, which is the same dead end this guidance exists to remove.
|
|
82
|
-
const command = env.hasDesktopSdk
|
|
83
|
-
? `${HUMANISH} run try-live`
|
|
84
|
-
: env.installedInProject
|
|
85
|
-
? `npm i -D @e2b/desktop && ${HUMANISH} run try-live`
|
|
86
|
-
: // Running from an npx cache: installing only the peer here would not be found, because
|
|
87
|
-
// Node resolves it relative to humanish. Both, or neither.
|
|
88
|
-
`npm i -D humanish @e2b/desktop && ${HUMANISH} run try-live`;
|
|
89
|
-
steps.push({
|
|
90
|
-
command,
|
|
91
|
-
why: `a live study: one participant drives a real app in a hosted desktop, using ${brain}` +
|
|
92
|
-
(env.hasDesktopSdk ? "" : " (the desktop SDK is an optional peer, so it installs first)") +
|
|
93
|
-
(!env.hasProviderKey
|
|
94
|
-
? "; automatic findings analysis is skipped without OPENAI_API_KEY"
|
|
95
|
-
: "") +
|
|
96
|
-
localUnavailable,
|
|
97
|
-
});
|
|
98
|
-
return steps;
|
|
64
|
+
else {
|
|
65
|
+
steps.push(hostedStep(env, humanish, localUnavailableNote(env)));
|
|
99
66
|
}
|
|
100
|
-
steps.push({
|
|
101
|
-
command: `${HUMANISH} keys set openai`,
|
|
102
|
-
why: "openai-computer-use needs an API key; to use a Codex or Claude Code login instead, sign in and change the study to actors[0].type: local-agent" +
|
|
103
|
-
localUnavailable,
|
|
104
|
-
});
|
|
105
67
|
return steps;
|
|
106
68
|
}
|
|
69
|
+
/** The local browser step, where the host passed init's checks and Codex is signed in. */
|
|
70
|
+
function localBrowserStep(env, humanish) {
|
|
71
|
+
if (env.localBrowserHost?.ok !== true)
|
|
72
|
+
return undefined;
|
|
73
|
+
if (!env.localAgents.some((agent) => agent.id === "codex"))
|
|
74
|
+
return undefined;
|
|
75
|
+
const prerequisites = env.platform === "darwin"
|
|
76
|
+
? "native ARM64 Node, Lima 2.2+, and a supported signed-in Codex CLI"
|
|
77
|
+
: "local rootful Docker, KVM, TUN, and a supported signed-in Codex CLI";
|
|
78
|
+
return {
|
|
79
|
+
command: `${humanish} doctor --study local-browser`,
|
|
80
|
+
why: `check the local browser study (${prerequisites}); no resources or quota used. Run \`${humanish} runtime setup\` to prepare it, then start your app and run \`${humanish} run local-browser\`; no E2B or model API key`,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
/** Why the local route is not offered, appended to a key step so the reader knows it was considered. */
|
|
84
|
+
function localUnavailableNote(env) {
|
|
85
|
+
const host = env.localBrowserHost;
|
|
86
|
+
if (host === undefined)
|
|
87
|
+
return "";
|
|
88
|
+
if (!host.ok)
|
|
89
|
+
return ` Local browser studies are unavailable on this host: ${host.reason}.`;
|
|
90
|
+
return " A local browser study also runs on this host once Codex is signed in (`codex login`).";
|
|
91
|
+
}
|
|
92
|
+
/** The hosted try-live step, or the key or setup step it still needs. */
|
|
93
|
+
function hostedStep(env, humanish, note) {
|
|
94
|
+
if (!env.hasE2bKey) {
|
|
95
|
+
return {
|
|
96
|
+
command: `${humanish} keys set e2b`,
|
|
97
|
+
why: `the hosted starter needs an E2B desktop.${note}`,
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
if (!env.hasProviderKey && env.localAgents.length === 0) {
|
|
101
|
+
return {
|
|
102
|
+
command: `${humanish} keys set openai`,
|
|
103
|
+
why: "openai-computer-use needs an API key; to use a Codex or Claude Code login instead, sign in and change the study to actor.type: local-agent." +
|
|
104
|
+
note,
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
const brain = env.hasProviderKey
|
|
108
|
+
? "your provider key"
|
|
109
|
+
: `${preferredAgent(env)?.label} (already signed in, so no API key is needed)`;
|
|
110
|
+
// Everything the step needs, in one line. Splitting it across two commands means the second
|
|
111
|
+
// one fails, which is the same dead end this guidance exists to remove.
|
|
112
|
+
if (!env.hasDesktopSdk && env.desktopPeerCommand === undefined) {
|
|
113
|
+
return {
|
|
114
|
+
command: `${humanish} doctor`,
|
|
115
|
+
why: "the hosted starter needs the desktop SDK, and installing it from here would remove what this directory's node_modules holds; doctor names the command to run from your project's directory",
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
// After the peer install, humanish is a dependency of this project, where `npx humanish` finds it.
|
|
119
|
+
const command = env.hasDesktopSdk
|
|
120
|
+
? `${humanish} run try-live`
|
|
121
|
+
: `${env.desktopPeerCommand} && npx humanish run try-live`;
|
|
122
|
+
return {
|
|
123
|
+
command,
|
|
124
|
+
why: `a live study: one participant drives a real app in a hosted desktop, using ${brain}` +
|
|
125
|
+
(env.hasDesktopSdk ? "" : " (the desktop SDK is an optional peer, so it installs first)") +
|
|
126
|
+
(!env.hasProviderKey
|
|
127
|
+
? "; automatic findings analysis is skipped without OPENAI_API_KEY"
|
|
128
|
+
: "") +
|
|
129
|
+
note,
|
|
130
|
+
};
|
|
131
|
+
}
|
|
107
132
|
/** The block init prints after its changes. */
|
|
108
133
|
export function firstRunGuidance(env) {
|
|
109
134
|
const steps = firstRunSteps(env);
|
|
110
135
|
return ["", "next:", ...steps.flatMap((step) => [` ${step.command}`, ` ${step.why}`])];
|
|
111
136
|
}
|
|
112
|
-
/**
|
|
137
|
+
/** Starts humanish's section of `AGENTS.md`, on its heading line. */
|
|
113
138
|
export const AGENTS_SECTION_MARKER = "<!-- humanish:agents-guide -->";
|
|
139
|
+
/** Ends humanish's section, so init can replace the section and nothing after it. */
|
|
140
|
+
export const AGENTS_SECTION_END_MARKER = "<!-- /humanish:agents-guide -->";
|
|
114
141
|
/**
|
|
115
142
|
* What the next coding agent needs to know about humanish in this project.
|
|
116
143
|
*
|
|
@@ -118,8 +145,23 @@ export const AGENTS_SECTION_MARKER = "<!-- humanish:agents-guide -->";
|
|
|
118
145
|
* read on arrival. Increasingly the thing that ran `humanish init` was itself an agent working for
|
|
119
146
|
* someone, and the agent that shows up tomorrow finds a `humanish/` directory with no idea what it
|
|
120
147
|
* is for. Deliberately short and command-first, because an agent acts on the commands it is given.
|
|
148
|
+
* Commands use `humanish`, the prefix init chose for where humanish is installed.
|
|
121
149
|
*/
|
|
122
|
-
export function agentsSection() {
|
|
150
|
+
export function agentsSection(humanish = "npx humanish") {
|
|
151
|
+
const commands = [
|
|
152
|
+
["study list --json", "the studies in this project"],
|
|
153
|
+
["run first-run --no-open", "dry run: no browser, model, keys, or spend"],
|
|
154
|
+
["doctor --study try-live", "what the hosted starter study still needs"],
|
|
155
|
+
[
|
|
156
|
+
"run try-live --no-open",
|
|
157
|
+
"demo app study: E2B plus the selected participant's authentication",
|
|
158
|
+
],
|
|
159
|
+
["doctor --study local-browser", "local Docker/Firecracker + Codex-account readiness"],
|
|
160
|
+
["run local-browser --no-open", "your loopback app; no E2B or model API key"],
|
|
161
|
+
["verify --run <id> --json", "is the evidence share-safe"],
|
|
162
|
+
["review --run <id>", "the run's outcome and its analysis findings"],
|
|
163
|
+
];
|
|
164
|
+
const width = Math.max(...commands.map(([command]) => command.length));
|
|
123
165
|
return [
|
|
124
166
|
"",
|
|
125
167
|
`## humanish ${AGENTS_SECTION_MARKER}`,
|
|
@@ -127,26 +169,28 @@ export function agentsSection() {
|
|
|
127
169
|
"This project uses humanish: synthetic participants use the product and leave evidence.",
|
|
128
170
|
"",
|
|
129
171
|
"```bash",
|
|
130
|
-
|
|
131
|
-
"humanish study list --json # the studies in this project",
|
|
132
|
-
"humanish run first-run # dry run: no browser, model, keys, or spend",
|
|
133
|
-
"humanish doctor --study local-browser # local Docker/Firecracker + Codex-account readiness",
|
|
134
|
-
"humanish run local-browser # your loopback app; no E2B or model API key",
|
|
135
|
-
"humanish run try-live # demo app study: E2B plus the selected participant's authentication",
|
|
136
|
-
"humanish verify --run latest --json # is the evidence share-safe",
|
|
172
|
+
...commands.map(([command, why]) => `${humanish} ${command.padEnd(width)} # ${why}`),
|
|
137
173
|
"```",
|
|
138
174
|
"",
|
|
175
|
+
`- After a live run, run \`${humanish} review --run <id>\` (or \`--json\`) and report its findings`,
|
|
176
|
+
" to the person you are working for: each one's title, impact, confidence, recovery and cited",
|
|
177
|
+
" captures. When it shows no findings, report its message and the command it names. Every run",
|
|
178
|
+
" prints its id; `latest` moves with each run, dry runs included.",
|
|
139
179
|
"- Studies are declared in `humanish/studies/*.yaml`. Edit `try-live.yaml`'s `subject` to point",
|
|
140
180
|
" at this project's own app once you have seen a run work.",
|
|
141
|
-
|
|
181
|
+
`- Configure the local study without editing YAML: \`${humanish} init --yes --local-browser`,
|
|
142
182
|
' http://127.0.0.1:3000 --local-mission "Complete the primary flow"` on first setup.',
|
|
143
183
|
"- Evidence lands in gitignored `.humanish/runs/`. Never commit it, and never paste raw run",
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
"
|
|
147
|
-
"- A live run spends money. `
|
|
184
|
+
` bundles into an issue; \`${humanish} feedback issue\` writes a redacted, share-safe draft.`,
|
|
185
|
+
`- \`${humanish} tui\` is for people and refuses to run in an agent session. Use the commands`,
|
|
186
|
+
" above instead, and tell the person you are working for that the TUI exists.",
|
|
187
|
+
"- A live run spends money. `caps.maxUsd` in each study caps estimated model spend: the run",
|
|
148
188
|
" stops before its next request once the estimate passes it, so the last request can go over, and",
|
|
149
189
|
" hosted desktop time is billed on top. Do not raise it without asking the person you are working for.",
|
|
190
|
+
`- \`${humanish} init --yes\` rewrites this section, between its two markers, to match the installed`,
|
|
191
|
+
" humanish. Write your own notes outside it.",
|
|
192
|
+
"",
|
|
193
|
+
AGENTS_SECTION_END_MARKER,
|
|
150
194
|
"",
|
|
151
195
|
].join("\n");
|
|
152
196
|
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { type HumanishInstall } from "../substrates/e2b/peer-install.js";
|
|
2
|
+
/** The prefix for a suggested command, by install. */
|
|
3
|
+
export declare function invocationFor(install: HumanishInstall, version: string): string;
|
|
4
|
+
/** How this process's suggested commands invoke humanish, read once per process. */
|
|
5
|
+
export declare function humanishCommand(): string;
|
|
6
|
+
/** A suggested command: `cli("review --run x")` is `npx humanish review --run x` in a project. */
|
|
7
|
+
export declare function cli(rest: string): string;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
// How the commands humanish suggests should invoke humanish, chosen from where the running copy is
|
|
2
|
+
// installed. A dev-dependency install puts no `humanish` on `PATH`, so a bare name there reaches a
|
|
3
|
+
// stale global install when one exists, or nothing. `npx humanish` with no version, run where no
|
|
4
|
+
// project declares humanish, also resolves a global install before it fetches one. A one-shot run
|
|
5
|
+
// therefore names its own version, so the next command runs the same CLI.
|
|
6
|
+
import { humanishInstall } from "../substrates/e2b/peer-install.js";
|
|
7
|
+
import { CLI_VERSION } from "./version.js";
|
|
8
|
+
/** The prefix for a suggested command, by install. */
|
|
9
|
+
export function invocationFor(install, version) {
|
|
10
|
+
switch (install.kind) {
|
|
11
|
+
case "project":
|
|
12
|
+
case "other":
|
|
13
|
+
return "npx humanish";
|
|
14
|
+
case "one-shot":
|
|
15
|
+
return `npx humanish@${version}`;
|
|
16
|
+
// A global install is on `PATH`. A source checkout runs through a link or `pnpm humanish`, and
|
|
17
|
+
// keeping the bare name there keeps test output the same on every machine.
|
|
18
|
+
case "global":
|
|
19
|
+
case "checkout":
|
|
20
|
+
return "humanish";
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
let cached;
|
|
24
|
+
/** How this process's suggested commands invoke humanish, read once per process. */
|
|
25
|
+
export function humanishCommand() {
|
|
26
|
+
cached ??= invocationFor(humanishInstall(), CLI_VERSION);
|
|
27
|
+
return cached;
|
|
28
|
+
}
|
|
29
|
+
/** A suggested command: `cli("review --run x")` is `npx humanish review --run x` in a project. */
|
|
30
|
+
export function cli(rest) {
|
|
31
|
+
return `${humanishCommand()} ${rest}`;
|
|
32
|
+
}
|
package/dist/cli/io.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ import { discoverProviderKeys, type DotenvLoad } from "../keys/key-resolution.js
|
|
|
3
3
|
import { type TelemetryProperties } from "./telemetry.js";
|
|
4
4
|
import type { RunResult } from "../run/results.js";
|
|
5
5
|
export declare const CLI_RESPONSE_SCHEMA = "humanish.cli-response.v1";
|
|
6
|
-
export
|
|
6
|
+
export { CLI_VERSION } from "./version.js";
|
|
7
7
|
export interface CliIo {
|
|
8
8
|
writeOut(text: string): void;
|
|
9
9
|
writeErr(text: string): void;
|
|
@@ -41,6 +41,8 @@ export interface StudyCommandOptions {
|
|
|
41
41
|
port?: string | undefined;
|
|
42
42
|
rerunFailedFrom?: string | undefined;
|
|
43
43
|
runId?: string | undefined;
|
|
44
|
+
/** watch only: serve and follow the Observer even without an interactive terminal. */
|
|
45
|
+
serve?: boolean | undefined;
|
|
44
46
|
/** Repo-relative path to an adopter scorer module; overrides review.scorer.ref when set. */
|
|
45
47
|
scorer?: string | undefined;
|
|
46
48
|
expose?: boolean | undefined;
|
package/dist/cli/io.js
CHANGED
|
@@ -1,23 +1,14 @@
|
|
|
1
1
|
import { automaticAnalysisSucceeded, defaultAnalysisOverBudget, } from "../analysis/automatic-completion.js";
|
|
2
2
|
import { DEFAULT_ANALYSIS_MAX_COST_USD } from "../analysis/automatic-config.js";
|
|
3
|
-
import { readFileSync } from "node:fs";
|
|
4
|
-
import { dirname, resolve } from "node:path";
|
|
5
|
-
import { fileURLToPath } from "node:url";
|
|
6
3
|
import { Command, Option } from "commander";
|
|
7
|
-
import { loadEnvFile } from "../keys/env-file.js";
|
|
4
|
+
import { loadEnvFile, recordDotenvNames } from "../keys/env-file.js";
|
|
8
5
|
import { discoverProviderKeys } from "../keys/key-resolution.js";
|
|
9
6
|
import { deriveRunFacts } from "./telemetry.js";
|
|
10
7
|
import { warnAndQueue, withQueuedWarnings } from "./deprecations.js";
|
|
11
8
|
import { forTerminal } from "../routes/terminal/encoding.js";
|
|
9
|
+
import { cli } from "./invocation.js";
|
|
12
10
|
export const CLI_RESPONSE_SCHEMA = "humanish.cli-response.v1";
|
|
13
|
-
|
|
14
|
-
const packageJsonPath = resolve(dirname(fileURLToPath(import.meta.url)), "..", "..", "package.json");
|
|
15
|
-
const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf8"));
|
|
16
|
-
return typeof packageJson.version === "string" && packageJson.version.trim()
|
|
17
|
-
? packageJson.version
|
|
18
|
-
: "0.0.0";
|
|
19
|
-
}
|
|
20
|
-
export const CLI_VERSION = readCliVersion();
|
|
11
|
+
export { CLI_VERSION } from "./version.js";
|
|
21
12
|
// One text per shared flag, so the same flag reads the same on every command.
|
|
22
13
|
// tests/cli/shared-options.test.ts walks the program and holds every use to these.
|
|
23
14
|
export const JSON_OPTION_DESCRIPTION = "Print a machine-readable JSON response.";
|
|
@@ -90,6 +81,7 @@ export async function applyEnvFileOption(args) {
|
|
|
90
81
|
}
|
|
91
82
|
for (const name of result.loaded)
|
|
92
83
|
env[name] = stagedEnv[name];
|
|
84
|
+
recordDotenvNames(env, result.loaded);
|
|
93
85
|
args.onLoaded?.({ path: args.envFile, names: result.loaded });
|
|
94
86
|
}
|
|
95
87
|
if (args.discoverKeys !== false) {
|
|
@@ -146,11 +138,12 @@ export function parseParticipantIds(value) {
|
|
|
146
138
|
* command: the run-not-found and missing-study messages do.
|
|
147
139
|
*/
|
|
148
140
|
const NEXT_COMMAND = {
|
|
149
|
-
HUMANISH_STUDY_NOT_FOUND: "
|
|
141
|
+
HUMANISH_STUDY_NOT_FOUND: "study list",
|
|
150
142
|
};
|
|
151
143
|
/** `<command> failed: <message>`, then `code: <CODE>`, then `next: <command>` when one is known. */
|
|
152
144
|
export function formatCliError(command, error) {
|
|
153
|
-
const
|
|
145
|
+
const rest = error.code === undefined ? undefined : NEXT_COMMAND[error.code];
|
|
146
|
+
const next = rest === undefined ? undefined : cli(rest);
|
|
154
147
|
return ([
|
|
155
148
|
`${command} failed: ${error.message}`,
|
|
156
149
|
...(error.code === undefined ? [] : [`code: ${error.code}`]),
|
|
@@ -315,7 +308,7 @@ function overBudgetAnalysisHint(result) {
|
|
|
315
308
|
const estimate = result.automaticAnalysis?.result?.admission?.estimatedCostUsd ?? null;
|
|
316
309
|
const suggested = Math.ceil(estimate ?? DEFAULT_ANALYSIS_MAX_COST_USD + 1);
|
|
317
310
|
const shown = estimate === null ? "" : ` ($${estimate.toFixed(2)})`;
|
|
318
|
-
return `analysis: its estimate${shown} is over the default $${DEFAULT_ANALYSIS_MAX_COST_USD} cap, so no request was sent. To analyze this run:
|
|
311
|
+
return `analysis: its estimate${shown} is over the default $${DEFAULT_ANALYSIS_MAX_COST_USD} cap, so no request was sent. To analyze this run: ${cli(`analyze --run ${result.runId ?? "latest"} --max-cost ${suggested}`)}\n`;
|
|
319
312
|
}
|
|
320
313
|
export function automaticAnalysisEnvelope(result) {
|
|
321
314
|
if (result === null ||
|
|
@@ -8,7 +8,26 @@ export interface ObserverPlan {
|
|
|
8
8
|
/** Open the page: the served URL when following, the static file otherwise. */
|
|
9
9
|
shouldOpen: boolean;
|
|
10
10
|
wantsFollow: boolean;
|
|
11
|
+
/** Set when the command would have followed, but nobody is at a terminal to stop it. */
|
|
12
|
+
unattended?: boolean;
|
|
11
13
|
}
|
|
14
|
+
/**
|
|
15
|
+
* Whether a person can stop a served Observer: stdin and stdout are terminals, and no agent runner
|
|
16
|
+
* claims the session. An agent's shell or a pipe has nobody to press Ctrl-C, so a server there
|
|
17
|
+
* waits forever. A TTY alone is not enough, since Codex gives the commands it runs a terminal.
|
|
18
|
+
*/
|
|
19
|
+
export declare function personAtTerminal(env?: NodeJS.ProcessEnv): boolean;
|
|
20
|
+
/** Whether `watch` or `observe` serves the Observer and stays attached. */
|
|
21
|
+
export declare function followDecision(args: {
|
|
22
|
+
wantsMachine: boolean;
|
|
23
|
+
detach?: boolean | undefined;
|
|
24
|
+
serve?: boolean | undefined;
|
|
25
|
+
}): {
|
|
26
|
+
follow: boolean;
|
|
27
|
+
unattended: boolean;
|
|
28
|
+
};
|
|
29
|
+
/** The warning a command adds when it printed the Observer and exited because nobody was attached. */
|
|
30
|
+
export declare function unattendedObserverWarning(command: "watch" | "observe"): string;
|
|
12
31
|
/**
|
|
13
32
|
* Parse --port and decide open and follow. On an invalid port it writes the failure, sets exit
|
|
14
33
|
* code 2 and returns null, so a caller refuses before starting a run.
|
|
@@ -20,6 +39,7 @@ export declare function planObserver(args: {
|
|
|
20
39
|
io: CliIo;
|
|
21
40
|
open?: boolean | undefined;
|
|
22
41
|
port: string;
|
|
42
|
+
serve?: boolean | undefined;
|
|
23
43
|
}): ObserverPlan | null;
|
|
24
44
|
/** The `open` a run's static Observer render takes: a followed Observer opens its served URL. */
|
|
25
45
|
export declare function staticObserverOpen(plan: ObserverPlan): boolean;
|
|
@@ -1,6 +1,32 @@
|
|
|
1
1
|
import { Command } from "commander";
|
|
2
2
|
import { serveObserver } from "../observer/render.js";
|
|
3
|
+
import { detectAgentSession } from "../actors/agent-session.js";
|
|
3
4
|
import { formatRunHuman, parseObserverPort, wantsJson, writeResult, humanError, } from "./io.js";
|
|
5
|
+
/**
|
|
6
|
+
* Whether a person can stop a served Observer: stdin and stdout are terminals, and no agent runner
|
|
7
|
+
* claims the session. An agent's shell or a pipe has nobody to press Ctrl-C, so a server there
|
|
8
|
+
* waits forever. A TTY alone is not enough, since Codex gives the commands it runs a terminal.
|
|
9
|
+
*/
|
|
10
|
+
export function personAtTerminal(env = process.env) {
|
|
11
|
+
return (process.stdin.isTTY === true &&
|
|
12
|
+
process.stdout.isTTY === true &&
|
|
13
|
+
detectAgentSession(env) === undefined);
|
|
14
|
+
}
|
|
15
|
+
/** Whether `watch` or `observe` serves the Observer and stays attached. */
|
|
16
|
+
export function followDecision(args) {
|
|
17
|
+
if (args.detach === true)
|
|
18
|
+
return { follow: false, unattended: false };
|
|
19
|
+
if (args.serve === true)
|
|
20
|
+
return { follow: true, unattended: false };
|
|
21
|
+
if (args.wantsMachine)
|
|
22
|
+
return { follow: false, unattended: false };
|
|
23
|
+
const attended = personAtTerminal();
|
|
24
|
+
return { follow: attended, unattended: !attended };
|
|
25
|
+
}
|
|
26
|
+
/** The warning a command adds when it printed the Observer and exited because nobody was attached. */
|
|
27
|
+
export function unattendedObserverWarning(command) {
|
|
28
|
+
return `No interactive terminal, so ${command} printed the Observer path and exited instead of serving it until Ctrl-C. Open the file, or pass --serve to keep serving.`;
|
|
29
|
+
}
|
|
4
30
|
/**
|
|
5
31
|
* Parse --port and decide open and follow. On an invalid port it writes the failure, sets exit
|
|
6
32
|
* code 2 and returns null, so a caller refuses before starting a run.
|
|
@@ -28,7 +54,13 @@ export function planObserver(args) {
|
|
|
28
54
|
: args.open === true
|
|
29
55
|
? true
|
|
30
56
|
: !wantsMachine && process.stdout.isTTY === true;
|
|
31
|
-
|
|
57
|
+
const decision = followDecision({ wantsMachine, detach: args.detach, serve: args.serve });
|
|
58
|
+
return {
|
|
59
|
+
port,
|
|
60
|
+
shouldOpen,
|
|
61
|
+
wantsFollow: decision.follow,
|
|
62
|
+
...(decision.unattended ? { unattended: true } : {}),
|
|
63
|
+
};
|
|
32
64
|
}
|
|
33
65
|
/** The `open` a run's static Observer render takes: a followed Observer opens its served URL. */
|
|
34
66
|
export function staticObserverOpen(plan) {
|
|
@@ -43,6 +75,9 @@ export async function showObserver(args) {
|
|
|
43
75
|
server = await serveObserver(rendered, { open: plan.shouldOpen, port: plan.port });
|
|
44
76
|
result = withObserverServer(rendered, server);
|
|
45
77
|
}
|
|
78
|
+
else if (rendered.ok && plan.unattended === true) {
|
|
79
|
+
result = { ...rendered, warnings: [...rendered.warnings, unattendedObserverWarning("watch")] };
|
|
80
|
+
}
|
|
46
81
|
writeResult(args.command, args.io, result, formatObserverHuman);
|
|
47
82
|
args.io.setExitCode(result.ok ? 0 : 2);
|
|
48
83
|
if (result.ok && server) {
|
package/dist/cli/orientation.js
CHANGED
|
@@ -17,6 +17,7 @@ import { listRuns } from "../run/stored-runs.js";
|
|
|
17
17
|
import { plural } from "../run/text.js";
|
|
18
18
|
import { supportsLocalBrowser } from "./first-run-path.js";
|
|
19
19
|
import { PRODUCT_SENTENCE } from "./product-sentence.js";
|
|
20
|
+
import { cli } from "./invocation.js";
|
|
20
21
|
export const ORIENTATION_SCHEMA = "humanish.orientation.v1";
|
|
21
22
|
/**
|
|
22
23
|
* Read the project's state. Pure-ish: it only reads, never writes, and never touches the network or
|
|
@@ -69,7 +70,7 @@ function nextCommandsFor(args) {
|
|
|
69
70
|
? []
|
|
70
71
|
: [
|
|
71
72
|
{
|
|
72
|
-
command: "
|
|
73
|
+
command: cli("migrate --dry-run"),
|
|
73
74
|
why: `${plural(v2, "study file")} ${v2 === 1 ? "uses" : "use"} humanish.lab.v2, which humanish no longer reads; this lists the conversion, and humanish migrate writes it`,
|
|
74
75
|
},
|
|
75
76
|
]),
|
|
@@ -77,41 +78,42 @@ function nextCommandsFor(args) {
|
|
|
77
78
|
? []
|
|
78
79
|
: [
|
|
79
80
|
{
|
|
80
|
-
command: "
|
|
81
|
+
command: cli("study list"),
|
|
81
82
|
why: `${plural(moved, "study file")} ${moved === 1 ? "is" : "are"} in a labs/ directory, which humanish no longer reads; this names each one and the studies/ directory to move it to`,
|
|
82
83
|
},
|
|
83
84
|
]),
|
|
84
85
|
];
|
|
85
|
-
// A fix that is already `
|
|
86
|
+
// A fix that is already `study list` replaces the plain suggestion of the same command.
|
|
86
87
|
const named = new Set(fixes.map((fix) => fix.command));
|
|
87
88
|
return [...fixes, ...nextCommandsForStudies(args).filter((next) => !named.has(next.command))];
|
|
88
89
|
}
|
|
89
90
|
function nextCommandsForStudies(args) {
|
|
90
91
|
const dryRun = {
|
|
91
|
-
command: "
|
|
92
|
+
command: cli("run first-run"),
|
|
92
93
|
why: "a dry run: no browser or model runs, no keys, no spend",
|
|
93
94
|
};
|
|
94
95
|
if (!args.initialized) {
|
|
95
96
|
return [
|
|
96
97
|
{
|
|
97
|
-
command: "
|
|
98
|
+
command: cli("init --yes"),
|
|
98
99
|
why: "write starter studies, personas and an AGENTS.md (--dry-run lists every file first)",
|
|
99
100
|
},
|
|
100
101
|
dryRun,
|
|
101
102
|
];
|
|
102
103
|
}
|
|
103
|
-
// The starter live study this host can run
|
|
104
|
-
//
|
|
104
|
+
// The starter live study this host can run: the hosted try-live study first, since it needs no
|
|
105
|
+
// local Docker or VM, then the local one where the host shape supports it. The other starter
|
|
106
|
+
// studies are templates whose subject still names your-org/your-app, so they are never suggested.
|
|
105
107
|
const liveStudy = (supportsLocalBrowser(args.host.platform, args.host.arch)
|
|
106
|
-
? ["
|
|
108
|
+
? ["try-live", "local-browser"]
|
|
107
109
|
: ["try-live"]).find((id) => args.studyIds.includes(id));
|
|
108
110
|
if (!args.hasRun) {
|
|
109
111
|
return [
|
|
110
112
|
dryRun,
|
|
111
113
|
liveStudy === undefined
|
|
112
|
-
? { command: "
|
|
114
|
+
? { command: cli("study list"), why: "see the studies this project declares" }
|
|
113
115
|
: {
|
|
114
|
-
command: `
|
|
116
|
+
command: cli(`doctor --study ${liveStudy}`),
|
|
115
117
|
why: `check what the ${liveStudy} study still needs before a live run`,
|
|
116
118
|
},
|
|
117
119
|
];
|
|
@@ -119,12 +121,12 @@ function nextCommandsForStudies(args) {
|
|
|
119
121
|
return [
|
|
120
122
|
...(liveStudy === undefined
|
|
121
123
|
? []
|
|
122
|
-
: [{ command: `
|
|
124
|
+
: [{ command: cli(`run ${liveStudy}`), why: "run a real participant against an app" }]),
|
|
123
125
|
{
|
|
124
|
-
command: "
|
|
126
|
+
command: cli("verify --run latest"),
|
|
125
127
|
why: "check the last run's evidence and public-safety gates",
|
|
126
128
|
},
|
|
127
|
-
{ command: "
|
|
129
|
+
{ command: cli("observe --run latest"), why: "reopen the last run's Observer" },
|
|
128
130
|
];
|
|
129
131
|
}
|
|
130
132
|
/**
|
|
@@ -151,6 +153,6 @@ export function formatOrientationHuman(state) {
|
|
|
151
153
|
lines.push(` ${next.why}`);
|
|
152
154
|
}
|
|
153
155
|
lines.push("");
|
|
154
|
-
lines.push("
|
|
156
|
+
lines.push(`\`${cli("--help")}\` lists every command.`);
|
|
155
157
|
return `${lines.join("\n")}\n`;
|
|
156
158
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The product in one sentence.
|
|
3
|
-
*
|
|
4
|
-
* equal.
|
|
2
|
+
* The product in one sentence. package.json's description, `humanish --help` and bare `humanish`
|
|
3
|
+
* use it, and line 3 of `README.md` and the humanish skill's description open with it;
|
|
4
|
+
* tests/cli/product-sentence.test.ts holds them equal.
|
|
5
5
|
*/
|
|
6
6
|
export declare const PRODUCT_SENTENCE = "Synthetic user research for apps, CLIs, and agent-facing product flows.";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The product in one sentence.
|
|
3
|
-
*
|
|
4
|
-
* equal.
|
|
2
|
+
* The product in one sentence. package.json's description, `humanish --help` and bare `humanish`
|
|
3
|
+
* use it, and line 3 of `README.md` and the humanish skill's description open with it;
|
|
4
|
+
* tests/cli/product-sentence.test.ts holds them equal.
|
|
5
5
|
*/
|
|
6
6
|
export const PRODUCT_SENTENCE = "Synthetic user research for apps, CLIs, and agent-facing product flows.";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const CLI_VERSION: string;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
import { dirname, resolve } from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
function readCliVersion() {
|
|
5
|
+
const packageJsonPath = resolve(dirname(fileURLToPath(import.meta.url)), "..", "..", "package.json");
|
|
6
|
+
const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf8"));
|
|
7
|
+
return typeof packageJson.version === "string" && packageJson.version.trim()
|
|
8
|
+
? packageJson.version
|
|
9
|
+
: "0.0.0";
|
|
10
|
+
}
|
|
11
|
+
export const CLI_VERSION = readCliVersion();
|
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
/** Undoes JSON and JS escapes, percent-encoding and HTML character references, one pass each. */
|
|
2
2
|
export declare function decodeEscapes(text: string): string;
|
|
3
|
+
/** How scanEncodedText reads text. */
|
|
4
|
+
export interface EncodedTextScanOptions {
|
|
5
|
+
/** Do not count an encoded binary run as opaque. */
|
|
6
|
+
readonly allowOpaqueBase64?: boolean;
|
|
7
|
+
/** Match secret shapes only, leaving local paths out, as a URL's path may look like one. */
|
|
8
|
+
readonly secretsOnly?: boolean;
|
|
9
|
+
}
|
|
3
10
|
export interface EncodedTextScan {
|
|
4
11
|
/** A secret or private path, in the text or in a decoding of it. */
|
|
5
12
|
readonly sensitive: boolean;
|
|
@@ -12,9 +19,7 @@ export interface EncodedTextScan {
|
|
|
12
19
|
* turn. A run that decodes to an archive, or to other binary past MIN_OPAQUE_BASE64_RUN characters,
|
|
13
20
|
* is opaque unless the caller allows opaque runs.
|
|
14
21
|
*/
|
|
15
|
-
export declare function scanEncodedText(text: string, options?:
|
|
16
|
-
allowOpaqueBase64?: boolean;
|
|
17
|
-
}, depth?: number): EncodedTextScan;
|
|
22
|
+
export declare function scanEncodedText(text: string, options?: EncodedTextScanOptions, depth?: number): EncodedTextScan;
|
|
18
23
|
/**
|
|
19
24
|
* scanEncodedText, cached in this process. The key is the sha256 of the scanned string's UTF-16
|
|
20
25
|
* code units, the options and ENCODED_SCAN_VERSION. Encoding as UTF-16LE keeps every code unit,
|
|
@@ -22,6 +27,4 @@ export declare function scanEncodedText(text: string, options?: {
|
|
|
22
27
|
* scan of the same string would give. Repeated verifies of an unchanged file, as serve admission
|
|
23
28
|
* and the Observer render do, skip the scan.
|
|
24
29
|
*/
|
|
25
|
-
export declare function scanEncodedTextCached(text: string, options?:
|
|
26
|
-
allowOpaqueBase64?: boolean;
|
|
27
|
-
}): EncodedTextScan;
|
|
30
|
+
export declare function scanEncodedTextCached(text: string, options?: EncodedTextScanOptions): EncodedTextScan;
|