@mnemom/mnemom 0.9.1 → 0.11.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.
@@ -0,0 +1,32 @@
1
+ /**
2
+ * `mnemom advisories ...` commands — Piece 6 of T1-3.1 (ADR-040, ADR-045, ADR-047).
3
+ *
4
+ * mnemom advisories list --agent <agent_id> [--source <source>] [--limit <n>] [--since <ISO>] [--json]
5
+ * mnemom advisories list --team <team_id> [--source <source>] [--limit <n>] [--since <ISO>] [--json]
6
+ * mnemom advisories show <advisory_id> [--agent <agent_id> | --team <team_id>] [--json]
7
+ *
8
+ * Surfaces the customer-facing read paths over `pending_advisories` rows
9
+ * with `source LIKE 'sideband.%'`. Detector firings — coherence,
10
+ * fault-line, fleet, drift — land here as the cross-turn carryover the
11
+ * gateway injects on the agent's next request.
12
+ *
13
+ * Either --agent or --team is required. Source filter accepts the closed
14
+ * enum values from ADR-047 §6 (sideband.coherence, sideband.fault_line,
15
+ * sideband.fleet, sideband.drift, manual.admin) — unknown values are
16
+ * accepted and applied verbatim (forward-compat for ADR-047 amendments).
17
+ */
18
+ interface ListOpts {
19
+ agent?: string;
20
+ team?: string;
21
+ source?: string;
22
+ limit?: string;
23
+ since?: string;
24
+ json?: boolean;
25
+ }
26
+ export declare function advisoriesListCommand(opts: ListOpts): Promise<void>;
27
+ export declare function advisoriesShowCommand(advisoryId: string, opts: {
28
+ agent?: string;
29
+ team?: string;
30
+ json?: boolean;
31
+ }): Promise<void>;
32
+ export {};
@@ -0,0 +1,158 @@
1
+ /**
2
+ * `mnemom advisories ...` commands — Piece 6 of T1-3.1 (ADR-040, ADR-045, ADR-047).
3
+ *
4
+ * mnemom advisories list --agent <agent_id> [--source <source>] [--limit <n>] [--since <ISO>] [--json]
5
+ * mnemom advisories list --team <team_id> [--source <source>] [--limit <n>] [--since <ISO>] [--json]
6
+ * mnemom advisories show <advisory_id> [--agent <agent_id> | --team <team_id>] [--json]
7
+ *
8
+ * Surfaces the customer-facing read paths over `pending_advisories` rows
9
+ * with `source LIKE 'sideband.%'`. Detector firings — coherence,
10
+ * fault-line, fleet, drift — land here as the cross-turn carryover the
11
+ * gateway injects on the agent's next request.
12
+ *
13
+ * Either --agent or --team is required. Source filter accepts the closed
14
+ * enum values from ADR-047 §6 (sideband.coherence, sideband.fault_line,
15
+ * sideband.fleet, sideband.drift, manual.admin) — unknown values are
16
+ * accepted and applied verbatim (forward-compat for ADR-047 amendments).
17
+ */
18
+ import chalk from "chalk";
19
+ import { listSidebandAdvisoriesForAgent, listSidebandAdvisoriesForTeam, } from "../lib/api.js";
20
+ import { requireAuth } from "../lib/auth.js";
21
+ import { fmt } from "../lib/format.js";
22
+ function statusBadge(s) {
23
+ switch (s) {
24
+ case "pending":
25
+ return fmt.badge("PENDING", "yellow");
26
+ case "consumed":
27
+ return chalk.dim("consumed");
28
+ case "expired":
29
+ return chalk.dim("expired");
30
+ default:
31
+ return chalk.dim(s.toUpperCase());
32
+ }
33
+ }
34
+ function shortId(id) {
35
+ return id.length <= 16 ? id : id.slice(0, 13) + "...";
36
+ }
37
+ function formatRow(a) {
38
+ return [
39
+ chalk.dim(shortId(a.id).padEnd(16)),
40
+ chalk.cyan(a.source.padEnd(22)),
41
+ statusBadge(a.status).padEnd(20),
42
+ chalk.dim(a.created_at.replace("T", " ").replace(/\.\d+Z$/, "Z")),
43
+ a.concerns_summary,
44
+ ].join(" ");
45
+ }
46
+ // ─── mnemom advisories list ──────────────────────────────────────────────
47
+ export async function advisoriesListCommand(opts) {
48
+ if (!opts.agent && !opts.team) {
49
+ console.error(fmt.error("--agent <id> or --team <id> is required") + "\n");
50
+ process.exit(1);
51
+ }
52
+ if (opts.agent && opts.team) {
53
+ console.error(fmt.error("--agent and --team are mutually exclusive") + "\n");
54
+ process.exit(1);
55
+ }
56
+ await requireAuth();
57
+ const limit = opts.limit ? parseInt(opts.limit, 10) : undefined;
58
+ if (opts.limit && (Number.isNaN(limit) || (limit ?? 0) <= 0)) {
59
+ console.error(fmt.error(`--limit must be a positive integer (got '${opts.limit}')`) + "\n");
60
+ process.exit(1);
61
+ }
62
+ let result;
63
+ let scopeLabel;
64
+ try {
65
+ if (opts.agent) {
66
+ result = await listSidebandAdvisoriesForAgent(opts.agent, {
67
+ limit,
68
+ since: opts.since,
69
+ });
70
+ scopeLabel = `agent ${opts.agent}`;
71
+ }
72
+ else {
73
+ result = await listSidebandAdvisoriesForTeam(opts.team, {
74
+ limit,
75
+ since: opts.since,
76
+ });
77
+ scopeLabel = `team ${opts.team}`;
78
+ }
79
+ }
80
+ catch (err) {
81
+ console.error(fmt.error(err instanceof Error ? err.message : String(err)) + "\n");
82
+ process.exit(1);
83
+ return;
84
+ }
85
+ let advisories = result.advisories;
86
+ if (opts.source) {
87
+ advisories = advisories.filter((a) => a.source === opts.source);
88
+ }
89
+ if (opts.json) {
90
+ console.log(fmt.json({ ...result, advisories }));
91
+ return;
92
+ }
93
+ if (advisories.length === 0) {
94
+ console.log(chalk.dim(`No sideband advisories for ${scopeLabel}.`));
95
+ return;
96
+ }
97
+ console.log(chalk.bold(`Sideband advisories — ${scopeLabel}`));
98
+ console.log(chalk.dim(`(${advisories.length} row(s))`));
99
+ console.log();
100
+ console.log(chalk.dim("ID SOURCE STATUS CREATED SUMMARY"));
101
+ for (const a of advisories) {
102
+ console.log(formatRow(a));
103
+ }
104
+ }
105
+ // ─── mnemom advisories show ──────────────────────────────────────────────
106
+ export async function advisoriesShowCommand(advisoryId, opts) {
107
+ if (!opts.agent && !opts.team) {
108
+ console.error(fmt.error("--agent <id> or --team <id> is required (the listing endpoint scopes results)") +
109
+ "\n");
110
+ process.exit(1);
111
+ }
112
+ await requireAuth();
113
+ let advisories;
114
+ try {
115
+ if (opts.agent) {
116
+ advisories = (await listSidebandAdvisoriesForAgent(opts.agent, { limit: 200 })).advisories;
117
+ }
118
+ else {
119
+ advisories = (await listSidebandAdvisoriesForTeam(opts.team, { limit: 500 })).advisories;
120
+ }
121
+ }
122
+ catch (err) {
123
+ console.error(fmt.error(err instanceof Error ? err.message : String(err)) + "\n");
124
+ process.exit(1);
125
+ return;
126
+ }
127
+ const match = advisories.find((a) => a.id === advisoryId);
128
+ if (!match) {
129
+ console.error(fmt.error(`Advisory '${advisoryId}' not found in scope.`) + "\n");
130
+ process.exit(1);
131
+ return;
132
+ }
133
+ if (opts.json) {
134
+ console.log(fmt.json(match));
135
+ return;
136
+ }
137
+ console.log(chalk.bold(match.id));
138
+ console.log();
139
+ console.log(fmt.label("source ", match.source));
140
+ console.log(fmt.label("status ", match.status));
141
+ console.log(fmt.label("agent_id ", match.agent_id));
142
+ console.log(fmt.label("created_at ", match.created_at));
143
+ console.log(fmt.label("expires_at ", match.expires_at));
144
+ if (match.consumed_at) {
145
+ console.log(fmt.label("consumed_at ", match.consumed_at));
146
+ }
147
+ console.log();
148
+ console.log(chalk.bold("Concerns summary"));
149
+ console.log(` ${match.concerns_summary}`);
150
+ console.log();
151
+ console.log(chalk.bold("Nudge content (injected to agent)"));
152
+ console.log(` ${match.nudge_content}`);
153
+ if (match.source_ref) {
154
+ console.log();
155
+ console.log(chalk.bold("source_ref"));
156
+ console.log(fmt.json(match.source_ref));
157
+ }
158
+ }
@@ -0,0 +1,37 @@
1
+ /**
2
+ * `mnemom api-key ...` commands — capability-based API key management
3
+ * (ADR-049). Mirrors the dashboard scope picker and the API mint-time
4
+ * vocabulary; one CLI surface for the same key lifecycle operations.
5
+ *
6
+ * mnemom api-key list — list active personal keys
7
+ * mnemom api-key create --name <name> [--scopes a,b,c]
8
+ * — mint a new key
9
+ * mnemom api-key rotate <key_id> — atomic mint-new + revoke-old
10
+ * mnemom api-key revoke <key_id> — soft-delete
11
+ *
12
+ * Auth: any of the standard CLI auth methods — JWT (`mnemom login`),
13
+ * MNEMOM_API_KEY env, or a stored API key. Note the chicken-and-egg:
14
+ * to mint your first key you'll need a JWT (the website's create-key
15
+ * dialog is the alternative). After that, an `api:write` key can mint
16
+ * additional keys for itself.
17
+ */
18
+ import { type ApiKeyScope } from "../lib/api.js";
19
+ /**
20
+ * Exported for tests. Parses a comma-separated `--scopes` flag value,
21
+ * accepts legacy `api` for backward compat, and returns a typed array.
22
+ * Throws on unknown scope names so users get a clear error instead of
23
+ * silently dropping intent.
24
+ */
25
+ export declare function parseScopes(input: string | undefined): ApiKeyScope[];
26
+ export declare function apiKeyListCommand(opts: {
27
+ json?: boolean;
28
+ }): Promise<void>;
29
+ export declare function apiKeyCreateCommand(opts: {
30
+ name?: string;
31
+ scopes?: string;
32
+ json?: boolean;
33
+ }): Promise<void>;
34
+ export declare function apiKeyRotateCommand(keyId: string | undefined, opts: {
35
+ json?: boolean;
36
+ }): Promise<void>;
37
+ export declare function apiKeyRevokeCommand(keyId: string | undefined): Promise<void>;
@@ -0,0 +1,181 @@
1
+ /**
2
+ * `mnemom api-key ...` commands — capability-based API key management
3
+ * (ADR-049). Mirrors the dashboard scope picker and the API mint-time
4
+ * vocabulary; one CLI surface for the same key lifecycle operations.
5
+ *
6
+ * mnemom api-key list — list active personal keys
7
+ * mnemom api-key create --name <name> [--scopes a,b,c]
8
+ * — mint a new key
9
+ * mnemom api-key rotate <key_id> — atomic mint-new + revoke-old
10
+ * mnemom api-key revoke <key_id> — soft-delete
11
+ *
12
+ * Auth: any of the standard CLI auth methods — JWT (`mnemom login`),
13
+ * MNEMOM_API_KEY env, or a stored API key. Note the chicken-and-egg:
14
+ * to mint your first key you'll need a JWT (the website's create-key
15
+ * dialog is the alternative). After that, an `api:write` key can mint
16
+ * additional keys for itself.
17
+ */
18
+ import { listApiKeys, createApiKey, rotateApiKey, revokeApiKey, isLegacyScopeSet, API_KEY_SCOPES, DEFAULT_API_KEY_SCOPES, } from "../lib/api.js";
19
+ import { requireAuth } from "../lib/auth.js";
20
+ import { fmt } from "../lib/format.js";
21
+ // ─── Helpers ─────────────────────────────────────────────────────────────
22
+ /**
23
+ * Exported for tests. Parses a comma-separated `--scopes` flag value,
24
+ * accepts legacy `api` for backward compat, and returns a typed array.
25
+ * Throws on unknown scope names so users get a clear error instead of
26
+ * silently dropping intent.
27
+ */
28
+ export function parseScopes(input) {
29
+ if (!input)
30
+ return [...DEFAULT_API_KEY_SCOPES];
31
+ const raw = input
32
+ .split(",")
33
+ .map((s) => s.trim())
34
+ .filter((s) => s.length > 0);
35
+ if (raw.length === 0)
36
+ return [...DEFAULT_API_KEY_SCOPES];
37
+ const valid = new Set(API_KEY_SCOPES);
38
+ // Accept legacy 'api' input here so users who copy-paste from older
39
+ // docs/scripts get a clean canonicalization. The API also aliases.
40
+ valid.add("api");
41
+ const unknown = raw.filter((s) => !valid.has(s));
42
+ if (unknown.length > 0) {
43
+ throw new Error(`Unknown scope(s): ${unknown.join(", ")}. ` +
44
+ `Valid: ${Array.from(API_KEY_SCOPES).join(", ")}`);
45
+ }
46
+ return raw;
47
+ }
48
+ function renderScopeBadge(scope) {
49
+ // Admin scopes get a visual marker so the audit trail in the CLI
50
+ // makes elevated keys obvious at a glance.
51
+ if (scope === "admin:platform" || scope === "admin:org") {
52
+ return fmt.warn(`[${scope}]`);
53
+ }
54
+ return `[${scope}]`;
55
+ }
56
+ function formatRow(key) {
57
+ const created = key.created_at
58
+ ? new Date(key.created_at).toISOString().slice(0, 10)
59
+ : "?";
60
+ const lastUsed = key.last_used_at
61
+ ? new Date(key.last_used_at).toISOString().slice(0, 10)
62
+ : "never";
63
+ const scopes = (key.scopes ?? []).map(renderScopeBadge).join(" ");
64
+ const legacy = isLegacyScopeSet(key.scopes) ? fmt.dim(" (legacy)") : "";
65
+ return ` ${key.key_id} ${key.key_prefix}… ${key.name.padEnd(24)} ${scopes}${legacy}\n created ${created} · last used ${lastUsed}`;
66
+ }
67
+ // ─── mnemom api-key list ─────────────────────────────────────────────────
68
+ export async function apiKeyListCommand(opts) {
69
+ await requireAuth();
70
+ let keys;
71
+ try {
72
+ keys = await listApiKeys();
73
+ }
74
+ catch (err) {
75
+ console.error(fmt.error(`Failed to list api keys: ${err instanceof Error ? err.message : err}`));
76
+ process.exit(1);
77
+ }
78
+ if (opts.json) {
79
+ console.log(JSON.stringify(keys, null, 2));
80
+ return;
81
+ }
82
+ if (keys.length === 0) {
83
+ console.log("\nNo active api keys.\n");
84
+ console.log("Create one with `mnemom api-key create --name <name>`.\n");
85
+ return;
86
+ }
87
+ console.log();
88
+ console.log(fmt.header(`Active api keys (${keys.length})`));
89
+ console.log();
90
+ for (const k of keys) {
91
+ console.log(formatRow(k));
92
+ console.log();
93
+ }
94
+ }
95
+ // ─── mnemom api-key create ───────────────────────────────────────────────
96
+ export async function apiKeyCreateCommand(opts) {
97
+ await requireAuth();
98
+ if (!opts.name || opts.name.trim() === "") {
99
+ console.error(fmt.error("--name is required. Example: --name 'ci-prod'"));
100
+ process.exit(1);
101
+ }
102
+ let scopes;
103
+ try {
104
+ scopes = parseScopes(opts.scopes);
105
+ }
106
+ catch (err) {
107
+ console.error(fmt.error(err instanceof Error ? err.message : String(err)));
108
+ process.exit(1);
109
+ }
110
+ let created;
111
+ try {
112
+ created = await createApiKey(opts.name.trim(), scopes);
113
+ }
114
+ catch (err) {
115
+ console.error(fmt.error(`Create failed: ${err instanceof Error ? err.message : err}`));
116
+ process.exit(1);
117
+ }
118
+ if (opts.json) {
119
+ console.log(JSON.stringify(created, null, 2));
120
+ return;
121
+ }
122
+ console.log();
123
+ console.log(fmt.success(`Created ${created.key_id}`));
124
+ console.log();
125
+ console.log(fmt.label(" Name: ", ` ${created.name}`));
126
+ console.log(fmt.label(" Scopes: ", ` ${(created.scopes ?? []).join(", ")}`));
127
+ console.log(fmt.label(" Created: ", ` ${created.created_at}`));
128
+ console.log();
129
+ console.log(fmt.warn(" Cleartext (shown once — copy now):"));
130
+ console.log(` ${created.key}`);
131
+ console.log();
132
+ console.log(fmt.dim(" This is the only time the cleartext is returned."));
133
+ console.log(fmt.dim(" Mnemom stores only the SHA-256 hash; the key cannot be retrieved later."));
134
+ console.log();
135
+ }
136
+ // ─── mnemom api-key rotate ───────────────────────────────────────────────
137
+ export async function apiKeyRotateCommand(keyId, opts) {
138
+ await requireAuth();
139
+ if (!keyId) {
140
+ console.error(fmt.error("Usage: mnemom api-key rotate <key_id>"));
141
+ process.exit(1);
142
+ }
143
+ let rotated;
144
+ try {
145
+ rotated = await rotateApiKey(keyId);
146
+ }
147
+ catch (err) {
148
+ console.error(fmt.error(`Rotate failed: ${err instanceof Error ? err.message : err}`));
149
+ process.exit(1);
150
+ }
151
+ if (opts.json) {
152
+ console.log(JSON.stringify(rotated, null, 2));
153
+ return;
154
+ }
155
+ console.log();
156
+ console.log(fmt.success(`Rotated. Old key ${keyId} is now revoked.`));
157
+ console.log();
158
+ console.log(fmt.label(" New key id:", ` ${rotated.key_id}`));
159
+ console.log(fmt.label(" Name: ", ` ${rotated.name}`));
160
+ console.log(fmt.label(" Scopes: ", ` ${(rotated.scopes ?? []).join(", ")}`));
161
+ console.log();
162
+ console.log(fmt.warn(" New cleartext (shown once — copy now):"));
163
+ console.log(` ${rotated.key}`);
164
+ console.log();
165
+ }
166
+ // ─── mnemom api-key revoke ───────────────────────────────────────────────
167
+ export async function apiKeyRevokeCommand(keyId) {
168
+ await requireAuth();
169
+ if (!keyId) {
170
+ console.error(fmt.error("Usage: mnemom api-key revoke <key_id>"));
171
+ process.exit(1);
172
+ }
173
+ try {
174
+ await revokeApiKey(keyId);
175
+ }
176
+ catch (err) {
177
+ console.error(fmt.error(`Revoke failed: ${err instanceof Error ? err.message : err}`));
178
+ process.exit(1);
179
+ }
180
+ console.log(fmt.success(`Revoked ${keyId}.`));
181
+ }
@@ -0,0 +1,85 @@
1
+ /**
2
+ * `mnemom governance` — operator CLI for governance signals (ADR-048).
3
+ *
4
+ * Subcommands:
5
+ *
6
+ * signals list | show | ack | resolve | dismiss
7
+ * destinations list | add | remove | test
8
+ * rules list | add | remove
9
+ *
10
+ * The signal surface is operator-actionable: cron-driven sideband
11
+ * detections, future protection / posture observations. Distinct from
12
+ * `mnemom advisories list` (now narrowed to runtime.* / manual.* per
13
+ * ADR-048 §1).
14
+ */
15
+ interface ListOpts {
16
+ org?: string;
17
+ team?: string;
18
+ agent?: string;
19
+ source?: string;
20
+ severity?: string;
21
+ status?: string;
22
+ scope?: string;
23
+ patternType?: string;
24
+ limit?: string;
25
+ since?: string;
26
+ json?: boolean;
27
+ }
28
+ interface AckOpts {
29
+ action?: string;
30
+ json?: boolean;
31
+ }
32
+ interface ResolveOpts {
33
+ status: string;
34
+ action?: string;
35
+ json?: boolean;
36
+ }
37
+ interface DismissOpts {
38
+ reason?: string;
39
+ json?: boolean;
40
+ }
41
+ interface DestAddOpts {
42
+ org: string;
43
+ channel: string;
44
+ config: string;
45
+ name?: string;
46
+ filter?: string;
47
+ json?: boolean;
48
+ }
49
+ interface RuleAddOpts {
50
+ org: string;
51
+ name: string;
52
+ predicate: string;
53
+ destinations: string;
54
+ json?: boolean;
55
+ }
56
+ export declare function governanceSignalsListCommand(opts: ListOpts): Promise<void>;
57
+ export declare function governanceSignalsShowCommand(signalId: string, opts: {
58
+ json?: boolean;
59
+ }): Promise<void>;
60
+ export declare function governanceSignalsAckCommand(signalId: string, opts: AckOpts): Promise<void>;
61
+ export declare function governanceSignalsResolveCommand(signalId: string, opts: ResolveOpts): Promise<void>;
62
+ export declare function governanceSignalsDismissCommand(signalId: string, opts: DismissOpts): Promise<void>;
63
+ export declare function governanceDestinationsListCommand(opts: {
64
+ org: string;
65
+ json?: boolean;
66
+ }): Promise<void>;
67
+ export declare function governanceDestinationsAddCommand(opts: DestAddOpts): Promise<void>;
68
+ export declare function governanceDestinationsRemoveCommand(destinationId: string, opts: {
69
+ org: string;
70
+ json?: boolean;
71
+ }): Promise<void>;
72
+ export declare function governanceDestinationsTestCommand(destinationId: string, opts: {
73
+ org: string;
74
+ json?: boolean;
75
+ }): Promise<void>;
76
+ export declare function governanceRulesListCommand(opts: {
77
+ org: string;
78
+ json?: boolean;
79
+ }): Promise<void>;
80
+ export declare function governanceRulesAddCommand(opts: RuleAddOpts): Promise<void>;
81
+ export declare function governanceRulesRemoveCommand(ruleId: string, opts: {
82
+ org: string;
83
+ json?: boolean;
84
+ }): Promise<void>;
85
+ export {};