@consiliency/agent-board-cli 1.26.0 → 1.27.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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-board",
3
- "version": "1.31.1",
3
+ "version": "1.31.2",
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.31.1",
13
+ "@consiliency/agent-board-mcp@1.31.2",
14
14
  "--harness",
15
15
  "claude"
16
16
  ]
package/dist/assets.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
- "mcpVersion": "1.31.1",
3
- "piVersion": "0.4.1",
4
- "opencodeVersion": "0.2.1",
5
- "releaseTag": "v1.54.0"
2
+ "mcpVersion": "1.31.2",
3
+ "piVersion": "0.4.2",
4
+ "opencodeVersion": "0.2.2",
5
+ "releaseTag": "v1.55.0"
6
6
  }
package/dist/doctor.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { type IdentityHarness, type OperationSuccess, type OsKeychainExec, type SessionRecordEntry, type TrustOptions } from "@consiliency/agent-board-client";
1
+ import { type IdentityHarness, type OperationSuccess, type OsKeychainBackendOptions, type OsKeychainExec, type SessionRecordEntry, type TrustOptions } from "@consiliency/agent-board-client";
2
2
  import { type ClaudePluginSummary, type RegistrationFs, type RegQuery, type RegistrationVerdict, type ScopeContext } from "./doctor_scopes.js";
3
3
  export type DoctorStatus = "pass" | "fail" | "undetermined";
4
4
  export declare const DOCTOR_CHECKS: readonly ["identity_file", "keystore_key", "key_storage", "whoami", "board_membership", "registration", "runtime"];
