@mnemom/mnemom 0.14.5 → 0.14.6
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/dist/commands/agents.js +26 -1
- package/dist/commands/auth.js +9 -0
- package/dist/commands/integrity.js +5 -2
- package/dist/commands/logs.js +13 -5
- package/dist/commands/status.js +3 -0
- package/dist/index.js +1 -1
- package/dist/lib/onboarding.d.ts +28 -0
- package/dist/lib/onboarding.js +35 -0
- package/dist/lib/prompt.d.ts +3 -1
- package/dist/lib/prompt.js +47 -1
- package/package.json +1 -1
package/dist/commands/agents.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
|
-
import { listAgents, listOrgAgents, listMyOrgs, getAgentByName, claimAgent, MnemomApiError, } from "../lib/api.js";
|
|
2
|
+
import { listAgents, listOrgAgents, listMyOrgs, getAgent, getAgentByName, claimAgent, MnemomApiError, } from "../lib/api.js";
|
|
3
3
|
import { requireAuth } from "../lib/auth.js";
|
|
4
4
|
import { fmt } from "../lib/format.js";
|
|
5
5
|
export async function agentsListCommand(options = {}) {
|
|
@@ -152,6 +152,15 @@ export async function agentsClaimCommand(idOrName, options = {}) {
|
|
|
152
152
|
return;
|
|
153
153
|
}
|
|
154
154
|
}
|
|
155
|
+
else if (!derivationName && options.key && !options.hashProof) {
|
|
156
|
+
// ID given, no --name, --key will be used for proof derivation: try to
|
|
157
|
+
// auto-resolve the agent's provisioned name so `claim <id> --key <k>`
|
|
158
|
+
// works on the first try without requiring an explicit --name.
|
|
159
|
+
const found = await getAgent(idOrName).catch(() => null);
|
|
160
|
+
if (found?.name) {
|
|
161
|
+
derivationName = found.name;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
155
164
|
// 2. Establish the hash proof: --hash-proof (raw) takes precedence, else
|
|
156
165
|
// derive from --key. Exactly one source is needed.
|
|
157
166
|
let hashProof;
|
|
@@ -242,6 +251,22 @@ export async function agentsClaimCommand(idOrName, options = {}) {
|
|
|
242
251
|
process.exit(1);
|
|
243
252
|
return;
|
|
244
253
|
}
|
|
254
|
+
if (err instanceof MnemomApiError &&
|
|
255
|
+
err.effectiveStatus === 403 &&
|
|
256
|
+
(err.code === "invalid_hash_proof" || /hash[ _]?proof/i.test(err.message))) {
|
|
257
|
+
// The proof derived from --key (and the agent's name) didn't match the
|
|
258
|
+
// agent's stored proof. Name the real cause instead of the bare "Invalid
|
|
259
|
+
// hash proof": verify the key, and let the user supply the name when it
|
|
260
|
+
// couldn't be auto-resolved from the id. Ordered AFTER the not-a-member
|
|
261
|
+
// 403 (also a 403, distinguished by isNotAMemberError) so it doesn't
|
|
262
|
+
// shadow the org-teaching path.
|
|
263
|
+
console.log(fmt.error("Failed to claim agent: the --key (and agent name) didn't match the agent's stored proof (invalid hash proof)."));
|
|
264
|
+
console.log(fmt.dim(` Confirm --key is the exact provider key the agent runs with.\n` +
|
|
265
|
+
` If the name couldn't be auto-resolved from the id, pass it explicitly:\n` +
|
|
266
|
+
` mnemom agents claim ${agentId} --key <key> --name <provisioned-name>`) + "\n");
|
|
267
|
+
process.exit(1);
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
245
270
|
const msg = err instanceof Error ? err.message : String(err);
|
|
246
271
|
console.log(fmt.error(`Failed to claim agent: ${msg}`) + "\n");
|
|
247
272
|
process.exit(1);
|
package/dist/commands/auth.js
CHANGED
|
@@ -5,14 +5,23 @@ export async function loginCommand(options = {}) {
|
|
|
5
5
|
try {
|
|
6
6
|
let tokens;
|
|
7
7
|
if (options.noBrowser) {
|
|
8
|
+
// Non-interactive hint: when stdin is piped, credentials are read as two
|
|
9
|
+
// lines (email, then password). If either is missing we fail loudly with
|
|
10
|
+
// a non-zero exit and never touch the stored session (MNE-269).
|
|
11
|
+
const piped = !process.stdin.isTTY;
|
|
12
|
+
const pipeHint = "Pipe credentials as two lines: printf 'EMAIL\\nPASSWORD\\n' | mnemom login --no-browser";
|
|
8
13
|
const email = await askInput("Email:");
|
|
9
14
|
if (!email) {
|
|
10
15
|
console.log(fmt.error("Email is required."));
|
|
16
|
+
if (piped)
|
|
17
|
+
console.log(fmt.error(`No email received on stdin. ${pipeHint}`));
|
|
11
18
|
process.exit(1);
|
|
12
19
|
}
|
|
13
20
|
const password = await askInput("Password:", true);
|
|
14
21
|
if (!password) {
|
|
15
22
|
console.log(fmt.error("Password is required."));
|
|
23
|
+
if (piped)
|
|
24
|
+
console.log(fmt.error(`No password received on stdin. ${pipeHint}`));
|
|
16
25
|
process.exit(1);
|
|
17
26
|
}
|
|
18
27
|
tokens = await loginWithPassword(email, password);
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { resolveAgentId, getIntegrity, MnemomApiError } from "../lib/api.js";
|
|
2
2
|
import { fmt } from "../lib/format.js";
|
|
3
|
+
import { TRACES_PENDING_NOTE } from "../lib/onboarding.js";
|
|
3
4
|
export async function integrityCommand(agentName) {
|
|
4
5
|
const agentId = await resolveAgentId(agentName);
|
|
5
6
|
console.log("\nFetching agent activity...\n");
|
|
@@ -18,7 +19,8 @@ export async function integrityCommand(agentName) {
|
|
|
18
19
|
console.log("\n" + fmt.warn("You have integrity violations. Run `mnemom logs` to investigate.") + "\n");
|
|
19
20
|
}
|
|
20
21
|
else if (integrity.total_traces === 0) {
|
|
21
|
-
console.log("\nNo traces recorded yet. Start using Claude to build your integrity score
|
|
22
|
+
console.log("\nNo traces recorded yet. Start using Claude to build your integrity score.");
|
|
23
|
+
console.log("\n" + fmt.dim(TRACES_PENDING_NOTE) + "\n");
|
|
22
24
|
}
|
|
23
25
|
else {
|
|
24
26
|
console.log("\n" + fmt.success("Your agent has a clean integrity record!") + "\n");
|
|
@@ -35,7 +37,8 @@ export async function integrityCommand(agentName) {
|
|
|
35
37
|
console.log(` ${fmt.label("Total: ", "0 traces")}`);
|
|
36
38
|
console.log(` ${fmt.label("Verified: ", "0")}`);
|
|
37
39
|
console.log(` ${fmt.label("Violations:", " 0")}`);
|
|
38
|
-
console.log("\nNo traces recorded yet. Start using Claude to build your integrity score
|
|
40
|
+
console.log("\nNo traces recorded yet. Start using Claude to build your integrity score.");
|
|
41
|
+
console.log("\n" + fmt.dim(TRACES_PENDING_NOTE) + "\n");
|
|
39
42
|
}
|
|
40
43
|
else {
|
|
41
44
|
const message = error instanceof Error ? error.message : String(error);
|
package/dist/commands/logs.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { resolveAgentId, getTraces, MnemomApiError, } from "../lib/api.js";
|
|
2
2
|
import { getGatewayUrl, getWebsiteUrl } from "../lib/config.js";
|
|
3
3
|
import { fmt } from "../lib/format.js";
|
|
4
|
+
import { TRACES_PENDING_NOTE, gatewayIntegrationHelp } from "../lib/onboarding.js";
|
|
4
5
|
export async function logsCommand(options = {}) {
|
|
5
6
|
const agentId = await resolveAgentId(options.agentName);
|
|
6
7
|
const gatewayUrl = getGatewayUrl();
|
|
@@ -10,9 +11,8 @@ export async function logsCommand(options = {}) {
|
|
|
10
11
|
const traces = await getTraces(agentId, limit);
|
|
11
12
|
if (traces.length === 0) {
|
|
12
13
|
console.log(fmt.header("No traces found"));
|
|
13
|
-
console.log("\
|
|
14
|
-
console.log(
|
|
15
|
-
console.log(` export ANTHROPIC_BASE_URL="${gatewayUrl}/v1/proxy/${agentId}"\n`);
|
|
14
|
+
console.log("\n" + TRACES_PENDING_NOTE + "\n");
|
|
15
|
+
console.log(gatewayIntegrationHelp(gatewayUrl, options.agentName) + "\n");
|
|
16
16
|
return;
|
|
17
17
|
}
|
|
18
18
|
console.log(fmt.header(`Recent Traces (${traces.length})`));
|
|
@@ -29,7 +29,8 @@ export async function logsCommand(options = {}) {
|
|
|
29
29
|
// effectiveStatus surfaces the true status (=== status when documented).
|
|
30
30
|
if (error instanceof MnemomApiError && error.effectiveStatus === 404) {
|
|
31
31
|
console.log(fmt.header("No traces found"));
|
|
32
|
-
console.log("\
|
|
32
|
+
console.log("\n" + TRACES_PENDING_NOTE + "\n");
|
|
33
|
+
console.log(gatewayIntegrationHelp(gatewayUrl, options.agentName) + "\n");
|
|
33
34
|
}
|
|
34
35
|
else {
|
|
35
36
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -51,8 +52,15 @@ function displayTrace(trace) {
|
|
|
51
52
|
// shape); fall back to the action type, then a dash.
|
|
52
53
|
const actionLabel = trace.action?.name ?? trace.action?.type ?? "—";
|
|
53
54
|
console.log(` ${fmt.label("Action:", ` ${actionLabel}`)}`);
|
|
55
|
+
// A pure-LLM inference turn carries category "escalation_trigger" on the wire
|
|
56
|
+
// (it is the *point at which* an escalation could fire, not a turn that demanded
|
|
57
|
+
// one). Rendering that raw reads as if every benign turn escalated, so present a
|
|
58
|
+
// friendly label instead. Presentation-only — the underlying category is unchanged.
|
|
54
59
|
if (trace.action?.category) {
|
|
55
|
-
|
|
60
|
+
const categoryLabel = trace.action.category === "escalation_trigger" && trace.action.name === "inference"
|
|
61
|
+
? "inference (no tool)"
|
|
62
|
+
: trace.action.category;
|
|
63
|
+
console.log(` ${fmt.label("Type: ", ` ${categoryLabel}`)}`);
|
|
56
64
|
}
|
|
57
65
|
const reasoning = trace.decision?.selection_reasoning;
|
|
58
66
|
if (reasoning) {
|
package/dist/commands/status.js
CHANGED
|
@@ -2,6 +2,7 @@ import { getGatewayUrl, getWebsiteUrl } from "../lib/config.js";
|
|
|
2
2
|
import { resolveAgentId, getAgent, getIntegrity, getTraces, MnemomApiError } from "../lib/api.js";
|
|
3
3
|
import { isLoggedIn, getAuthInfo } from "../lib/auth.js";
|
|
4
4
|
import { fmt } from "../lib/format.js";
|
|
5
|
+
import { TRACES_PENDING_NOTE } from "../lib/onboarding.js";
|
|
5
6
|
export async function statusCommand(agentName) {
|
|
6
7
|
console.log(fmt.header("mnemom status"));
|
|
7
8
|
console.log();
|
|
@@ -195,6 +196,8 @@ async function showTraceSummary(agentId) {
|
|
|
195
196
|
}
|
|
196
197
|
else {
|
|
197
198
|
console.log("\nLast Activity: None");
|
|
199
|
+
// Eventual consistency: don't let a just-sent request read as "broken".
|
|
200
|
+
console.log("\n" + fmt.dim(TRACES_PENDING_NOTE));
|
|
198
201
|
}
|
|
199
202
|
}
|
|
200
203
|
catch {
|
package/dist/index.js
CHANGED
|
@@ -322,7 +322,7 @@ agentsCmd
|
|
|
322
322
|
.option("--org <slug>", "Org slug or id to claim into (default: your personal org)")
|
|
323
323
|
.option("--key <key>", "The agent's provider API key — the CLI derives the hash proof from it")
|
|
324
324
|
.option("--hash-proof <hex>", "A pre-computed 64-hex SHA-256 proof (advanced/CI; alternative to --key)")
|
|
325
|
-
.option("--name <name>", "Provisioned agent name
|
|
325
|
+
.option("--name <name>", "Provisioned agent name for proof derivation — auto-resolved from the agent id when possible; pass only to override")
|
|
326
326
|
.option("--json", "Output JSON instead of human-readable text")
|
|
327
327
|
.action(async (idOrName, opts) => {
|
|
328
328
|
try {
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared first-run / onboarding copy for the CLI's empty states.
|
|
3
|
+
*
|
|
4
|
+
* Centralised so the gateway-integration instructions and the trace-ingestion
|
|
5
|
+
* note stay identical across `logs`, `status`, and `integrity`, and stay in
|
|
6
|
+
* lockstep with the canonical gateway quickstart
|
|
7
|
+
* (docs.mnemom.ai/quickstart/gateway).
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Traces are eventually-consistent: they're processed asynchronously and take
|
|
11
|
+
* a short while to land. Surfacing this turns a bare "0 traces" — which reads
|
|
12
|
+
* as a broken pipeline right after a successful call — into an expected,
|
|
13
|
+
* temporary state (MNE-270).
|
|
14
|
+
*/
|
|
15
|
+
export declare const TRACES_PENDING_NOTE: string;
|
|
16
|
+
/**
|
|
17
|
+
* Canonical gateway integration instructions (MNE-268).
|
|
18
|
+
*
|
|
19
|
+
* Mirrors the quickstart: point the provider client at the gateway's
|
|
20
|
+
* `/anthropic` base URL and name the agent with the `x-mnemom-agent` header.
|
|
21
|
+
* The previously-printed `/v1/proxy/<id>` base-URL form is NOT a real gateway
|
|
22
|
+
* route — the gateway only serves `/anthropic/*`, `/openai/*`, `/gemini/*` and
|
|
23
|
+
* resolves the agent from the `x-mnemom-agent` header (which is a name, not an
|
|
24
|
+
* id). `agentName` is the customer-chosen agent name; we fall back to a
|
|
25
|
+
* placeholder rather than printing the resolved `mnm-` id, which would be the
|
|
26
|
+
* wrong value for that header.
|
|
27
|
+
*/
|
|
28
|
+
export declare function gatewayIntegrationHelp(gatewayUrl: string, agentName?: string): string;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared first-run / onboarding copy for the CLI's empty states.
|
|
3
|
+
*
|
|
4
|
+
* Centralised so the gateway-integration instructions and the trace-ingestion
|
|
5
|
+
* note stay identical across `logs`, `status`, and `integrity`, and stay in
|
|
6
|
+
* lockstep with the canonical gateway quickstart
|
|
7
|
+
* (docs.mnemom.ai/quickstart/gateway).
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Traces are eventually-consistent: they're processed asynchronously and take
|
|
11
|
+
* a short while to land. Surfacing this turns a bare "0 traces" — which reads
|
|
12
|
+
* as a broken pipeline right after a successful call — into an expected,
|
|
13
|
+
* temporary state (MNE-270).
|
|
14
|
+
*/
|
|
15
|
+
export const TRACES_PENDING_NOTE = "Traces are processed asynchronously and can take a minute or two to appear.\n" +
|
|
16
|
+
"If you just made a request, wait a moment and run this again.";
|
|
17
|
+
/**
|
|
18
|
+
* Canonical gateway integration instructions (MNE-268).
|
|
19
|
+
*
|
|
20
|
+
* Mirrors the quickstart: point the provider client at the gateway's
|
|
21
|
+
* `/anthropic` base URL and name the agent with the `x-mnemom-agent` header.
|
|
22
|
+
* The previously-printed `/v1/proxy/<id>` base-URL form is NOT a real gateway
|
|
23
|
+
* route — the gateway only serves `/anthropic/*`, `/openai/*`, `/gemini/*` and
|
|
24
|
+
* resolves the agent from the `x-mnemom-agent` header (which is a name, not an
|
|
25
|
+
* id). `agentName` is the customer-chosen agent name; we fall back to a
|
|
26
|
+
* placeholder rather than printing the resolved `mnm-` id, which would be the
|
|
27
|
+
* wrong value for that header.
|
|
28
|
+
*/
|
|
29
|
+
export function gatewayIntegrationHelp(gatewayUrl, agentName) {
|
|
30
|
+
const label = agentName ?? "<your-agent-name>";
|
|
31
|
+
return ("Route requests through the gateway and name your agent with a header:\n\n" +
|
|
32
|
+
` ANTHROPIC_BASE_URL="${gatewayUrl}/anthropic"\n` +
|
|
33
|
+
` header x-mnemom-agent: ${label}\n\n` +
|
|
34
|
+
"Quickstart: https://docs.mnemom.ai/quickstart/gateway");
|
|
35
|
+
}
|
package/dist/lib/prompt.d.ts
CHANGED
|
@@ -5,9 +5,11 @@
|
|
|
5
5
|
* @returns Promise<boolean> - true for yes, false for no
|
|
6
6
|
*/
|
|
7
7
|
export declare function askYesNo(question: string, defaultYes?: boolean): Promise<boolean>;
|
|
8
|
+
/** Test-only: reset the cached piped-stdin reader between cases. */
|
|
9
|
+
export declare function __resetPipedStdinForTests(): void;
|
|
8
10
|
/**
|
|
9
11
|
* Prompt for a single line of text input.
|
|
10
|
-
* If mask is true, input is hidden (for API keys).
|
|
12
|
+
* If mask is true, input is hidden (for API keys / passwords).
|
|
11
13
|
*/
|
|
12
14
|
export declare function askInput(question: string, mask?: boolean): Promise<string>;
|
|
13
15
|
/**
|
package/dist/lib/prompt.js
CHANGED
|
@@ -32,11 +32,57 @@ export async function askYesNo(question, defaultYes = true) {
|
|
|
32
32
|
});
|
|
33
33
|
});
|
|
34
34
|
}
|
|
35
|
+
// ----------------------------------------------------------------------------
|
|
36
|
+
// Non-interactive (piped / scripted / CI) stdin.
|
|
37
|
+
//
|
|
38
|
+
// When stdin is NOT a TTY, prompts must read sequential lines from a SINGLE
|
|
39
|
+
// shared reader. Creating a fresh `readline.createInterface` per prompt — as
|
|
40
|
+
// askInput did historically — silently drops buffered input on a pipe: the
|
|
41
|
+
// first interface reads all currently-available bytes into its internal
|
|
42
|
+
// buffer, emits the first line, then discards the rest on close, so the next
|
|
43
|
+
// prompt sees EOF and resolves empty. That made `mnemom login --no-browser`
|
|
44
|
+
// with a piped password silently fail (MNE-269): the email read fine, the
|
|
45
|
+
// password came back empty, and login looked like it had asked but got
|
|
46
|
+
// nothing. We read every line of stdin once, then hand them out in order. On
|
|
47
|
+
// EOF (no more lines) callers get "" and can fail loudly instead of hanging.
|
|
48
|
+
let pipedStdinLines = null;
|
|
49
|
+
let pipedStdinPromise = null;
|
|
50
|
+
function readPipedStdinLines() {
|
|
51
|
+
if (pipedStdinLines)
|
|
52
|
+
return Promise.resolve(pipedStdinLines);
|
|
53
|
+
if (!pipedStdinPromise) {
|
|
54
|
+
pipedStdinPromise = new Promise((resolve) => {
|
|
55
|
+
const lines = [];
|
|
56
|
+
const rl = readline.createInterface({ input: process.stdin });
|
|
57
|
+
rl.on("line", (line) => lines.push(line));
|
|
58
|
+
rl.on("close", () => {
|
|
59
|
+
pipedStdinLines = lines;
|
|
60
|
+
resolve(lines);
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
return pipedStdinPromise;
|
|
65
|
+
}
|
|
66
|
+
/** Test-only: reset the cached piped-stdin reader between cases. */
|
|
67
|
+
export function __resetPipedStdinForTests() {
|
|
68
|
+
pipedStdinLines = null;
|
|
69
|
+
pipedStdinPromise = null;
|
|
70
|
+
}
|
|
35
71
|
/**
|
|
36
72
|
* Prompt for a single line of text input.
|
|
37
|
-
* If mask is true, input is hidden (for API keys).
|
|
73
|
+
* If mask is true, input is hidden (for API keys / passwords).
|
|
38
74
|
*/
|
|
39
75
|
export async function askInput(question, mask = false) {
|
|
76
|
+
// Non-interactive stdin (pipe / CI / script): serve the next buffered line
|
|
77
|
+
// from the shared reader. Masking is meaningless on a pipe — just consume the
|
|
78
|
+
// line. On EOF this returns "" so callers can fail loudly (and never hang).
|
|
79
|
+
if (!process.stdin.isTTY) {
|
|
80
|
+
process.stdout.write(`${question} `);
|
|
81
|
+
const lines = await readPipedStdinLines();
|
|
82
|
+
const next = lines.shift();
|
|
83
|
+
process.stdout.write("\n");
|
|
84
|
+
return (next ?? "").trim();
|
|
85
|
+
}
|
|
40
86
|
const rl = readline.createInterface({
|
|
41
87
|
input: process.stdin,
|
|
42
88
|
output: process.stdout,
|