@mnemom/mnemom 0.7.2 → 0.8.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,15 +1 @@
1
1
  export declare function agentsListCommand(): Promise<void>;
2
- export declare function agentsDefaultCommand(name: string): Promise<void>;
3
- /**
4
- * smoltbot agents add <name-or-id> [--alias <alias>]
5
- * Register an existing API agent in the local config.
6
- */
7
- export declare function agentsAddCommand(nameOrId: string, alias?: string): Promise<void>;
8
- export declare function agentsRemoveCommand(name: string): Promise<void>;
9
- /**
10
- * smoltbot agents rekey [agent-name]
11
- * Re-bind a claimed agent to a new provider API key.
12
- * The raw key is hashed locally (SHA-256) and never transmitted.
13
- */
14
- export declare function agentsRekeyCommand(name?: string): Promise<void>;
15
- export declare function agentsCheckBindingCommand(name?: string): Promise<void>;
@@ -1,303 +1,43 @@
1
- import { loadConfig, saveConfig, computeAgentHash } from "../lib/config.js";
2
- import { listAgents, getAgent, getAgentByName, postApi, verifyBinding } from "../lib/api.js";
1
+ import { listAgents } from "../lib/api.js";
2
+ import { requireAuth } from "../lib/auth.js";
3
3
  import { fmt } from "../lib/format.js";