@@ -147,7 +147,7 @@ export declare function fileIdentityEnv(ambient: NodeJS.ProcessEnv, values: Reco
147
147
  * for the keychain CLI. Nothing here reads its output; doctor.test.ts K-0 pins that no
148
148
  * key-decoding API is reachable.
149
149
  */
150
- export declare function osKeychainProbe(spawn?: OsKeychainExec): KeystoreProbe;
150
+ export declare function osKeychainProbe(spawn?: OsKeychainExec, where?: Pick<OsKeychainBackendOptions, "platform" | "homeDir">): KeystoreProbe;
151
151
  /** The default agent_socket probe: the loaded key's fingerprint, derived from the agent's identity list. */
152
152
  export declare const agentSocketProbe: KeystoreProbe;
153
153
  export declare const OS_KEYCHAIN_PRESENT = "present; key_id proven server-side by whoami; file fingerprint unverified for os_keychain";
package/dist/doctor.js CHANGED
@@ -14,7 +14,7 @@ import { randomBytes } from "node:crypto";
14
14
  import { existsSync, readdirSync } from "node:fs";
15
15
  import { homedir, userInfo } from "node:os";
16
16
  import { join, resolve } from "node:path";
17
- import { AUTHORED_CAUSE_CODES, AgentSocketSelectionError, GENERIC_SUPABASE_FIELDS, IDENTITY_FILE_KEYS, IDENTITY_TRIGGER_FIELDS, KeystoreUnavailableError, assertIdentityHarness, codeProvenance, createAgentBoardClient, createOsKeychainBackend, expectedWakeTier, normalizeKeyReference, readIdentityFile, readSessionRecords, resolveCredentials, selectAgentSocketIdentity, } from "@consiliency/agent-board-client";
17
+ import { AUTHORED_CAUSE_CODES, AgentSocketSelectionError, GENERIC_SUPABASE_FIELDS, IDENTITY_FILE_KEYS, IDENTITY_TRIGGER_FIELDS, KeystoreUnavailableError, assertIdentityHarness, codeProvenance, createAgentBoardClient, createOsKeychainBackend, expectedWakeTier, normalizeKeyReference, osKeychainFailureOf, readIdentityFile, readSessionRecords, resolveCredentials, selectAgentSocketIdentity, } from "@consiliency/agent-board-client";
18
18
  import { judgeRegistration, nodeRegistrationFs, } from "./doctor_scopes.js";
19
19
  import { resolveKeyslotDir } from "./assets.js";
20
20
  import { SLOT_RE, externalDeclarationProblems, fileIdentity, keyslotSocketDir, machineIdOf, resolveAgentSocketPath, unsealedOptinProblems, } from "./setup_storage.js";
@@ -176,11 +176,11 @@ function defaultClient(fileEnv) {
176
176
  * for the keychain CLI. Nothing here reads its output; doctor.test.ts K-0 pins that no
177
177
  * key-decoding API is reachable.
178
178
  */
179
- export function osKeychainProbe(spawn) {
179
+ export function osKeychainProbe(spawn, where = {}) {
180
180
  return async (keyReference) => {
181
181
  const reference = normalizeKeyReference(keyReference);
182
182
  try {
183
- const capability = await createOsKeychainBackend(spawn ? { exec: spawn } : {})(reference);
183
+ const capability = await createOsKeychainBackend({ ...where, ...(spawn ? { exec: spawn } : {}) })(reference);
184
184
  await capability.sign(randomBytes(32));
185
185
  return { kind: "present_unverified" };
186
186
  }
@@ -190,6 +190,15 @@ export function osKeychainProbe(spawn) {
190
190
  if (error instanceof KeystoreUnavailableError && /no key found/.test(error.message)) {
191
191
  return { kind: "absent", reason: "key_absent" };
192
192
  }
193
+ // #554: the client classifies a platform keychain failure. A key held only OUTSIDE the macOS
194
+ // login keychain is not usable (the adapter reads the login keychain) and is a fail; any other
195
+ // class is carried in the reason, so a locked keychain reads `keystore_unavailable:keychain_locked`
196
+ // (fix: `security unlock-keychain <login keychain>`) instead of the bare code.
197
+ const classified = osKeychainFailureOf(error);
198
+ if (classified?.failure === "item_outside_login_keychain")
199
+ return { kind: "absent", reason: "key_outside_login_keychain" };
200
+ if (classified)
201
+ return { kind: "unreachable", reason: `keystore_unavailable:${classified.failure}` };
193
202
  return { kind: "unreachable", reason: codeOf(error, "keystore_unavailable") };
194
203
  }
195
204
  };
@@ -110,6 +110,18 @@ export declare function repairKeyDir(env: NodeJS.ProcessEnv, uid: number): {
110
110
  problems: string[];
111
111
  root: string | null;
112
112
  };
113
+ /**
114
+ * Why the macOS login keychain did not round-trip, from the probe's classification (#554). Only a
115
+ * LOCKED keychain is told to unlock: before #554 every cause got that fix, including the
116
+ * write-permission failure an SSH session produced by writing to System.keychain, where unlocking
117
+ * the login keychain changed nothing.
118
+ */
119
+ /** A path as one POSIX shell word: bare when it is plainly safe, else single-quoted ('\'' for a quote). */
120
+ export declare function shellWord(value: string): string;
121
+ export declare function macosKeychainRefusal(probe: OsKeychainProbeResult | null): {
122
+ reason: string;
123
+ fix: string;
124
+ };
113
125
  /**
114
126
  * IF-0-SETUP-3: the key storage, measured, in the IF-0-KEYSEAL-6 order. Never throws for a host
115
127
  * condition; a refusal carries the fix.
@@ -23,7 +23,7 @@ import { execFile } from "node:child_process";
23
23
  import { createHash } from "node:crypto";
24
24
  import { chmodSync, closeSync, constants, fstatSync, lstatSync, mkdirSync, openSync, readFileSync, realpathSync, statSync } from "node:fs";
25
25
  import { dirname, isAbsolute, join } from "node:path";
26
- import { probeOsKeychainRoundTrip } from "@consiliency/agent-board-client";
26
+ import { OS_KEYCHAIN_SERVICE, probeOsKeychainRoundTrip } from "@consiliency/agent-board-client";
27
27
  import { resolveKeyslotDir } from "./assets.js";
28
28
  export const SLOT_RE = /^[a-z][a-z0-9-]{0,31}$/;
29
29
  const REFERENCE_LINE = /^agent_socket:\/\S+#[A-Za-z0-9_-]{43}$/;
@@ -347,6 +347,48 @@ function externalAgent(agentSocket, env, uid, machineIdFile, repairs) {
347
347
  return { storage: "external", reason: "external_declared", keyReference: reference, repairs };
348
348
  return refused(problems.includes("missing") ? "external_undeclared" : "external_declaration_mismatch", EXTERNAL_FIX(declaration), repairs);
349
349
  }
350
+ const OR_EXTERNAL = "or pass --agent-socket <ref> with an external-agent declaration";
351
+ /**
352
+ * Why the macOS login keychain did not round-trip, from the probe's classification (#554). Only a
353
+ * LOCKED keychain is told to unlock: before #554 every cause got that fix, including the
354
+ * write-permission failure an SSH session produced by writing to System.keychain, where unlocking
355
+ * the login keychain changed nothing.
356
+ */
357
+ /** A path as one POSIX shell word: bare when it is plainly safe, else single-quoted ('\'' for a quote). */
358
+ export function shellWord(value) {
359
+ return /^[A-Za-z0-9_\/.,:@%+=-]+$/.test(value) ? value : `'${value.replace(/'/g, "'\\''")}'`;
360
+ }
361
+ export function macosKeychainRefusal(probe) {
362
+ // Printed for pasting into a shell: a real path is quoted; the fallback keeps `~` unquoted so it expands.
363
+ const kc = probe?.keychain !== undefined ? shellWord(probe.keychain) : "~/Library/Keychains/login.keychain-db";
364
+ switch (probe?.failure) {
365
+ case "keychain_locked":
366
+ return { reason: "macos_keychain_locked", fix: `the login keychain is locked, and this session cannot show an unlock prompt: run \`security unlock-keychain ${kc}\` and re-run setup` };
367
+ case "write_denied":
368
+ return { reason: "macos_keychain_write_denied", fix: `the keychain refused the write (errSecWrPerm) to ${kc}: check that it is your login keychain and that you own it (ls -l ${kc}), ${OR_EXTERNAL}` };
369
+ case "login_keychain_absent":
370
+ return { reason: "macos_login_keychain_absent", fix: `there is no login keychain in ~/Library/Keychains (login.keychain-db or login.keychain): log in once at this Mac's console, which creates it, ${OR_EXTERNAL}` };
371
+ case "item_outside_login_keychain": {
372
+ // Setup only ever sees its PROBE: the item found outside the login keychain is the probe's own
373
+ // throwaway, not this harness's key (a harness key held elsewhere surfaces at enrollment, and in
374
+ // doctor as key_outside_login_keychain). So say that, and offer only the throwaway's cleanup.
375
+ const holder = probe?.heldBy !== undefined ? shellWord(probe.heldBy) : null;
376
+ const cleanup = holder !== null && probe?.handle !== undefined
377
+ ? `; remove the test item with \`security delete-generic-password -s ${OS_KEYCHAIN_SERVICE} -a ${probe.handle} ${holder}\``
378
+ : "";
379
+ return { reason: "macos_keychain_item_elsewhere", fix: `setup's throwaway test item was found outside the login keychain, in ${holder ?? "another keychain on your search list"}, after it was deleted from the login keychain, so this account's keychain search list or default keychain is unusual: check \`security list-keychains -d user\` and \`security default-keychain -d user\`${cleanup}, then re-run setup, ${OR_EXTERNAL}` };
380
+ }
381
+ case "item_search_undetermined":
382
+ return { reason: "macos_keychain_search_undetermined", fix: "a keychain on your search list could not be searched (it may be locked): unlock it or remove it from the search list (security list-keychains -d user), then re-run setup" };
383
+ case "login_keychain_untrusted":
384
+ case "login_keychain_path_unsafe":
385
+ return { reason: "macos_login_keychain_unusable", fix: `${kc} is not usable: it must be a regular file you own (not a symlink) at a path with no whitespace, quotes or backslashes; fix it, ${OR_EXTERNAL}` };
386
+ default: {
387
+ const detail = probe ? `${probe.outcome}${probe.failure ? `, ${probe.failure}` : ""}` : "the probe did not answer";
388
+ return { reason: "macos_keychain_unavailable", fix: `the login keychain did not complete a round trip (${detail}): run setup from a logged-in session, ${OR_EXTERNAL}` };
389
+ }
390
+ }
391
+ }
350
392
  /**
351
393
  * IF-0-SETUP-3: the key storage, measured, in the IF-0-KEYSEAL-6 order. Never throws for a host
352
394
  * condition; a refusal carries the fix.
@@ -378,7 +420,7 @@ export async function selectKeyStorage(ctx) {
378
420
  let gate = null;
379
421
  if (platform !== "linux") {
380
422
  gate = platform === "darwin"
381
- ? { reason: "macos_keychain_unavailable", fix: "the login keychain did not complete a round trip: unlock it (security unlock-keychain) in a logged-in session and re-run setup" }
423
+ ? macosKeychainRefusal(keychain)
382
424
  : { reason: "platform_unsupported", fix: "setup's key storage supports Linux and macOS; on Windows run setup inside WSL, or pass --agent-socket <ref> with an external-agent declaration" };
383
425
  }
384
426
  else {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@consiliency/agent-board-cli",
3
- "version": "1.26.0",
3
+ "version": "1.27.0",
4
4
  "repository": "https://github.com/Consiliency/message-board.git",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -33,7 +33,7 @@
33
33
  "registry": "https://registry.npmjs.org"
34
34
  },
35
35
  "dependencies": {
36
- "@consiliency/agent-board-client": "1.40.0",
36
+ "@consiliency/agent-board-client": "1.41.0",
37
37
  "commander": "^14.0.1"
38
38
  },
39
39
  "license": "Apache-2.0",