@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.
- package/dist/commands/advisories.d.ts +32 -0
- package/dist/commands/advisories.js +158 -0
- package/dist/commands/api-key.d.ts +37 -0
- package/dist/commands/api-key.js +181 -0
- package/dist/commands/governance.d.ts +85 -0
- package/dist/commands/governance.js +331 -0
- package/dist/commands/org.d.ts +23 -0
- package/dist/commands/org.js +122 -0
- package/dist/commands/posture.d.ts +71 -0
- package/dist/commands/posture.js +440 -0
- package/dist/commands/team.d.ts +56 -0
- package/dist/commands/team.js +507 -0
- package/dist/commands/validate.d.ts +23 -0
- package/dist/commands/validate.js +150 -0
- package/dist/index.js +713 -14
- package/dist/lib/api.d.ts +576 -0
- package/dist/lib/api.js +884 -5
- package/dist/lib/format.d.ts +4 -0
- package/dist/lib/format.js +6 -0
- package/package.json +1 -1
|
@@ -0,0 +1,331 @@
|
|
|
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
|
+
import chalk from "chalk";
|
|
16
|
+
import { acknowledgeGovernanceSignal, createGovernanceDestination, createGovernanceRule, deleteGovernanceDestination, deleteGovernanceRule, dismissGovernanceSignal, getGovernanceSignal, listGovernanceDestinations, listGovernanceRules, listGovernanceSignalsForAgent, listGovernanceSignalsForOrg, listGovernanceSignalsForTeam, resolveGovernanceSignal, testGovernanceDestination, } from "../lib/api.js";
|
|
17
|
+
import { requireAuth } from "../lib/auth.js";
|
|
18
|
+
// ─── Formatting helpers ──────────────────────────────────────────────────
|
|
19
|
+
function shortId(id) {
|
|
20
|
+
return id.length <= 16 ? id : id.slice(0, 13) + "...";
|
|
21
|
+
}
|
|
22
|
+
function severityColor(s) {
|
|
23
|
+
switch (s) {
|
|
24
|
+
case "critical":
|
|
25
|
+
return chalk.bgRed.white;
|
|
26
|
+
case "high":
|
|
27
|
+
return chalk.red;
|
|
28
|
+
case "warn":
|
|
29
|
+
return chalk.yellow;
|
|
30
|
+
default:
|
|
31
|
+
return chalk.cyan;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
function statusBadge(s) {
|
|
35
|
+
switch (s) {
|
|
36
|
+
case "open":
|
|
37
|
+
return chalk.bold.red("OPEN");
|
|
38
|
+
case "acknowledged":
|
|
39
|
+
return chalk.yellow("ACK");
|
|
40
|
+
case "resolved":
|
|
41
|
+
return chalk.green("RESOLVED");
|
|
42
|
+
case "dismissed":
|
|
43
|
+
return chalk.dim("DISMISSED");
|
|
44
|
+
case "expired":
|
|
45
|
+
return chalk.dim("expired");
|
|
46
|
+
default:
|
|
47
|
+
return chalk.dim(s.toUpperCase());
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
function formatSignalRow(s) {
|
|
51
|
+
const sev = severityColor(s.severity)(s.severity.toUpperCase().padEnd(8));
|
|
52
|
+
return [
|
|
53
|
+
chalk.dim(shortId(s.id).padEnd(16)),
|
|
54
|
+
sev,
|
|
55
|
+
chalk.cyan(s.source.padEnd(22)),
|
|
56
|
+
chalk.magenta((s.pattern_type || "—").slice(0, 28).padEnd(28)),
|
|
57
|
+
statusBadge(s.status).padEnd(20),
|
|
58
|
+
chalk.dim(s.detected_at.replace("T", " ").replace(/\.\d+Z$/, "Z")),
|
|
59
|
+
].join(" ");
|
|
60
|
+
}
|
|
61
|
+
// ─── signals list ────────────────────────────────────────────────────────
|
|
62
|
+
export async function governanceSignalsListCommand(opts) {
|
|
63
|
+
await requireAuth();
|
|
64
|
+
const filters = {
|
|
65
|
+
source: opts.source,
|
|
66
|
+
severity: opts.severity,
|
|
67
|
+
status: opts.status,
|
|
68
|
+
scope: opts.scope,
|
|
69
|
+
pattern_type: opts.patternType,
|
|
70
|
+
since: opts.since,
|
|
71
|
+
limit: opts.limit ? parseInt(opts.limit, 10) : 100,
|
|
72
|
+
};
|
|
73
|
+
let result;
|
|
74
|
+
if (opts.org) {
|
|
75
|
+
result = await listGovernanceSignalsForOrg(opts.org, filters);
|
|
76
|
+
}
|
|
77
|
+
else if (opts.team) {
|
|
78
|
+
result = await listGovernanceSignalsForTeam(opts.team, filters);
|
|
79
|
+
}
|
|
80
|
+
else if (opts.agent) {
|
|
81
|
+
result = await listGovernanceSignalsForAgent(opts.agent, filters);
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
throw new Error("One of --org, --team, --agent is required.");
|
|
85
|
+
}
|
|
86
|
+
if (opts.json) {
|
|
87
|
+
console.log(JSON.stringify(result, null, 2));
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
const signals = result.signals;
|
|
91
|
+
if (signals.length === 0) {
|
|
92
|
+
console.log(chalk.dim("(no governance signals match)"));
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
console.log([
|
|
96
|
+
chalk.bold("ID".padEnd(16)),
|
|
97
|
+
chalk.bold("SEVERITY".padEnd(8)),
|
|
98
|
+
chalk.bold("SOURCE".padEnd(22)),
|
|
99
|
+
chalk.bold("PATTERN".padEnd(28)),
|
|
100
|
+
chalk.bold("STATUS".padEnd(20)),
|
|
101
|
+
chalk.bold("DETECTED"),
|
|
102
|
+
].join(" "));
|
|
103
|
+
for (const s of signals)
|
|
104
|
+
console.log(formatSignalRow(s));
|
|
105
|
+
console.log("");
|
|
106
|
+
console.log(chalk.dim(`${signals.length} signal(s).`));
|
|
107
|
+
}
|
|
108
|
+
// ─── signals show ────────────────────────────────────────────────────────
|
|
109
|
+
export async function governanceSignalsShowCommand(signalId, opts) {
|
|
110
|
+
await requireAuth();
|
|
111
|
+
const signal = await getGovernanceSignal(signalId);
|
|
112
|
+
if (opts.json) {
|
|
113
|
+
console.log(JSON.stringify(signal, null, 2));
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
console.log(chalk.bold(`Signal ${signal.id}`));
|
|
117
|
+
console.log(` scope: ${signal.scope} (${signal.scope_id})`);
|
|
118
|
+
console.log(` source: ${chalk.cyan(signal.source)}`);
|
|
119
|
+
console.log(` pattern: ${chalk.magenta(signal.pattern_type)}`);
|
|
120
|
+
console.log(` severity: ${severityColor(signal.severity)(signal.severity)}`);
|
|
121
|
+
console.log(` status: ${statusBadge(signal.status)}`);
|
|
122
|
+
console.log(` detected: ${signal.detected_at} by ${signal.detected_by}`);
|
|
123
|
+
console.log(` org: ${signal.org_id}`);
|
|
124
|
+
if (signal.team_id)
|
|
125
|
+
console.log(` team: ${signal.team_id}`);
|
|
126
|
+
console.log(` agents: ${signal.agent_ids.length === 0 ? "(none)" : signal.agent_ids.join(", ")}`);
|
|
127
|
+
if (signal.acknowledged_at) {
|
|
128
|
+
console.log(` ack: ${signal.acknowledged_at} (${signal.acknowledged_actor_role ?? "?"})${signal.acknowledged_by ? ` by ${signal.acknowledged_by}` : ""}`);
|
|
129
|
+
}
|
|
130
|
+
if (signal.resolved_at) {
|
|
131
|
+
console.log(` resolved: ${signal.resolved_at} (${signal.resolution_status ?? "?"})${signal.action_taken ? ` — ${signal.action_taken}` : ""}`);
|
|
132
|
+
}
|
|
133
|
+
if (Object.keys(signal.detail ?? {}).length > 0) {
|
|
134
|
+
console.log(chalk.dim(" detail:"));
|
|
135
|
+
console.log(JSON.stringify(signal.detail, null, 2)
|
|
136
|
+
.split("\n")
|
|
137
|
+
.map((l) => " " + l)
|
|
138
|
+
.join("\n"));
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
// ─── signals ack/resolve/dismiss ─────────────────────────────────────────
|
|
142
|
+
export async function governanceSignalsAckCommand(signalId, opts) {
|
|
143
|
+
await requireAuth();
|
|
144
|
+
const signal = await acknowledgeGovernanceSignal(signalId, {
|
|
145
|
+
action_taken: opts.action,
|
|
146
|
+
});
|
|
147
|
+
if (opts.json) {
|
|
148
|
+
console.log(JSON.stringify(signal, null, 2));
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
console.log(chalk.green(`✓ Acknowledged ${signal.id} as ${signal.acknowledged_actor_role}.`));
|
|
152
|
+
}
|
|
153
|
+
export async function governanceSignalsResolveCommand(signalId, opts) {
|
|
154
|
+
await requireAuth();
|
|
155
|
+
const valid = [
|
|
156
|
+
"action_taken",
|
|
157
|
+
"wont_fix",
|
|
158
|
+
"duplicate",
|
|
159
|
+
"false_positive",
|
|
160
|
+
"self_resolved",
|
|
161
|
+
];
|
|
162
|
+
if (!valid.includes(opts.status)) {
|
|
163
|
+
throw new Error(`--status must be one of: ${valid.join(", ")}`);
|
|
164
|
+
}
|
|
165
|
+
const signal = await resolveGovernanceSignal(signalId, {
|
|
166
|
+
resolution_status: opts.status,
|
|
167
|
+
action_taken: opts.action,
|
|
168
|
+
});
|
|
169
|
+
if (opts.json) {
|
|
170
|
+
console.log(JSON.stringify(signal, null, 2));
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
console.log(chalk.green(`✓ Resolved ${signal.id} (${signal.resolution_status}).`));
|
|
174
|
+
}
|
|
175
|
+
export async function governanceSignalsDismissCommand(signalId, opts) {
|
|
176
|
+
await requireAuth();
|
|
177
|
+
const signal = await dismissGovernanceSignal(signalId, { reason: opts.reason });
|
|
178
|
+
if (opts.json) {
|
|
179
|
+
console.log(JSON.stringify(signal, null, 2));
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
console.log(chalk.dim(`✓ Dismissed ${signal.id}.`));
|
|
183
|
+
}
|
|
184
|
+
// ─── destinations ────────────────────────────────────────────────────────
|
|
185
|
+
function formatDestinationRow(d) {
|
|
186
|
+
return [
|
|
187
|
+
chalk.dim(shortId(d.id).padEnd(16)),
|
|
188
|
+
chalk.cyan(d.channel.padEnd(10)),
|
|
189
|
+
(d.display_name ?? "(unnamed)").slice(0, 32).padEnd(32),
|
|
190
|
+
d.enabled ? chalk.green("enabled") : chalk.dim("disabled"),
|
|
191
|
+
d.last_test_status === "ok"
|
|
192
|
+
? chalk.green(`tested ✓ ${d.last_tested_at?.slice(0, 19) ?? ""}`)
|
|
193
|
+
: d.last_test_status === "failed"
|
|
194
|
+
? chalk.red(`tested ✗ ${d.last_test_error?.slice(0, 64) ?? ""}`)
|
|
195
|
+
: chalk.dim("untested"),
|
|
196
|
+
].join(" ");
|
|
197
|
+
}
|
|
198
|
+
export async function governanceDestinationsListCommand(opts) {
|
|
199
|
+
await requireAuth();
|
|
200
|
+
const result = await listGovernanceDestinations(opts.org);
|
|
201
|
+
if (opts.json) {
|
|
202
|
+
console.log(JSON.stringify(result, null, 2));
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
if (result.destinations.length === 0) {
|
|
206
|
+
console.log(chalk.dim("(no destinations configured for this org)"));
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
for (const d of result.destinations)
|
|
210
|
+
console.log(formatDestinationRow(d));
|
|
211
|
+
}
|
|
212
|
+
export async function governanceDestinationsAddCommand(opts) {
|
|
213
|
+
await requireAuth();
|
|
214
|
+
const validChannels = [
|
|
215
|
+
"webhook",
|
|
216
|
+
"slack",
|
|
217
|
+
"email",
|
|
218
|
+
"pagerduty",
|
|
219
|
+
];
|
|
220
|
+
if (!validChannels.includes(opts.channel)) {
|
|
221
|
+
throw new Error(`--channel must be one of: ${validChannels.join(", ")}`);
|
|
222
|
+
}
|
|
223
|
+
let config;
|
|
224
|
+
try {
|
|
225
|
+
config = JSON.parse(opts.config);
|
|
226
|
+
}
|
|
227
|
+
catch {
|
|
228
|
+
throw new Error("--config must be valid JSON");
|
|
229
|
+
}
|
|
230
|
+
let filter;
|
|
231
|
+
if (opts.filter) {
|
|
232
|
+
try {
|
|
233
|
+
filter = JSON.parse(opts.filter);
|
|
234
|
+
}
|
|
235
|
+
catch {
|
|
236
|
+
throw new Error("--filter must be valid JSON");
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
const dest = await createGovernanceDestination(opts.org, {
|
|
240
|
+
channel: opts.channel,
|
|
241
|
+
config,
|
|
242
|
+
filter,
|
|
243
|
+
display_name: opts.name,
|
|
244
|
+
});
|
|
245
|
+
if (opts.json) {
|
|
246
|
+
console.log(JSON.stringify(dest, null, 2));
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
console.log(chalk.green(`✓ Created destination ${dest.id} (${dest.channel}).`));
|
|
250
|
+
}
|
|
251
|
+
export async function governanceDestinationsRemoveCommand(destinationId, opts) {
|
|
252
|
+
await requireAuth();
|
|
253
|
+
await deleteGovernanceDestination(opts.org, destinationId);
|
|
254
|
+
if (opts.json) {
|
|
255
|
+
console.log(JSON.stringify({ ok: true, removed: destinationId }, null, 2));
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
console.log(chalk.green(`✓ Removed destination ${destinationId}.`));
|
|
259
|
+
}
|
|
260
|
+
export async function governanceDestinationsTestCommand(destinationId, opts) {
|
|
261
|
+
await requireAuth();
|
|
262
|
+
const result = await testGovernanceDestination(opts.org, destinationId);
|
|
263
|
+
if (opts.json) {
|
|
264
|
+
console.log(JSON.stringify(result, null, 2));
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
if (result.result.ok) {
|
|
268
|
+
console.log(chalk.green(`✓ Test signal delivered via ${result.channel} (${result.result.attempts} attempt).`));
|
|
269
|
+
}
|
|
270
|
+
else {
|
|
271
|
+
console.log(chalk.red(`✗ Test failed via ${result.channel}: ${result.result.last_error ?? "unknown"}`));
|
|
272
|
+
process.exitCode = 1;
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
// ─── rules ───────────────────────────────────────────────────────────────
|
|
276
|
+
function formatRuleRow(r) {
|
|
277
|
+
return [
|
|
278
|
+
chalk.dim(shortId(r.id).padEnd(16)),
|
|
279
|
+
r.name.slice(0, 32).padEnd(32),
|
|
280
|
+
r.enabled ? chalk.green("enabled") : chalk.dim("disabled"),
|
|
281
|
+
chalk.dim(`fired ${r.fire_count}×`),
|
|
282
|
+
r.last_fired_at ? chalk.dim(`last ${r.last_fired_at.slice(0, 19)}`) : "",
|
|
283
|
+
].join(" ");
|
|
284
|
+
}
|
|
285
|
+
export async function governanceRulesListCommand(opts) {
|
|
286
|
+
await requireAuth();
|
|
287
|
+
const result = await listGovernanceRules(opts.org);
|
|
288
|
+
if (opts.json) {
|
|
289
|
+
console.log(JSON.stringify(result, null, 2));
|
|
290
|
+
return;
|
|
291
|
+
}
|
|
292
|
+
if (result.rules.length === 0) {
|
|
293
|
+
console.log(chalk.dim("(no escalation rules configured for this org)"));
|
|
294
|
+
return;
|
|
295
|
+
}
|
|
296
|
+
for (const r of result.rules)
|
|
297
|
+
console.log(formatRuleRow(r));
|
|
298
|
+
}
|
|
299
|
+
export async function governanceRulesAddCommand(opts) {
|
|
300
|
+
await requireAuth();
|
|
301
|
+
let predicate;
|
|
302
|
+
try {
|
|
303
|
+
predicate = JSON.parse(opts.predicate);
|
|
304
|
+
}
|
|
305
|
+
catch {
|
|
306
|
+
throw new Error("--predicate must be valid JSON");
|
|
307
|
+
}
|
|
308
|
+
const destinationIds = opts.destinations.split(",").map((s) => s.trim()).filter(Boolean);
|
|
309
|
+
if (destinationIds.length === 0) {
|
|
310
|
+
throw new Error("--destinations must list at least one destination ID (comma-separated)");
|
|
311
|
+
}
|
|
312
|
+
const rule = await createGovernanceRule(opts.org, {
|
|
313
|
+
name: opts.name,
|
|
314
|
+
predicate,
|
|
315
|
+
destination_ids: destinationIds,
|
|
316
|
+
});
|
|
317
|
+
if (opts.json) {
|
|
318
|
+
console.log(JSON.stringify(rule, null, 2));
|
|
319
|
+
return;
|
|
320
|
+
}
|
|
321
|
+
console.log(chalk.green(`✓ Created rule ${rule.id} ("${rule.name}").`));
|
|
322
|
+
}
|
|
323
|
+
export async function governanceRulesRemoveCommand(ruleId, opts) {
|
|
324
|
+
await requireAuth();
|
|
325
|
+
await deleteGovernanceRule(opts.org, ruleId);
|
|
326
|
+
if (opts.json) {
|
|
327
|
+
console.log(JSON.stringify({ ok: true, removed: ruleId }, null, 2));
|
|
328
|
+
return;
|
|
329
|
+
}
|
|
330
|
+
console.log(chalk.green(`✓ Removed rule ${ruleId}.`));
|
|
331
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `mnemom org list`
|
|
3
|
+
*
|
|
4
|
+
* Lists every org the user is a member of, including their personal-org-of-one
|
|
5
|
+
* (per ADR-044 Option A — GitHub model). Personal orgs are tagged `(personal)`
|
|
6
|
+
* in the Name column. Output is a human-readable table; pass `--json` for
|
|
7
|
+
* machine-readable output.
|
|
8
|
+
*/
|
|
9
|
+
export declare function orgListCommand(opts: {
|
|
10
|
+
json?: boolean;
|
|
11
|
+
}): Promise<void>;
|
|
12
|
+
/**
|
|
13
|
+
* `mnemom org show [<org_id>]` or `mnemom org show --personal`
|
|
14
|
+
*
|
|
15
|
+
* Print details for one organization. With `--personal`, fetches the user's
|
|
16
|
+
* personal org via `/v1/auth/me/personal-org`. Otherwise, the membership
|
|
17
|
+
* matching `<org_id>` is printed (or, if no id is given and the user has
|
|
18
|
+
* exactly one membership, that one).
|
|
19
|
+
*/
|
|
20
|
+
export declare function orgShowCommand(orgIdArg: string | undefined, opts: {
|
|
21
|
+
personal?: boolean;
|
|
22
|
+
json?: boolean;
|
|
23
|
+
}): Promise<void>;
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { listMyOrgs, getMyPersonalOrg } from "../lib/api.js";
|
|
2
|
+
import { requireAuth } from "../lib/auth.js";
|
|
3
|
+
import { fmt } from "../lib/format.js";
|
|
4
|
+
/**
|
|
5
|
+
* `mnemom org list`
|
|
6
|
+
*
|
|
7
|
+
* Lists every org the user is a member of, including their personal-org-of-one
|
|
8
|
+
* (per ADR-044 Option A — GitHub model). Personal orgs are tagged `(personal)`
|
|
9
|
+
* in the Name column. Output is a human-readable table; pass `--json` for
|
|
10
|
+
* machine-readable output.
|
|
11
|
+
*/
|
|
12
|
+
export async function orgListCommand(opts) {
|
|
13
|
+
await requireAuth();
|
|
14
|
+
let orgs;
|
|
15
|
+
try {
|
|
16
|
+
orgs = await listMyOrgs();
|
|
17
|
+
}
|
|
18
|
+
catch (err) {
|
|
19
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
20
|
+
console.log(fmt.error(`Failed to list orgs: ${msg}`) + "\n");
|
|
21
|
+
process.exit(1);
|
|
22
|
+
}
|
|
23
|
+
if (opts.json) {
|
|
24
|
+
console.log(JSON.stringify(orgs, null, 2));
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
console.log(fmt.header("Organizations"));
|
|
28
|
+
console.log();
|
|
29
|
+
if (orgs.length === 0) {
|
|
30
|
+
console.log(" No organizations found.\n");
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
const nameW = 32;
|
|
34
|
+
const idW = 24;
|
|
35
|
+
const roleW = 10;
|
|
36
|
+
const ownerW = 8;
|
|
37
|
+
const header = "Name".padEnd(nameW) +
|
|
38
|
+
"ID".padEnd(idW) +
|
|
39
|
+
"Role".padEnd(roleW) +
|
|
40
|
+
"Owner".padEnd(ownerW);
|
|
41
|
+
console.log(` ${header}`);
|
|
42
|
+
console.log(` ${"─".repeat(nameW + idW + roleW + ownerW)}`);
|
|
43
|
+
for (const org of orgs) {
|
|
44
|
+
const tag = org.is_personal ? " (personal)" : "";
|
|
45
|
+
const name = `${org.name}${tag}`.slice(0, nameW - 2).padEnd(nameW);
|
|
46
|
+
const id = org.org_id.slice(0, idW - 2).padEnd(idW);
|
|
47
|
+
const role = (org.role ?? "-").padEnd(roleW);
|
|
48
|
+
const owner = (org.is_owner ? "yes" : "no").padEnd(ownerW);
|
|
49
|
+
console.log(` ${name}${id}${role}${owner}`);
|
|
50
|
+
}
|
|
51
|
+
console.log(`\n Total: ${orgs.length} organization(s)\n`);
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* `mnemom org show [<org_id>]` or `mnemom org show --personal`
|
|
55
|
+
*
|
|
56
|
+
* Print details for one organization. With `--personal`, fetches the user's
|
|
57
|
+
* personal org via `/v1/auth/me/personal-org`. Otherwise, the membership
|
|
58
|
+
* matching `<org_id>` is printed (or, if no id is given and the user has
|
|
59
|
+
* exactly one membership, that one).
|
|
60
|
+
*/
|
|
61
|
+
export async function orgShowCommand(orgIdArg, opts) {
|
|
62
|
+
await requireAuth();
|
|
63
|
+
let target = null;
|
|
64
|
+
try {
|
|
65
|
+
if (opts.personal) {
|
|
66
|
+
const ref = await getMyPersonalOrg();
|
|
67
|
+
// Re-resolve to the full org row so we can print every field.
|
|
68
|
+
const orgs = await listMyOrgs();
|
|
69
|
+
target = orgs.find((o) => o.org_id === ref.org_id) ?? null;
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
const orgs = await listMyOrgs();
|
|
73
|
+
if (orgIdArg) {
|
|
74
|
+
target = orgs.find((o) => o.org_id === orgIdArg) ?? null;
|
|
75
|
+
if (!target) {
|
|
76
|
+
console.log(fmt.error(`Org '${orgIdArg}' not found in your memberships.`) + "\n");
|
|
77
|
+
process.exit(1);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
else if (orgs.length === 1) {
|
|
81
|
+
target = orgs[0];
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
console.log(fmt.warn(`You have ${orgs.length} memberships. Specify an org_id or pass --personal.`) + "\n");
|
|
85
|
+
process.exit(1);
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
catch (err) {
|
|
91
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
92
|
+
console.log(fmt.error(`Failed to fetch org: ${msg}`) + "\n");
|
|
93
|
+
process.exit(1);
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
if (!target) {
|
|
97
|
+
console.log(fmt.error("Org not found.") + "\n");
|
|
98
|
+
process.exit(1);
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
if (opts.json) {
|
|
102
|
+
console.log(JSON.stringify(target, null, 2));
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
console.log(fmt.header(target.name));
|
|
106
|
+
console.log();
|
|
107
|
+
console.log(` ID: ${target.org_id}`);
|
|
108
|
+
console.log(` Slug: ${target.slug}`);
|
|
109
|
+
console.log(` Personal: ${target.is_personal ? "yes" : "no"}`);
|
|
110
|
+
console.log(` Role: ${target.role ?? "-"}`);
|
|
111
|
+
console.log(` Owner: ${target.is_owner ? "yes (you)" : "no"}`);
|
|
112
|
+
if (target.billing_email) {
|
|
113
|
+
console.log(` Billing: ${target.billing_email}`);
|
|
114
|
+
}
|
|
115
|
+
if (target.company_name) {
|
|
116
|
+
console.log(` Company: ${target.company_name}`);
|
|
117
|
+
}
|
|
118
|
+
if (target.accepted_at) {
|
|
119
|
+
console.log(` Accepted at: ${new Date(target.accepted_at).toLocaleDateString()}`);
|
|
120
|
+
}
|
|
121
|
+
console.log();
|
|
122
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `mnemom posture ...` commands — Piece 3 of T1-3.1 (ADR-045).
|
|
3
|
+
*
|
|
4
|
+
* mnemom posture list [--org <id>] [--include-platform=false] [--json]
|
|
5
|
+
* mnemom posture show <posture_id> [--json]
|
|
6
|
+
* mnemom posture create --org <id> --slug <slug> --name <name>
|
|
7
|
+
* --from <file> [--description <text>]
|
|
8
|
+
* mnemom posture update <posture_id> --from <file> [--summary <text>]
|
|
9
|
+
* mnemom posture clone <posture_id> --org <id> [--slug <slug>] [--name <name>]
|
|
10
|
+
* mnemom posture revisions <posture_id> [--json]
|
|
11
|
+
* mnemom posture diff <posture_id> --from <N> --to <M> [--json]
|
|
12
|
+
* mnemom posture assign <posture_id> --team <team_id> [--pin-revision <N>]
|
|
13
|
+
* mnemom posture unassign <posture_id> --team <team_id>
|
|
14
|
+
* mnemom posture preview-compose <posture_id> --team <team_id> [--json]
|
|
15
|
+
* mnemom posture delete <posture_id>
|
|
16
|
+
*
|
|
17
|
+
* Per ADR-045: postures are team-scoped policy input; cards remain
|
|
18
|
+
* agent-scoped runtime output. The CLI authenticates the user the same
|
|
19
|
+
* way `mnemom team` and `mnemom org` do — login token or MNEMOM_API_KEY.
|
|
20
|
+
*/
|
|
21
|
+
export declare function postureListCommand(opts: {
|
|
22
|
+
org?: string;
|
|
23
|
+
includePlatform?: boolean;
|
|
24
|
+
json?: boolean;
|
|
25
|
+
}): Promise<void>;
|
|
26
|
+
export declare function postureShowCommand(postureId: string | undefined, opts: {
|
|
27
|
+
json?: boolean;
|
|
28
|
+
}): Promise<void>;
|
|
29
|
+
export declare function postureCreateCommand(opts: {
|
|
30
|
+
org?: string;
|
|
31
|
+
slug?: string;
|
|
32
|
+
name?: string;
|
|
33
|
+
from?: string;
|
|
34
|
+
description?: string;
|
|
35
|
+
summary?: string;
|
|
36
|
+
json?: boolean;
|
|
37
|
+
}): Promise<void>;
|
|
38
|
+
export declare function postureUpdateCommand(postureId: string | undefined, opts: {
|
|
39
|
+
from?: string;
|
|
40
|
+
summary?: string;
|
|
41
|
+
name?: string;
|
|
42
|
+
description?: string;
|
|
43
|
+
json?: boolean;
|
|
44
|
+
}): Promise<void>;
|
|
45
|
+
export declare function postureCloneCommand(postureId: string | undefined, opts: {
|
|
46
|
+
org?: string;
|
|
47
|
+
slug?: string;
|
|
48
|
+
name?: string;
|
|
49
|
+
description?: string;
|
|
50
|
+
json?: boolean;
|
|
51
|
+
}): Promise<void>;
|
|
52
|
+
export declare function postureRevisionsCommand(postureId: string | undefined, opts: {
|
|
53
|
+
json?: boolean;
|
|
54
|
+
}): Promise<void>;
|
|
55
|
+
export declare function postureDiffCommand(postureId: string | undefined, opts: {
|
|
56
|
+
from?: string;
|
|
57
|
+
to?: string;
|
|
58
|
+
json?: boolean;
|
|
59
|
+
}): Promise<void>;
|
|
60
|
+
export declare function postureAssignCommand(postureId: string | undefined, opts: {
|
|
61
|
+
team?: string;
|
|
62
|
+
pinRevision?: string;
|
|
63
|
+
}): Promise<void>;
|
|
64
|
+
export declare function postureUnassignCommand(postureId: string | undefined, opts: {
|
|
65
|
+
team?: string;
|
|
66
|
+
}): Promise<void>;
|
|
67
|
+
export declare function posturePreviewComposeCommand(postureId: string | undefined, opts: {
|
|
68
|
+
team?: string;
|
|
69
|
+
json?: boolean;
|
|
70
|
+
}): Promise<void>;
|
|
71
|
+
export declare function postureDeleteCommand(postureId: string | undefined): Promise<void>;
|