@mnemom/mnemom 0.17.18 → 0.17.19

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.
@@ -14,9 +14,8 @@
14
14
  */
15
15
  import { spawnSync } from "node:child_process";
16
16
  import { existsSync } from "node:fs";
17
- import { anthropicKeySource, checkRcCapability, defaultConsoleKeyDeps, resolveCliBin, } from "./agent.js";
18
- import { readConsoleKey } from "../lib/console-key.js";
19
- import { resolveAgentAuth } from "../lib/subscription-login.js";
17
+ import { anthropicKeySource, checkRcCapability, resolveCliBin } from "./agent.js";
18
+ import { resolveAgentAuth, savedAuthIsRetiredConsole, } from "../lib/subscription-login.js";
20
19
  import { isProdGateway, resolveAnthropicDoor, resolveGatewayHost, singleCellError, } from "../lib/agent.js";
21
20
  import { getApiUrl, getGatewayUrl } from "../lib/config.js";
22
21
  import { LAUNCHER_CONFIG_PATH } from "../lib/launcher-config.js";
@@ -55,17 +54,14 @@ export async function agentDoctorCommand(options = {}) {
55
54
  auth = resolveAgentAuth(undefined, env);
56
55
  }
57
56
  catch (err) {
58
- add("miss", `auth: ${err instanceof Error ? err.message : String(err)}`, "`mnemom agent config set auth api-key` (or subscription, console)");
57
+ add("miss", `auth: ${err instanceof Error ? err.message : String(err)}`, "`mnemom agent config set auth api-key` (or subscription)");
58
+ }
59
+ if (savedAuthIsRetiredConsole(env)) {
60
+ add("info", "auth: console is no longer supported — launches use an Anthropic API key", "`mnemom agent config set auth api-key` (create a key at console.anthropic.com)");
59
61
  }
60
62
  if (auth === "subscription") {
61
63
  add("ok", "auth: subscription — Claude Code uses your claude.ai login (Team/Enterprise plans only); no Anthropic key needed");
62
64
  }
