@consiliency/agent-board-cli 1.27.1 → 1.29.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/claude-marketplace/plugins/agent-board/.claude-plugin/plugin.json +2 -2
- package/dist/assets.json +4 -4
- package/dist/cli.d.ts +5 -2
- package/dist/cli.js +41 -13
- package/dist/doctor.d.ts +143 -2
- package/dist/doctor.js +346 -39
- package/dist/identity_writer.d.ts +1 -46
- package/dist/identity_writer.js +5 -174
- package/dist/keyslot_status.d.ts +134 -0
- package/dist/keyslot_status.js +348 -0
- package/dist/setup.d.ts +112 -5
- package/dist/setup.js +345 -73
- package/dist/setup_all.d.ts +51 -0
- package/dist/setup_all.js +164 -0
- package/dist/setup_storage.d.ts +74 -11
- package/dist/setup_storage.js +173 -30
- package/dist/setup_writers.d.ts +7 -0
- package/dist/setup_writers.js +11 -2
- package/keyslot/lib.sh +835 -38
- package/keyslot/status.sh +439 -173
- package/package.json +2 -2
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agent-board",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.33.0",
|
|
4
4
|
"description": "Message Board for Claude Code: the agent-board MCP server and the local wake hook that nudges an idle session when its inbox has unhandled items.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Consiliency"
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"command": "npx",
|
|
11
11
|
"args": [
|
|
12
12
|
"-y",
|
|
13
|
-
"@consiliency/agent-board-mcp@1.
|
|
13
|
+
"@consiliency/agent-board-mcp@1.33.0",
|
|
14
14
|
"--harness",
|
|
15
15
|
"claude"
|
|
16
16
|
]
|
package/dist/assets.json
CHANGED
package/dist/cli.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { Command } from "commander";
|
|
2
|
-
import { createAgentBoardClient, type EnrollKeystore, type EnrollmentIntent, type FleetOnboardingIntent, type PendingEnrollmentStore, type OAuthFetchLike, type FetchLike, type AgentJoinDeployment, type AgentJoinInstructions, type DeviceAuthorizationPrompt, resolveCredentials } from "@consiliency/agent-board-client";
|
|
2
|
+
import { createAgentBoardClient, type EnrollKeystore, type EnrollmentIntent, type FleetOnboardingIntent, type PendingEnrollmentStore, type OAuthFetchLike, type FetchLike, type AgentJoinDeployment, type AgentJoinInstructions, type DeviceAuthorizationPrompt, resolveCredentials, type MyBoardRequest } from "@consiliency/agent-board-client";
|
|
3
3
|
import { type DoctorOptions, type DoctorReport } from "./doctor.js";
|
|
4
4
|
import { type SetupEnroller, type SetupRunner } from "./setup.js";
|
|
5
|
+
import { type SetupAllRunner } from "./setup_all.js";
|
|
5
6
|
import { DevicePendingEnrollmentStore } from "./enroll_runtime.js";
|
|
6
7
|
type JsonObject = Record<string, unknown>;
|
|
7
8
|
export interface CliIo {
|
|
@@ -64,6 +65,7 @@ interface ClientLike {
|
|
|
64
65
|
}>;
|
|
65
66
|
revokeInviteLink(input: JsonObject): Promise<unknown>;
|
|
66
67
|
redeemInviteLink(input: JsonObject): Promise<unknown>;
|
|
68
|
+
listMyBoardRequests(): Promise<MyBoardRequest[]>;
|
|
67
69
|
listBoardMembers(input: JsonObject): Promise<unknown>;
|
|
68
70
|
listBoardPendingMemberships(input: JsonObject): Promise<unknown>;
|
|
69
71
|
resolveAgentHandle(input: JsonObject): Promise<unknown>;
|
|
@@ -333,13 +335,14 @@ export interface CredentialSelection {
|
|
|
333
335
|
* for the same reason. Without it: `resolveCredentials(descriptor, env)`, exactly as before.
|
|
334
336
|
*/
|
|
335
337
|
export declare function fileAwareCredentials(selection: CredentialSelection, env?: NodeJS.ProcessEnv): (descriptor?: Parameters<typeof resolveCredentials>[0]) => Promise<import("@consiliency/agent-board-client").ResolvedCredentials>;
|
|
336
|
-
export declare function createProgram(client: ClientLike, io: CliIo, enrollment?: EnrollmentCommandRunner, doctor?: DoctorRunner, selection?: CredentialSelection, setup?: SetupRunner): Command;
|
|
338
|
+
export declare function createProgram(client: ClientLike, io: CliIo, enrollment?: EnrollmentCommandRunner, doctor?: DoctorRunner, selection?: CredentialSelection, setup?: SetupRunner, setupAll?: SetupAllRunner): Command;
|
|
337
339
|
export interface RunCliOptions {
|
|
338
340
|
/** The env the default client's credential resolver reads. Default `process.env`. */
|
|
339
341
|
env?: NodeJS.ProcessEnv;
|
|
340
342
|
/** Extra options for the DEFAULT client (tests: a `dataSourceFactory`); never its resolver. */
|
|
341
343
|
clientOptions?: Omit<NonNullable<Parameters<typeof createAgentBoardClient>[0]>, "resolveCredentials">;
|
|
342
344
|
setup?: SetupRunner;
|
|
345
|
+
setupAll?: SetupAllRunner;
|
|
343
346
|
}
|
|
344
347
|
export declare function runCli(argv: string[], io?: CliIo, client?: ClientLike, enrollment?: EnrollmentCommandRunner, doctor?: DoctorRunner, options?: RunCliOptions): Promise<void>;
|
|
345
348
|
export {};
|
package/dist/cli.js
CHANGED
|
@@ -3,9 +3,10 @@ import { createHash, randomUUID } from "node:crypto";
|
|
|
3
3
|
import * as nodeFs from "node:fs";
|
|
4
4
|
import { homedir } from "node:os";
|
|
5
5
|
import { dirname, join } from "node:path";
|
|
6
|
-
import { createAgentBoardClient, toOperationFailure, beginEnrollment, completeEnrollment, beginDeviceEnrollment, completeDeviceEnrollment, runEnrollment, runDeviceEnrollment, resolveEnrollmentDescriptor, EnrollmentDescriptorError, EnrollmentCommandError, resolveEnrollEndpoint, resolveEnrollSessionUrl, resolveEnrollPublishableKey, resolveJoinDeployment, buildFleetHarnessTemplate, finalizeFleetHarnessTemplate, normalizePublishableKeyFieldReference, normalizeFleetTupleValue, normalizeKeyReference, enrollCommitMarker, assertPresentationSafe, FLEET_TEMPLATE_FAILURE_MESSAGE, PROHIBITED_PRESENTATION_REMOVE_CLASS, buildAgentJoinInstructions, nodeFetchAdapter, buildEnrollKeystore, AUTHORED_CAUSE_CODES, codeProvenance, markAuthoredCode, assertIdentityHarness, identityFilePath, resolveHostSegment, identityConfigRoot, walkTrustedDirectory, IDENTITY_HARNESSES, CredentialResolutionError, readIdentityFile, resolveCredentials, } from "@consiliency/agent-board-client";
|
|
6
|
+
import { createAgentBoardClient, toOperationFailure, beginEnrollment, completeEnrollment, beginDeviceEnrollment, completeDeviceEnrollment, runEnrollment, runDeviceEnrollment, resolveEnrollmentDescriptor, EnrollmentDescriptorError, EnrollmentCommandError, resolveEnrollEndpoint, resolveEnrollSessionUrl, resolveEnrollPublishableKey, resolveJoinDeployment, buildFleetHarnessTemplate, finalizeFleetHarnessTemplate, normalizePublishableKeyFieldReference, normalizeFleetTupleValue, normalizeKeyReference, enrollCommitMarker, assertPresentationSafe, FLEET_TEMPLATE_FAILURE_MESSAGE, PROHIBITED_PRESENTATION_REMOVE_CLASS, buildAgentJoinInstructions, nodeFetchAdapter, buildEnrollKeystore, AUTHORED_CAUSE_CODES, codeProvenance, markAuthoredCode, assertIdentityHarness, identityFilePath, resolveHostSegment, identityConfigRoot, walkTrustedDirectory, IDENTITY_HARNESSES, CredentialResolutionError, readIdentityFile, resolveCredentials, parseInviteInput, } from "@consiliency/agent-board-client";
|
|
7
7
|
import { fileIdentityEnv, formatDoctorReport, runDoctor } from "./doctor.js";
|
|
8
8
|
import { addSetupCommand, runSetup } from "./setup.js";
|
|
9
|
+
import { runSetupAll, setupAllCommand } from "./setup_all.js";
|
|
9
10
|
import { identityValuesFromEnrollment, resolveIdentityDeployment, writeIdentityFile, } from "./identity_writer.js";
|
|
10
11
|
import { DevicePendingEnrollmentStore, FilePendingEnrollmentStore, FleetDeviceContinuationStore, makeBrowserOpener, prepareFleetRuntimeOutput, publishFinalizedFleetTemplate, } from "./enroll_runtime.js";
|
|
11
12
|
function isRuntimeStatus(value) {
|
|
@@ -2900,7 +2901,12 @@ function renderDevicePrompt(prompt) {
|
|
|
2900
2901
|
lines.push("");
|
|
2901
2902
|
return `${lines.join("\n")}\n`;
|
|
2902
2903
|
}
|
|
2903
|
-
/**
|
|
2904
|
+
/**
|
|
2905
|
+
* Read a single-line code from a piped stdin (A5), trimmed. Returns undefined on a TTY. Its callers
|
|
2906
|
+
* are the OAuth authorization code (not an invite) and the fleet onboarding invite code, which
|
|
2907
|
+
* IF-0-ONBOARD-2 keeps out of the invite parser (a fleet invite has no designated field). A room
|
|
2908
|
+
* invite is read by `readInviteFromStdin` and parsed.
|
|
2909
|
+
*/
|
|
2904
2910
|
async function readCodeFromStdin() {
|
|
2905
2911
|
if (process.stdin.isTTY)
|
|
2906
2912
|
return undefined;
|
|
@@ -2911,6 +2917,17 @@ async function readCodeFromStdin() {
|
|
|
2911
2917
|
const text = Buffer.concat(chunks).toString("utf8").trim();
|
|
2912
2918
|
return text.length > 0 ? text : undefined;
|
|
2913
2919
|
}
|
|
2920
|
+
/** All of a piped stdin, unparsed (a room invite message is several lines). undefined on a TTY. */
|
|
2921
|
+
async function readInviteFromStdin() {
|
|
2922
|
+
if (process.stdin.isTTY)
|
|
2923
|
+
return undefined;
|
|
2924
|
+
const chunks = [];
|
|
2925
|
+
for await (const chunk of process.stdin) {
|
|
2926
|
+
chunks.push(chunk);
|
|
2927
|
+
}
|
|
2928
|
+
const text = Buffer.concat(chunks).toString("utf8");
|
|
2929
|
+
return text.trim().length > 0 ? text : undefined;
|
|
2930
|
+
}
|
|
2914
2931
|
function addRoomsCommands(program, client, io) {
|
|
2915
2932
|
const rooms = program
|
|
2916
2933
|
.command("rooms")
|
|
@@ -3094,17 +3111,27 @@ function addRoomsCommands(program, client, io) {
|
|
|
3094
3111
|
.description("Redeem an invite link by code, joining as the agent identity resolved from this host's env credentials. The code is a BEARER SECRET — prefer piping it on stdin.")
|
|
3095
3112
|
.option("--code <code>", "the invite code (prefer stdin; argv leaks via /proc/<pid>/cmdline and shell history)")
|
|
3096
3113
|
.action(async (o) => {
|
|
3097
|
-
|
|
3098
|
-
|
|
3099
|
-
|
|
3100
|
-
|
|
3101
|
-
if (!
|
|
3102
|
-
io.writeStderr("link-redeem needs an invite code: pipe it on stdin (e.g. `agent-board rooms link-redeem <<'MB_INVITE_CODE'` ... `MB_INVITE_CODE`) or pass --code (which leaks the bearer code via /proc and shell history).\n");
|
|
3114
|
+
// IF-0-ONBOARD-2: the code, or the whole invite message on stdin, goes through the ONE
|
|
3115
|
+
// shared parser, which reads only the designated invite-code field. Neither form is a
|
|
3116
|
+
// terminal read (a TTY stdin is not read at all), so a message is accepted from either.
|
|
3117
|
+
const text = o.code ?? (await readInviteFromStdin());
|
|
3118
|
+
if (!text) {
|
|
3119
|
+
io.writeStderr("link-redeem needs an invite code: pipe it (or the whole invite message) on stdin (e.g. `agent-board rooms link-redeem <<'MB_INVITE_CODE'` ... `MB_INVITE_CODE`) or pass --code (which leaks the bearer code via /proc and shell history).\n");
|
|
3103
3120
|
process.exitCode = 1;
|
|
3104
3121
|
return;
|
|
3105
3122
|
}
|
|
3106
|
-
return runHandler(io, "agent_board.redeem_invite_link", () => client.redeemInviteLink({ p_code: code }));
|
|
3123
|
+
return runHandler(io, "agent_board.redeem_invite_link", () => client.redeemInviteLink({ p_code: parseInviteInput(text, { tty: false }).code }));
|
|
3107
3124
|
});
|
|
3125
|
+
// IF-0-ONBOARD-4: the caller's OWN join requests as they stand now (the RPC takes no identity:
|
|
3126
|
+
// the session is the requester).
|
|
3127
|
+
rooms
|
|
3128
|
+
.command("my-requests")
|
|
3129
|
+
.description("List your own join requests and where each stands (requested, joined, left, …; expired by age)")
|
|
3130
|
+
.action(() => runHandler(io, "agent_board.list_my_board_requests", async () => ({
|
|
3131
|
+
ok: true,
|
|
3132
|
+
operation: "agent_board.list_my_board_requests",
|
|
3133
|
+
data: await client.listMyBoardRequests(),
|
|
3134
|
+
})));
|
|
3108
3135
|
}
|
|
3109
3136
|
function addInviteCommands(program, client, io) {
|
|
3110
3137
|
const invite = program.command("invite").description("create bounded fleet onboarding invites");
|
|
@@ -3189,6 +3216,7 @@ function addDoctorCommand(program, io, doctor) {
|
|
|
3189
3216
|
// Not `requiredOption`: commander's own usage exit would bypass the JSON envelope (#402).
|
|
3190
3217
|
.option("--harness <harness>", "the harness to check (agy, claude, codex, grok, opencode, pi)")
|
|
3191
3218
|
.option("--json", "print the report as JSON")
|
|
3219
|
+
.option("--rescan", "scan for plaintext copies of a keyslot key now, instead of reusing a scan from the last 24 hours")
|
|
3192
3220
|
.action(async (options) => {
|
|
3193
3221
|
const harness = String(options.harness ?? "");
|
|
3194
3222
|
let report;
|
|
@@ -3201,7 +3229,7 @@ function addDoctorCommand(program, io, doctor) {
|
|
|
3201
3229
|
return;
|
|
3202
3230
|
}
|
|
3203
3231
|
try {
|
|
3204
|
-
report = await doctor({ harness });
|
|
3232
|
+
report = await doctor({ harness, ...(options.rescan === true ? { rescan: true } : {}) });
|
|
3205
3233
|
}
|
|
3206
3234
|
catch (error) {
|
|
3207
3235
|
writeFailureEnvelope(io, toOperationFailure("agent_board.doctor", error));
|
|
@@ -3228,7 +3256,7 @@ export function fileAwareCredentials(selection, env = process.env) {
|
|
|
3228
3256
|
return resolveCredentials(undefined, fileIdentityEnv(env, values));
|
|
3229
3257
|
};
|
|
3230
3258
|
}
|
|
3231
|
-
export function createProgram(client, io, enrollment = defaultEnrollmentRunner(), doctor = runDoctor, selection = {}, setup) {
|
|
3259
|
+
export function createProgram(client, io, enrollment = defaultEnrollmentRunner(), doctor = runDoctor, selection = {}, setup, setupAll) {
|
|
3232
3260
|
const program = new Command()
|
|
3233
3261
|
.name("agent-board")
|
|
3234
3262
|
.description("Thin CLI surface for the agent_board RPC and canonical read contracts")
|
|
@@ -3259,7 +3287,7 @@ export function createProgram(client, io, enrollment = defaultEnrollmentRunner()
|
|
|
3259
3287
|
addInviteCommands(program, client, io);
|
|
3260
3288
|
addEnrollCommands(program, io, enrollment);
|
|
3261
3289
|
addDoctorCommand(program, io, doctor);
|
|
3262
|
-
addSetupCommand(program, io, setup ?? ((options) => runSetup(options, { enroll: setupEnroller })));
|
|
3290
|
+
addSetupCommand(program, io, setup ?? ((options) => runSetup(options, { enroll: setupEnroller })), setupAllCommand(setupAll ?? ((options) => runSetupAll(options, { enroll: setupEnroller })), io));
|
|
3263
3291
|
return program;
|
|
3264
3292
|
}
|
|
3265
3293
|
export async function runCli(argv, io = {
|
|
@@ -3272,6 +3300,6 @@ export async function runCli(argv, io = {
|
|
|
3272
3300
|
...options.clientOptions,
|
|
3273
3301
|
resolveCredentials: fileAwareCredentials(selection, options.env ?? process.env),
|
|
3274
3302
|
});
|
|
3275
|
-
const program = createProgram(resolved, io, enrollment, doctor, selection, options.setup);
|
|
3303
|
+
const program = createProgram(resolved, io, enrollment, doctor, selection, options.setup, options.setupAll);
|
|
3276
3304
|
await program.parseAsync(argv, { from: "user" });
|
|
3277
3305
|
}
|
package/dist/doctor.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { type IdentityHarness, type OperationSuccess, type OsKeychainBackendOptions, type OsKeychainExec, type SessionRecordEntry, type TrustOptions } from "@consiliency/agent-board-client";
|
|
2
|
-
import { type ClaudePluginSummary, type RegistrationFs, type RegQuery, type RegistrationVerdict, type ScopeContext } from "./doctor_scopes.js";
|
|
1
|
+
import { type HarnessProcess, type IdentityHarness, type OperationSuccess, type OsKeychainBackendOptions, type OsKeychainExec, type SessionRecordEntry, type TrustOptions } from "@consiliency/agent-board-client";
|
|
2
|
+
import { type ClaudePluginSummary, type RegistrationFs, type RegQuery, type RegistrationVerdict, type ScopeContext, type ScopeFile } from "./doctor_scopes.js";
|
|
3
|
+
import { type ScanBudgets, type StorageRunner } from "./keyslot_status.js";
|
|
3
4
|
export type DoctorStatus = "pass" | "fail" | "undetermined";
|
|
4
5
|
export declare const DOCTOR_CHECKS: readonly ["identity_file", "keystore_key", "key_storage", "whoami", "board_membership", "registration", "runtime"];
|
|
5
6
|
export type DoctorCheckName = (typeof DOCTOR_CHECKS)[number];
|
|
@@ -7,6 +8,19 @@ export interface DoctorCheck {
|
|
|
7
8
|
status: DoctorStatus;
|
|
8
9
|
reason: string;
|
|
9
10
|
}
|
|
11
|
+
/**
|
|
12
|
+
* ONBOARD IF-0-ONBOARD-3 — `runtime`'s informational hint. It never changes `runtime`'s status or
|
|
13
|
+
* reason, or the exit code (pinned in `doctor_runtime_hint.test.ts`'s exit-invariance cases); where
|
|
14
|
+
* it overlaps `registration`'s existing `plugin_pin_mismatch`, that reason stays and the hint only
|
|
15
|
+
* explains it. Precedence when more than one applies: `session_predates_install`, then `no_session`
|
|
16
|
+
* (zero live records, and no pre-install process found either), then `stale_session_version`, then
|
|
17
|
+
* `no_target_first_session`, then `none`. `session_predates_install` is checked FIRST, ahead of
|
|
18
|
+
* `no_session`, because the motivating case (a harness that wrote no record at all before setup
|
|
19
|
+
* finished — the macmini claude case) has zero live records: a `no_session`-first order would
|
|
20
|
+
* mask it.
|
|
21
|
+
*/
|
|
22
|
+
export declare const RUNTIME_HINTS: readonly ["no_session", "stale_session_version", "session_predates_install", "no_target_first_session", "none"];
|
|
23
|
+
export type RuntimeHint = (typeof RUNTIME_HINTS)[number];
|
|
10
24
|
/**
|
|
11
25
|
* CCPLUGIN IF-0-CCPLUGIN-3 — the claude plugin report. Outside `DOCTOR_CHECKS`: it never changes
|
|
12
26
|
* the exit code. A version or ref prints only if it matches `REPORTABLE_TOKEN`, else
|
|
@@ -25,10 +39,37 @@ export interface PluginReport {
|
|
|
25
39
|
version_from: "source" | "cache" | null;
|
|
26
40
|
auto_update: ClaudePluginSummary["autoUpdate"];
|
|
27
41
|
}
|
|
42
|
+
/**
|
|
43
|
+
* #552 IF-552-3 — when the plaintext-key scan behind `key_storage` ran, and from where. A report,
|
|
44
|
+
* never a check: it does not change the exit. Present only once status.sh's measurement fields
|
|
45
|
+
* matched doctor's own (a report about another host, user or slot does not say when OUR scan ran).
|
|
46
|
+
* `source`: `fresh` (this doctor scanned), `cached` (a valid scan cache), `setup_run` (setup's
|
|
47
|
+
* in-process result, carried only where the cache could not store it), or `none` with `gave_up`
|
|
48
|
+
* when the scan hit its 30-minute bound.
|
|
49
|
+
*/
|
|
50
|
+
export interface KeyStorageScanReport {
|
|
51
|
+
source: "fresh" | "cached" | "setup_run" | "none";
|
|
52
|
+
scanned_at: string | null;
|
|
53
|
+
age_s: number | null;
|
|
54
|
+
duration_s: number | null;
|
|
55
|
+
cache_state: string | null;
|
|
56
|
+
gave_up?: {
|
|
57
|
+
after_s: number;
|
|
58
|
+
folders: number | null;
|
|
59
|
+
};
|
|
60
|
+
}
|
|
28
61
|
export interface DoctorReport {
|
|
29
62
|
harness: IdentityHarness;
|
|
30
63
|
exit: 0 | 1 | 2;
|
|
31
64
|
checks: Record<DoctorCheckName, DoctorCheck>;
|
|
65
|
+
/**
|
|
66
|
+
* ONBOARD IF-0-ONBOARD-3 — informational only; omitted (not `"none"`) when nothing was found, so
|
|
67
|
+
* `Object.keys(report)` for a report with no `installedAt`/`installedVersion` evidence carried in
|
|
68
|
+
* is byte-unchanged from before this field existed.
|
|
69
|
+
*/
|
|
70
|
+
runtime_hint?: RuntimeHint;
|
|
71
|
+
/** keyslot identities only (IF-552-3). */
|
|
72
|
+
key_storage_scan?: KeyStorageScanReport;
|
|
32
73
|
/** claude only. */
|
|
33
74
|
plugin?: PluginReport;
|
|
34
75
|
/** claude only: wake hooks per required event in doctor's cwd, across settings and plugins. */
|
|
@@ -113,7 +154,73 @@ export interface DoctorOptions {
|
|
|
113
154
|
machineIdFile?: string;
|
|
114
155
|
/** key_storage: doctor's own uid. Default `process.getuid()`. */
|
|
115
156
|
uid?: number;
|
|
157
|
+
/** `--rescan`: phase 2 always runs, as `--scan-cache fresh` (IF-552-3). */
|
|
158
|
+
rescan?: boolean;
|
|
159
|
+
/**
|
|
160
|
+
* SETUP ONLY, never a CLI flag: the key step's FRESH, validated clean scan, carried in-process so
|
|
161
|
+
* setup scans once where the cache could not store it. Used only under IF-552-3's three conditions.
|
|
162
|
+
*/
|
|
163
|
+
setupScan?: SetupScan;
|
|
164
|
+
/** key_storage: where the scan notices go. Default `process.stderr`. */
|
|
165
|
+
writeStderr?: (line: string) => void;
|
|
166
|
+
/** key_storage: the status.sh runner and budgets (tests). */
|
|
167
|
+
statusRunner?: StorageRunner;
|
|
168
|
+
statusBudgets?: Partial<ScanBudgets>;
|
|
169
|
+
/** key_storage: the clock the carry's validity window reads, epoch seconds. Default now. */
|
|
170
|
+
nowSeconds?: () => number;
|
|
171
|
+
/**
|
|
172
|
+
* ONBOARD IF-0-ONBOARD-3 — when this harness's board package was last installed or upgraded.
|
|
173
|
+
* Overrides the default (`defaultInstallEvidence`, derived from doctor's own registration scan:
|
|
174
|
+
* claude's plugin verdict, or the winning scope file's mtime for every other harness). Where
|
|
175
|
+
* neither an override nor the default can prove one, `runtime_hint` never fires
|
|
176
|
+
* `session_predates_install`/`stale_session_version` from it — never fabricated from a guess.
|
|
177
|
+
*/
|
|
178
|
+
installedAt?: Date;
|
|
179
|
+
/**
|
|
180
|
+
* ONBOARD IF-0-ONBOARD-3 — the version setup just installed or upgraded a package to, by that
|
|
181
|
+
* package's `packageName` (a live record's own field, since one harness's live records can carry
|
|
182
|
+
* more than one package — e.g. pi's own `wake_producer` plus mcp's `tools_only`, R-2). Overrides
|
|
183
|
+
* the default the same way `installedAt` does; null (or omitted entirely) makes no claim.
|
|
184
|
+
*/
|
|
185
|
+
installedVersion?: (packageName: string) => string | null;
|
|
186
|
+
/**
|
|
187
|
+
* ONBOARD IF-0-ONBOARD-3 — this harness's own running processes, for `session_predates_install`.
|
|
188
|
+
* Default: the client's `listHarnessProcesses`. Test injection only needs to honour
|
|
189
|
+
* `startedBefore` when it cares about the cutoff; production always passes it.
|
|
190
|
+
*/
|
|
191
|
+
harnessProcesses?: (harness: IdentityHarness, options: {
|
|
192
|
+
startedBefore?: Date;
|
|
193
|
+
}) => HarnessProcess[];
|
|
194
|
+
/**
|
|
195
|
+
* ONBOARD IF-0-ONBOARD-3 — the default `installedAt` derivation's own file-mtime read, for the
|
|
196
|
+
* config/plugin-source file that named a package's pinned version. Default: `fs.statSync(path).mtime`.
|
|
197
|
+
* Unrelated to `registrationFs.lstat`, which carries no mtime.
|
|
198
|
+
*/
|
|
199
|
+
statMtime?: (path: string) => Date | null;
|
|
200
|
+
}
|
|
201
|
+
/** The key step's clean fresh scan, as setup carries it to its own doctor (IF-552-3). */
|
|
202
|
+
export interface SetupScan {
|
|
203
|
+
/** `scan.inputs` of the key step's report: every field must deep-equal phase 1's. */
|
|
204
|
+
inputs: Record<string, unknown>;
|
|
205
|
+
/** The scan object itself (clean: `plaintext_matches` empty). */
|
|
206
|
+
scan: Record<string, unknown>;
|
|
207
|
+
/** The scan's start, epoch seconds. */
|
|
208
|
+
scannedAt: number;
|
|
209
|
+
/** The key step's own cache write, `<state>:<reason>` (e.g. `not_written:write_failed`). */
|
|
210
|
+
cacheWrite: string;
|
|
116
211
|
}
|
|
212
|
+
/** The IF-552-2 validity window, the same for the carry: `scannedAt ≤ now + 300` and `now − scannedAt < 86400`. */
|
|
213
|
+
export declare const SCAN_CACHE_TTL_S = 86400;
|
|
214
|
+
export declare const SCAN_CACHE_FUTURE_S = 300;
|
|
215
|
+
/**
|
|
216
|
+
* IF-552-3's carry: setup's result serves instead of phase 2 only when (1) phase 1's `scan.inputs`
|
|
217
|
+
* deep-equals the carried inputs, (2) the cache could not serve STRUCTURALLY (phase 1 `unusable`
|
|
218
|
+
* with a structural reason, or `miss:absent` beside a key-step write of `not_written:untrusted_dir`
|
|
219
|
+
* or `write_failed`; `lock_busy` never qualifies: a held lock may be a tombstone being written), and
|
|
220
|
+
* (3) the carried scan is inside the validity window. Reads phase 1's `scan.cache`, never only
|
|
221
|
+
* `exit2_reasons`.
|
|
222
|
+
*/
|
|
223
|
+
export declare function carryQualifies(phase1: Record<string, unknown>, carried: SetupScan, now: number): boolean;
|
|
117
224
|
/**
|
|
118
225
|
* An error's `code`, only when this repository wrote it: a member of the derived
|
|
119
226
|
* AUTHORED_CAUSE_CODES that this instance carries under the authored provenance brand — the
|
|
@@ -151,12 +258,46 @@ export declare function osKeychainProbe(spawn?: OsKeychainExec, where?: Pick<OsK
|
|
|
151
258
|
/** The default agent_socket probe: the loaded key's fingerprint, derived from the agent's identity list. */
|
|
152
259
|
export declare const agentSocketProbe: KeystoreProbe;
|
|
153
260
|
export declare const OS_KEYCHAIN_PRESENT = "present; key_id proven server-side by whoami; file fingerprint unverified for os_keychain";
|
|
261
|
+
/** Every exit-2 cause status.sh can emit (KSC-8 derives the list from status.sh and probe.sh). */
|
|
262
|
+
export declare const STATUS_EXIT2_REASON: RegExp;
|
|
154
263
|
interface FileIdentity {
|
|
155
264
|
handle: string;
|
|
156
265
|
fingerprint: string;
|
|
157
266
|
}
|
|
158
267
|
/** IF-0-CREDPATH-4 runtime, over already-read records. Exported for its fixtures. */
|
|
159
268
|
export declare function judgeRuntime(harness: IdentityHarness, entries: readonly SessionRecordEntry[], identity: FileIdentity | null): DoctorCheck;
|
|
269
|
+
/**
|
|
270
|
+
* ONBOARD IF-0-ONBOARD-3 — `runtime_hint`, over the SAME already-read `entries` `judgeRuntime` saw.
|
|
271
|
+
* Exported for its fixtures. Never reads `checks.runtime`'s own verdict, and never influences it:
|
|
272
|
+
* this is purely informational (see `RUNTIME_HINTS`'s doc comment for the precedence).
|
|
273
|
+
*/
|
|
274
|
+
export declare function judgeRuntimeHint(harness: IdentityHarness, entries: readonly SessionRecordEntry[], fs: Pick<RegistrationFs, "realpath">, options: Pick<DoctorOptions, "installedAt" | "installedVersion" | "harnessProcesses">): RuntimeHint;
|
|
275
|
+
export interface DefaultInstallEvidence {
|
|
276
|
+
installedAt?: Date;
|
|
277
|
+
installedVersion: (packageName: string) => string | null;
|
|
278
|
+
}
|
|
279
|
+
/**
|
|
280
|
+
* ONBOARD IF-0-ONBOARD-3 — the default `installedAt`/`installedVersion`, derived ONLY from what
|
|
281
|
+
* doctor already reads for its own `registration` check (never a new probe): each scope file's
|
|
282
|
+
* board-identified mcp entry (codex/grok/agy, and claude's own non-plugin mcp registrations — an
|
|
283
|
+
* `npx -y @consiliency/agent-board-mcp@<version> …` stanza) and `packages`/`plugin` spec (pi,
|
|
284
|
+
* opencode — a pinned `<package>@<version>` entry). `files` is in ascending precedence (SAME as
|
|
285
|
+
* `readScopes` returns it), so a later file's match for the same package overrides an earlier
|
|
286
|
+
* one's, exactly as the harness itself would resolve it.
|
|
287
|
+
*
|
|
288
|
+
* For claude, the plugin verdict `pinCheck` already computed and trusts (never an unvalidated
|
|
289
|
+
* directory source) is preferred over a raw config scan, per its own `versionFrom`/`sourceKind`
|
|
290
|
+
* guard — the same one `pinCheck` applies.
|
|
291
|
+
*
|
|
292
|
+
* `installedAt` is the LATEST mtime among every file that named a version this way: several files
|
|
293
|
+
* naming different packages (pi's own `packages` row plus an unrelated mcp row) both count, since
|
|
294
|
+
* either being freshly written is evidence this harness's board setup was just touched. A file (or
|
|
295
|
+
* a claude directory source) whose mtime cannot be read contributes nothing — not an error, and
|
|
296
|
+
* never lowers the claim to "no evidence at all" by itself.
|
|
297
|
+
*
|
|
298
|
+
* Exported for its fixtures.
|
|
299
|
+
*/
|
|
300
|
+
export declare function defaultInstallEvidence(harness: IdentityHarness, plugin: ClaudePluginSummary | undefined, files: readonly ScopeFile[], fs: RegistrationFs, statMtime: (path: string) => Date | null): DefaultInstallEvidence;
|
|
160
301
|
export declare function runDoctor(options: DoctorOptions): Promise<DoctorReport>;
|
|
161
302
|
/** Human-readable form; `--json` prints the report object itself. */
|
|
162
303
|
export declare function formatDoctorReport(report: DoctorReport): string;
|