@mnemom/mnemom 0.10.0 → 0.12.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/README.md +25 -25
- package/dist/commands/agents.js +2 -7
- package/dist/commands/api-key.d.ts +37 -0
- package/dist/commands/api-key.js +179 -0
- package/dist/commands/auth.js +1 -2
- package/dist/commands/card.js +33 -22
- package/dist/commands/governance.d.ts +85 -0
- package/dist/commands/governance.js +329 -0
- package/dist/commands/integrity.js +1 -2
- package/dist/commands/license.js +13 -4
- package/dist/commands/listen.d.ts +29 -0
- package/dist/commands/listen.js +201 -0
- package/dist/commands/logs.js +1 -3
- package/dist/commands/org.js +4 -6
- package/dist/commands/protection.js +70 -23
- package/dist/commands/recipes.d.ts +34 -0
- package/dist/commands/recipes.js +80 -0
- package/dist/commands/status.js +6 -169
- package/dist/commands/team.js +5 -10
- package/dist/commands/validate.js +7 -4
- package/dist/commands/webhooks.d.ts +61 -0
- package/dist/commands/webhooks.js +328 -0
- package/dist/index.js +489 -1
- package/dist/lib/api.d.ts +199 -2
- package/dist/lib/api.js +207 -0
- package/dist/lib/auth.js +1 -5
- package/dist/lib/format.d.ts +4 -0
- package/dist/lib/format.js +6 -0
- package/dist/lib/listen-stream.d.ts +40 -0
- package/dist/lib/listen-stream.js +101 -0
- package/dist/lib/webhooks-api.d.ts +80 -0
- package/dist/lib/webhooks-api.js +172 -0
- package/dist/smoltbot-shim.js +3 -3
- package/package.json +1 -1
- package/dist/lib/model-cache.d.ts +0 -16
- package/dist/lib/model-cache.js +0 -137
- package/dist/lib/models.d.ts +0 -41
- package/dist/lib/models.js +0 -357
- package/dist/lib/openclaw.d.ts +0 -221
- package/dist/lib/openclaw.js +0 -474
|
@@ -11,27 +11,53 @@ const PROTECTION_MODES = ["off", "observe", "nudge", "enforce"];
|
|
|
11
11
|
const SURFACE_KEYS = ["incoming", "outgoing", "tool_calls", "tool_responses"];
|
|
12
12
|
// Per ADR-037 Decision 4: deny public LLM endpoints + public DNS providers,
|
|
13
13
|
// and the any-host CIDRs, at write time.
|
|
14
|
+
// T8-4 (2026-05-19) extended the corpus per
|
|
15
|
+
// `safe-house-hardening/audit/deny-list-completeness.md`: added emerging
|
|
16
|
+
// LLM providers (Replicate, Together, Anyscale, Fireworks, xAI,
|
|
17
|
+
// Perplexity, DeepSeek) and three reserved IP ranges (link-local v4/v6
|
|
18
|
+
// + multicast v4).
|
|
19
|
+
// Mirror lives at `mnemom-api/src/composition/validate.ts` —
|
|
20
|
+
// must stay in sync; T8-4 F3 (filed) extracts to a shared package.
|
|
14
21
|
const DENY_DOMAINS = new Set([
|
|
22
|
+
// OpenAI + Anthropic + Google
|
|
15
23
|
"api.openai.com",
|
|
16
24
|
"api.anthropic.com",
|
|
17
25
|
"generativelanguage.googleapis.com",
|
|
26
|
+
"cloud.google.com",
|
|
27
|
+
// Other model-API providers (T8-4 additions 2026-05-19)
|
|
18
28
|
"api.cohere.ai",
|
|
19
29
|
"api.mistral.ai",
|
|
20
30
|
"api.groq.com",
|
|
21
|
-
"
|
|
31
|
+
"replicate.com",
|
|
32
|
+
"api.replicate.com",
|
|
33
|
+
"api.together.xyz",
|
|
34
|
+
"api.together.ai",
|
|
35
|
+
"api.endpoints.anyscale.com",
|
|
36
|
+
"api.fireworks.ai",
|
|
37
|
+
"api.x.ai",
|
|
38
|
+
"api.perplexity.ai",
|
|
39
|
+
"api.deepseek.com",
|
|
40
|
+
// Public DNS providers
|
|
22
41
|
"dns.google",
|
|
23
42
|
"cloudflare-dns.com",
|
|
24
43
|
"one.one.one.one",
|
|
25
44
|
"dns.quad9.net",
|
|
26
45
|
]);
|
|
27
46
|
const DENY_IP_PREFIXES = [
|
|
47
|
+
// Any-host
|
|
28
48
|
"0.0.0.0/0",
|
|
29
49
|
"::/0",
|
|
50
|
+
// Public DNS provider /24s
|
|
30
51
|
"8.8.8.0/24",
|
|
31
52
|
"8.8.4.0/24",
|
|
32
53
|
"1.1.1.0/24",
|
|
33
54
|
"1.0.0.0/24",
|
|
34
55
|
"9.9.9.0/24",
|
|
56
|
+
// T8-4 additions 2026-05-19: reserved ranges with no legitimate
|
|
57
|
+
// trusted-source semantics. Link-local + multicast.
|
|
58
|
+
"169.254.0.0/16", // link-local IPv4 (RFC 3927) — SSRF/imds vector
|
|
59
|
+
"fe80::/10", // link-local IPv6 (RFC 4291)
|
|
60
|
+
"224.0.0.0/4", // multicast IPv4 (RFC 5771) — never legitimate trust target
|
|
35
61
|
];
|
|
36
62
|
const DOMAIN_RE = /^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?(\.[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?)+(:\d{1,5})?$/i;
|
|
37
63
|
const AGENT_ID_RE = /^mnm-[a-z0-9-]{4,}$/i;
|
|
@@ -41,6 +67,12 @@ function isObject(v) {
|
|
|
41
67
|
}
|
|
42
68
|
function validateDomain(d) {
|
|
43
69
|
const lower = d.toLowerCase();
|
|
70
|
+
// T8-4: explicit wildcard rejection. DOMAIN_RE already implicitly rejects
|
|
71
|
+
// a sole '*' (no dot), but `*.example.com` shapes also need an explicit
|
|
72
|
+
// guard — operator footgun if we ever loosened DOMAIN_RE.
|
|
73
|
+
if (lower.includes("*")) {
|
|
74
|
+
return "wildcards are not permitted in trusted_sources.domains; enumerate the specific hosts";
|
|
75
|
+
}
|
|
44
76
|
if (DENY_DOMAINS.has(lower.split(":")[0])) {
|
|
45
77
|
return "domain is on the static deny-list (public LLM/DNS endpoint)";
|
|
46
78
|
}
|
|
@@ -191,7 +223,9 @@ export function validateProtectionCard(card) {
|
|
|
191
223
|
});
|
|
192
224
|
bad = true;
|
|
193
225
|
}
|
|
194
|
-
if (nums.quarantine !== undefined &&
|
|
226
|
+
if (nums.quarantine !== undefined &&
|
|
227
|
+
nums.block !== undefined &&
|
|
228
|
+
nums.quarantine > nums.block) {
|
|
195
229
|
checks.push({
|
|
196
230
|
name: "thresholds.quarantine",
|
|
197
231
|
passed: false,
|
|
@@ -214,11 +248,15 @@ export function validateProtectionCard(card) {
|
|
|
214
248
|
checks.push({
|
|
215
249
|
name: "screen_surfaces",
|
|
216
250
|
passed: false,
|
|
217
|
-
message:
|
|
251
|
+
message: "Must be an object of booleans, not an array. Per ADR-037 use { incoming: true, outgoing: true, tool_calls: true, tool_responses: true }.",
|
|
218
252
|
});
|
|
219
253
|
}
|
|
220
254
|
else if (!isObject(card.screen_surfaces)) {
|
|
221
|
-
checks.push({
|
|
255
|
+
checks.push({
|
|
256
|
+
name: "screen_surfaces",
|
|
257
|
+
passed: false,
|
|
258
|
+
message: "Must be an object if present",
|
|
259
|
+
});
|
|
222
260
|
}
|
|
223
261
|
else {
|
|
224
262
|
const s = card.screen_surfaces;
|
|
@@ -245,7 +283,7 @@ export function validateProtectionCard(card) {
|
|
|
245
283
|
}
|
|
246
284
|
}
|
|
247
285
|
if (!bad) {
|
|
248
|
-
const enabled = SURFACE_KEYS.filter(k => s[k] === true).length;
|
|
286
|
+
const enabled = SURFACE_KEYS.filter((k) => s[k] === true).length;
|
|
249
287
|
checks.push({
|
|
250
288
|
name: "screen_surfaces",
|
|
251
289
|
passed: true,
|
|
@@ -260,11 +298,15 @@ export function validateProtectionCard(card) {
|
|
|
260
298
|
checks.push({
|
|
261
299
|
name: "trusted_sources",
|
|
262
300
|
passed: false,
|
|
263
|
-
message:
|
|
301
|
+
message: "Must be an object of typed buckets, not an array. Per ADR-037 use { domains: [...], agent_ids: [...], ip_ranges: [...] } — the legacy [{pattern, ...}] shape is no longer accepted.",
|
|
264
302
|
});
|
|
265
303
|
}
|
|
266
304
|
else if (!isObject(card.trusted_sources)) {
|
|
267
|
-
checks.push({
|
|
305
|
+
checks.push({
|
|
306
|
+
name: "trusted_sources",
|
|
307
|
+
passed: false,
|
|
308
|
+
message: "Must be an object if present",
|
|
309
|
+
});
|
|
268
310
|
}
|
|
269
311
|
else {
|
|
270
312
|
const ts = card.trusted_sources;
|
|
@@ -290,7 +332,7 @@ export function validateProtectionCard(card) {
|
|
|
290
332
|
function parseProtectionFile(filePath) {
|
|
291
333
|
const raw = fs.readFileSync(filePath, "utf-8");
|
|
292
334
|
const ext = path.extname(filePath).toLowerCase();
|
|
293
|
-
const format =
|
|
335
|
+
const format = ext === ".yaml" || ext === ".yml" ? "yaml" : "json";
|
|
294
336
|
if (format === "yaml") {
|
|
295
337
|
const parsed = yaml.load(raw);
|
|
296
338
|
if (!parsed || typeof parsed !== "object") {
|
|
@@ -450,19 +492,20 @@ export async function protectionEditCommand(agentName, options = {}) {
|
|
|
450
492
|
if (!original) {
|
|
451
493
|
console.log(fmt.warn("No protection card found. Creating a template..."));
|
|
452
494
|
}
|
|
453
|
-
const cardYaml = original ||
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
495
|
+
const cardYaml = original ||
|
|
496
|
+
yaml.dump({
|
|
497
|
+
card_version: "protection/2026-04-26",
|
|
498
|
+
agent_id: agentId,
|
|
499
|
+
mode: "observe",
|
|
500
|
+
thresholds: { warn: 0.3, quarantine: 0.6, block: 0.9 },
|
|
501
|
+
screen_surfaces: {
|
|
502
|
+
incoming: true,
|
|
503
|
+
outgoing: true,
|
|
504
|
+
tool_calls: true,
|
|
505
|
+
tool_responses: true,
|
|
506
|
+
},
|
|
507
|
+
trusted_sources: { domains: [], agent_ids: [], ip_ranges: [] },
|
|
508
|
+
}, { lineWidth: 120, noRefs: true });
|
|
466
509
|
const tmpDir = os.tmpdir();
|
|
467
510
|
const tmpFile = path.join(tmpDir, `mnemom-protection-${agentId}.yaml`);
|
|
468
511
|
fs.writeFileSync(tmpFile, cardYaml);
|
|
@@ -474,14 +517,18 @@ export async function protectionEditCommand(agentName, options = {}) {
|
|
|
474
517
|
try {
|
|
475
518
|
fs.unlinkSync(tmpFile);
|
|
476
519
|
}
|
|
477
|
-
catch {
|
|
520
|
+
catch {
|
|
521
|
+
/* ignore */
|
|
522
|
+
}
|
|
478
523
|
process.exit(1);
|
|
479
524
|
}
|
|
480
525
|
const edited = fs.readFileSync(tmpFile, "utf-8");
|
|
481
526
|
try {
|
|
482
527
|
fs.unlinkSync(tmpFile);
|
|
483
528
|
}
|
|
484
|
-
catch {
|
|
529
|
+
catch {
|
|
530
|
+
/* ignore */
|
|
531
|
+
}
|
|
485
532
|
if (edited === cardYaml) {
|
|
486
533
|
console.log("\nNo changes made.\n");
|
|
487
534
|
return;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `mnemom recipes ...` commands — AEGIS-6b (Phase 2 Day 5).
|
|
3
|
+
*
|
|
4
|
+
* mnemom recipes report-fn <recipe-id> [--summary "..."] [--evidence "..."]
|
|
5
|
+
* [--agent <id>] [--checkpoint <id>]
|
|
6
|
+
* [--json]
|
|
7
|
+
* mnemom recipes report-fp <recipe-id> [--summary "..."] [--evidence "..."]
|
|
8
|
+
* [--agent <id>] [--checkpoint <id>]
|
|
9
|
+
* [--json]
|
|
10
|
+
*
|
|
11
|
+
* POSTs to /v1/recipes/:id/report (AEGIS-6a; mnemom-api#508). Authentication
|
|
12
|
+
* uses the same JWT/API-key resolution as every other authed CLI command.
|
|
13
|
+
* Idempotency-Key is generated per call by postApi(); the server folds the
|
|
14
|
+
* request body into the fingerprint per `feedback_idempotency_key_body_hash`.
|
|
15
|
+
*
|
|
16
|
+
* Output (human mode):
|
|
17
|
+
* - candidate_id of the resulting sideband_analyses row
|
|
18
|
+
* - status hint ("pending — admin review or auto-mode promotion will
|
|
19
|
+
* determine the outcome")
|
|
20
|
+
* - a pointer to the dashboard URL where the customer can track review
|
|
21
|
+
* status. The customer-facing dashboard form (AEGIS-6c) is a future
|
|
22
|
+
* follow-up; until it lands, the URL points to the static "report
|
|
23
|
+
* filed" anchor and the candidate_id is the primary track-handle.
|
|
24
|
+
*/
|
|
25
|
+
interface ReportOpts {
|
|
26
|
+
summary?: string;
|
|
27
|
+
evidence?: string;
|
|
28
|
+
agent?: string;
|
|
29
|
+
checkpoint?: string;
|
|
30
|
+
json?: boolean;
|
|
31
|
+
}
|
|
32
|
+
export declare function recipesReportFnCommand(recipeId: string, opts: ReportOpts): Promise<void>;
|
|
33
|
+
export declare function recipesReportFpCommand(recipeId: string, opts: ReportOpts): Promise<void>;
|
|
34
|
+
export {};
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `mnemom recipes ...` commands — AEGIS-6b (Phase 2 Day 5).
|
|
3
|
+
*
|
|
4
|
+
* mnemom recipes report-fn <recipe-id> [--summary "..."] [--evidence "..."]
|
|
5
|
+
* [--agent <id>] [--checkpoint <id>]
|
|
6
|
+
* [--json]
|
|
7
|
+
* mnemom recipes report-fp <recipe-id> [--summary "..."] [--evidence "..."]
|
|
8
|
+
* [--agent <id>] [--checkpoint <id>]
|
|
9
|
+
* [--json]
|
|
10
|
+
*
|
|
11
|
+
* POSTs to /v1/recipes/:id/report (AEGIS-6a; mnemom-api#508). Authentication
|
|
12
|
+
* uses the same JWT/API-key resolution as every other authed CLI command.
|
|
13
|
+
* Idempotency-Key is generated per call by postApi(); the server folds the
|
|
14
|
+
* request body into the fingerprint per `feedback_idempotency_key_body_hash`.
|
|
15
|
+
*
|
|
16
|
+
* Output (human mode):
|
|
17
|
+
* - candidate_id of the resulting sideband_analyses row
|
|
18
|
+
* - status hint ("pending — admin review or auto-mode promotion will
|
|
19
|
+
* determine the outcome")
|
|
20
|
+
* - a pointer to the dashboard URL where the customer can track review
|
|
21
|
+
* status. The customer-facing dashboard form (AEGIS-6c) is a future
|
|
22
|
+
* follow-up; until it lands, the URL points to the static "report
|
|
23
|
+
* filed" anchor and the candidate_id is the primary track-handle.
|
|
24
|
+
*/
|
|
25
|
+
import chalk from "chalk";
|
|
26
|
+
import { reportRecipeFnFp, } from "../lib/api.js";
|
|
27
|
+
import { requireAuth } from "../lib/auth.js";
|
|
28
|
+
async function readSummary(opts) {
|
|
29
|
+
if (typeof opts.summary === "string" && opts.summary.trim()) {
|
|
30
|
+
return opts.summary.trim();
|
|
31
|
+
}
|
|
32
|
+
// Allow piping a longer description via stdin: `cat report.md | mnemom
|
|
33
|
+
// recipes report-fn rcp_abc`. The server requires a non-empty summary;
|
|
34
|
+
// we'd rather fail-here with a clear CLI message than send an empty
|
|
35
|
+
// body that 400s.
|
|
36
|
+
if (!process.stdin.isTTY) {
|
|
37
|
+
const chunks = [];
|
|
38
|
+
for await (const chunk of process.stdin) {
|
|
39
|
+
chunks.push(typeof chunk === "string" ? Buffer.from(chunk) : chunk);
|
|
40
|
+
}
|
|
41
|
+
const piped = Buffer.concat(chunks).toString("utf8").trim();
|
|
42
|
+
if (piped)
|
|
43
|
+
return piped;
|
|
44
|
+
}
|
|
45
|
+
throw new Error("summary required: pass --summary \"…\" or pipe text on stdin.");
|
|
46
|
+
}
|
|
47
|
+
async function runReport(recipeId, type, opts) {
|
|
48
|
+
await requireAuth();
|
|
49
|
+
const summary = await readSummary(opts);
|
|
50
|
+
const input = {
|
|
51
|
+
type,
|
|
52
|
+
summary,
|
|
53
|
+
...(opts.evidence ? { evidence: opts.evidence } : {}),
|
|
54
|
+
...(opts.agent ? { agent_id: opts.agent } : {}),
|
|
55
|
+
...(opts.checkpoint ? { checkpoint_id: opts.checkpoint } : {}),
|
|
56
|
+
};
|
|
57
|
+
const result = await reportRecipeFnFp(recipeId, input);
|
|
58
|
+
if (opts.json) {
|
|
59
|
+
process.stdout.write(JSON.stringify(result) + "\n");
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
const verb = type === "fn" ? "false-negative" : "false-positive";
|
|
63
|
+
const label = type === "fn" ? "FN" : "FP";
|
|
64
|
+
process.stdout.write([
|
|
65
|
+
chalk.green(`✓ ${label} report filed`),
|
|
66
|
+
` candidate_id: ${chalk.bold(result.candidate_id)}`,
|
|
67
|
+
` related_recipe_id: ${chalk.dim(result.related_recipe_id)}`,
|
|
68
|
+
` status: ${chalk.dim("pending — awaiting admin review or auto-mode promotion")}`,
|
|
69
|
+
"",
|
|
70
|
+
chalk.dim(`Track this ${verb} report under candidate id ${result.candidate_id}.`),
|
|
71
|
+
chalk.dim("Once the customer dashboard form (AEGIS-6c) ships, a direct URL will appear here."),
|
|
72
|
+
"",
|
|
73
|
+
].join("\n"));
|
|
74
|
+
}
|
|
75
|
+
export async function recipesReportFnCommand(recipeId, opts) {
|
|
76
|
+
await runReport(recipeId, "fn", opts);
|
|
77
|
+
}
|
|
78
|
+
export async function recipesReportFpCommand(recipeId, opts) {
|
|
79
|
+
await runReport(recipeId, "fp", opts);
|
|
80
|
+
}
|
package/dist/commands/status.js
CHANGED
|
@@ -1,26 +1,13 @@
|
|
|
1
1
|
import { getGatewayUrl } from "../lib/config.js";
|
|
2
2
|
import { resolveAgentId, getAgent, getIntegrity, getTraces } from "../lib/api.js";
|
|
3
3
|
import { isLoggedIn, getAuthInfo } from "../lib/auth.js";
|
|
4
|
-
import { detectOpenClaw, detectProviders, getCurrentModel, getSmoltbotConfiguredProviders, PROVIDER_CONFIG_KEYS, } from "../lib/openclaw.js";
|
|
5
|
-
import { formatModelName, detectProvider } from "../lib/models.js";
|
|
6
|
-
import { refreshModelCache } from "../lib/model-cache.js";
|
|
7
4
|
import { fmt } from "../lib/format.js";
|
|
8
5
|
const DASHBOARD_URL = "https://mnemom.ai";
|
|
9
|
-
const PROVIDER_LABELS = {
|
|
10
|
-
anthropic: "Anthropic",
|
|
11
|
-
openai: "OpenAI",
|
|
12
|
-
gemini: "Gemini",
|
|
13
|
-
};
|
|
14
|
-
const AIP_SUPPORT = {
|
|
15
|
-
anthropic: "Full (thinking blocks)",
|
|
16
|
-
openai: "Via reasoning summaries",
|
|
17
|
-
gemini: "Full (thought parts)",
|
|
18
|
-
};
|
|
19
6
|
export async function statusCommand(agentName) {
|
|
20
7
|
console.log(fmt.header("mnemom status"));
|
|
21
8
|
console.log();
|
|
22
9
|
const checks = [];
|
|
23
|
-
// 1.
|
|
10
|
+
// 1. Auth status
|
|
24
11
|
const authCheck = await checkAuthStatus();
|
|
25
12
|
checks.push(authCheck);
|
|
26
13
|
if (authCheck.status === "error") {
|
|
@@ -31,55 +18,25 @@ export async function statusCommand(agentName) {
|
|
|
31
18
|
// Resolve agent ID from server
|
|
32
19
|
const agentId = await resolveAgentId(agentName);
|
|
33
20
|
const gatewayUrl = getGatewayUrl();
|
|
34
|
-
// 2.
|
|
35
|
-
const openclawCheck = checkOpenClawConfig();
|
|
36
|
-
checks.push(openclawCheck);
|
|
37
|
-
// 3. Check configured providers
|
|
38
|
-
const providerChecks = checkConfiguredProviders();
|
|
39
|
-
checks.push(...providerChecks);
|
|
40
|
-
// 4. Check current model
|
|
41
|
-
const modelCheck = checkCurrentModel();
|
|
42
|
-
checks.push(modelCheck);
|
|
43
|
-
// 5. Test gateway connectivity
|
|
21
|
+
// 2. Gateway connectivity
|
|
44
22
|
const gatewayCheck = await checkGatewayConnectivity(gatewayUrl);
|
|
45
23
|
checks.push(gatewayCheck);
|
|
46
|
-
//
|
|
24
|
+
// 3. API connectivity
|
|
47
25
|
const apiCheck = await checkApiConnectivity(agentId);
|
|
48
26
|
checks.push(apiCheck);
|
|
49
27
|
// Print all checks
|
|
50
28
|
printChecks(checks);
|
|
51
|
-
//
|
|
29
|
+
// Configuration details
|
|
52
30
|
console.log(fmt.section("Configuration"));
|
|
53
31
|
console.log();
|
|
54
32
|
console.log(fmt.label("Agent ID: ", agentId));
|
|
55
33
|
console.log(fmt.label("Gateway: ", gatewayUrl));
|
|
56
34
|
console.log(fmt.label("Dashboard:", ` ${DASHBOARD_URL}/agents/${agentId}`));
|
|
57
|
-
//
|
|
58
|
-
const { fullPath, provider, modelId } = getCurrentModel();
|
|
59
|
-
if (fullPath) {
|
|
60
|
-
console.log(`\nCurrent Model: ${fullPath}`);
|
|
61
|
-
if (modelId) {
|
|
62
|
-
console.log(` (${formatModelName(modelId)})`);
|
|
63
|
-
}
|
|
64
|
-
if (provider && (provider === "smoltbot" || provider.startsWith("smoltbot"))) {
|
|
65
|
-
console.log(" Status: Traced mode ACTIVE");
|
|
66
|
-
}
|
|
67
|
-
else {
|
|
68
|
-
console.log(" Status: Traced mode NOT ACTIVE");
|
|
69
|
-
if (modelId) {
|
|
70
|
-
const detectedProvider = detectProvider(modelId);
|
|
71
|
-
const configKey = detectedProvider ? PROVIDER_CONFIG_KEYS[detectedProvider] : "mnemom";
|
|
72
|
-
console.log(`\n To enable: openclaw models set ${configKey}/${modelId}`);
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
// Show provider summary
|
|
77
|
-
showProviderSummary();
|
|
78
|
-
// Show trace summary if available
|
|
35
|
+
// Trace summary if API connectivity is healthy
|
|
79
36
|
if (apiCheck.status === "ok") {
|
|
80
37
|
await showTraceSummary(agentId);
|
|
81
38
|
}
|
|
82
|
-
//
|
|
39
|
+
// Overall status
|
|
83
40
|
const hasErrors = checks.some((c) => c.status === "error");
|
|
84
41
|
const hasWarnings = checks.some((c) => c.status === "warning");
|
|
85
42
|
if (hasErrors) {
|
|
@@ -94,8 +51,6 @@ export async function statusCommand(agentName) {
|
|
|
94
51
|
console.log("\n" + fmt.header("Status: ALL SYSTEMS GO"));
|
|
95
52
|
console.log();
|
|
96
53
|
}
|
|
97
|
-
// Refresh model cache in background (non-blocking)
|
|
98
|
-
refreshModelCache().catch(() => { });
|
|
99
54
|
}
|
|
100
55
|
async function checkAuthStatus() {
|
|
101
56
|
const loggedIn = await isLoggedIn();
|
|
@@ -122,124 +77,6 @@ async function checkAuthStatus() {
|
|
|
122
77
|
message: "Authenticated via API key",
|
|
123
78
|
};
|
|
124
79
|
}
|
|
125
|
-
function checkOpenClawConfig() {
|
|
126
|
-
const detection = detectOpenClaw();
|
|
127
|
-
if (!detection.installed) {
|
|
128
|
-
return {
|
|
129
|
-
name: "OpenClaw",
|
|
130
|
-
status: "error",
|
|
131
|
-
message: "Not installed",
|
|
132
|
-
details: "Install from https://openclaw.ai",
|
|
133
|
-
};
|
|
134
|
-
}
|
|
135
|
-
if (!detection.hasApiKey) {
|
|
136
|
-
if (detection.isOAuth) {
|
|
137
|
-
return {
|
|
138
|
-
name: "OpenClaw",
|
|
139
|
-
status: "error",
|
|
140
|
-
message: "OAuth auth (not supported)",
|
|
141
|
-
details: "mnemom requires API key authentication",
|
|
142
|
-
};
|
|
143
|
-
}
|
|
144
|
-
// Check if any provider has a key (not just Anthropic)
|
|
145
|
-
const providerDetection = detectProviders();
|
|
146
|
-
const anyKey = Object.values(providerDetection.providers).some((p) => p.hasApiKey);
|
|
147
|
-
if (anyKey) {
|
|
148
|
-
return {
|
|
149
|
-
name: "OpenClaw",
|
|
150
|
-
status: "ok",
|
|
151
|
-
message: "API key(s) found",
|
|
152
|
-
};
|
|
153
|
-
}
|
|
154
|
-
return {
|
|
155
|
-
name: "OpenClaw",
|
|
156
|
-
status: "error",
|
|
157
|
-
message: "No API keys configured",
|
|
158
|
-
details: "Run `openclaw auth` to add your API key",
|
|
159
|
-
};
|
|
160
|
-
}
|
|
161
|
-
if (!detection.smoltbotAlreadyConfigured) {
|
|
162
|
-
return {
|
|
163
|
-
name: "OpenClaw",
|
|
164
|
-
status: "warning",
|
|
165
|
-
message: "mnemom provider not configured",
|
|
166
|
-
details: "Run `mnemom register <name>` to configure",
|
|
167
|
-
};
|
|
168
|
-
}
|
|
169
|
-
return {
|
|
170
|
-
name: "OpenClaw",
|
|
171
|
-
status: "ok",
|
|
172
|
-
message: "mnemom provider configured",
|
|
173
|
-
};
|
|
174
|
-
}
|
|
175
|
-
function checkConfiguredProviders() {
|
|
176
|
-
const results = [];
|
|
177
|
-
const providerDetection = detectProviders();
|
|
178
|
-
if (!providerDetection.installed)
|
|
179
|
-
return results;
|
|
180
|
-
const configuredProviders = getSmoltbotConfiguredProviders();
|
|
181
|
-
for (const provider of ["anthropic", "openai", "gemini"]) {
|
|
182
|
-
const info = providerDetection.providers[provider];
|
|
183
|
-
const isConfigured = configuredProviders.includes(provider);
|
|
184
|
-
if (info.hasApiKey && isConfigured) {
|
|
185
|
-
results.push({
|
|
186
|
-
name: `${PROVIDER_LABELS[provider]}`,
|
|
187
|
-
status: "ok",
|
|
188
|
-
message: `Configured (AIP: ${AIP_SUPPORT[provider]})`,
|
|
189
|
-
});
|
|
190
|
-
}
|
|
191
|
-
else if (info.hasApiKey && !isConfigured) {
|
|
192
|
-
results.push({
|
|
193
|
-
name: `${PROVIDER_LABELS[provider]}`,
|
|
194
|
-
status: "warning",
|
|
195
|
-
message: "API key found but not configured",
|
|
196
|
-
details: "Run `mnemom register <name>` to configure",
|
|
197
|
-
});
|
|
198
|
-
}
|
|
199
|
-
// Don't show providers without keys (too noisy)
|
|
200
|
-
}
|
|
201
|
-
return results;
|
|
202
|
-
}
|
|
203
|
-
function checkCurrentModel() {
|
|
204
|
-
const { fullPath, provider, modelId } = getCurrentModel();
|
|
205
|
-
if (!fullPath) {
|
|
206
|
-
return {
|
|
207
|
-
name: "Current Model",
|
|
208
|
-
status: "warning",
|
|
209
|
-
message: "No default model set",
|
|
210
|
-
details: "Run `openclaw models set mnemom/<model>`",
|
|
211
|
-
};
|
|
212
|
-
}
|
|
213
|
-
if (provider && (provider === "smoltbot" || provider.startsWith("smoltbot"))) {
|
|
214
|
-
return {
|
|
215
|
-
name: "Current Model",
|
|
216
|
-
status: "ok",
|
|
217
|
-
message: `${modelId} (traced)`,
|
|
218
|
-
};
|
|
219
|
-
}
|
|
220
|
-
return {
|
|
221
|
-
name: "Current Model",
|
|
222
|
-
status: "warning",
|
|
223
|
-
message: `${fullPath} (not traced)`,
|
|
224
|
-
details: `Switch with: openclaw models set mnemom/${modelId}`,
|
|
225
|
-
};
|
|
226
|
-
}
|
|
227
|
-
function showProviderSummary() {
|
|
228
|
-
const providerDetection = detectProviders();
|
|
229
|
-
if (!providerDetection.installed)
|
|
230
|
-
return;
|
|
231
|
-
const configuredProviders = getSmoltbotConfiguredProviders();
|
|
232
|
-
if (configuredProviders.length === 0)
|
|
233
|
-
return;
|
|
234
|
-
console.log(fmt.section("Configured Providers"));
|
|
235
|
-
console.log();
|
|
236
|
-
for (const provider of configuredProviders) {
|
|
237
|
-
const label = PROVIDER_LABELS[provider];
|
|
238
|
-
const aip = AIP_SUPPORT[provider];
|
|
239
|
-
const configKey = PROVIDER_CONFIG_KEYS[provider];
|
|
240
|
-
console.log(` ${label}: ${configKey}/* (AIP: ${aip})`);
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
80
|
async function checkGatewayConnectivity(gatewayUrl) {
|
|
244
81
|
try {
|
|
245
82
|
const response = await fetch(`${gatewayUrl}/health`, {
|
package/dist/commands/team.js
CHANGED
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
* grant managed via `mnemom team admin`.
|
|
22
22
|
*/
|
|
23
23
|
import { readFileSync } from "node:fs";
|
|
24
|
-
import { listMyTeams, getTeam, getTeamTemplate, putTeamTemplate, deleteTeamTemplate, previewComposeTeamTemplate, grantTeamAdmin, revokeTeamAdmin, listTeamAdmins, } from "../lib/api.js";
|
|
24
|
+
import { listMyTeams, getTeam, getTeamTemplate, putTeamTemplate, deleteTeamTemplate, previewComposeTeamTemplate, grantTeamAdmin, revokeTeamAdmin, listTeamAdmins, getTeamSidebandCoverage, } from "../lib/api.js";
|
|
25
25
|
import { requireAuth } from "../lib/auth.js";
|
|
26
26
|
import { fmt } from "../lib/format.js";
|
|
27
27
|
// ─── mnemom team list ────────────────────────────────────────────────────
|
|
@@ -52,10 +52,7 @@ export async function teamListCommand(opts) {
|
|
|
52
52
|
const idW = 38;
|
|
53
53
|
const orgW = 22;
|
|
54
54
|
const memberW = 8;
|
|
55
|
-
const header = "Name".padEnd(nameW) +
|
|
56
|
-
"Team ID".padEnd(idW) +
|
|
57
|
-
"Org".padEnd(orgW) +
|
|
58
|
-
"Members".padEnd(memberW);
|
|
55
|
+
const header = "Name".padEnd(nameW) + "Team ID".padEnd(idW) + "Org".padEnd(orgW) + "Members".padEnd(memberW);
|
|
59
56
|
console.log(` ${header}`);
|
|
60
57
|
console.log(` ${"─".repeat(nameW + idW + orgW + memberW)}`);
|
|
61
58
|
for (const team of teams) {
|
|
@@ -241,7 +238,8 @@ export async function teamPreviewComposeCommand(teamId, opts) {
|
|
|
241
238
|
// Read from stdin if no file specified.
|
|
242
239
|
body = await readAllStdin();
|
|
243
240
|
if (!body.trim()) {
|
|
244
|
-
console.log(fmt.error("No template body supplied. Pass --from <file> or pipe YAML/JSON via stdin.") +
|
|
241
|
+
console.log(fmt.error("No template body supplied. Pass --from <file> or pipe YAML/JSON via stdin.") +
|
|
242
|
+
"\n");
|
|
245
243
|
process.exit(1);
|
|
246
244
|
return;
|
|
247
245
|
}
|
|
@@ -403,9 +401,7 @@ export async function teamAdminListCommand(teamId, opts) {
|
|
|
403
401
|
const userW = 36;
|
|
404
402
|
const grantedByW = 36;
|
|
405
403
|
const grantedAtW = 22;
|
|
406
|
-
const header = "User ID".padEnd(userW) +
|
|
407
|
-
"Granted by".padEnd(grantedByW) +
|
|
408
|
-
"Granted at".padEnd(grantedAtW);
|
|
404
|
+
const header = "User ID".padEnd(userW) + "Granted by".padEnd(grantedByW) + "Granted at".padEnd(grantedAtW);
|
|
409
405
|
console.log(` ${header}`);
|
|
410
406
|
console.log(` ${"─".repeat(userW + grantedByW + grantedAtW)}`);
|
|
411
407
|
for (const grant of result.admins) {
|
|
@@ -436,7 +432,6 @@ async function readAllStdin() {
|
|
|
436
432
|
// last 30 days + last-swept heartbeat. Surfaces the data SOC 2 / EU AI Act
|
|
437
433
|
// control mappings need to answer "show evidence the detector ran during
|
|
438
434
|
// the audit window" — answers it from a single endpoint.
|
|
439
|
-
import { getTeamSidebandCoverage, } from "../lib/api.js";
|
|
440
435
|
import chalk from "chalk";
|
|
441
436
|
function freshnessColor(lastSweptAt) {
|
|
442
437
|
if (!lastSweptAt)
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
*/
|
|
15
15
|
import { fmt } from "../lib/format.js";
|
|
16
16
|
import { requireAuth } from "../lib/auth.js";
|
|
17
|
-
import { getSafeHouseHarnessState
|
|
17
|
+
import { getSafeHouseHarnessState } from "../lib/api.js";
|
|
18
18
|
/**
|
|
19
19
|
* Parse `--max-age=...` strings: 30m, 2h, 24h, 1d, etc.
|
|
20
20
|
* Returns milliseconds. Defaults to 24h.
|
|
@@ -112,7 +112,8 @@ export async function validateSafeHouseCommand(opts) {
|
|
|
112
112
|
state = await getSafeHouseHarnessState();
|
|
113
113
|
}
|
|
114
114
|
catch (err) {
|
|
115
|
-
console.log(fmt.error(`Failed to fetch harness state: ${err instanceof Error ? err.message : err}`) +
|
|
115
|
+
console.log(fmt.error(`Failed to fetch harness state: ${err instanceof Error ? err.message : err}`) +
|
|
116
|
+
"\n");
|
|
116
117
|
process.exit(1);
|
|
117
118
|
return;
|
|
118
119
|
}
|
|
@@ -123,8 +124,10 @@ export async function validateSafeHouseCommand(opts) {
|
|
|
123
124
|
const fullVerdict = evaluateLane("full", state.full, maxAgeMs);
|
|
124
125
|
const fastVerdict = evaluateLane("fast", state.fast, maxAgeMs);
|
|
125
126
|
const wantedLane = opts.lane ?? "both";
|
|
126
|
-
const verdicts = wantedLane === "full"
|
|
127
|
-
|
|
127
|
+
const verdicts = wantedLane === "full"
|
|
128
|
+
? [fullVerdict]
|
|
129
|
+
: wantedLane === "fast"
|
|
130
|
+
? [fastVerdict]
|
|
128
131
|
: [fullVerdict, fastVerdict];
|
|
129
132
|
console.log(fmt.header(`Safe House harness — ${against}`));
|
|
130
133
|
console.log("");
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `mnemom webhooks ...` commands — Track 2 W3.5.
|
|
3
|
+
*
|
|
4
|
+
* mnemom webhooks list <org_id> — list endpoints
|
|
5
|
+
* mnemom webhooks get <org_id> <endpoint_id> — show one
|
|
6
|
+
* mnemom webhooks create <org_id> --url <u> [--events <list>]
|
|
7
|
+
* [--description <d>]
|
|
8
|
+
* mnemom webhooks update <org_id> <endpoint_id> [--url <u>]
|
|
9
|
+
* [--events <list>]
|
|
10
|
+
* [--active <bool>]
|
|
11
|
+
* mnemom webhooks delete <org_id> <endpoint_id> — remove endpoint
|
|
12
|
+
* mnemom webhooks rotate-secret <org_id> <endpoint_id> — mint new secret
|
|
13
|
+
* mnemom webhooks list-deliveries <org_id> [--endpoint <id>] [--limit <n>]
|
|
14
|
+
* mnemom webhooks redeliver <org_id> <delivery_id> — retry one delivery
|
|
15
|
+
* mnemom webhooks replay <org_id> <event_id> [--endpoint <id>...]
|
|
16
|
+
* mnemom webhooks trigger <org_id> <endpoint_id> — fire a test event
|
|
17
|
+
*
|
|
18
|
+
* Every mutation auto-generates a fresh `Idempotency-Key`. Re-running the
|
|
19
|
+
* same command does NOT replay the cache (you'd need to keep the key
|
|
20
|
+
* around for that — the curl/programmatic clients do); for the typical
|
|
21
|
+
* CLI ergonomic path, each invocation is its own logical mutation.
|
|
22
|
+
*/
|
|
23
|
+
export declare function webhooksListCommand(orgId: string, opts?: {
|
|
24
|
+
json?: boolean;
|
|
25
|
+
}): Promise<void>;
|
|
26
|
+
export declare function webhooksGetCommand(orgId: string, endpointId: string, opts?: {
|
|
27
|
+
json?: boolean;
|
|
28
|
+
}): Promise<void>;
|
|
29
|
+
export declare function webhooksCreateCommand(orgId: string, opts?: {
|
|
30
|
+
url?: string;
|
|
31
|
+
events?: string;
|
|
32
|
+
description?: string;
|
|
33
|
+
json?: boolean;
|
|
34
|
+
}): Promise<void>;
|
|
35
|
+
export declare function webhooksUpdateCommand(orgId: string, endpointId: string, opts?: {
|
|
36
|
+
url?: string;
|
|
37
|
+
events?: string;
|
|
38
|
+
description?: string;
|
|
39
|
+
active?: string;
|
|
40
|
+
json?: boolean;
|
|
41
|
+
}): Promise<void>;
|
|
42
|
+
export declare function webhooksDeleteCommand(orgId: string, endpointId: string): Promise<void>;
|
|
43
|
+
export declare function webhooksRotateSecretCommand(orgId: string, endpointId: string, opts?: {
|
|
44
|
+
json?: boolean;
|
|
45
|
+
}): Promise<void>;
|
|
46
|
+
export declare function webhooksTriggerCommand(orgId: string, endpointId: string, opts?: {
|
|
47
|
+
json?: boolean;
|
|
48
|
+
}): Promise<void>;
|
|
49
|
+
export declare function webhooksListDeliveriesCommand(orgId: string, opts?: {
|
|
50
|
+
endpoint?: string;
|
|
51
|
+
limit?: string;
|
|
52
|
+
offset?: string;
|
|
53
|
+
json?: boolean;
|
|
54
|
+
}): Promise<void>;
|
|
55
|
+
export declare function webhooksRedeliverCommand(orgId: string, deliveryId: string, opts?: {
|
|
56
|
+
json?: boolean;
|
|
57
|
+
}): Promise<void>;
|
|
58
|
+
export declare function webhooksReplayCommand(orgId: string, eventId: string, opts?: {
|
|
59
|
+
endpoint?: string[];
|
|
60
|
+
json?: boolean;
|
|
61
|
+
}): Promise<void>;
|