63
- else if (auth === "console") {
64
- if (readConsoleKey(defaultConsoleKeyDeps(env)))
65
- add("ok", "auth: console — using the API key your Claude Code Anthropic Console login created");
66
- else
67
- add("miss", "auth: console — Claude Code has no Anthropic Console login on this machine", 'run `claude`, then /login → "Anthropic Console account" — or `mnemom agent config set auth api-key`');
68
- }
69
65
  else {
70
66
  const keySrc = anthropicKeySource(env);
71
67
  if (keySrc === "env")
@@ -28,7 +28,6 @@
28
28
  import { type MnemomKeyDeps } from "../lib/subscription-login.js";
29
29
  import { type SessionRecord } from "../lib/agent-ledger.js";
30
30
  import { type ExperimentalId } from "../lib/experimental.js";
31
- import { type ConsoleKeyDeps } from "../lib/console-key.js";
32
31
  import { type GovernanceOptIn } from "./agent-setup.js";
33
32
  import { type ContextProfile, type LaunchShape, type SealedContractEnvelope } from "../lib/agent.js";
34
33
  export interface AgentOptions {
@@ -228,8 +227,6 @@ export declare function recordSessionLaunch(ctx: {
228
227
  }): void;
229
228
  /** The real I/O behind a subscription launch's Mnemom key (lib/subscription-login.ts). */
230
229
  export declare function defaultMnemomKeyDeps(storePath?: string): MnemomKeyDeps;
231
- /** The real machine behind lib/console-key.ts: the macOS keychain via `security`, no shell. */
232
- export declare function defaultConsoleKeyDeps(env: NodeJS.ProcessEnv): ConsoleKeyDeps;
233
230
  /**
234
231
  * Resolve the Anthropic key with NO 1Password dependency (built for external
235
232
  * customer adoption), in order:
@@ -28,13 +28,13 @@
28
28
  import { spawn, spawnSync } from "node:child_process";
29
29
  import { existsSync, mkdtempSync, readdirSync, readFileSync, rmSync, statSync, writeFileSync, renameSync, } from "node:fs";
30
30
  import { randomUUID } from "node:crypto";
31
- import { tmpdir, homedir, hostname, userInfo } from "node:os";
31
+ import { tmpdir, homedir, hostname } from "node:os";
32
32
  import { basename, join, dirname } from "node:path";
33
33
  import { fileURLToPath } from "node:url";
34
34
  import { MEASURED_ENV, statuslineLaunch } from "../lib/statusline.js";
35
35
  import { getApiUrl, getGatewayUrl, MNEMOM_DIR } from "../lib/config.js";
36
36
  import { createApiKey } from "../lib/api.js";
37
- import { MNEMOM_KEY_STORE_FIELD, resolveAgentAuth, resolveSubscriptionMnemomKey, subscriptionChildEnv, subscriptionShape, } from "../lib/subscription-login.js";
37
+ import { MNEMOM_KEY_STORE_FIELD, resolveAgentAuth, sayRetiredConsoleNotice, resolveSubscriptionMnemomKey, subscriptionChildEnv, subscriptionShape, } from "../lib/subscription-login.js";
38
38
  import { LEDGER_FILE, appendSessionRecord, findResumeCandidate, readSessionRecords, resolveRetentionDays, resumedContextProfile, toLedgerContract, } from "../lib/agent-ledger.js";
39
39
  import { isLoggedIn } from "../lib/auth.js";
40
40
  import { loadCliConfig } from "../lib/cli-config.js";
@@ -43,7 +43,6 @@ import { experimentalNotice } from "../lib/experimental.js";
43
43
  import { fmt } from "../lib/format.js";
44
44
  import { askInput, askSelect, askYesNo, isInteractive } from "../lib/prompt.js";
45
45
  import { authConfigured, chooseAuth, detectClaudeLogin, readClaudeLoginState, } from "../lib/login-detect.js";
46
- import { requireConsoleKey, readConsoleKey } from "../lib/console-key.js";
47
46
  import { goalAlignmentLine, resolveSessionSetup } from "./agent-session.js";
48
47
  import { defaultAgentSlug, ensureClaimedServedAgent, ensureSessionAndBalance, isQuietPreambleExit, resolveDefaultSlug, resolvePreflightedAccount, } from "./agent-setup.js";
49
48
  import { assembleContract, buildGoalModeHeader, buildCustomHeaders, buildHandoffMcpConfigJson, buildSessionConfig, buildSessionConfigB64, assertCompactAtCompatible, assertOffProfileCompatible, compactAtWindowWarning, describeCompactAt, launchWindow, resolveCompactAt, applyGatewayHintHeaders, resolveCompactionIntercept, resolveContextHandoff, resolveContextProfile, resolveHandoffGatewayHost, resolveUsageSavings, HANDOFF_RC_KEY_ENV_NAME, isProdGateway, launchPreferenceExplicit, launchPreflighted, remoteControlAllowed, applyToolSearchDefault, DISABLE_1M_ENV, readCcSettings, resolveWindowModel, resolveAnthropicDoor, resolveLaunchModel, resolveConversationId, resolveGatewayHost, singleCellError, resolveLaunchShape, resolveRemoteControlShape, passthroughName, sanitizeAgentName, resumeCommand, sessionDisplayName, SESSION_NAME_ENV, SESSION_CONFIG_HEADER, } from "../lib/agent.js";
@@ -141,13 +140,13 @@ export async function agentCommand(scenarioArg, options = {}) {
141
140
  // How Claude Code authenticates: an Anthropic key (default) or the user's claude.ai
142
141
  // subscription login (Team/Enterprise; lib/subscription-login.ts).
143
142
  let auth = resolveAgentAuth(options.auth, env);
143
+ sayRetiredConsoleNotice(options.auth, env, say);
144
144
  // Nothing configured it yet: detect Claude Code's own login and, when there is a real
145
145
  // choice, ask once and save the answer (lib/login-detect.ts). Never on --dry-run.
146
146
  if (!options.dryRun && !authConfigured(options.auth, env)) {
147
147
  const loginState = readClaudeLoginState(env, homedir(), (path) => readFileSync(path, "utf8"));
148
148
  auth = await chooseAuth({
149
149
  login: detectClaudeLogin(loginState),
150
- consoleKeyAvailable: () => readConsoleKey(defaultConsoleKeyDeps(env)) !== undefined,
151
150
  interactive: isInteractive(),
152
151
  select: askSelect,
153
152
  persist: (choice) => {
@@ -309,9 +308,6 @@ export async function agentCommand(scenarioArg, options = {}) {
309
308
  say("mnemom agent: auth = subscription — Claude Code would use your claude.ai login (Team/Enterprise plans only) with a Mnemom API key; no Anthropic key.");
310
309
  }
311
310
  say(`mnemom agent: the real launch first confirms the agent the gateway serves for '${agent}' is claimed — claiming it into the org the launch bills if it is not — and never runs on an unclaimed agent.`);
312
- if (auth === "console") {
313
- say("mnemom agent: auth = console — the launch would use the API key your Claude Code Anthropic Console login created.");
314
- }
315
311
  if (expNotice)
316
312
  say(expNotice);
317
313
  if (shape !== "terminal") {
@@ -354,12 +350,9 @@ export async function agentCommand(scenarioArg, options = {}) {
354
350
  // AFTER the account gate above: an account Mnemom Agent is not enabled for learns that
355
351
  // (and how to get access) before it is asked for, or refused for want of, a key.
356
352
  // Not on a subscription launch: Claude Code then sends its own claude.ai login instead.
357
- // A Console launch uses the key Claude Code's Console login created, then runs as api-key.
358
353
  const anthropicKey = auth === "subscription"
359
354
  ? undefined
360
- : auth === "console"
361
- ? requireConsoleKey(defaultConsoleKeyDeps(env))
362
- : await resolveAnthropicKey(env, { allowPrompt: true, keySource: options.keySource });
355
+ : await resolveAnthropicKey(env, { allowPrompt: true, keySource: options.keySource });
363
356
  // ── Remote-Control entitlement gate ─────────────────────────────────────────
364
357
  // Remote Control (a session steerable from claude.ai / the Claude app) is
365
358
  // internal-only. Now that we know the org's entitlement, resolve the final
@@ -837,23 +830,6 @@ export function defaultMnemomKeyDeps(storePath = CODE_STORE) {
837
830
  say,
838
831
  };
839
832
  }
840
- /** The real machine behind lib/console-key.ts: the macOS keychain via `security`, no shell. */
841
- export function defaultConsoleKeyDeps(env) {
842
- return {
843
- platform: process.platform,
844
- env,
845
- username: () => userInfo().username,
846
- readKeychain: (service, account) => {
847
- const r = spawnSync("security", ["find-generic-password", "-a", account, "-w", "-s", service], {
848
- encoding: "utf8",
849
- timeout: 5_000,
850
- });
851
- return r.status === 0 ? r.stdout : undefined;
852
- },
853
- claudeJson: readClaudeLoginState(env, homedir(), (path) => readFileSync(path, "utf8"))
854
- .claudeJson,
855
- };
856
- }
857
833
  /**
858
834
  * Resolve the Anthropic key with NO 1Password dependency (built for external
859
835
  * customer adoption), in order:
package/dist/index.js CHANGED
@@ -165,7 +165,7 @@ const agentCmd = program
165
165
  // built at load, before any sign-in, so it cannot ask the entitlement; the flags still parse.
166
166
  .addOption(new Option("--remote-control", "Interactive Remote Control session in THIS terminal (behind a local proxy)").hideHelp())
167
167
  .addOption(new Option("--server", "Headless `claude remote-control` dispatcher (behind a local proxy)").hideHelp())
168
- .option("--auth <mode>", "How Claude Code authenticates: api-key (default) | subscription (your Claude Team/Enterprise login) | console (the API key your Claude Code Anthropic Console login created)")
168
+ .option("--auth <mode>", "How Claude Code authenticates: api-key (default) | subscription (your Claude Team/Enterprise login)")
169
169
  .option("--model <alias|id>", "Model to launch (CLI alias or concrete id)")
170
170
  .addOption(new Option("--effort <level>", "Reasoning effort").choices([
171
171
  "low",
@@ -15,7 +15,7 @@
15
15
  * resolvers in lib/code.ts keep the built-in default as the final fallback);
16
16
  * the caller folds the plain-value settings under the CLI flags.
17
17
  */
18
- import { type AgentAuth } from "./subscription-login.js";
18
+ import { type SavedAgentAuth } from "./subscription-login.js";
19
19
  import { type ExperimentalId } from "./experimental.js";
20
20
  import { type ContextProfile, type GoalModeDefault, type LaunchShape } from "./agent.js";
21
21
  /** The human-editable settings file. Secret key lives elsewhere (launcher.json). */
@@ -31,7 +31,7 @@ export interface LauncherConfig {
31
31
  cli?: string;
32
32
  launch?: LaunchShape;
33
33
  /** How Claude Code authenticates (lib/subscription-login.ts); api-key when unset. */
34
- auth?: AgentAuth;
34
+ auth?: SavedAgentAuth;
35
35
  keySource?: KeySource;
36
36
  /** Standing goal-alignment default when neither --goal nor --implicit is given. */
37
37
  goalMode?: GoalModeDefault;
@@ -15,7 +15,7 @@
15
15
  * resolvers in lib/code.ts keep the built-in default as the final fallback);
16
16
  * the caller folds the plain-value settings under the CLI flags.
17
17
  */
18
- import { AGENT_AUTH_ENV, AGENT_AUTHS, coerceAgentAuth, } from "./subscription-login.js";
18
+ import { AGENT_AUTH_ENV, AGENT_AUTHS, coerceAgentAuth, parseSavedAgentAuth, } from "./subscription-login.js";
19
19
  import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
20
20
  import { dirname, join } from "node:path";
21
21
  import { parse as parseToml, stringify as stringifyToml, TomlError } from "smol-toml";
@@ -143,7 +143,7 @@ export const CONFIG_KEYS = [
143
143
  },
144
144
  {
145
145
  key: "auth",
146
- describe: `How Claude Code authenticates (${AGENT_AUTHS.join("|")}); subscription = your Claude Team/Enterprise login, console = your Claude Code Anthropic Console login's key`,
146
+ describe: `How Claude Code authenticates (${AGENT_AUTHS.join("|")}); subscription = your Claude Team/Enterprise login`,
147
147
  get: (c) => c.auth,
148
148
  set: (c, v) => {
149
149
  c.auth = coerceAgentAuth(v);
@@ -292,7 +292,7 @@ export function parseLauncherConfig(text) {
292
292
  if (raw.launch !== undefined)
293
293
  cfg.launch = coerceLaunch(str(raw.launch) ?? "");
294
294
  if (raw.auth !== undefined)
295
- cfg.auth = coerceAgentAuth(str(raw.auth) ?? "");
295
+ cfg.auth = parseSavedAgentAuth(str(raw.auth) ?? "");
296
296
  if (raw.key_source !== undefined)
297
297
  cfg.keySource = coerceKeySource(str(raw.key_source) ?? "");
298
298
  if (raw.goal_mode !== undefined)
@@ -6,9 +6,8 @@
6
6
  * Four ways Claude Code can be signed in, and what the gateway does with each today:
7
7
  * - Claude subscription, Team or Enterprise → `auth = subscription` (supported).
8
8
  * - Claude subscription, Pro / Max (individual) → not supported (Team/Enterprise only).
9
- * - Anthropic Console login (`/login` → Console account) → `auth = console` (supported): the
10
- * API key Claude Code created is read from its keychain (lib/console-key.ts). A
11
- * `claude auth login --console` profile creates no key → not supported.
9
+ * - Anthropic Console login (`/login` → Console account) → not used directly: Console
10
+ * customers use an Anthropic API key (created in the Console).
12
11
  * - Third-party provider (Bedrock / Vertex / Foundry) → not supported (no gateway door).
13
12
  * An Anthropic API key always works (`auth = api-key`, the default).
14
13
  *
@@ -48,13 +47,10 @@ export type UnsupportedLogin = Extract<ClaudeLogin, {
48
47
  /** Why a detected login can't be used through the gateway. */
49
48
  export declare function unsupportedReason(login: UnsupportedLogin): string;
50
49
  export declare const MENU_SUBSCRIPTION = "Claude subscription \u2014 Team or Enterprise (your claude.ai login)";
51
- export declare const MENU_CONSOLE = "Anthropic Console \u2014 the API key your Claude Code /login created (API billing)";
52
50
  export declare const MENU_API_KEY = "Anthropic API key";
53
51
  export interface ChooseAuthDeps {
54
52
  login: ClaudeLogin;
55
53
  interactive: boolean;
56
- /** Whether Claude Code's Console-login key is readable (lib/console-key.ts); asked only for a Console login. */
57
- consoleKeyAvailable(): boolean;
58
54
  select(question: string, options: string[]): Promise<string | null>;
59
55
  persist(auth: AgentAuth): void;
60
56
  say(line: string): void;
@@ -62,10 +58,11 @@ export interface ChooseAuthDeps {
62
58
  /**
63
59
  * Pick `auth` when nothing configured it (no --auth, MNEMOM_AGENT_AUTH or launcher.toml).
64
60
  * - No Claude login detected → api-key, silently, nothing saved (today's behaviour).
65
- * - A login we can't use → api-key, say why once, save it so the note isn't repeated.
66
- * - Team/Enterprise, or a Console login whose key is readable, and non-interactive →
67
- * api-key, nothing saved (never switch a script's auth without asking); interactive → a
68
- * two-option menu (that login or an API key), answer saved.
61
+ * - A login we can't use (personal plan, Console, third-party) → api-key, say why once,
62
+ * save it so the note isn't repeated.
63
+ * - Team/Enterprise and non-interactive → api-key, nothing saved (never switch a script's
64
+ * auth without asking); interactive → a two-option menu (subscription or an API key),
65
+ * answer saved.
69
66
  */
70
67
  export declare function chooseAuth(deps: ChooseAuthDeps): Promise<AgentAuth>;
71
68
  /** True when `auth` is already decided: `--auth`, or MNEMOM_AGENT_AUTH (launcher.toml folds in). */
@@ -6,9 +6,8 @@
6
6
  * Four ways Claude Code can be signed in, and what the gateway does with each today:
7
7
  * - Claude subscription, Team or Enterprise → `auth = subscription` (supported).
8
8
  * - Claude subscription, Pro / Max (individual) → not supported (Team/Enterprise only).
9
- * - Anthropic Console login (`/login` → Console account) → `auth = console` (supported): the
10
- * API key Claude Code created is read from its keychain (lib/console-key.ts). A
11
- * `claude auth login --console` profile creates no key → not supported.
9
+ * - Anthropic Console login (`/login` → Console account) → not used directly: Console
10
+ * customers use an Anthropic API key (created in the Console).
12
11
  * - Third-party provider (Bedrock / Vertex / Foundry) → not supported (no gateway door).
13
12
  * An Anthropic API key always works (`auth = api-key`, the default).
14
13
  *
@@ -63,13 +62,12 @@ export function unsupportedReason(login) {
63
62
  case "individual":
64
63
  return `your Claude ${login.plan} plan is a personal plan — Mnemom supports subscription login on Team and Enterprise plans only`;
65
64
  case "console":
66
- return "your Anthropic Console login has no API key from Claude Code's /login (Mnemom uses the key /login creates)";
65
+ return "Claude Code is signed in with an Anthropic Console account, and Mnemom uses an Anthropic API key for Console billing (create one at console.anthropic.com)";
67
66
  case "third-party":
68
- return `Claude Code is set to use ${PROVIDER_NAMES[login.provider]}, and Mnemom supports Anthropic sign-in only (an Anthropic API key, an Anthropic Console login, or a Claude Team or Enterprise plan)`;
67
+ return `Claude Code is set to use ${PROVIDER_NAMES[login.provider]}, and Mnemom supports an Anthropic API key or a Claude Team or Enterprise plan`;
69
68
  }
70
69
  }
71
70
  export const MENU_SUBSCRIPTION = "Claude subscription — Team or Enterprise (your claude.ai login)";
72
- export const MENU_CONSOLE = "Anthropic Console — the API key your Claude Code /login created (API billing)";
73
71
  export const MENU_API_KEY = "Anthropic API key";
74
72
  const SAVED_HINT = `change it any time with \`mnemom agent config set auth ${AGENT_AUTHS.join("|")}\``;
75
73
  function save(deps, auth, line) {
@@ -85,38 +83,25 @@ function save(deps, auth, line) {
85
83
  /**
86
84
  * Pick `auth` when nothing configured it (no --auth, MNEMOM_AGENT_AUTH or launcher.toml).
87
85
  * - No Claude login detected → api-key, silently, nothing saved (today's behaviour).
88
- * - A login we can't use → api-key, say why once, save it so the note isn't repeated.
89
- * - Team/Enterprise, or a Console login whose key is readable, and non-interactive →
90
- * api-key, nothing saved (never switch a script's auth without asking); interactive → a
91
- * two-option menu (that login or an API key), answer saved.
86
+ * - A login we can't use (personal plan, Console, third-party) → api-key, say why once,
87
+ * save it so the note isn't repeated.
88
+ * - Team/Enterprise and non-interactive → api-key, nothing saved (never switch a script's
89
+ * auth without asking); interactive → a two-option menu (subscription or an API key),
90
+ * answer saved.
92
91
  */
93
92
  export async function chooseAuth(deps) {
94
93
  const { login } = deps;
95
94
  if (login.kind === "none")
96
95
  return "api-key";
97
- if (login.kind === "console" && deps.consoleKeyAvailable()) {
98
- if (!deps.interactive)
99
- return "api-key";
100
- const picked = await deps.select("mnemom agent: Claude Code is signed in with an Anthropic Console account. How should it sign in through Mnemom?", [MENU_CONSOLE, MENU_API_KEY]);
101
- if (picked === MENU_CONSOLE) {
102
- return save(deps, "console", "mnemom agent: using your Anthropic Console login's API key.");
103
- }
104
- if (picked === MENU_API_KEY) {
105
- return save(deps, "api-key", "mnemom agent: using an Anthropic API key.");
106
- }
107
- deps.say("mnemom agent: no choice made — using an Anthropic API key for this launch.");
108
- return "api-key";
109
- }
110
96
  if (login.kind !== "subscription") {
111
97
  return save(deps, "api-key", `mnemom agent: ${unsupportedReason(login)}, so this launch uses an Anthropic API key.`);
112
98
  }
113
99
  if (!deps.interactive)
114
100
  return "api-key";
115
- // Only options that work. The earlier menu also listed Console and Bedrock/Vertex/Foundry
116
- // as "(not supported yet)" entries that re-asked when picked: a dead end. Operator ruling
117
- // (2026-10-01): the product never offers a choice it can't fulfil, and never advertises
118
- // what's coming. A login type comes back here only once it works end to end. Those logins
119
- // already skip this menu above with a one-line reason.
101
+ // Only options that work. The menu never offers a choice it can't fulfil (operator ruling
102
+ // 2026-10-01). Console and Bedrock/Vertex/Foundry logins skip it above with a one-line
103
+ // reason; the public CLI supports an Anthropic API key and a Team/Enterprise subscription
104
+ // only (operator ruling 2026-10-07 retired the Console-login key path).
120
105
  const options = [MENU_SUBSCRIPTION, MENU_API_KEY];
121
106
  const question = `mnemom agent: Claude Code is signed in with your Claude ${login.plan} plan. How should it sign in through Mnemom?`;
122
107
  const picked = await deps.select(question, options);
@@ -17,9 +17,23 @@
17
17
  * Pure apart from the injected deps, so every arm is unit-tested.
18
18
  */
19
19
  import type { LaunchShape } from "./agent.js";
20
- /** `console` = the API key Claude Code's Anthropic Console login created (lib/console-key.ts). */
21
- export type AgentAuth = "api-key" | "subscription" | "console";
20
+ export type AgentAuth = "api-key" | "subscription";
22
21
  export declare const AGENT_AUTHS: readonly AgentAuth[];
22
+ /**
23
+ * The retired `console` value (it read the key Claude Code's Anthropic Console /login
24
+ * created). A saved `auth = console` (launcher.toml / MNEMOM_AGENT_AUTH) now runs as
25
+ * api-key with a one-line notice instead of failing the launch; Console customers use an
26
+ * Anthropic API key. An explicit `--auth console` or `config set auth console` is refused.
27
+ */
28
+ export declare const RETIRED_CONSOLE_AUTH = "console";
29
+ /** What launcher.toml `auth` may hold: a live mode, or the retired `console` (kept so it folds
30
+ * into MNEMOM_AGENT_AUTH like any saved value — the notice and the doctor row then see it). */
31
+ export type SavedAgentAuth = AgentAuth | typeof RETIRED_CONSOLE_AUTH;
32
+ export declare const RETIRED_CONSOLE_NOTICE = "mnemom agent: auth = console is no longer supported \u2014 this launch uses an Anthropic API key (create one at console.anthropic.com). Run `mnemom agent config set auth api-key` to stop this notice.";
33
+ /** True when the saved setting (MNEMOM_AGENT_AUTH; launcher.toml folds in) is the retired `console`. */
34
+ export declare function savedAuthIsRetiredConsole(env: Record<string, string | undefined>): boolean;
35
+ /** Print the retired-console notice when the saved setting is `console`; a launch never fails on it. */
36
+ export declare function sayRetiredConsoleNotice(flag: string | undefined, env: Record<string, string | undefined>, say: (line: string) => void): void;
23
37
  /** Env override for the setting (the launcher.toml `auth` key folds into it). */
24
38
  export declare const AGENT_AUTH_ENV = "MNEMOM_AGENT_AUTH";
25
39
  /** Env override for the Mnemom key a subscription launch sends. */
@@ -27,7 +41,12 @@ export declare const MNEMOM_KEY_ENV = "MNEMOM_AGENT_MNEMOM_KEY";
27
41
  /** Field in ~/.mnemom/launcher.json (0600) holding the minted key. */
28
42
  export declare const MNEMOM_KEY_STORE_FIELD = "mnemom_api_key";
29
43
  export declare function coerceAgentAuth(raw: string): AgentAuth;
30
- /** `--auth` flag > MNEMOM_AGENT_AUTH (launcher.toml folds in here) > api-key. */
44
+ /**
45
+ * launcher.toml `auth`: the retired `console` is kept as-is (never a load error); it folds into
46
+ * MNEMOM_AGENT_AUTH, where resolveAgentAuth runs it as api-key and the launch says so once.
47
+ */
48
+ export declare function parseSavedAgentAuth(raw: string): SavedAgentAuth;
49
+ /** `--auth` flag > MNEMOM_AGENT_AUTH (launcher.toml folds in here; a saved retired `console` → api-key) > api-key. */
31
50
  export declare function resolveAgentAuth(flag: string | undefined, env: Record<string, string | undefined>): AgentAuth;
32
51
  export interface MnemomKeyDeps {
33
52
  env: Record<string, string | undefined>;
@@ -16,7 +16,24 @@
16
16
  *
17
17
  * Pure apart from the injected deps, so every arm is unit-tested.
18
18
  */
19
- export const AGENT_AUTHS = ["api-key", "subscription", "console"];
19
+ export const AGENT_AUTHS = ["api-key", "subscription"];
20
+ /**
21
+ * The retired `console` value (it read the key Claude Code's Anthropic Console /login
22
+ * created). A saved `auth = console` (launcher.toml / MNEMOM_AGENT_AUTH) now runs as
23
+ * api-key with a one-line notice instead of failing the launch; Console customers use an
24
+ * Anthropic API key. An explicit `--auth console` or `config set auth console` is refused.
25
+ */
26
+ export const RETIRED_CONSOLE_AUTH = "console";
27
+ export const RETIRED_CONSOLE_NOTICE = "mnemom agent: auth = console is no longer supported — this launch uses an Anthropic API key (create one at console.anthropic.com). Run `mnemom agent config set auth api-key` to stop this notice.";
28
+ /** True when the saved setting (MNEMOM_AGENT_AUTH; launcher.toml folds in) is the retired `console`. */
29
+ export function savedAuthIsRetiredConsole(env) {
30
+ return (env[AGENT_AUTH_ENV] ?? "").trim().toLowerCase() === RETIRED_CONSOLE_AUTH;
31
+ }
32
+ /** Print the retired-console notice when the saved setting is `console`; a launch never fails on it. */
33
+ export function sayRetiredConsoleNotice(flag, env, say) {
34
+ if (!flag?.trim() && savedAuthIsRetiredConsole(env))
35
+ say(RETIRED_CONSOLE_NOTICE);
36
+ }
20
37
  /** Env override for the setting (the launcher.toml `auth` key folds into it). */
21
38
  export const AGENT_AUTH_ENV = "MNEMOM_AGENT_AUTH";
22
39
  /** Env override for the Mnemom key a subscription launch sends. */
@@ -36,10 +53,21 @@ export function coerceAgentAuth(raw) {
36
53
  return v;
37
54
  throw new Error(`auth must be one of ${AGENT_AUTHS.join(", ")} (got '${raw}').`);
38
55
  }
39
- /** `--auth` flag > MNEMOM_AGENT_AUTH (launcher.toml folds in here) > api-key. */
56
+ /**
57
+ * launcher.toml `auth`: the retired `console` is kept as-is (never a load error); it folds into
58
+ * MNEMOM_AGENT_AUTH, where resolveAgentAuth runs it as api-key and the launch says so once.
59
+ */
60
+ export function parseSavedAgentAuth(raw) {
61
+ return raw.trim().toLowerCase() === RETIRED_CONSOLE_AUTH
62
+ ? RETIRED_CONSOLE_AUTH
63
+ : coerceAgentAuth(raw);
64
+ }
65
+ /** `--auth` flag > MNEMOM_AGENT_AUTH (launcher.toml folds in here; a saved retired `console` → api-key) > api-key. */
40
66
  export function resolveAgentAuth(flag, env) {
41
67
  if (flag !== undefined && flag.trim())
42
68
  return coerceAgentAuth(flag);
69
+ if (savedAuthIsRetiredConsole(env))
70
+ return "api-key";
43
71
  const fromEnv = (env[AGENT_AUTH_ENV] ?? "").trim();
44
72
  return fromEnv ? coerceAgentAuth(fromEnv) : "api-key";
45
73
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mnemom/mnemom",
3
- "version": "0.17.18",
3
+ "version": "0.17.19",
4
4
  "description": "Transparent AI agent tracing",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,40 +0,0 @@
1
- /**
2
- * `mnemom agent` with an Anthropic Console login (`auth = console`).
3
- *
4
- * Claude Code's `/login` → "Anthropic Console account" uses OAuth only to create a real
5
- * Anthropic API key, which Claude Code stores and then sends as `x-api-key` ("/login
6
- * managed key"). This module reads that key from where Claude Code keeps it, so the launch
7
- * runs exactly like `auth = api-key` — same headers, same birth and claim, same identity —
8
- * with no key for the user to copy. The key is read per launch and never stored by us.
9
- *
10
- * Where Claude Code keeps it (Claude Code 2.1.285, confirmed against the binary and a live
11
- * probe on 2026-10-02):
12
- * - macOS: the login keychain, service `Claude Code`, account $USER. When
13
- * CLAUDE_CONFIG_DIR is set the service gains `-<first 8 hex of sha256(the raw
14
- * CLAUDE_CONFIG_DIR value, NFC)>`; CLAUDE_SECURESTORAGE_CONFIG_DIR, when set, replaces
15
- * that value (empty = no suffix).
16
- * - everywhere (and the macOS fallback): `primaryApiKey` in Claude Code's .claude.json.
17
- *
18
- * A `claude auth login --console` *profile* (keyless OAuth, ~/.config/anthropic) creates no
19
- * such key, so it is not read here.
20
- *
21
- * Pure apart from the injected deps, so every arm is unit-tested.
22
- */
23
- export interface ConsoleKeyDeps {
24
- platform: NodeJS.Platform;
25
- env: Record<string, string | undefined>;
26
- /** os.userInfo().username, for when $USER is unset. */
27
- username(): string;
28
- /** The keychain password for (service, account), or undefined when absent. */
29
- readKeychain(service: string, account: string): string | undefined;
30
- /** Parsed .claude.json (see readClaudeLoginState), or undefined. */
31
- claudeJson: unknown;
32
- }
33
- /** The keychain service name Claude Code stores the managed key under. */
34
- export declare function claudeKeychainService(env: Record<string, string | undefined>): string;
35
- /** The keychain account Claude Code uses: $USER, else the OS username, else a fixed name. */
36
- export declare function claudeKeychainAccount(env: Record<string, string | undefined>, username: () => string): string;
37
- /** Claude Code's Console-login API key on this machine, or undefined. Never throws. */
38
- export declare function readConsoleKey(deps: ConsoleKeyDeps): string | undefined;
39
- /** The key for an `auth = console` launch; throws with what to do when there is none. */
40
- export declare function requireConsoleKey(deps: ConsoleKeyDeps): string;
@@ -1,77 +0,0 @@
1
- /**
2
- * `mnemom agent` with an Anthropic Console login (`auth = console`).
3
- *
4
- * Claude Code's `/login` → "Anthropic Console account" uses OAuth only to create a real
5
- * Anthropic API key, which Claude Code stores and then sends as `x-api-key` ("/login
6
- * managed key"). This module reads that key from where Claude Code keeps it, so the launch
7
- * runs exactly like `auth = api-key` — same headers, same birth and claim, same identity —
8
- * with no key for the user to copy. The key is read per launch and never stored by us.
9
- *
10
- * Where Claude Code keeps it (Claude Code 2.1.285, confirmed against the binary and a live
11
- * probe on 2026-10-02):
12
- * - macOS: the login keychain, service `Claude Code`, account $USER. When
13
- * CLAUDE_CONFIG_DIR is set the service gains `-<first 8 hex of sha256(the raw
14
- * CLAUDE_CONFIG_DIR value, NFC)>`; CLAUDE_SECURESTORAGE_CONFIG_DIR, when set, replaces
15
- * that value (empty = no suffix).
16
- * - everywhere (and the macOS fallback): `primaryApiKey` in Claude Code's .claude.json.
17
- *
18
- * A `claude auth login --console` *profile* (keyless OAuth, ~/.config/anthropic) creates no
19
- * such key, so it is not read here.
20
- *
21
- * Pure apart from the injected deps, so every arm is unit-tested.
22
- */
23
- import { createHash } from "node:crypto";
24
- const ACCOUNT_RE = /^[a-zA-Z0-9._-]+$/;
25
- /** The keychain service name Claude Code stores the managed key under. */
26
- export function claudeKeychainService(env) {
27
- const secure = env.CLAUDE_SECURESTORAGE_CONFIG_DIR;
28
- const dir = secure !== undefined ? secure : (env.CLAUDE_CONFIG_DIR ?? "");
29
- if (!dir)
30
- return "Claude Code";
31
- const hash = createHash("sha256").update(dir.normalize("NFC")).digest("hex").slice(0, 8);
32
- return `Claude Code-${hash}`;
33
- }
34
- /** The keychain account Claude Code uses: $USER, else the OS username, else a fixed name. */
35
- export function claudeKeychainAccount(env, username) {
36
- let name;
37
- try {
38
- name = env.USER || username();
39
- }
40
- catch {
41
- name = "";
42
- }
43
- return ACCOUNT_RE.test(name) ? name : "claude-code-user";
44
- }
45
- /** The key is spliced into a newline-joined header block, so CR/LF must never survive. */
46
- function clean(raw) {
47
- const v = (raw ?? "").replace(/[\r\n]+/g, "").trim();
48
- return v || undefined;
49
- }
50
- /** Claude Code's Console-login API key on this machine, or undefined. Never throws. */
51
- export function readConsoleKey(deps) {
52
- if (deps.platform === "darwin") {
53
- let fromKeychain;
54
- try {
55
- fromKeychain = clean(deps.readKeychain(claudeKeychainService(deps.env), claudeKeychainAccount(deps.env, deps.username)));
56
- }
57
- catch {
58
- fromKeychain = undefined;
59
- }
60
- if (fromKeychain)
61
- return fromKeychain;
62
- }
63
- const json = deps.claudeJson;
64
- const stored = json && typeof json === "object"
65
- ? json.primaryApiKey
66
- : undefined;
67
- return typeof stored === "string" ? clean(stored) : undefined;
68
- }
69
- /** The key for an `auth = console` launch; throws with what to do when there is none. */
70
- export function requireConsoleKey(deps) {
71
- const key = readConsoleKey(deps);
72
- if (key)
73
- return key;
74
- throw new Error("mnemom agent: auth = console, but Claude Code has no Anthropic Console login on this machine.\n" +
75
- ' Sign in: run `claude`, then /login → "Anthropic Console account".\n' +
76
- " Or use a key instead: `mnemom agent config set auth api-key`.");
77
- }