4
- import { askInput } from "../lib/prompt.js";
5
4
  export async function agentsListCommand() {
6
- const config = loadConfig();
7
- if (!config) {
8
- console.log("\n" + fmt.error("smoltbot is not initialized") + "\n");
9
- console.log("Run `smoltbot init` to get started.\n");
10
- process.exit(1);
11
- }
12
- console.log(fmt.header("Registered Agents"));
13
- console.log();
14
- // Build reverse map: agentId → local alias name
15
- const localAliases = new Map();
16
- for (const [name, agent] of Object.entries(config.agents)) {
17
- localAliases.set(agent.agentId, name);
18
- }
19
- // Fetch ALL agents from API for the authenticated user
20
- let apiAgents = [];
21
- try {
22
- apiAgents = await listAgents();
23
- }
24
- catch {
25
- console.log(fmt.warn("Could not reach API — showing local agents only") + "\n");
26
- }
27
- const shown = new Set();
28
- if (apiAgents.length > 0) {
29
- for (const agent of apiAgents) {
30
- shown.add(agent.id);
31
- const localName = localAliases.get(agent.id);
32
- const isDefault = localName === config.defaultAgent;
33
- const displayName = localName ?? agent.name ?? agent.email ?? agent.id;
34
- const defaultMarker = isDefault ? " (default)" : "";
35
- console.log(` ${fmt.label(displayName + defaultMarker, "")}`);
36
- console.log(` ${fmt.label("Agent ID: ", agent.id)}`);
37
- if (agent.key_prefix) {
38
- console.log(` ${fmt.label("Key: ", ` ${agent.key_prefix}…`)}`);
39
- }
40
- if (localName && localName !== displayName) {
41
- console.log(` ${fmt.label("Local alias:", " " + localName)}`);
42
- }
43
- if (agent.last_seen) {
44
- console.log(` ${fmt.label("Last seen:", " " + new Date(agent.last_seen).toLocaleDateString())}`);
45
- }
46
- if (agent.created_at) {
47
- console.log(` ${fmt.label("Created: ", new Date(agent.created_at).toLocaleDateString())}`);
48
- }
49
- console.log();
50
- }
51
- }
52
- // Show locally-registered agents not in the API response
53
- for (const [name, agent] of Object.entries(config.agents)) {
54
- if (!shown.has(agent.agentId)) {
55
- const isDefault = name === config.defaultAgent;
56
- const defaultMarker = isDefault ? " (default)" : "";
57
- const providerInfo = agent.providers?.join(", ") ?? (agent.openclawConfigured ? "openclaw" : "unknown");
58
- console.log(` ${fmt.label(name + defaultMarker, "")} ${fmt.warn("(local only — not found in account)")}`);
59
- console.log(` ${fmt.label("Agent ID: ", agent.agentId)}`);
60
- console.log(` ${fmt.label("Provider: ", providerInfo)}`);
61
- console.log();
62
- }
63
- }
64
- const total = apiAgents.length > 0 ? apiAgents.length : Object.keys(config.agents).length;
65
- console.log(` Total: ${total} agent(s)\n`);
66
- }
67
- export async function agentsDefaultCommand(name) {
68
- const config = loadConfig();
69
- if (!config) {
70
- console.log("\n" + fmt.error("smoltbot is not initialized") + "\n");
71
- process.exit(1);
72
- }
73
- if (!config.agents[name]) {
74
- console.log(fmt.error(`Agent "${name}" is not registered locally.`) + "\n");
75
- console.log(`Run \`smoltbot agents add ${name}\` to register it first.\n`);
76
- process.exit(1);
77
- }
78
- config.defaultAgent = name;
79
- saveConfig(config);
80
- console.log(fmt.success(`Default agent set to "${name}"`) + "\n");
81
- console.log(fmt.label("Agent ID:", ` ${config.agents[name].agentId}`) + "\n");
82
- }
83
- /**
84
- * smoltbot agents add <name-or-id> [--alias <alias>]
85
- * Register an existing API agent in the local config.
86
- */
87
- export async function agentsAddCommand(nameOrId, alias) {
88
- const config = loadConfig();
89
- if (!config) {
90
- console.log("\n" + fmt.error("smoltbot is not initialized") + "\n");
91
- console.log("Run `smoltbot init` to get started.\n");
92
- process.exit(1);
93
- }
94
- // Fetch the agent from the API
95
- let agentId;
96
- let apiName = null;
97
- let createdAt;
98
- try {
99
- if (/^smolt-[0-9a-f]{8}$/.test(nameOrId)) {
100
- const a = await getAgent(nameOrId);
101
- agentId = a.id;
102
- createdAt = a.created_at;
103
- }
104
- else {
105
- const a = await getAgentByName(nameOrId);
106
- if (!a) {
107
- console.log(fmt.error(`Agent not found: ${nameOrId}`) + "\n");
108
- console.log("Run `smoltbot agents` to see agents in your account.\n");
109
- process.exit(1);
110
- }
111
- agentId = a.id;
112
- apiName = a.name;
113
- createdAt = a.created_at;
114
- }
115
- }
116
- catch (err) {
117
- const msg = err instanceof Error ? err.message : String(err);
118
- console.log("\n" + fmt.error(msg) + "\n");
119
- process.exit(1);
120
- }
121
- const localAlias = alias ?? apiName ?? nameOrId;
122
- // Check for conflicts
123
- const existing = config.agents[localAlias];
124
- if (existing) {
125
- if (existing.agentId === agentId) {
126
- console.log(fmt.success(`Agent "${localAlias}" is already registered (${agentId})`) + "\n");
127
- return;
128
- }
129
- console.log(fmt.error(`Alias "${localAlias}" is already in use by agent ${existing.agentId}.`) + "\n");
130
- console.log(`Use --alias <name> to choose a different local name.\n`);
131
- process.exit(1);
132
- }
133
- config.agents[localAlias] = { agentId, configuredAt: createdAt };
134
- saveConfig(config);
135
- console.log(fmt.success(`Agent registered as "${localAlias}"`) + "\n");
136
- console.log(fmt.label("Agent ID:", ` ${agentId}`) + "\n");
137
- console.log(`Use --agent ${localAlias} to target this agent.\n`);
138
- }
139
- export async function agentsRemoveCommand(name) {
140
- const config = loadConfig();
141
- if (!config) {
142
- console.log("\n" + fmt.error("smoltbot is not initialized") + "\n");
143
- process.exit(1);
144
- }
145
- if (!config.agents[name]) {
146
- console.log(fmt.error(`Agent "${name}" not found`) + "\n");
147
- console.log("Available agents: " + Object.keys(config.agents).join(", ") + "\n");
148
- process.exit(1);
149
- }
150
- if (name === "default" && Object.keys(config.agents).length === 1) {
151
- console.log(fmt.error("Cannot remove the only agent") + "\n");
152
- console.log("Register another agent first with `smoltbot register <name>`.\n");
153
- process.exit(1);
154
- }
155
- const removedId = config.agents[name].agentId;
156
- delete config.agents[name];
157
- // If we removed the default, switch to first remaining agent
158
- if (config.defaultAgent === name) {
159
- const remaining = Object.keys(config.agents);
160
- config.defaultAgent = remaining[0] || "default";
161
- console.log(fmt.warn(`Default agent switched to "${config.defaultAgent}"`) + "\n");
162
- }
163
- saveConfig(config);
164
- console.log(fmt.success(`Agent "${name}" removed (${removedId})`) + "\n");
165
- }
166
- /**
167
- * smoltbot agents rekey [agent-name]
168
- * Re-bind a claimed agent to a new provider API key.
169
- * The raw key is hashed locally (SHA-256) and never transmitted.
170
- */
171
- export async function agentsRekeyCommand(name) {
172
- const config = loadConfig();
173
- if (!config) {
174
- console.log("\n" + fmt.error("smoltbot is not initialized") + "\n");
175
- console.log("Run `smoltbot init` to get started.\n");
176
- process.exit(1);
177
- }
178
- // Resolve which agent to rekey
179
- const agentName = name ?? config.defaultAgent;
180
- const agentEntry = config.agents[agentName];
181
- if (!agentEntry) {
182
- console.log(fmt.error(`Agent "${agentName}" not found`) + "\n");
183
- console.log("Available agents: " + Object.keys(config.agents).join(", ") + "\n");
184
- process.exit(1);
185
- }
186
- const { agentId } = agentEntry;
187
- // Fetch canonical name from API — required for named-agent hash computation
188
- // (named agents: hash = SHA256(key + '|' + name), unnamed: SHA256(key))
189
- let agentApiName = null;
190
- try {
191
- const agent = await getAgent(agentId);
192
- agentApiName = agent.name ?? null;
193
- }
194
- catch {
195
- console.log(fmt.warn("Could not fetch agent details from API — proceeding without name confirmation"));
196
- }
197
- console.log("\n" + fmt.header("Rekey Agent"));
198
- console.log();
199
- console.log(` Agent: ${agentName} (${agentId})`);
200
- if (agentApiName) {
201
- console.log(` Name: ${agentApiName}`);
202
- }
203
- console.log();
204
- console.log(fmt.warn("Your new provider key will be hashed locally. The raw key never leaves your machine."));
5
+ await requireAuth();
6
+ console.log(fmt.header("Agents"));
205
7
  console.log();
206
- // Read new key — prefer env var for CI, otherwise prompt interactively
207
- let newKey = (process.env.SMOLTBOT_NEW_KEY ?? "").trim();
208
- if (!newKey) {
209
- newKey = (await askInput("Enter your new provider API key:", true)).trim();
210
- }
211
- if (!newKey) {
212
- console.log(fmt.error("No key provided") + "\n");
213
- process.exit(1);
214
- }
215
- // Compute hash client-side — raw key never sent to API
216
- const newKeyHash = computeAgentHash(newKey, agentApiName);
217
- console.log(" Computing hash and calling API...\n");
218
- // Call API
219
- let result;
8
+ let agents;
220
9
  try {
221
- result = await postApi(`/v1/agents/${agentId}/rekey`, { new_key_hash: newKeyHash });
10
+ agents = await listAgents();
222
11
  }
223
12
  catch (err) {
224
13
  const msg = err instanceof Error ? err.message : String(err);
225
- if (msg.startsWith("409") || msg.includes("conflict")) {
226
- // Extract conflict agent ID if present
227
- const match = msg.match(/conflict: ((?:smolt-[0-9a-f]{8}|mnm-[0-9a-f-]{36}))/);
228
- const shadowId = match ? match[1] : "(unknown)";
229
- console.log(fmt.error("Key conflict: a different agent was already auto-created for this key.\n"));
230
- console.log(` Shadow agent ID: ${shadowId}`);
231
- console.log(" Steps to resolve:");
232
- console.log(" 1. Open the Mnemom dashboard and deactivate the shadow agent.");
233
- console.log(" 2. Run `smoltbot agents rekey` again.\n");
234
- console.log(" Or visit: https://www.mnemom.ai/docs/guides/agent-key-rotation\n");
235
- }
236
- else {
237
- console.log(fmt.error(msg) + "\n");
238
- }
239
- process.exit(1);
240
- }
241
- // Agent ID is stable — no config changes needed
242
- console.log(fmt.success("Agent rekeyed successfully") + "\n");
243
- console.log(fmt.label("Agent ID: ", ` ${result.agent_id}`));
244
- console.log(fmt.label("Rekeyed at:", ` ${new Date(result.rekeyed_at).toLocaleString()}`));
245
- console.log();
246
- console.log("Your agent history, alignment card, and integrity score are preserved.");
247
- console.log("Update your ANTHROPIC_API_KEY (or equivalent) to the new key.\n");
248
- }
249
- export async function agentsCheckBindingCommand(name) {
250
- const config = loadConfig();
251
- if (!config) {
252
- console.log('\n' + fmt.error('smoltbot is not initialized') + '\n');
253
- console.log('Run `smoltbot init` to get started.\n');
254
- process.exit(1);
255
- }
256
- const agentName = name ?? config.defaultAgent;
257
- const agentEntry = config.agents[agentName];
258
- if (!agentEntry) {
259
- console.log(fmt.error(`Agent "${agentName}" not found`) + '\n');
260
- console.log('Available agents: ' + Object.keys(config.agents).join(', ') + '\n');
261
- process.exit(1);
262
- }
263
- const { agentId } = agentEntry;
264
- // Fetch canonical name from API (needed for named-agent hash)
265
- let agentApiName = null;
266
- try {
267
- const agent = await getAgent(agentId);
268
- agentApiName = agent.name ?? null;
269
- }
270
- catch {
271
- console.log(fmt.warn('Could not fetch agent details — proceeding without name confirmation'));
272
- }
273
- console.log('\n' + fmt.header('Check Key Binding'));
274
- console.log(` Agent: ${agentName} (${agentId})`);
275
- console.log();
276
- let key = (process.env.SMOLTBOT_CHECK_KEY ?? '').trim();
277
- if (!key) {
278
- key = (await askInput('Enter the API key to check:', true)).trim();
279
- }
280
- if (!key) {
281
- console.log(fmt.error('No key provided') + '\n');
282
- process.exit(1);
283
- }
284
- const keyHash = computeAgentHash(key, agentApiName);
285
- let result;
286
- try {
287
- result = await verifyBinding(agentId, keyHash);
288
- }
289
- catch (err) {
290
- console.log(fmt.error(err instanceof Error ? err.message : String(err)) + '\n');
291
- process.exit(1);
292
- }
293
- if (result.bound) {
294
- console.log(fmt.success('Key is bound to this agent') + '\n');
295
- }
296
- else {
297
- console.log(fmt.error('Key is NOT bound to this agent') + '\n');
298
- }
299
- if (result.key_prefix) {
300
- const label = result.bound ? 'Key prefix: ' : 'Current prefix:';
301
- console.log(fmt.label(label, ` ${result.key_prefix}…`) + '\n');
302
- }
14
+ console.log(fmt.error(`Failed to list agents: ${msg}`) + "\n");
15
+ process.exit(1);
16
+ }
17
+ if (agents.length === 0) {
18
+ console.log(" No agents found.\n");
19
+ return;
20
+ }
21
+ // Table header
22
+ const nameW = 24;
23
+ const idW = 40;
24
+ const seenW = 14;
25
+ const statusW = 14;
26
+ const header = "Name".padEnd(nameW) +
27
+ "ID".padEnd(idW) +
28
+ "Last Seen".padEnd(seenW) +
29
+ "Containment";
30
+ console.log(` ${header}`);
31
+ console.log(` ${"─".repeat(nameW + idW + seenW + statusW)}`);
32
+ for (const agent of agents) {
33
+ const name = (agent.name ?? "-").slice(0, nameW - 2).padEnd(nameW);
34
+ const id = agent.id.padEnd(idW);
35
+ const lastSeen = agent.last_seen
36
+ ? new Date(agent.last_seen).toLocaleDateString()
37
+ : "-";
38
+ const seen = lastSeen.padEnd(seenW);
39
+ const containment = agent.containment_status ?? "-";
40
+ console.log(` ${name}${id}${seen}${containment}`);
41
+ }
42
+ console.log(`\n Total: ${agents.length} agent(s)\n`);
303
43
  }
