humanish 0.112.0 → 0.114.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 +41 -0
- package/CONTEXT.md +1 -0
- package/README.md +12 -1
- package/TELEMETRY.md +21 -0
- package/dist/actors/codex/restricted-participant-policy.d.ts +21 -1
- package/dist/actors/codex/restricted-participant-policy.js +20 -2
- package/dist/actors/codex/restricted-participant.d.ts +1 -1
- package/dist/actors/codex/restricted-participant.js +2 -1
- package/dist/actors/computer-use/loop/debrief.d.ts +12 -4
- package/dist/actors/computer-use/loop/debrief.js +57 -20
- package/dist/actors/computer-use/loop/ending.d.ts +5 -2
- package/dist/actors/computer-use/loop/impressions.d.ts +14 -0
- package/dist/actors/computer-use/loop/impressions.js +40 -0
- package/dist/actors/computer-use/loop/session.d.ts +3 -1
- package/dist/actors/computer-use/loop/session.js +2 -0
- package/dist/actors/computer-use/loop/trace.js +2 -0
- package/dist/actors/computer-use/loop/types.d.ts +8 -1
- package/dist/actors/computer-use/loop.d.ts +2 -1
- package/dist/actors/computer-use/loop.js +15 -3
- package/dist/actors/computer-use/openai-conversation.d.ts +9 -4
- package/dist/actors/computer-use/openai-conversation.js +60 -29
- package/dist/actors/computer-use/openai-provider.js +18 -12
- package/dist/actors/contract.d.ts +47 -5
- package/dist/actors/contract.js +13 -0
- package/dist/analysis/admission.d.ts +37 -0
- package/dist/analysis/admission.js +60 -0
- package/dist/analysis/automatic-completion.d.ts +0 -6
- package/dist/analysis/automatic-completion.js +3 -3
- package/dist/analysis/automatic-config.d.ts +10 -2
- package/dist/analysis/automatic-config.js +29 -14
- package/dist/analysis/automatic.js +7 -0
- package/dist/analysis/evidence-sources.d.ts +3 -1
- package/dist/analysis/evidence-sources.js +10 -1
- package/dist/analysis/evidence.js +2 -2
- package/dist/analysis/execute.d.ts +20 -6
- package/dist/analysis/execute.js +61 -28
- package/dist/analysis/image-tokens.d.ts +2 -0
- package/dist/analysis/image-tokens.js +1 -1
- package/dist/analysis/job.d.ts +9 -1
- package/dist/analysis/job.js +11 -2
- package/dist/analysis/service.js +6 -3
- package/dist/cli/commands/analyze.js +7 -2
- package/dist/cli/commands/notes.d.ts +3 -0
- package/dist/cli/commands/notes.js +144 -0
- package/dist/cli/commands/observe.js +1 -1
- package/dist/cli/commands/runs.js +33 -10
- package/dist/cli/commands/setup.js +95 -6
- package/dist/cli/commands/study-format.js +7 -4
- package/dist/cli/commands/study-run.js +2 -1
- package/dist/cli/commands/study.js +1 -1
- package/dist/cli/commands/tui.js +1 -1
- package/dist/cli/doctor.d.ts +2 -0
- package/dist/cli/doctor.js +18 -0
- package/dist/cli/findings.js +4 -1
- package/dist/cli/io.d.ts +3 -0
- package/dist/cli/io.js +11 -12
- package/dist/cli/program.js +37 -1
- package/dist/cli/secret-prompt.d.ts +5 -1
- package/dist/cli/secret-prompt.js +6 -2
- package/dist/cli/update-check-worker.d.ts +1 -0
- package/dist/cli/update-check-worker.js +5 -0
- package/dist/cli/update-check.d.ts +46 -0
- package/dist/cli/update-check.js +203 -0
- package/dist/evidence/encoded-text.d.ts +15 -2
- package/dist/evidence/encoded-text.js +68 -4
- package/dist/evidence/secret-scrub.d.ts +1 -1
- package/dist/evidence/secret-scrub.js +23 -2
- package/dist/feedback/draft.d.ts +19 -0
- package/dist/feedback/draft.js +69 -1
- package/dist/feedback/export-bundle.js +2 -2
- package/dist/feedback/export.js +47 -3
- package/dist/feedback/feedback.js +42 -2
- package/dist/keys/key-resolution.js +2 -0
- package/dist/keys/key-status.d.ts +37 -0
- package/dist/keys/key-status.js +68 -0
- package/dist/observer/artifact.d.ts +5 -0
- package/dist/observer/artifact.js +18 -2
- package/dist/observer/notes-route.d.ts +21 -0
- package/dist/observer/notes-route.js +140 -0
- package/dist/observer/pinned-files.d.ts +8 -1
- package/dist/observer/pinned-files.js +32 -3
- package/dist/observer/render.js +38 -14
- package/dist/observer/run-routes.d.ts +8 -2
- package/dist/observer/run-routes.js +47 -6
- package/dist/observer/serve.d.ts +3 -0
- package/dist/observer/serve.js +28 -3
- package/dist/observer-app.html +7 -6
- package/dist/routes/computer-use/local-vm.js +9 -2
- package/dist/routes/computer-use/participant-desktop.d.ts +6 -0
- package/dist/routes/computer-use/participant-execution.js +9 -1
- package/dist/routes/computer-use/rerun-selection.js +23 -21
- package/dist/routes/computer-use/result.js +3 -2
- package/dist/routes/computer-use/self-report.js +7 -1
- package/dist/routes/computer-use/setup.js +14 -0
- package/dist/routes/computer-use/types.d.ts +4 -1
- package/dist/routes/shared-world/bundle-records.js +8 -7
- package/dist/routes/shared-world/bundle.js +1 -1
- package/dist/run/contained-output.d.ts +7 -0
- package/dist/run/contained-output.js +35 -1
- package/dist/run/detail.js +4 -4
- package/dist/run/note-files.d.ts +73 -0
- package/dist/run/note-files.js +184 -0
- package/dist/run/notes.d.ts +37 -0
- package/dist/run/notes.js +198 -0
- package/dist/run/participant-caption.d.ts +11 -1
- package/dist/run/participant-caption.js +30 -1
- package/dist/run/participant-records.d.ts +3 -1
- package/dist/run/participant-records.js +6 -0
- package/dist/run-study.js +17 -1
- package/dist/study/discover.js +6 -1
- package/dist/study/doctor.d.ts +2 -0
- package/dist/study/doctor.js +46 -8
- package/dist/study/init-templates.js +10 -5
- package/dist/study/plan.js +3 -2
- package/dist/study/preflight-result.d.ts +1 -0
- package/dist/study/preflight-result.js +3 -2
- package/dist/study/preflight.d.ts +2 -0
- package/dist/study/preflight.js +54 -14
- package/dist/study/summary.js +2 -2
- package/dist/study/warnings.d.ts +5 -0
- package/dist/study/warnings.js +79 -2
- package/dist/substrates/local/capacity.d.ts +75 -0
- package/dist/substrates/local/capacity.js +143 -0
- package/dist/substrates/local/firecracker-desktop.d.ts +8 -0
- package/dist/substrates/local/firecracker-desktop.js +34 -3
- package/dist/substrates/local/runtime-host.d.ts +16 -3
- package/dist/substrates/local/runtime-host.js +49 -4
- package/dist/substrates/local/runtime.d.ts +10 -0
- package/dist/substrates/local/runtime.js +44 -5
- package/dist/tui-app.js +58 -58
- package/docs/architecture/actor-contract.md +20 -6
- package/docs/contracts/errors.md +2 -2
- package/docs/contracts/feedback.md +8 -1
- package/docs/contracts/run-bundle.md +57 -0
- package/docs/contracts/schemas.md +62 -36
- package/docs/contracts/study-analysis.md +41 -8
- package/docs/principles/invariants-and-defaults.md +2 -2
- package/docs/product/automatic-analysis.md +22 -16
- package/package.json +2 -2
- package/skills/humanish/SKILL.md +26 -10
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
|
-
import { Command, Option } from "commander";
|
|
2
|
+
import { Command, InvalidArgumentError, Option } from "commander";
|
|
3
3
|
import { shellArg } from "../../substrates/shell.js";
|
|
4
4
|
import { computeStats, formatStatsHuman } from "../../run/stats.js";
|
|
5
5
|
import { DEFAULT_EXPORT_MAX_BYTES, exportRun, formatExportHuman } from "../../feedback/export.js";
|
|
@@ -8,7 +8,7 @@ import { verifyRun } from "../../verify/verify.js";
|
|
|
8
8
|
import { reclaimPreflightSandboxes, reclaimRunSandboxes, } from "../../run/reclaim.js";
|
|
9
9
|
import { runDisplay } from "../../run/display.js";
|
|
10
10
|
import { addRunOptions, handleRun } from "./run-command.js";
|
|
11
|
-
import { applyEnvFileOption, CWD_OPTION_DESCRIPTION, DOTENV_OPTION_DESCRIPTION, dotenvPathOf, envFileAliasOption, JSON_OPTION_DESCRIPTION, RUN_OPTION_DESCRIPTION, writeResult, humanError, } from "../io.js";
|
|
11
|
+
import { applyEnvFileOption, CWD_OPTION_DESCRIPTION, DOTENV_OPTION_DESCRIPTION, dotenvPathOf, envFileAliasOption, JSON_OPTION_DESCRIPTION, parsePositiveInteger, RUN_OPTION_DESCRIPTION, writeResult, humanError, } from "../io.js";
|
|
12
12
|
import { plural } from "../../run/text.js";
|
|
13
13
|
import { warnAndQueue } from "../deprecations.js";
|
|
14
14
|
import { formatFindings, readRunFindings } from "../findings.js";
|
|
@@ -136,29 +136,52 @@ export function registerRunsCommand(parent, io) {
|
|
|
136
136
|
io.setExitCode(result.ok ? 0 : 2);
|
|
137
137
|
});
|
|
138
138
|
}
|
|
139
|
+
/** A whole number of GiB or CPUs above zero, for `runtime setup --memory` and `--cpus`. */
|
|
140
|
+
function wholeNumber(flag) {
|
|
141
|
+
return (value) => {
|
|
142
|
+
const parsed = parsePositiveInteger(value);
|
|
143
|
+
if (parsed === null)
|
|
144
|
+
throw new InvalidArgumentError(`${flag} takes a whole number above zero, such as 8.`);
|
|
145
|
+
return parsed;
|
|
146
|
+
};
|
|
147
|
+
}
|
|
139
148
|
export function registerRuntimeCommands(parent, io) {
|
|
140
149
|
const runtime = parent
|
|
141
150
|
.command("runtime")
|
|
142
151
|
.description("Prepare or inspect the local browser runtime.");
|
|
143
152
|
for (const action of ["status", "setup"]) {
|
|
144
|
-
runtime
|
|
153
|
+
const command = runtime
|
|
145
154
|
.command(action)
|
|
146
155
|
.description(action === "status"
|
|
147
|
-
? "Check local Docker, virtualization and the cached browser image without downloads."
|
|
156
|
+
? "Check local Docker, virtualization and the cached browser image without downloads, and how many participant desktops fit."
|
|
148
157
|
: "Download and install the local browser image. Does not start a study or use model quota.")
|
|
149
158
|
.option("--json", JSON_OPTION_DESCRIPTION)
|
|
150
|
-
.option("--media", "Prepare or inspect the optional camera and speech runtime.")
|
|
151
|
-
|
|
159
|
+
.option("--media", "Prepare or inspect the optional camera and speech runtime.");
|
|
160
|
+
if (action === "setup")
|
|
161
|
+
command
|
|
162
|
+
.option("--memory <GiB>", "Mac only: the humanish Lima VM's memory. Resizes an existing VM, which stops it first.", wholeNumber("--memory"))
|
|
163
|
+
.option("--cpus <n>", "Mac only: the humanish Lima VM's CPUs. Resizes an existing VM, which stops it first.", wholeNumber("--cpus"));
|
|
164
|
+
command.action(async (options, command) => {
|
|
152
165
|
const { localRuntimeStatus, prepareLocalRuntime } = await import("../../substrates/local/runtime.js");
|
|
166
|
+
const { describeCapacity } = await import("../../substrates/local/capacity.js");
|
|
167
|
+
const media = options.media === true;
|
|
153
168
|
try {
|
|
154
|
-
if (action === "setup")
|
|
169
|
+
if (action === "setup") {
|
|
170
|
+
const size = {
|
|
171
|
+
...(options.memory === undefined ? {} : { memoryGiB: options.memory }),
|
|
172
|
+
...(options.cpus === undefined ? {} : { cpus: options.cpus }),
|
|
173
|
+
};
|
|
155
174
|
await prepareLocalRuntime({
|
|
156
|
-
media
|
|
175
|
+
media,
|
|
176
|
+
...(Object.keys(size).length === 0 ? {} : { size }),
|
|
157
177
|
progress: (message) => io.writeErr(`${message}\n`),
|
|
158
178
|
});
|
|
159
|
-
|
|
179
|
+
}
|
|
180
|
+
const status = await localRuntimeStatus({ media });
|
|
160
181
|
const result = { schema: "humanish.runtime-result.v1", ...status };
|
|
161
|
-
writeResult(command, io, result, () =>
|
|
182
|
+
writeResult(command, io, result, () => [status.message, ...(status.capacity ? [describeCapacity(status.capacity)] : [])]
|
|
183
|
+
.map((line) => `${line}\n`)
|
|
184
|
+
.join(""));
|
|
162
185
|
io.setExitCode(status.ok ? 0 : 2);
|
|
163
186
|
}
|
|
164
187
|
catch (error) {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Command } from "commander";
|
|
2
2
|
import { listUserKeys, resolveKeyName, setUserKey, unsetUserKey, userKeyStorePath, } from "../../keys/key-resolution.js";
|
|
3
|
+
import { askForMissingKeys, formatKeyStatus, keyStatus, otherStoredKeys, } from "../../keys/key-status.js";
|
|
3
4
|
import { promptSecret } from "../secret-prompt.js";
|
|
4
5
|
import { runInit } from "../../study/init.js";
|
|
5
6
|
import { buildPayload, disabledByEnvironment, readTelemetryState, telemetryStatePath, writeTelemetryState, } from "../telemetry.js";
|
|
@@ -177,20 +178,107 @@ async function readSecretValue(useStdin, promptLabel) {
|
|
|
177
178
|
const text = Buffer.concat(chunks).toString("utf8").trim();
|
|
178
179
|
return text.length > 0 ? text : null;
|
|
179
180
|
}
|
|
180
|
-
|
|
181
|
+
// An empty line is no value here: only the walk over missing keys reads it as "skip".
|
|
182
|
+
return (await promptSecret(promptLabel, process.stdin, process.stderr)) || null;
|
|
183
|
+
}
|
|
184
|
+
/** `humanish keys` and `humanish keys status`: each provider key's source, never a value. */
|
|
185
|
+
async function writeKeyStatus(command, io) {
|
|
186
|
+
const rows = await keyStatus({ cwd: process.cwd(), env: process.env });
|
|
187
|
+
const others = otherStoredKeys(process.env);
|
|
188
|
+
const missing = rows.filter((row) => row.source === null).length;
|
|
189
|
+
const result = {
|
|
190
|
+
schema: KEYS_RESULT_SCHEMA,
|
|
191
|
+
ok: true,
|
|
192
|
+
action: "status",
|
|
193
|
+
store: userKeyStorePath(process.env),
|
|
194
|
+
names: rows.filter((row) => row.source !== null).map((row) => row.name),
|
|
195
|
+
keys: rows.map(({ name, use, source, hint }) => ({
|
|
196
|
+
name,
|
|
197
|
+
use,
|
|
198
|
+
source,
|
|
199
|
+
...(source === null ? { hint } : {}),
|
|
200
|
+
})),
|
|
201
|
+
...(others.length > 0 ? { otherStored: others } : {}),
|
|
202
|
+
message: missing === 0
|
|
203
|
+
? "Every provider key is set."
|
|
204
|
+
: `${plural(missing, "provider key")} ${missing === 1 ? "is" : "are"} missing.`,
|
|
205
|
+
};
|
|
206
|
+
writeResult(command, io, result, () => formatKeyStatus(rows, others));
|
|
207
|
+
io.setExitCode(0);
|
|
208
|
+
}
|
|
209
|
+
/** "A", "A and B", "A, B and C". */
|
|
210
|
+
function listNames(names) {
|
|
211
|
+
return names.length < 2 ? names.join("") : `${names.slice(0, -1).join(", ")} and ${names.at(-1)}`;
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* `humanish keys set` with no key named. On a terminal it asks for each missing provider key in
|
|
215
|
+
* turn; without one there is no one to ask, so it refuses and names the --stdin form.
|
|
216
|
+
*/
|
|
217
|
+
async function setMissingKeys(command, io, useStdin) {
|
|
218
|
+
const store = userKeyStorePath(process.env);
|
|
219
|
+
const finish = (ok, names, message) => {
|
|
220
|
+
const result = {
|
|
221
|
+
schema: KEYS_RESULT_SCHEMA,
|
|
222
|
+
ok,
|
|
223
|
+
action: "set",
|
|
224
|
+
store,
|
|
225
|
+
names,
|
|
226
|
+
message,
|
|
227
|
+
};
|
|
228
|
+
writeResult(command, io, result, formatKeysHuman);
|
|
229
|
+
io.setExitCode(ok ? 0 : 2);
|
|
230
|
+
};
|
|
231
|
+
if (useStdin || process.stdin.isTTY !== true || process.stderr.isTTY !== true) {
|
|
232
|
+
finish(false, [], `missing required argument 'vendor-or-name'. Without a terminal humanish cannot ask for each key, so name the key and pipe its value, for example: printf '%s' "$E2B_API_KEY" | ${cli("keys set e2b --stdin")}`);
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
const rows = await keyStatus({ cwd: process.cwd(), env: process.env });
|
|
236
|
+
const missing = rows.filter((row) => row.source === null).length;
|
|
237
|
+
if (missing === 0) {
|
|
238
|
+
finish(true, [], `Every provider key is already set, so there is nothing to ask for. Replace one with \`${cli("keys set <vendor>")}\`.`);
|
|
239
|
+
return;
|
|
240
|
+
}
|
|
241
|
+
io.writeErr(`${plural(missing, "provider key")} ${missing === 1 ? "is" : "are"} missing. Paste each one when asked, or press Enter to skip it.\n`);
|
|
242
|
+
const outcome = await askForMissingKeys({
|
|
243
|
+
rows,
|
|
244
|
+
env: process.env,
|
|
245
|
+
prompt: (label) => promptSecret(label, process.stdin, process.stderr),
|
|
246
|
+
});
|
|
247
|
+
const sentences = [
|
|
248
|
+
outcome.stored.length > 0
|
|
249
|
+
? `Stored ${listNames(outcome.stored)} (0600).`
|
|
250
|
+
: "Nothing was stored.",
|
|
251
|
+
...(outcome.skipped.length > 0 ? [`Skipped ${listNames(outcome.skipped)}.`] : []),
|
|
252
|
+
...(outcome.rejected ?? []).map((entry) => `${entry.name} was not stored: ${entry.message}`),
|
|
253
|
+
...(outcome.stoppedAt === undefined
|
|
254
|
+
? []
|
|
255
|
+
: [`Stopped at ${outcome.stoppedAt}; the keys after it were not asked for.`]),
|
|
256
|
+
`\`${cli("keys")}\` shows where each key comes from.`,
|
|
257
|
+
];
|
|
258
|
+
// A cancel is the person's choice, so only a value the store refused fails the command.
|
|
259
|
+
finish(outcome.rejected === undefined, outcome.stored, sentences.join(" "));
|
|
181
260
|
}
|
|
182
261
|
export function registerKeysCommand(parent, io) {
|
|
183
262
|
const keys = parent
|
|
184
263
|
.command("keys")
|
|
185
|
-
.description("
|
|
186
|
-
.summary("
|
|
264
|
+
.description("Show which provider keys are set and where each comes from, and manage the humanish user key store that provider-key discovery reads.")
|
|
265
|
+
.summary("Check and store your provider keys.");
|
|
266
|
+
keys
|
|
267
|
+
.command("status", { isDefault: true })
|
|
268
|
+
.description("Show each provider key humanish uses: where it comes from, or the command that adds it. Values are never printed.")
|
|
269
|
+
.option("--json", JSON_OPTION_DESCRIPTION)
|
|
270
|
+
.action(async (_options, command) => writeKeyStatus(command, io));
|
|
187
271
|
keys
|
|
188
272
|
.command("set")
|
|
189
|
-
.argument("
|
|
190
|
-
.description("Store one provider key in the user store (0600), prompted with hidden input.")
|
|
273
|
+
.argument("[vendor-or-name]", "A vendor alias (openai, e2b, anthropic, github, agentmail) or a raw ENV_NAME. Without one, a terminal asks for each missing key in turn.")
|
|
274
|
+
.description("Store one provider key in the user store (0600), prompted with hidden input. With no key named, ask for each missing key in turn.")
|
|
191
275
|
.option("--stdin", "Read the value from stdin instead of prompting (for agents/pipes).")
|
|
192
276
|
.option("--json", JSON_OPTION_DESCRIPTION)
|
|
193
277
|
.action(async (vendorOrName, options, command) => {
|
|
278
|
+
if (vendorOrName === undefined) {
|
|
279
|
+
await setMissingKeys(command, io, options.stdin === true);
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
194
282
|
const name = resolveKeyName(vendorOrName);
|
|
195
283
|
const storePath = userKeyStorePath(process.env);
|
|
196
284
|
if (name === null) {
|
|
@@ -294,11 +382,12 @@ export function registerKeysCommand(parent, io) {
|
|
|
294
382
|
function formatDoctorHuman(result) {
|
|
295
383
|
return ([
|
|
296
384
|
`humanish doctor ${result.ok ? "ok" : "needs setup"}`,
|
|
385
|
+
...(result.next === undefined ? [] : [`next: ${result.next}`]),
|
|
297
386
|
`cwd: ${result.cwd}`,
|
|
298
387
|
// "missing" is a verdict, and a row that never ran has none. A participant reading doctor on a
|
|
299
388
|
// fresh desktop got `- missing package.json: package.json is present and safe to read`, which
|
|
300
389
|
// contradicts itself in eleven words (labs/tui-self-study.yaml).
|
|
301
|
-
...result.checks.map((check) => `- ${check.status.replace("_", " ")} ${check.name}: ${check.message}`),
|
|
390
|
+
...result.checks.map((check) => `- ${check.status.replace("_", " ")} ${check.name}: ${check.message.replaceAll("\n", "\n ")}`),
|
|
302
391
|
].join("\n") + "\n");
|
|
303
392
|
}
|
|
304
393
|
function formatInitHuman(result) {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { participantCaption } from "../../run/participant-caption.js";
|
|
1
2
|
import { formatCuaDiagnostics, formatCuaStopCause } from "../../routes/computer-use/diagnostics.js";
|
|
2
3
|
/** A run's first lines: the command that ran, whether it was a dry run, how it ended, and its route. */
|
|
3
4
|
function runHeader(result, route) {
|
|
@@ -39,17 +40,19 @@ export function formatConcurrentSharedWorldStudyHuman(result) {
|
|
|
39
40
|
...runHeader(result, "shared-world"),
|
|
40
41
|
`run: ${result.runId}`,
|
|
41
42
|
`actor: ${result.actor}`,
|
|
42
|
-
`
|
|
43
|
+
`participants: ${result.roleCount}, up to ${result.concurrency} at once`,
|
|
43
44
|
...(result.host ? [`host: ${result.host}`] : []),
|
|
44
45
|
...(result.subject?.commit
|
|
45
|
-
? [`
|
|
46
|
+
? [`app: ${result.subject.repo}@${result.subject.commit.slice(0, 12)}`]
|
|
46
47
|
: []),
|
|
47
48
|
...(result.overlapProven === undefined
|
|
48
49
|
? []
|
|
49
50
|
: [
|
|
50
|
-
|
|
51
|
+
result.overlapProven
|
|
52
|
+
? "Participants used the app at the same time (observed in this run only)."
|
|
53
|
+
: "Participants were not observed using the app at the same time.",
|
|
51
54
|
]),
|
|
52
|
-
...result.roles.map((participant) =>
|
|
55
|
+
...result.roles.map((participant) => `${participantCaption({ id: participant.id, personaId: participant.persona })}: ${participantStatus(participant.status)}${participant.ok ? "" : ", not ok"}`),
|
|
53
56
|
...(result.subjectSandbox
|
|
54
57
|
? [
|
|
55
58
|
`subject sandbox: ${sandboxName(result.subjectSandbox)} killed=${result.subjectSandbox.killed ? "yes" : "no"}`,
|
|
@@ -7,6 +7,7 @@ import { warnAndQueue } from "../deprecations.js";
|
|
|
7
7
|
import { planStudy, resolveStudyDryRun } from "../../study/plan.js";
|
|
8
8
|
import { keyNamesOf } from "../../study/requirements.js";
|
|
9
9
|
import { routeOf } from "../../study/plan.js";
|
|
10
|
+
import { computerUseParticipantCount } from "../../study/routing.js";
|
|
10
11
|
import { computerUseRouteRun } from "./study-route-computer-use.js";
|
|
11
12
|
import { runRoute } from "./study-route-run.js";
|
|
12
13
|
import { scriptedRouteRun } from "./study-route-scripted.js";
|
|
@@ -130,7 +131,7 @@ export async function runStudyCommand(args) {
|
|
|
130
131
|
// Surface it so the invoker (who may not be the manifest author) knows executable code ran.
|
|
131
132
|
args.io.writeErr(`warning: review scorer ${scorer.provenance.ref} (${scorer.provenance.source}) is code that humanish loaded and ran in this process. Review it as you would any code you run.\n`);
|
|
132
133
|
}
|
|
133
|
-
const analysisBudget = automaticAnalysisBudget(config.review?.analysis, route);
|
|
134
|
+
const analysisBudget = automaticAnalysisBudget(config.review?.analysis, route, computerUseParticipantCount(config));
|
|
134
135
|
if (analysisBudget && resolveStudyDryRun(config, args.options.dryRun, true) === false) {
|
|
135
136
|
args.io.writeErr(`${formatAutomaticAnalysisBudget(analysisBudget)}\n`);
|
|
136
137
|
}
|
|
@@ -140,7 +140,7 @@ function formatStudyCheckHuman(result) {
|
|
|
140
140
|
...(result.sandbox.created
|
|
141
141
|
? [`sandbox: created=yes killed=${result.sandbox.killed === true ? "yes" : "no"}`]
|
|
142
142
|
: []),
|
|
143
|
-
...result.checks.map((check) => `- ${check.ok ? "ok" : "fail"} ${check.name}: ${check.message}`),
|
|
143
|
+
...result.checks.map((check) => `- ${check.checked === false ? "not checked" : check.ok ? "ok" : "fail"} ${check.name}: ${check.message}`),
|
|
144
144
|
...result.warnings.map((warning) => `warning: ${warning}`),
|
|
145
145
|
].join("\n") + "\n";
|
|
146
146
|
return result.error === undefined ? stdout : { stdout, error: result.error };
|
package/dist/cli/commands/tui.js
CHANGED
|
@@ -278,7 +278,7 @@ async function storeAgentmailKey(session) {
|
|
|
278
278
|
const { cwd, runtime, sessionEnv } = session;
|
|
279
279
|
// startTui has unmounted: only the host reads the credential, then remounts the view.
|
|
280
280
|
const value = await runtime.promptSecret("AgentMail API key", runtime.stdin, runtime.stdout);
|
|
281
|
-
if (value === null)
|
|
281
|
+
if (value === null || value === "")
|
|
282
282
|
return "Key entry cancelled. Nothing was changed.";
|
|
283
283
|
try {
|
|
284
284
|
setUserKey("AGENTMAIL_API_KEY", value, sessionEnv);
|
package/dist/cli/doctor.d.ts
CHANGED
package/dist/cli/doctor.js
CHANGED
|
@@ -11,6 +11,8 @@ import { studySetupChecks, studiesByRequiredKey, } from "../study/doctor.js";
|
|
|
11
11
|
import { prepareSelectedOutputDirectory, } from "../run/contained-output.js";
|
|
12
12
|
import { implicitProjectDirectoryExists, readImplicitProjectFile, validateCwd, } from "../run/project.js";
|
|
13
13
|
import { cli } from "./invocation.js";
|
|
14
|
+
import { recordedVersion } from "./update-check.js";
|
|
15
|
+
import { CLI_VERSION } from "./version.js";
|
|
14
16
|
const DOCTOR_SCHEMA = "humanish.doctor-result.v1";
|
|
15
17
|
/**
|
|
16
18
|
* Version 2.3.1 stopped holding a launched background command's event stream open. Version 2.3.2
|
|
@@ -94,6 +96,7 @@ export async function doctor(cwdInput, options = {}) {
|
|
|
94
96
|
...(await projectChecks(projectRoot)),
|
|
95
97
|
await desktopSdkCheck(setup),
|
|
96
98
|
terminalSurfaceCheck(),
|
|
99
|
+
...versionCheck(env),
|
|
97
100
|
...localAgentChecks(agents),
|
|
98
101
|
await claudeTranscriptCheck(env),
|
|
99
102
|
...keyChecks(probes, receivingKey, setup, keyUsers),
|
|
@@ -109,6 +112,7 @@ export async function doctor(cwdInput, options = {}) {
|
|
|
109
112
|
schema: DOCTOR_SCHEMA,
|
|
110
113
|
ok: checks.every((check) => check.ok),
|
|
111
114
|
cwd,
|
|
115
|
+
...(setup?.next === undefined ? {} : { next: setup.next }),
|
|
112
116
|
checks: withStatus(checks),
|
|
113
117
|
};
|
|
114
118
|
}
|
|
@@ -246,6 +250,20 @@ function terminalSurfaceCheck() {
|
|
|
246
250
|
}),
|
|
247
251
|
};
|
|
248
252
|
}
|
|
253
|
+
/** The latest version the update check recorded on this machine; no row before a check. */
|
|
254
|
+
function versionCheck(env) {
|
|
255
|
+
const recorded = recordedVersion(CLI_VERSION, env, env.HOME ?? homedir());
|
|
256
|
+
if (recorded === undefined)
|
|
257
|
+
return [];
|
|
258
|
+
return [
|
|
259
|
+
{
|
|
260
|
+
name: "humanish version",
|
|
261
|
+
ok: true,
|
|
262
|
+
...(recorded.newer ? { status: "note" } : {}),
|
|
263
|
+
message: recorded.message,
|
|
264
|
+
},
|
|
265
|
+
];
|
|
266
|
+
}
|
|
249
267
|
/** Probes the live-run keys, plus the receiving-email key a real-comms study names. */
|
|
250
268
|
async function probeDoctorKeys(cwd, env, study, keyDeps, dotenv) {
|
|
251
269
|
const keyNames = new Set(["OPENAI_API_KEY", "E2B_API_KEY", "GH_TOKEN", "CODEX_API_KEY"]);
|
package/dist/cli/findings.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
// `humanish.analysis-findings.v1` projection and its text. The analysis artifact stays the record;
|
|
3
3
|
// this reads the version `analyze show` selects and never starts an analysis.
|
|
4
4
|
import path from "node:path";
|
|
5
|
+
import { admittingMaxCost, costRefusalText } from "../analysis/admission.js";
|
|
5
6
|
import { DEFAULT_ANALYSIS_MAX_COST_USD } from "../analysis/automatic-config.js";
|
|
6
7
|
import { loadAnalysis } from "../analysis/load.js";
|
|
7
8
|
import { projectShareCheckedAnalysis } from "../analysis/sharing.js";
|
|
@@ -199,9 +200,11 @@ export function analysisFindings(source) {
|
|
|
199
200
|
const outcome = analysisOutcomeText(job);
|
|
200
201
|
return withoutFindings(source, "failed", job.reason, `The automatic analysis ${outcome.startsWith("failed") ? "" : "was "}${outcome}.`, analyzeCommand(source, job.reason === "AUTOMATIC_ANALYSIS_CODEX_UNAVAILABLE" ? "codex" : undefined));
|
|
201
202
|
}
|
|
203
|
+
if (job?.state === "skipped" && job.admission)
|
|
204
|
+
return withoutFindings(source, "skipped", job.reason, `The automatic analysis was refused before it started. ${costRefusalText(job.admission)}`, cli(`analyze --run ${source.runId}${source.cwdFlag} --max-cost ${admittingMaxCost(job.admission)}`));
|
|
202
205
|
if (job?.state === "skipped") {
|
|
203
206
|
const refused = job.reason === "AUTOMATIC_ANALYSIS_ADMISSION_REFUSED";
|
|
204
|
-
return withoutFindings(source, "skipped", job.reason, `The automatic analysis was ${analysisOutcomeText(job)}.${refused ? " The dry run
|
|
207
|
+
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"
|
|
205
208
|
? null
|
|
206
209
|
: `${analyzeCommand(source, undefined)}${refused ? " --dry-run" : ""}`);
|
|
207
210
|
}
|
package/dist/cli/io.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Command, Option } from "commander";
|
|
2
2
|
import { discoverProviderKeys, type DotenvLoad } from "../keys/key-resolution.js";
|
|
3
3
|
import { type TelemetryProperties } from "./telemetry.js";
|
|
4
|
+
import type { UpdateCheckContext } from "./update-check.js";
|
|
4
5
|
import type { RunResult } from "../run/results.js";
|
|
5
6
|
export declare const CLI_RESPONSE_SCHEMA = "humanish.cli-response.v1";
|
|
6
7
|
export { CLI_VERSION } from "./version.js";
|
|
@@ -9,6 +10,7 @@ export interface CliIo {
|
|
|
9
10
|
writeErr(text: string): void;
|
|
10
11
|
setExitCode(code: number): void;
|
|
11
12
|
keyDiscovery?: typeof discoverProviderKeys;
|
|
13
|
+
updateCheck?: Partial<UpdateCheckContext>;
|
|
12
14
|
}
|
|
13
15
|
export declare const JSON_OPTION_DESCRIPTION = "Print a machine-readable JSON response.";
|
|
14
16
|
export declare const CWD_OPTION_DESCRIPTION = "Project directory.";
|
|
@@ -136,4 +138,5 @@ export declare function analysisOutcomeText(analysis: {
|
|
|
136
138
|
state: string;
|
|
137
139
|
reason: string | null;
|
|
138
140
|
}): string;
|
|
141
|
+
/** Preserve the run's own result while making requested post-processing failures machine-visible. */
|
|
139
142
|
export declare function automaticAnalysisEnvelope<T>(result: T): T;
|
package/dist/cli/io.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { automaticAnalysisSucceeded,
|
|
2
|
-
import {
|
|
1
|
+
import { automaticAnalysisSucceeded, } from "../analysis/automatic-completion.js";
|
|
2
|
+
import { admittingMaxCost, costRefusalText, refusedCost, } from "../analysis/admission.js";
|
|
3
3
|
import { Command, Option } from "commander";
|
|
4
4
|
import { loadEnvFile, recordDotenvNames } from "../keys/env-file.js";
|
|
5
5
|
import { discoverProviderKeys } from "../keys/key-resolution.js";
|
|
@@ -188,13 +188,14 @@ export function writeResult(command, io, result, formatHuman) {
|
|
|
188
188
|
writeHuman(command, io, formatHuman(output));
|
|
189
189
|
if (output !== null && typeof output === "object" && "automaticAnalysis" in output) {
|
|
190
190
|
const analysis = output.automaticAnalysis;
|
|
191
|
-
|
|
191
|
+
const cost = analysis?.result?.admission && refusedCost(analysis.result.admission);
|
|
192
|
+
if (cost)
|
|
193
|
+
io.writeOut(`analysis: refused before it started. ${refusedAnalysisText(cost, analysis.result?.run ?? "latest")}\n`);
|
|
194
|
+
else if (analysis)
|
|
192
195
|
io.writeOut(`analysis: ${analysisOutcomeText(analysis)}\n`);
|
|
193
196
|
const rejected = analysis?.result?.rejectedOutputPath;
|
|
194
197
|
if (rejected)
|
|
195
198
|
io.writeOut(`analysis: ${analysis.result?.error?.code ?? "rejected"}; the rejected output is kept locally at ${rejected}\n`);
|
|
196
|
-
if (defaultAnalysisOverBudget(output))
|
|
197
|
-
io.writeOut(overBudgetAnalysisHint(output));
|
|
198
199
|
}
|
|
199
200
|
}
|
|
200
201
|
markInvocationEnvelopeWritten(command);
|
|
@@ -286,7 +287,7 @@ const ANALYSIS_REASON_TEXT = {
|
|
|
286
287
|
AUTOMATIC_ANALYSIS_SOURCE_UNAVAILABLE: "skipped because the run's evidence could not be read",
|
|
287
288
|
AUTOMATIC_ANALYSIS_ALREADY_REQUESTED: "skipped because this run's analysis was already requested",
|
|
288
289
|
AUTOMATIC_ANALYSIS_BUSY: "skipped because another analysis is running",
|
|
289
|
-
AUTOMATIC_ANALYSIS_ADMISSION_REFUSED: "
|
|
290
|
+
AUTOMATIC_ANALYSIS_ADMISSION_REFUSED: "refused before it started: its configuration, question, evidence or cost did not pass admission",
|
|
290
291
|
AUTOMATIC_ANALYSIS_CLEANUP_UNCONFIRMED: "not started because the run's cleanup was not confirmed",
|
|
291
292
|
AUTOMATIC_ANALYSIS_REUSED: "complete, reusing an earlier analysis of the same evidence",
|
|
292
293
|
AUTOMATIC_ANALYSIS_LIMITATIONS: "partial, because it covered only part of the evidence",
|
|
@@ -305,13 +306,11 @@ export function analysisOutcomeText(analysis) {
|
|
|
305
306
|
return analysis.state;
|
|
306
307
|
return ANALYSIS_REASON_TEXT[analysis.reason] ?? `${analysis.state} (${analysis.reason})`;
|
|
307
308
|
}
|
|
308
|
-
/**
|
|
309
|
-
function
|
|
310
|
-
|
|
311
|
-
const suggested = Math.ceil(estimate ?? DEFAULT_ANALYSIS_MAX_COST_USD + 1);
|
|
312
|
-
const shown = estimate === null ? "" : ` ($${estimate.toFixed(2)})`;
|
|
313
|
-
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`;
|
|
309
|
+
/** What a refused analysis would cost and the command that runs it with a cap that admits it. */
|
|
310
|
+
function refusedAnalysisText(cost, runId) {
|
|
311
|
+
return `${costRefusalText(cost)} To run it, raise the cap: ${cli(`analyze --run ${runId} --max-cost ${admittingMaxCost(cost)}`)}`;
|
|
314
312
|
}
|
|
313
|
+
/** Preserve the run's own result while making requested post-processing failures machine-visible. */
|
|
315
314
|
export function automaticAnalysisEnvelope(result) {
|
|
316
315
|
if (result === null ||
|
|
317
316
|
typeof result !== "object" ||
|
package/dist/cli/program.js
CHANGED
|
@@ -2,16 +2,18 @@ import { readFileSync } from "node:fs";
|
|
|
2
2
|
import { formatOrientationHuman, readOrientation } from "./orientation.js";
|
|
3
3
|
import { dirname } from "node:path";
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
5
|
-
import { Command } from "commander";
|
|
5
|
+
import { Command, Help } from "commander";
|
|
6
6
|
import { redactText } from "../evidence/redaction.js";
|
|
7
7
|
import { PortInUseError } from "../observer/listen.js";
|
|
8
8
|
import { buildPayload, disabledByEnvironment, durationBucket, readTelemetryState, sendTelemetry, TELEMETRY_NOTICE, writeTelemetryState, isOwnCheckoutRun, } from "./telemetry.js";
|
|
9
|
+
import { checkForUpdate, startRefreshWorker } from "./update-check.js";
|
|
9
10
|
import { registerAnalyzeCommand } from "./commands/analyze.js";
|
|
10
11
|
import { registerCodexCommands } from "./commands/codex.js";
|
|
11
12
|
import { registerCommsCommands } from "./commands/comms.js";
|
|
12
13
|
import { registerFeedbackCommands } from "./commands/feedback.js";
|
|
13
14
|
import { registerStudyCommands } from "./commands/study.js";
|
|
14
15
|
import { registerMigrateCommand } from "./commands/migrate.js";
|
|
16
|
+
import { registerNotesCommand } from "./commands/notes.js";
|
|
15
17
|
import { registerObserveCommand } from "./commands/observe.js";
|
|
16
18
|
import { registerCleanupCommand, registerExportCommand, registerReclaimCommand, registerReviewCommand, registerRunCommand, registerRunsCommand, registerRuntimeCommands, registerStatsCommand, registerVerifyCommand, } from "./commands/runs.js";
|
|
17
19
|
import { registerDoctorCommand, registerInitCommand, registerKeysCommand, registerTelemetryCommand, } from "./commands/setup.js";
|
|
@@ -95,6 +97,29 @@ async function recordCommandTelemetry(command, ok, durationMs, exitCode) {
|
|
|
95
97
|
// Metrics are our problem, never the operator's.
|
|
96
98
|
}
|
|
97
99
|
}
|
|
100
|
+
/**
|
|
101
|
+
* After a command, one line on stderr when a newer humanish is published. It reads only the cache;
|
|
102
|
+
* the registry request runs detached (see update-check.ts), so nothing here waits on the network.
|
|
103
|
+
*/
|
|
104
|
+
function noticeNewerVersion(command, io) {
|
|
105
|
+
try {
|
|
106
|
+
const notice = checkForUpdate({
|
|
107
|
+
installed: CLI_VERSION,
|
|
108
|
+
env: process.env,
|
|
109
|
+
terminal: process.stderr.isTTY === true,
|
|
110
|
+
json: wantsJson(command),
|
|
111
|
+
ownCheckout: isOwnCheckoutRun(process.cwd(), dirname(fileURLToPath(import.meta.url)), (p) => readFileSync(p, "utf8")),
|
|
112
|
+
now: Date.now(),
|
|
113
|
+
startRefresh: startRefreshWorker,
|
|
114
|
+
...io.updateCheck,
|
|
115
|
+
});
|
|
116
|
+
if (notice !== undefined)
|
|
117
|
+
io.writeErr(`${notice}\n`);
|
|
118
|
+
}
|
|
119
|
+
catch {
|
|
120
|
+
// An update notice is never a reason for a command to fail.
|
|
121
|
+
}
|
|
122
|
+
}
|
|
98
123
|
/**
|
|
99
124
|
* HUMANISH_DEBUG_HANDLES=1: after a command's handler settles, name what is still keeping the
|
|
100
125
|
* process alive. A live terminal run wrote its result 64 s in and the CLI stayed up for
|
|
@@ -165,6 +190,7 @@ class HumanishCommand extends Command {
|
|
|
165
190
|
const noticed = announceTelemetryOnce(this, cliIo);
|
|
166
191
|
const finish = (ok) => {
|
|
167
192
|
void recordCommandTelemetry(this, ok, Date.now() - startedAt, lastExitCode);
|
|
193
|
+
noticeNewerVersion(this, cliIo);
|
|
168
194
|
reportActiveHandles(this, cliIo);
|
|
169
195
|
};
|
|
170
196
|
try {
|
|
@@ -326,6 +352,15 @@ export function createProgram(io = {}) {
|
|
|
326
352
|
// each subcommand when it is created.
|
|
327
353
|
.helpOption("-h, --help", "Show help for this command.")
|
|
328
354
|
.helpCommand("help [command]", "Show help for a command.")
|
|
355
|
+
// `help <command>` and `<command> help` keep working; a command's listing names only the
|
|
356
|
+
// commands that do something, so `help` is left out of every Commands section.
|
|
357
|
+
.configureHelp({
|
|
358
|
+
visibleCommands(command) {
|
|
359
|
+
return Help.prototype.visibleCommands
|
|
360
|
+
.call(this, command)
|
|
361
|
+
.filter((child) => child.name() !== "help");
|
|
362
|
+
},
|
|
363
|
+
})
|
|
329
364
|
.showHelpAfterError()
|
|
330
365
|
.option("--json", "Print machine-readable JSON responses where supported.")
|
|
331
366
|
.configureOutput({
|
|
@@ -358,6 +393,7 @@ export function createProgram(io = {}) {
|
|
|
358
393
|
registerVerifyCommand(program, cliIo);
|
|
359
394
|
registerReviewCommand(program, cliIo);
|
|
360
395
|
registerAnalyzeCommand(program, cliIo);
|
|
396
|
+
registerNotesCommand(program, cliIo);
|
|
361
397
|
registerFeedbackCommands(program, cliIo);
|
|
362
398
|
registerExportCommand(program, cliIo);
|
|
363
399
|
registerRunsCommand(program, cliIo);
|
|
@@ -1,2 +1,6 @@
|
|
|
1
|
-
/**
|
|
1
|
+
/**
|
|
2
|
+
* Host-owned entry: no secret crosses the TUI view contract or reaches a writable terminal.
|
|
3
|
+
* Resolves to the trimmed line, an empty string when Enter was pressed alone, or null when the
|
|
4
|
+
* person cancelled or there is no terminal.
|
|
5
|
+
*/
|
|
2
6
|
export declare function promptSecret(label: string, stdin: NodeJS.ReadStream, stdout: NodeJS.WriteStream): Promise<string | null>;
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { createInterface } from "node:readline/promises";
|
|
2
2
|
import { Writable } from "node:stream";
|
|
3
|
-
/**
|
|
3
|
+
/**
|
|
4
|
+
* Host-owned entry: no secret crosses the TUI view contract or reaches a writable terminal.
|
|
5
|
+
* Resolves to the trimmed line, an empty string when Enter was pressed alone, or null when the
|
|
6
|
+
* person cancelled or there is no terminal.
|
|
7
|
+
*/
|
|
4
8
|
export async function promptSecret(label, stdin, stdout) {
|
|
5
9
|
if (!stdin.isTTY || !stdout.isTTY)
|
|
6
10
|
return null;
|
|
@@ -27,7 +31,7 @@ export async function promptSecret(label, stdin, stdout) {
|
|
|
27
31
|
stdout.write("\n");
|
|
28
32
|
resolve(value);
|
|
29
33
|
};
|
|
30
|
-
reader.once("line", (line) => finish(line.trim()
|
|
34
|
+
reader.once("line", (line) => finish(line.trim()));
|
|
31
35
|
reader.once("SIGINT", () => finish(null));
|
|
32
36
|
reader.once("close", () => finish(null));
|
|
33
37
|
reader.once("error", () => finish(null));
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
export interface UpdateCheckContext {
|
|
2
|
+
/** The running CLI's version. */
|
|
3
|
+
installed: string;
|
|
4
|
+
env: NodeJS.ProcessEnv;
|
|
5
|
+
/** Whether stderr is a terminal. */
|
|
6
|
+
terminal: boolean;
|
|
7
|
+
/** Whether the command prints a JSON document. */
|
|
8
|
+
json: boolean;
|
|
9
|
+
/** Whether this CLI runs from humanish's own source checkout. */
|
|
10
|
+
ownCheckout: boolean;
|
|
11
|
+
/** The clock, in epoch milliseconds. */
|
|
12
|
+
now: number;
|
|
13
|
+
home?: string;
|
|
14
|
+
/** Starts the background registry request that writes the cache. Never awaited. */
|
|
15
|
+
startRefresh: (cachePath: string) => void;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* The one line to print after a command when the cache names a newer humanish, at most once a day.
|
|
19
|
+
* Records that it was shown. Every failure inside is swallowed: an update notice is never a reason
|
|
20
|
+
* for a command to fail.
|
|
21
|
+
*/
|
|
22
|
+
export declare function checkForUpdate(context: UpdateCheckContext): string | undefined;
|
|
23
|
+
/**
|
|
24
|
+
* The version the last successful check recorded, compared with `installed`, for `doctor`. Reads the
|
|
25
|
+
* cache only; undefined until a check has succeeded on this machine.
|
|
26
|
+
*/
|
|
27
|
+
export declare function recordedVersion(installed: string, env: NodeJS.ProcessEnv, home?: string): {
|
|
28
|
+
newer: boolean;
|
|
29
|
+
message: string;
|
|
30
|
+
} | undefined;
|
|
31
|
+
/**
|
|
32
|
+
* Ask the npm registry for humanish's `latest` dist-tag and record it in the cache. The detached
|
|
33
|
+
* worker runs this. The request is a GET with an Accept header and nothing else: no machine id,
|
|
34
|
+
* installed version, command or path. A failed or malformed answer leaves the cache as it was.
|
|
35
|
+
*/
|
|
36
|
+
export declare function refreshUpdateCache(args: {
|
|
37
|
+
cachePath: string;
|
|
38
|
+
fetchFn?: (url: string, init: RequestInit) => Promise<Response>;
|
|
39
|
+
now?: () => number;
|
|
40
|
+
timeoutMs?: number;
|
|
41
|
+
}): Promise<void>;
|
|
42
|
+
/**
|
|
43
|
+
* Start the worker that runs refreshUpdateCache, detached and unreferenced so this process exits
|
|
44
|
+
* without waiting for it. A source checkout has no built worker, and nothing starts.
|
|
45
|
+
*/
|
|
46
|
+
export declare function startRefreshWorker(cachePath: string): void;
|