@@ -1,13 +1,8 @@
1
- import { configExists, getAuthInfo, clearAuthTokens } from "../lib/config.js";
1
+ import { getAuthInfo, clearAuthTokens } from "../lib/auth.js";
2
2
  import { loginWithBrowser, loginWithPassword } from "../lib/auth.js";
3
3
  import { fmt } from "../lib/format.js";
4
4
  import { askInput } from "../lib/prompt.js";
5
5
  export async function loginCommand(options = {}) {
6
- if (!configExists()) {
7
- console.log("\n" + fmt.error("smoltbot is not initialized") + "\n");
8
- console.log("Run `smoltbot init` to get started.\n");
9
- process.exit(1);
10
- }
11
6
  try {
12
7
  let tokens;
13
8
  if (options.noBrowser) {
@@ -45,7 +40,7 @@ export async function logoutCommand() {
45
40
  export async function whoamiCommand() {
46
41
  const auth = getAuthInfo();
47
42
  if (!auth) {
48
- console.log("\nNot logged in. Run `smoltbot login` to authenticate.\n");
43
+ console.log("\nNot logged in. Run `mnemom login` to authenticate.\n");
49
44
  return;
50
45
  }
51
46
  const now = Math.floor(Date.now() / 1000);
@@ -1,8 +1,8 @@
1
- import { type AlignmentCard } from "../lib/api.js";
2
1
  export type CardFormat = "json" | "yaml";
3
2
  export interface ParsedCard {
4
3
  format: CardFormat;
5
- parsed: AlignmentCard;
4
+ parsed: Record<string, unknown>;
5
+ raw: string;
6
6
  }
7
7
  export declare function parseCardFile(filePath: string): ParsedCard;
8
8
  export interface ValidationCheck {
@@ -10,14 +10,27 @@ export interface ValidationCheck {
10
10
  passed: boolean;
11
11
  message: string;
12
12
  }
13
- /** @deprecated Use validateCard instead */
14
- export declare const validateCardJson: typeof validateCard;
15
- export declare function validateCard(raw: string): ValidationCheck[];
16
13
  /**
17
- * Validate a pre-parsed card object (used for YAML cards).
18
- * Re-serializes to JSON so the same checks run identically.
14
+ * Validate a unified alignment card object against ADR-008 schema.
15
+ * Sections: principal, values, conscience, integrity, autonomy,
16
+ * capabilities, enforcement, audit, extensions
19
17
  */
20
- export declare function validateCardObject(card: AlignmentCard): ValidationCheck[];
18
+ export declare function validateUnifiedCard(card: Record<string, unknown>): ValidationCheck[];
19
+ /** @deprecated Use validateUnifiedCard instead */
20
+ export declare const validateCardJson: (raw: string) => ValidationCheck[];
21
21
  export declare function cardShowCommand(agentName?: string): Promise<void>;
22
22
  export declare function cardPublishCommand(file: string, agentName?: string): Promise<void>;
23
23
  export declare function cardValidateCommand(file: string): Promise<void>;
24
+ export declare function cardEditCommand(agentName?: string): Promise<void>;
25
+ /**
26
+ * mnemom card evaluate <card-file> --tools <tools> -- local CI/CD evaluation
27
+ *
28
+ * Runs entirely locally using the embedded policy engine. No API key needed.
29
+ * The card IS the policy source -- capabilities and enforcement sections
30
+ * are extracted by @mnemom/policy-engine 0.3.0's evaluatePolicy().
31
+ */
32
+ export declare function cardEvaluateCommand(file: string, options: {
33
+ tools?: string;
34
+ toolManifest?: string;
35
+ strict?: boolean;
36
+ }): Promise<void>;