@knowttl/mg-axi 0.1.1 → 0.1.3
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 +251 -77
- package/dist/api.js +253 -8
- package/dist/bin/mg-axi-hook.js +31 -0
- package/dist/catalogue.js +693 -2
- package/dist/cli.js +155 -17
- package/dist/docs.js +175 -46
- package/dist/doctor.js +2 -2
- package/dist/entra-access-reviews.js +11 -4
- package/dist/entra-apps.js +171 -14
- package/dist/entra-audit-logs.js +101 -16
- package/dist/entra-auth-methods.js +8 -4
- package/dist/entra-certificate-auth.js +12 -8
- package/dist/entra-conditional-access.js +425 -15
- package/dist/entra-contacts.js +19 -12
- package/dist/entra-contracts.js +7 -4
- package/dist/entra-custom-security-attributes.js +19 -12
- package/dist/entra-data-policy-operations.js +7 -4
- package/dist/entra-delegated-admin.js +55 -6
- package/dist/entra-deleted-items.js +6 -4
- package/dist/entra-directory-objects.js +6 -4
- package/dist/entra-directory.js +19 -12
- package/dist/entra-domains.js +13 -8
- package/dist/entra-entitlement-management.js +12 -4
- package/dist/entra-federation-configurations.js +7 -4
- package/dist/entra-grants.js +8 -4
- package/dist/entra-group-lifecycle.js +13 -8
- package/dist/entra-group-pim.js +431 -0
- package/dist/entra-groups.js +13 -8
- package/dist/entra-identity-providers.js +7 -4
- package/dist/entra-lifecycle-workflows.js +152 -13
- package/dist/entra-multi-tenant-organization.js +7 -4
- package/dist/entra-on-premises-synchronization.js +6 -4
- package/dist/entra-organization.js +13 -8
- package/dist/entra-risk-prevention.js +442 -0
- package/dist/entra-risk.js +224 -9
- package/dist/entra-roles.js +204 -12
- package/dist/entra-subscriptions.js +6 -4
- package/dist/entra-terms-of-use.js +13 -8
- package/dist/entra-users.js +7 -4
- package/dist/graph-session.js +30 -5
- package/dist/hook.js +84 -0
- package/dist/list-totals.js +52 -0
- package/dist/setup.js +9 -1
- package/docs/coverage.md +743 -610
- package/inventory/operations.json +2 -2
- package/inventory/schema.json +1 -1
- package/package.json +4 -3
- package/skills/mg-axi/SKILL.md +86 -1
package/dist/docs.js
CHANGED
|
@@ -37,6 +37,8 @@ const READ_AREA_LABELS = [
|
|
|
37
37
|
["entra user ", "users"],
|
|
38
38
|
["entra group ", "groups"],
|
|
39
39
|
["entra directory-role ", "roles"],
|
|
40
|
+
["entra directory-role-template ", "roles"],
|
|
41
|
+
["entra scoped-role-membership ", "roles"],
|
|
40
42
|
["entra role-assignment ", "roles"],
|
|
41
43
|
["entra pim ", "roles"],
|
|
42
44
|
["entra device ", "devices"],
|
|
@@ -70,14 +72,20 @@ const READ_AREA_LABELS = [
|
|
|
70
72
|
["entra tenant-information ", "tenant information"],
|
|
71
73
|
["entra sign-in ", "sign-ins and audit logs"],
|
|
72
74
|
["entra directory-audit ", "sign-ins and audit logs"],
|
|
75
|
+
["entra provisioning ", "sign-ins and audit logs"],
|
|
73
76
|
["entra application ", "applications and consent grants"],
|
|
74
77
|
["entra service-principal ", "applications and consent grants"],
|
|
75
78
|
["entra risky-user ", "risk"],
|
|
76
79
|
["entra risk-detection ", "risk"],
|
|
80
|
+
["entra risky-service-principal ", "risk"],
|
|
81
|
+
["entra service-principal-risk-detection ", "risk"],
|
|
77
82
|
["entra conditional-access ", "Conditional Access"],
|
|
78
83
|
["entra identity-provider ", "identity providers"],
|
|
79
84
|
["entra federation-configuration ", "federation configurations"],
|
|
80
85
|
["entra data-policy-operation ", "data policy operations"],
|
|
86
|
+
["entra fraud-protection-provider ", "risk prevention"],
|
|
87
|
+
["entra web-application-firewall-provider ", "risk prevention"],
|
|
88
|
+
["entra web-application-firewall-verification ", "risk prevention"],
|
|
81
89
|
["entra access-review ", "access reviews"],
|
|
82
90
|
["entra entitlement catalog ", "entitlement catalogs and access packages"],
|
|
83
91
|
["entra entitlement access-package ", "entitlement catalogs and access packages"],
|
|
@@ -200,6 +208,19 @@ export function skillDocument() {
|
|
|
200
208
|
"Configuration defaults to `~/.mg-axi/config.json`; `MG_AXI_CONFIG` selects a separate file.",
|
|
201
209
|
"`mg-axi setup` shows the selected path and writes nothing.",
|
|
202
210
|
"",
|
|
211
|
+
"Agent discovery has two install paths, and either suffices alone.",
|
|
212
|
+
"For ambient context at every agent session start instead of the skill, install the CLI globally and opt into the session hook:",
|
|
213
|
+
"",
|
|
214
|
+
"```sh",
|
|
215
|
+
"npm install --global @knowttl/mg-axi",
|
|
216
|
+
"mg-axi setup hooks",
|
|
217
|
+
"```",
|
|
218
|
+
"",
|
|
219
|
+
"This installs a `SessionStart` hook for Claude Code, Codex and OpenCode that prints a short local-only summary (configured profile names with tenant labels, auth-cache presence, write posture and version).",
|
|
220
|
+
"The hook makes no network, Graph or sign-in call, and prints a short `not configured` record with exit 0 when unconfigured.",
|
|
221
|
+
"Restart the agent session after running the installer.",
|
|
222
|
+
"No other ordinary command installs hooks, plugins or configuration.",
|
|
223
|
+
"",
|
|
203
224
|
"## Selecting a profile",
|
|
204
225
|
"",
|
|
205
226
|
"Read leaves accept `--profile <name>`.",
|
|
@@ -224,14 +245,14 @@ export function skillDocument() {
|
|
|
224
245
|
// remains unimplemented. Family membership comes from the inventory
|
|
225
246
|
// owningSlice and shipped status comes from the catalogue, so a merged
|
|
226
247
|
// write slice flips its own row without touching this template. Blocked,
|
|
227
|
-
// deprecated and
|
|
248
|
+
// deprecated, unavailable and deferred-out-of-v1 variants never count as pending.
|
|
228
249
|
function initialWriteStatus(rows) {
|
|
229
250
|
const families = new Map();
|
|
230
251
|
for (const row of rows) {
|
|
231
252
|
const operation = /^v1\.0:(.+)$/.exec(row.id)?.[1];
|
|
232
253
|
if (operation === undefined || row.owningSlice === null || !/^WRITE-0[1-9]$/.test(row.owningSlice))
|
|
233
254
|
continue;
|
|
234
|
-
if (row.disposition === "intentionally-blocked" || row.disposition === "deprecated" || row.disposition === "unavailable")
|
|
255
|
+
if (row.disposition === "intentionally-blocked" || row.disposition === "deprecated" || row.disposition === "unavailable" || row.disposition === "deferred")
|
|
235
256
|
continue;
|
|
236
257
|
families.set(row.owningSlice, [...(families.get(row.owningSlice) ?? []), operation]);
|
|
237
258
|
}
|
|
@@ -246,7 +267,79 @@ function initialWriteStatus(rows) {
|
|
|
246
267
|
}
|
|
247
268
|
return { shipped, pending };
|
|
248
269
|
}
|
|
249
|
-
const DISPOSITIONS = ["named-command", "reviewed-raw-read", "scheduled", "intentionally-blocked", "deprecated", "unavailable", "excluded"];
|
|
270
|
+
const DISPOSITIONS = ["named-command", "reviewed-raw-read", "scheduled", "deferred", "intentionally-blocked", "deprecated", "unavailable", "excluded"];
|
|
271
|
+
// FULL-01 audit record (2026-10-07, latest origin/main at PR #78): the cutoff
|
|
272
|
+
// is met when no v1.0 row is left scheduled, so the counts below are read
|
|
273
|
+
// from the same generated inventory the Counts section reports. Beta stays
|
|
274
|
+
// scheduled by definition and was never agreed for v1.
|
|
275
|
+
function auditRecord(rows) {
|
|
276
|
+
const v1 = (disposition) => rows.filter(row => row.id.startsWith("v1.0:") && row.disposition === disposition).length;
|
|
277
|
+
const betaScheduled = rows.filter(row => row.id.startsWith("beta:") && row.disposition === "scheduled").length;
|
|
278
|
+
const named = v1("named-command");
|
|
279
|
+
const raw = v1("reviewed-raw-read");
|
|
280
|
+
const scheduled = v1("scheduled");
|
|
281
|
+
if (scheduled !== 0)
|
|
282
|
+
throw new Error(`FULL-01 audit blocked: ${scheduled} v1.0 rows remain scheduled`);
|
|
283
|
+
return [
|
|
284
|
+
"## FULL-01 audit record",
|
|
285
|
+
"",
|
|
286
|
+
"FULL-01 audit (2026-10-07): every agreed v1.0 read has a named command or an",
|
|
287
|
+
"explicit reviewed blocked/unavailable/deprecated/deferred disposition, and no",
|
|
288
|
+
"v1.0 row is left scheduled. The deferred tail is declared out of v1.",
|
|
289
|
+
`- v1.0 named-command: ${named}, reviewed-raw-read: ${raw}, scheduled: ${scheduled}`,
|
|
290
|
+
`- v1.0 deferred (out of v1): ${v1("deferred")}, intentionally-blocked: ${v1("intentionally-blocked")}, deprecated: ${v1("deprecated")}, unavailable: ${v1("unavailable")}`,
|
|
291
|
+
`- beta scheduled (never agreed for v1): ${betaScheduled}`,
|
|
292
|
+
`PR #78 (historical point-in-time) proposed 275 named / 19 raw (294 backed); the generated inventory carries ${named} named / ${raw} raw (${named + raw} backed).`,
|
|
293
|
+
"",
|
|
294
|
+
];
|
|
295
|
+
}
|
|
296
|
+
// COMPLETE-01 audit record (2026-10-07, latest origin/main at this change):
|
|
297
|
+
// the full agreed Entra capability audit including later writes. Five writes
|
|
298
|
+
// ship as named, gated commands through the WRITE-00 mutation coordinator,
|
|
299
|
+
// WRITE-N carries explicit reviewed deferred/blocked/deprecated dispositions
|
|
300
|
+
// rather than scheduled, and no agreed v1.0 operation, read or write, is
|
|
301
|
+
// left merely scheduled. Beta stays scheduled by definition and was never
|
|
302
|
+
// agreed for v1. The declined EXT-02c access-review history reads stay out.
|
|
303
|
+
function completeAuditRecord(rows) {
|
|
304
|
+
const v1 = (disposition) => rows.filter(row => row.id.startsWith("v1.0:") && row.disposition === disposition).length;
|
|
305
|
+
const betaScheduled = rows.filter(row => row.id.startsWith("beta:") && row.disposition === "scheduled").length;
|
|
306
|
+
const named = v1("named-command");
|
|
307
|
+
const raw = v1("reviewed-raw-read");
|
|
308
|
+
const scheduled = v1("scheduled");
|
|
309
|
+
const writeRows = rows.filter(row => row.id.startsWith("v1.0:") && row.disposition === "named-command" && !row.id.includes(":GET:"));
|
|
310
|
+
const writeN = (version, disposition) => rows.filter(row => row.owningSlice === "WRITE-N" && row.id.startsWith(`${version}:`) && row.disposition === disposition).length;
|
|
311
|
+
const writeNScheduled = rows.filter(row => row.owningSlice === "WRITE-N" && row.disposition === "scheduled").length;
|
|
312
|
+
const declinedOut = rows.filter(row => row.owningSlice === "EXT-02c"
|
|
313
|
+
&& (row.disposition === "named-command" || row.disposition === "reviewed-raw-read"));
|
|
314
|
+
if (scheduled !== 0)
|
|
315
|
+
throw new Error(`COMPLETE-01 audit blocked: ${scheduled} v1.0 rows remain scheduled`);
|
|
316
|
+
if (writeNScheduled !== 0)
|
|
317
|
+
throw new Error(`COMPLETE-01 audit blocked: ${writeNScheduled} WRITE-N rows remain scheduled`);
|
|
318
|
+
const writeStatus = initialWriteStatus(rows);
|
|
319
|
+
const expectedWrites = ["WRITE-01", "WRITE-02", "WRITE-03", "WRITE-04", "WRITE-05"];
|
|
320
|
+
const missing = expectedWrites.filter(slice => !writeStatus.shipped.some(entry => entry.startsWith(`${slice} `)));
|
|
321
|
+
if (writeStatus.pending.length > 0 || missing.length > 0) {
|
|
322
|
+
throw new Error(`COMPLETE-01 audit blocked: pending writes: ${[...missing, ...writeStatus.pending].join(", ")}`);
|
|
323
|
+
}
|
|
324
|
+
if (declinedOut.length > 0)
|
|
325
|
+
throw new Error(`COMPLETE-01 audit blocked: declined EXT-02c rows ship: ${declinedOut.map(row => row.id).join(", ")}`);
|
|
326
|
+
return [
|
|
327
|
+
"## COMPLETE-01 audit record",
|
|
328
|
+
"",
|
|
329
|
+
"COMPLETE-01 audit (2026-10-07): the full agreed Entra capability audit including later writes.",
|
|
330
|
+
"Five writes ship as named, gated commands through the WRITE-00 mutation coordinator; WRITE-N carries explicit",
|
|
331
|
+
"reviewed deferred/blocked/deprecated dispositions rather than scheduled; no agreed v1.0 operation, read or",
|
|
332
|
+
"write, is left merely scheduled. Beta stays scheduled by definition and was never agreed for v1.",
|
|
333
|
+
"The declined EXT-02c access-review history reads (ReadWrite least privilege, SAS URLs) stay out.",
|
|
334
|
+
`- v1.0 named-command: ${named} (${named - writeRows.length} reads plus ${writeRows.length} writes below), reviewed-raw-read: ${raw}, scheduled: ${scheduled}`,
|
|
335
|
+
`- v1.0 deferred (out of v1): ${v1("deferred")}, intentionally-blocked: ${v1("intentionally-blocked")}, deprecated: ${v1("deprecated")}, unavailable: ${v1("unavailable")}`,
|
|
336
|
+
`- beta scheduled (never agreed for v1): ${betaScheduled}`,
|
|
337
|
+
`- shipped writes: ${writeStatus.shipped.join(", ")}.`,
|
|
338
|
+
`- WRITE-N (remaining agreed mutations): deferred ${writeN("v1.0", "deferred")}, intentionally-blocked ${writeN("v1.0", "intentionally-blocked")} v1.0 plus ${writeN("beta", "intentionally-blocked")} beta, deprecated ${writeN("v1.0", "deprecated")} v1.0 plus ${writeN("beta", "deprecated")} beta, scheduled ${writeNScheduled}.`,
|
|
339
|
+
"- outstanding upstream limitations (already documented in the repo): P1 for Conditional Access and P2 for risk-based CA, PIM P2 or ID Governance, riskyUsers P2 and Workload Identities Premium for workload risk with hidden riskDetail/riskLevel without it (docs/graph-coverage.md); conservative P1/P2 Graph prerequisite for sign-in/audit logs, registration report excludes disabled users, no tenant-wide method enumeration (docs/graph-coverage.md); v1.0 group-members route omits service principals (README.md member-read warning); 14 unavailable rows with no documented v1.0 GET contract, invitation Create-only Methods table, org-scoped certificateBasedAuthConfiguration routes only, contacts sync-behavior reads without operation permission docs (inventory reasons); EXT-02c history reads need AccessReview.ReadWrite.All with no read scope and return SAS download URLs (docs/coverage.md); write-side limits beside each command, revoke lag/external-user scope/unknown outcome, CA update lockout gates with no concurrency promise, dismissal is not remediation (README.md, docs/execution.md).",
|
|
340
|
+
"",
|
|
341
|
+
];
|
|
342
|
+
}
|
|
250
343
|
// The capability report is generated from the catalogue: every executable
|
|
251
344
|
// read leaf owns one row with its inventory disposition and owning slice,
|
|
252
345
|
// and the counts separate implemented leaves from inventory dispositions.
|
|
@@ -273,6 +366,7 @@ export function capabilityDocument() {
|
|
|
273
366
|
const deferredProviderRows = rows.filter(row => row.owningSlice === "EXT-03" && row.reason.startsWith("Deferred to a later EXT-03 "));
|
|
274
367
|
const deferredFederationRows = rows.filter(row => row.owningSlice === "EXT-01" && row.reason.startsWith("Deferred by firstmate federation-configuration scope "));
|
|
275
368
|
const deferredDataPolicyRows = rows.filter(row => row.owningSlice === "EXT-03" && row.reason.startsWith("Deferred by firstmate data-policy-operations scope "));
|
|
369
|
+
const deferredRiskPreventionRows = rows.filter(row => row.owningSlice === "EXT-03" && row.reason.startsWith("Deferred by firstmate risk-prevention scope "));
|
|
276
370
|
const unavailableInvitationRows = rows.filter(row => row.owningSlice === "EXT-03" && row.reason.startsWith("Marked unavailable by firstmate EXT-03b decision: "));
|
|
277
371
|
const deferredOrganizationRows = rows.filter(row => row.owningSlice === "EXT-01" && row.reason.startsWith("Deferred by firstmate organization scope "));
|
|
278
372
|
const unavailableCertAuthRows = rows.filter(row => row.owningSlice === "EXT-01" && row.reason.startsWith("Marked unavailable by firstmate mg-ext-01d decision: "));
|
|
@@ -285,10 +379,12 @@ export function capabilityDocument() {
|
|
|
285
379
|
const deferredContractRows = rows.filter(row => row.owningSlice === "EXT-04" && row.reason.startsWith("Deferred by firstmate contracts scope "));
|
|
286
380
|
const deferredDelegatedAdminRows = rows.filter(row => row.owningSlice === "EXT-04" && row.reason.startsWith("Deferred by firstmate delegated-admin scope "));
|
|
287
381
|
const deferredMtoRows = rows.filter(row => row.owningSlice === "EXT-04" && row.reason.startsWith("Deferred by firstmate multi-tenant-organization scope "));
|
|
382
|
+
const deferredReportsRows = rows.filter(row => row.owningSlice === "EXT-04" && row.reason.startsWith("Deferred by firstmate reports scope "));
|
|
288
383
|
const deferredGroupLifecycleRows = rows.filter(row => row.owningSlice === "EXT-01" && row.reason.startsWith("Deferred by firstmate group-lifecycle scope "));
|
|
289
384
|
const deferredCustomSecurityRows = rows.filter(row => row.owningSlice === "EXT-01" && row.reason.startsWith("Deferred by firstmate custom-security-attributes scope "));
|
|
290
385
|
const deferredAccessReviewRows = rows.filter(row => row.owningSlice === "EXT-02c");
|
|
291
386
|
const deferredEntitlementRows = rows.filter(row => row.owningSlice === "EXT-02" && row.reason.startsWith("Deferred by firstmate entitlement-management scope "));
|
|
387
|
+
const deferredAppLookupRows = rows.filter(row => row.owningSlice === "READ-07" && row.reason.startsWith("Deferred by firstmate application-lookups scope "));
|
|
292
388
|
const readCount = readLeaves.length + 2;
|
|
293
389
|
const localCount = LEAVES.length - readCount - writeLeaves.length;
|
|
294
390
|
return [
|
|
@@ -298,9 +394,11 @@ export function capabilityDocument() {
|
|
|
298
394
|
"",
|
|
299
395
|
"This is the supported Entra read surface, not full Entra coverage.",
|
|
300
396
|
"Only the commands below have an implemented, tested leaf; every other",
|
|
301
|
-
"operation
|
|
302
|
-
"
|
|
303
|
-
"
|
|
397
|
+
"v1.0 operation is deferred out of v1, and every other beta operation remains",
|
|
398
|
+
"scheduled (never agreed for v1), blocked, deprecated, unavailable or excluded.",
|
|
399
|
+
"Inventory dispositions are generated records from INV-01 joined to the catalogue;",
|
|
400
|
+
"the command list below is the implemented truth and never counts deferred or",
|
|
401
|
+
"scheduled work as complete.",
|
|
304
402
|
"",
|
|
305
403
|
"## Counts",
|
|
306
404
|
"",
|
|
@@ -309,6 +407,8 @@ export function capabilityDocument() {
|
|
|
309
407
|
`- local leaves: ${localCount} (home, profile, login and setup views)`,
|
|
310
408
|
...DISPOSITIONS.map(disposition => `- inventory ${disposition}: ${count(disposition)}`),
|
|
311
409
|
"",
|
|
410
|
+
...auditRecord(rows),
|
|
411
|
+
...completeAuditRecord(rows),
|
|
312
412
|
"## Commands",
|
|
313
413
|
"",
|
|
314
414
|
"| Command | Operation | Inventory (v1.0) | Owning slice |",
|
|
@@ -320,49 +420,61 @@ export function capabilityDocument() {
|
|
|
320
420
|
"## EXT-01 domain scope decisions",
|
|
321
421
|
"",
|
|
322
422
|
"Firstmate decision R1: approve narrowing this change to the eight v1.0 domain reads above.",
|
|
323
|
-
"The operations below
|
|
423
|
+
"The v1.0 operations below carry the deferred disposition (out of v1) and the beta operations remain scheduled (never agreed for v1); no new commands or raw access are approved.",
|
|
324
424
|
"Federation stays out because it can carry signing-certificate material.",
|
|
325
425
|
"Firstmate decision R2: keep the write-family status section as the coverage fix added to scope (resolved-kept).",
|
|
326
426
|
"Both firstmate decisions must also be stated in the PR body by the delivery phase.",
|
|
327
427
|
"",
|
|
328
428
|
"| Inventory operation | Disposition | Owning slice | Deferral reason |",
|
|
329
429
|
"|---|---|---|---|",
|
|
330
|
-
...deferredDomainRows.map(row => `| \`${row.id}\` | ${row.disposition}
|
|
430
|
+
...deferredDomainRows.map(row => `| \`${row.id}\` | ${row.disposition} | ${row.owningSlice} | ${row.reason} |`),
|
|
331
431
|
"",
|
|
332
432
|
"## EXT-03 identity-provider scope decisions",
|
|
333
433
|
"",
|
|
334
434
|
"This change covers the four v1.0 workforce identity-provider reads above (list, show, count, available-types).",
|
|
335
|
-
"The four beta operations below remain scheduled
|
|
435
|
+
"The four beta operations below remain scheduled (never agreed for v1) for a later EXT-03 subfamily.",
|
|
336
436
|
"Identity-provider reads support v1.0 only; beta needs its own review.",
|
|
337
437
|
"Workforce context only; no external-customer (B2C/External ID tenant) support is claimed.",
|
|
338
438
|
"",
|
|
339
439
|
"| Inventory operation | Disposition | Owning slice | Deferral reason |",
|
|
340
440
|
"|---|---|---|---|",
|
|
341
|
-
...deferredProviderRows.map(row => `| \`${row.id}\` | ${row.disposition}
|
|
441
|
+
...deferredProviderRows.map(row => `| \`${row.id}\` | ${row.disposition} | ${row.owningSlice} | ${row.reason} |`),
|
|
342
442
|
"",
|
|
343
443
|
"## EXT-01 federation-configuration scope decisions",
|
|
344
444
|
"",
|
|
345
445
|
"This change covers the four v1.0 workforce directory federation-configuration reads above (federation-configuration list, show, count and available-types).",
|
|
346
|
-
"The four beta operations below remain scheduled
|
|
446
|
+
"The four beta operations below remain scheduled (never agreed for v1) for a later EXT-01 subfamily; no new commands or raw access are approved.",
|
|
347
447
|
"Federation-configuration reads support v1.0 only; beta needs its own review.",
|
|
348
448
|
"signingCertificate carries the public token-signing key only and is omitted from default selects; no private key material exists on these resources and the domains navigation property needs its own review.",
|
|
349
|
-
"Workforce context only; domain federationConfiguration sub-reads (/domains/{domain-id}/federationConfiguration)
|
|
449
|
+
"Workforce context only; domain federationConfiguration sub-reads (/domains/{domain-id}/federationConfiguration) carry the deferred disposition in v1.0 (out of v1) and remain scheduled in beta (never agreed for v1) in the later domain federation subfamily.",
|
|
350
450
|
"",
|
|
351
451
|
"| Inventory operation | Disposition | Owning slice | Deferral reason |",
|
|
352
452
|
"|---|---|---|---|",
|
|
353
|
-
...deferredFederationRows.map(row => `| \`${row.id}\` | ${row.disposition}
|
|
453
|
+
...deferredFederationRows.map(row => `| \`${row.id}\` | ${row.disposition} | ${row.owningSlice} | ${row.reason} |`),
|
|
354
454
|
"",
|
|
355
455
|
"## EXT-03 data-policy-operations scope decisions",
|
|
356
456
|
"",
|
|
357
457
|
"This change covers the three v1.0 workforce data-policy-operation reads above (data-policy-operation list, show and count).",
|
|
358
|
-
"The three beta operations below remain scheduled
|
|
458
|
+
"The three beta operations below remain scheduled (never agreed for v1) for a later EXT-03 subfamily; no new commands or raw access are approved.",
|
|
359
459
|
"Data-policy-operation reads support v1.0 only; beta needs its own review.",
|
|
360
460
|
"storageLocation always renders as the redaction marker; export submission (POST /users/{id}/exportPersonalData) belongs to no read slice.",
|
|
361
461
|
"Workforce context only; no external-customer support is claimed.",
|
|
362
462
|
"",
|
|
363
463
|
"| Inventory operation | Disposition | Owning slice | Deferral reason |",
|
|
364
464
|
"|---|---|---|---|",
|
|
365
|
-
...deferredDataPolicyRows.map(row => `| \`${row.id}\` | ${row.disposition}
|
|
465
|
+
...deferredDataPolicyRows.map(row => `| \`${row.id}\` | ${row.disposition} | ${row.owningSlice} | ${row.reason} |`),
|
|
466
|
+
"",
|
|
467
|
+
"## EXT-03 risk-prevention scope decisions",
|
|
468
|
+
"",
|
|
469
|
+
"This change covers the ten v1.0 workforce risk-prevention reads above (fraud-protection-provider list, show and count; web-application-firewall-provider list, show and count; web-application-firewall-verification list, show, count and provider show).",
|
|
470
|
+
"The eleven beta operations below remain scheduled (never agreed for v1) for a later EXT-03 subfamily; no new commands or raw access are approved.",
|
|
471
|
+
"Risk-prevention reads support v1.0 only; beta needs its own review.",
|
|
472
|
+
"API keys, client secrets and other key material (Arkose privateKey/publicKey, HUMAN serverToken, Akamai clientSecret/clientToken/accessToken, Cloudflare apiToken) are never selectable and never projected; application profiles are refused before credentials because Graph documents no supported application permission.",
|
|
473
|
+
"Workforce context only; no external-customer support is claimed.",
|
|
474
|
+
"",
|
|
475
|
+
"| Inventory operation | Disposition | Owning slice | Deferral reason |",
|
|
476
|
+
"|---|---|---|---|",
|
|
477
|
+
...deferredRiskPreventionRows.map(row => `| \`${row.id}\` | ${row.disposition} | ${row.owningSlice} | ${row.reason} |`),
|
|
366
478
|
"",
|
|
367
479
|
"## EXT-03 invitations scope decisions",
|
|
368
480
|
"",
|
|
@@ -378,12 +490,12 @@ export function capabilityDocument() {
|
|
|
378
490
|
"## EXT-01 organization scope decisions",
|
|
379
491
|
"",
|
|
380
492
|
"The organization scope covers the five organization/branding/localization reads above; the remaining subfamilies need the separate contracts described below.",
|
|
381
|
-
"The operations below
|
|
493
|
+
"The v1.0 operations below carry the deferred disposition (out of v1) and the beta operations remain scheduled (never agreed for v1); no new commands or raw access are approved.",
|
|
382
494
|
"Stream image and CSS bytes stay out because they need a separate binary-output contract; beta-only settings, partner and theme contracts need separate review.",
|
|
383
495
|
"",
|
|
384
496
|
"| Inventory operation | Disposition | Owning slice | Deferral reason |",
|
|
385
497
|
"|---|---|---|---|",
|
|
386
|
-
...deferredOrganizationRows.map(row => `| \`${row.id}\` | ${row.disposition}
|
|
498
|
+
...deferredOrganizationRows.map(row => `| \`${row.id}\` | ${row.disposition} | ${row.owningSlice} | ${row.reason} |`),
|
|
387
499
|
"",
|
|
388
500
|
"## EXT-01 certificate-auth scope decisions",
|
|
389
501
|
"",
|
|
@@ -405,61 +517,61 @@ export function capabilityDocument() {
|
|
|
405
517
|
"## EXT-01 group lifecycle scope decisions",
|
|
406
518
|
"",
|
|
407
519
|
"This change covers the six v1.0 group lifecycle policy and group setting template reads above (list, show, count per family).",
|
|
408
|
-
"The operations below
|
|
520
|
+
"The v1.0 operations below carry the deferred disposition (out of v1) and the beta operations remain scheduled (never agreed for v1); no new commands or raw access are approved.",
|
|
409
521
|
"Beta policies and templates need their own review; delta-token sync needs its own change-tracking contract beyond the approved list/show/count reads; the POST lookup actions need their own request and projection review.",
|
|
410
522
|
"",
|
|
411
523
|
"| Inventory operation | Disposition | Owning slice | Deferral reason |",
|
|
412
524
|
"|---|---|---|---|",
|
|
413
|
-
...deferredGroupLifecycleRows.map(row => `| \`${row.id}\` | ${row.disposition}
|
|
525
|
+
...deferredGroupLifecycleRows.map(row => `| \`${row.id}\` | ${row.disposition} | ${row.owningSlice} | ${row.reason} |`),
|
|
414
526
|
"",
|
|
415
527
|
"## EXT-01 custom-security-attributes scope decisions",
|
|
416
528
|
"",
|
|
417
529
|
"This change covers the nine v1.0 attribute-set, custom-security-attribute-definition and allowed-value reads above (list, show, count per family).",
|
|
418
|
-
"The nine beta operations below remain scheduled
|
|
530
|
+
"The nine beta operations below remain scheduled (never agreed for v1) for a later EXT-01 subfamily; no new commands or raw access are approved.",
|
|
419
531
|
"Beta attribute sets, definitions and allowed values need their own review; definition $expand (inline allowedValues) is not reviewed here and stays on the dedicated allowed-value list/show reads.",
|
|
420
532
|
"",
|
|
421
533
|
"| Inventory operation | Disposition | Owning slice | Deferral reason |",
|
|
422
534
|
"|---|---|---|---|",
|
|
423
|
-
...deferredCustomSecurityRows.map(row => `| \`${row.id}\` | ${row.disposition}
|
|
535
|
+
...deferredCustomSecurityRows.map(row => `| \`${row.id}\` | ${row.disposition} | ${row.owningSlice} | ${row.reason} |`),
|
|
424
536
|
"",
|
|
425
537
|
"## EXT-01 on-premises-synchronization scope decisions",
|
|
426
538
|
"",
|
|
427
539
|
"This change covers the two v1.0 on-premises-synchronization reads above (on-premises-synchronization list and show).",
|
|
428
|
-
"The $count scalar and the three beta operations
|
|
540
|
+
"The $count scalar below carries the deferred disposition (out of v1) and the three beta operations remain scheduled (never agreed for v1); no new commands or raw access are approved.",
|
|
429
541
|
"On-premises-synchronization reads support v1.0 only; beta needs its own review.",
|
|
430
542
|
"Scalar counts need a separate query and response contract from the approved list/show reads.",
|
|
431
543
|
"",
|
|
432
544
|
"| Inventory operation | Disposition | Owning slice | Deferral reason |",
|
|
433
545
|
"|---|---|---|---|",
|
|
434
|
-
...deferredOnPremSyncRows.map(row => `| \`${row.id}\` | ${row.disposition}
|
|
546
|
+
...deferredOnPremSyncRows.map(row => `| \`${row.id}\` | ${row.disposition} | ${row.owningSlice} | ${row.reason} |`),
|
|
435
547
|
"",
|
|
436
548
|
"## EXT-01 terms-of-use scope decisions",
|
|
437
549
|
"",
|
|
438
550
|
"This change covers the six v1.0 agreement and agreement-acceptance reads above (agreement list/show, one agreement's acceptance list/show, and tenant-wide acceptance list/show).",
|
|
439
|
-
"The
|
|
551
|
+
"The v1.0 operations below carry the deferred disposition (out of v1) and the beta operations remain scheduled (never agreed for v1); no new commands or raw access are approved.",
|
|
440
552
|
"Terms-of-use reads support v1.0 only; beta needs its own review.",
|
|
441
553
|
"Scalar counts need a separate query and response contract from the approved list/show reads; agreement file contents and localizations need a separate binary-output and projection contract, and file bytes are never downloaded or printed.",
|
|
442
554
|
"",
|
|
443
555
|
"| Inventory operation | Disposition | Owning slice | Deferral reason |",
|
|
444
556
|
"|---|---|---|---|",
|
|
445
|
-
...deferredTermsOfUseRows.map(row => `| \`${row.id}\` | ${row.disposition}
|
|
557
|
+
...deferredTermsOfUseRows.map(row => `| \`${row.id}\` | ${row.disposition} | ${row.owningSlice} | ${row.reason} |`),
|
|
446
558
|
"",
|
|
447
559
|
"## EXT-01 directory-objects scope decisions",
|
|
448
560
|
"",
|
|
449
561
|
"This change covers the three v1.0 directory-object reads above (directory-object list, show and count).",
|
|
450
|
-
"The
|
|
562
|
+
"The v1.0 operations below carry the deferred disposition (out of v1) and the beta operations remain scheduled (never agreed for v1); no new commands or raw access are approved.",
|
|
451
563
|
"Directory-object reads support v1.0 only; beta needs its own review.",
|
|
452
564
|
"Delta-token sync needs its own change-tracking contract beyond the approved list/show/count reads; the POST lookup and validation actions need their own request and projection review.",
|
|
453
565
|
"Rows are polymorphic: only the base-type properties plus the @odata.type discriminator are projected, so subtype secrets or credentials can never appear.",
|
|
454
566
|
"",
|
|
455
567
|
"| Inventory operation | Disposition | Owning slice | Deferral reason |",
|
|
456
568
|
"|---|---|---|---|",
|
|
457
|
-
...deferredDirectoryObjectRows.map(row => `| \`${row.id}\` | ${row.disposition}
|
|
569
|
+
...deferredDirectoryObjectRows.map(row => `| \`${row.id}\` | ${row.disposition} | ${row.owningSlice} | ${row.reason} |`),
|
|
458
570
|
"",
|
|
459
571
|
"## EXT-01 deleted-items scope decisions",
|
|
460
572
|
"",
|
|
461
573
|
"This change covers the eleven v1.0 deleted directory-item reads above (deleted-user, deleted-group, deleted-application, deleted-service-principal and deleted-administrative-unit list and count, plus deleted-item show).",
|
|
462
|
-
"The rows below
|
|
574
|
+
"The rows below carry the deferred (v1.0, out of v1) or scheduled (beta, never agreed for v1) disposition shown; no new commands or raw access are approved for them.",
|
|
463
575
|
"Deleted-item reads support v1.0 only; beta needs its own review.",
|
|
464
576
|
"Untyped list/count commands do not exist because upstream requires the OData cast as part of the list URI.",
|
|
465
577
|
"Device casts carry no documented v1.0 permission contract: the list/get pages name the supported types and device is not among them.",
|
|
@@ -468,12 +580,12 @@ export function capabilityDocument() {
|
|
|
468
580
|
"",
|
|
469
581
|
"| Inventory operation | Disposition | Owning slice | Deferral reason |",
|
|
470
582
|
"|---|---|---|---|",
|
|
471
|
-
...deferredDeletedItemsRows.map(row => `| \`${row.id}\` | ${row.disposition}
|
|
583
|
+
...deferredDeletedItemsRows.map(row => `| \`${row.id}\` | ${row.disposition} | ${row.owningSlice} | ${row.reason} |`),
|
|
472
584
|
"",
|
|
473
585
|
"## EXT-01 contacts scope decisions",
|
|
474
586
|
"",
|
|
475
587
|
"This change covers the three v1.0 top-level contact reads above (contact list, show and count), the ten v1.0 manager/directReports navigation reads above (show-manager, list/show/count-direct-reports with user/contact casts), and the eighteen v1.0 memberOf/transitiveMemberOf navigation reads above (list/show/count-member-of with transitive and group/administrativeUnit casts).",
|
|
476
|
-
"The
|
|
588
|
+
"The v1.0 operations below carry the deferred disposition (out of v1) and the beta operations remain scheduled (never agreed for v1); no new commands or raw access are approved.",
|
|
477
589
|
"Contact reads support v1.0 only; beta needs its own review.",
|
|
478
590
|
"Delta-token sync needs its own change-tracking contract beyond the approved reads; the POST lookup actions need their own request and projection review.",
|
|
479
591
|
"Contacts are personal data: top-level default rows carry id, displayName, mail and companyName only; navigation rows carry the @odata.type discriminator plus id and displayName only, and identifying fields need an explicit --select.",
|
|
@@ -481,35 +593,43 @@ export function capabilityDocument() {
|
|
|
481
593
|
"",
|
|
482
594
|
"| Inventory operation | Disposition | Owning slice | Deferral reason |",
|
|
483
595
|
"|---|---|---|---|",
|
|
484
|
-
...deferredContactRows.map(row => `| \`${row.id}\` | ${row.disposition}
|
|
596
|
+
...deferredContactRows.map(row => `| \`${row.id}\` | ${row.disposition} | ${row.owningSlice} | ${row.reason} |`),
|
|
485
597
|
...unavailableContactRows.map(row => `| \`${row.id}\` | ${row.disposition} | ${row.owningSlice} | ${row.reason} |`),
|
|
486
598
|
"",
|
|
487
599
|
"## EXT-04 partner contracts scope decisions",
|
|
488
600
|
"",
|
|
489
601
|
"Firstmate scope: approve narrowing this change to the three v1.0 partner-contract reads above (list, show, count).",
|
|
490
|
-
"The operations below
|
|
602
|
+
"The v1.0 operations below carry the deferred disposition (out of v1) and the beta operations remain scheduled (never agreed for v1); no new commands or raw access are approved.",
|
|
491
603
|
"Beta contracts need their own review; delta-token sync needs its own change-tracking contract beyond the approved list/show/count reads.",
|
|
492
604
|
"",
|
|
493
605
|
"| Inventory operation | Disposition | Owning slice | Deferral reason |",
|
|
494
606
|
"|---|---|---|---|",
|
|
495
|
-
...deferredContractRows.map(row => `| \`${row.id}\` | ${row.disposition}
|
|
607
|
+
...deferredContractRows.map(row => `| \`${row.id}\` | ${row.disposition} | ${row.owningSlice} | ${row.reason} |`), "",
|
|
496
608
|
"## EXT-04 delegated-admin scope decisions",
|
|
497
609
|
"",
|
|
498
|
-
"Firstmate scope: approve narrowing this change to the
|
|
499
|
-
"The
|
|
500
|
-
"
|
|
610
|
+
"Firstmate scope: approve narrowing this change to the eighteen v1.0 delegated-admin reads above (customer list/show/count, relationship list/show/count, access-assignment list/show/count, operation list/show/count, request list/show/count and service-management-detail list/show/count).",
|
|
611
|
+
"The operation below carries the deferred disposition (out of v1); no new commands or raw access are approved.",
|
|
612
|
+
"The tenantRelationship container root needs its own projection review; multi-tenant-organization reads shipped in the section below, and tenant-lookup functions ship as tenant-information show under the mg-ext-04e function-argument contract.",
|
|
501
613
|
"",
|
|
502
614
|
"| Inventory operation | Disposition | Owning slice | Deferral reason |",
|
|
503
615
|
"|---|---|---|---|",
|
|
504
|
-
...deferredDelegatedAdminRows.map(row => `| \`${row.id}\` | ${row.disposition}
|
|
616
|
+
...deferredDelegatedAdminRows.map(row => `| \`${row.id}\` | ${row.disposition} | ${row.owningSlice} | ${row.reason} |`), "",
|
|
505
617
|
"## EXT-04 multi-tenant-organization scope decisions",
|
|
506
618
|
"",
|
|
507
619
|
"Firstmate scope: approve narrowing this change to the four v1.0 multi-tenant-organization reads above (organization show, join-request show, tenant list/count).",
|
|
508
|
-
"The single-member read below
|
|
620
|
+
"The single-member read below carries the deferred disposition (out of v1): its documented least privilege is the write scope MultiTenantOrganization.ReadWrite.All in both delegated and application modes, so no named command or raw access is approved for it until its own least-privilege review lands.",
|
|
621
|
+
"",
|
|
622
|
+
"| Inventory operation | Disposition | Owning slice | Deferral reason |",
|
|
623
|
+
"|---|---|---|---|",
|
|
624
|
+
...deferredMtoRows.map(row => `| \`${row.id}\` | ${row.disposition} | ${row.owningSlice} | ${row.reason} |`), "",
|
|
625
|
+
"## EXT-04 reports scope decisions",
|
|
626
|
+
"",
|
|
627
|
+
"Firstmate scope: approve deferring the v1.0 relying-party summary read below; no named command or raw access is approved for it.",
|
|
628
|
+
"The relying-party summary read below carries the deferred disposition (out of v1): its period function argument needs a new session-guard function-argument binding beyond the three allowlisted tenant-lookup/commerce routes, and the request-path guard must stay byte-identical to origin/main apart from READ_SCOPES additions, so no named command or raw access is approved for it until its own guard review lands.",
|
|
509
629
|
"",
|
|
510
630
|
"| Inventory operation | Disposition | Owning slice | Deferral reason |",
|
|
511
631
|
"|---|---|---|---|",
|
|
512
|
-
...
|
|
632
|
+
...deferredReportsRows.map(row => `| \`${row.id}\` | ${row.disposition} | ${row.owningSlice} | ${row.reason} |`), "",
|
|
513
633
|
"## EXT-04 tenant-lookup scope decisions",
|
|
514
634
|
"",
|
|
515
635
|
"Firstmate scope: approve the mg-ext-04e function-argument contract with the three v1.0 reads it unblocks (tenant-information show by domain name or tenant ID, subscription show by commerce subscription ID).",
|
|
@@ -519,7 +639,7 @@ export function capabilityDocument() {
|
|
|
519
639
|
"## EXT-02 access-review scope decisions",
|
|
520
640
|
"",
|
|
521
641
|
"The access-review scope covers the ten reads above (definition, instance, decision, contacted-reviewer and stage list/show reads).",
|
|
522
|
-
"The legacy accessReviews and unified alias reads below
|
|
642
|
+
"The legacy accessReviews and unified alias reads below carry the deferred disposition (out of v1) under the captain-declined EXT-02c follow-up (see docs/build-plan.md); beta-only operations remain scheduled (\"beta needs its own review\").", ,
|
|
523
643
|
"The history reads below are blocked: their documented least privilege is the write scope AccessReview.ReadWrite.All (no read scope), and history instances return SAS download URLs in downloadUri.",
|
|
524
644
|
"No new commands or raw access are approved for any row below.",
|
|
525
645
|
"",
|
|
@@ -530,17 +650,26 @@ export function capabilityDocument() {
|
|
|
530
650
|
"## EXT-02 entitlement-management scope decisions",
|
|
531
651
|
"",
|
|
532
652
|
"The entitlement-management scope covers the eighteen reads above (catalog, access-package, assignment-policy and resource-role-scope list/show/count reads plus assignment and assignment-request list/show/count reads).",
|
|
533
|
-
"Approval and subject reads carry personal data and stay
|
|
653
|
+
"Approval and subject reads carry personal data and stay deferred (out of v1) for later parts with no new commands or raw access approved; so do assignment and request navigation sub-reads (assignment target/accessPackage/assignmentPolicy, request accessPackage/assignment/requestor), the top-level assignment-policy and resource-role-scope lists, catalog navigation reads (accessPackages, resources, resourceScopes, resourceRoles, customWorkflowExtensions), package navigation reads (catalog, incompatible sets), policy navigation reads (questions, custom-extension stages), role/scope link expansion, filterByCurrentUser and additionalAccess functions (parenthesised function segments need their own session-guard binding review), while every beta operation below remains scheduled (never agreed for v1). Raw $expand review is a separate change: the named assignment and request reads flatten one fixed documented expansion in code, and no user-supplied $expand exists on any route.",
|
|
534
654
|
"",
|
|
535
655
|
"| Inventory operation | Disposition | Owning slice | Deferral reason |",
|
|
536
656
|
"|---|---|---|---|",
|
|
537
|
-
...deferredEntitlementRows.map(row => `| \`${row.id}\` | ${row.disposition}
|
|
657
|
+
...deferredEntitlementRows.map(row => `| \`${row.id}\` | ${row.disposition} | ${row.owningSlice} | ${row.reason} |`),
|
|
538
658
|
"",
|
|
539
659
|
"## EXT-02 lifecycle-workflows scope decisions",
|
|
540
660
|
"",
|
|
541
|
-
"This change covers the
|
|
542
|
-
"
|
|
543
|
-
"No new commands or raw access are approved for any lifecycle-workflows row outside the
|
|
661
|
+
"This change covers the thirty-three v1.0 lifecycle-workflows first-, second-, third- and fourth-part reads above (workflow, workflow-template and task-definition list/show/count reads, the tenant settings show, top-level run and user/subject processing-result list/show/count reads, task-report list/show/count reads with the task and taskDefinition singles, plus run-nested user/subject/task processing-result list/show/count reads).",
|
|
662
|
+
"Third-level processing results, taskProcessingResults sub-reads under a user or subject result, reprocessed runs, subject sub-reads, workflow tasks, template tasks, versions, insights, deleted items and custom task extensions stay deferred (out of v1) for follow-up parts with no new commands or raw access approved; so do the six summary functions (the run user/subject summaries plus the four top-level ones), which stay deferred because they need bracketed start/end arguments outside the session-guard function-binding allowlist while the request-path guard cannot change beyond READ_SCOPES additions. Every beta operation below remains scheduled (never agreed for v1) and every mutation (no workflow create/update/delete/activate/run, no settings update, no restore) stays out.",
|
|
663
|
+
"No new commands or raw access are approved for any lifecycle-workflows row outside the thirty-three above.",
|
|
664
|
+
"",
|
|
665
|
+
"## READ-07 application lookups scope decisions",
|
|
666
|
+
"",
|
|
667
|
+
"This change covers seven v1.0 application and service-principal reads above (application and service-principal federated-credential list/show reads with credential metadata only, service-principal app-role-assigned-to list/show reads, and the service-principal app-role-assignment show companion).",
|
|
668
|
+
"The four parenthesised lookups below carry the deferred disposition (out of v1): the (appId='...') alternate-key forms and the federated-credential (name='...') function forms each need a new session-guard binding, and the request-path guard must stay byte-identical to origin/main apart from READ_SCOPES additions, so no named command or raw access is approved for them until their own guard review lands.", ,
|
|
669
|
+
"",
|
|
670
|
+
"| Inventory operation | Disposition | Owning slice | Deferral reason |",
|
|
671
|
+
"|---|---|---|---|",
|
|
672
|
+
...deferredAppLookupRows.map(row => `| \`${row.id}\` | ${row.disposition} | ${row.owningSlice} | ${row.reason} |`),
|
|
544
673
|
"",
|
|
545
674
|
"## Named writes",
|
|
546
675
|
"",
|
|
@@ -553,7 +682,7 @@ export function capabilityDocument() {
|
|
|
553
682
|
`Shipped initial writes: ${writeStatus.shipped.length ? writeStatus.shipped.join(", ") : "none"}.`,
|
|
554
683
|
`Pending initial writes: ${writeStatus.pending.length ? writeStatus.pending.join(", ") : "none"}.`,
|
|
555
684
|
"Extended families (EXT-01 through EXT-04) and the full-Entra audit (FULL-01, COMPLETE-01) own the remaining",
|
|
556
|
-
"
|
|
685
|
+
"deferred rows out of v1; see docs/build-plan.md for their dispatch.",
|
|
557
686
|
"",
|
|
558
687
|
].join("\n");
|
|
559
688
|
}
|
package/dist/doctor.js
CHANGED
|
@@ -44,8 +44,8 @@ export async function runDoctor(args) {
|
|
|
44
44
|
// surfaced row, remainder buffered in a discarded cursor. Doctor keeps
|
|
45
45
|
// no cursor and accepts none; recovery reruns doctor, not the read.
|
|
46
46
|
const result = (await listUsers(args.session, { limit: "1" }, selected.profile, operation, `npx -y @knowttl/mg-axi ${DOCTOR_CHECK} --help`, name));
|
|
47
|
-
if (!result.
|
|
48
|
-
throw new AxiError(`Doctor read incomplete: ${result.
|
|
47
|
+
if (!result.complete && result.reason !== "row limit reached; buffered remainder is preserved in the cursor") {
|
|
48
|
+
throw new AxiError(`Doctor read incomplete: ${result.reason}`, "GRAPH_ERROR", [rerun]);
|
|
49
49
|
}
|
|
50
50
|
profiles.push({ name, mode: selected.profile.mode, check: DOCTOR_CHECK, status: "ok",
|
|
51
51
|
detail: result.users.length ? "1 user row returned" : "0 users matched; the absence of results is the answer" });
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { AxiError } from "axi-sdk-js";
|
|
2
|
+
import { listTotals } from "./list-totals.js";
|
|
2
3
|
// EXT-02 access-reviews subfamily: the read-only access-review mapping.
|
|
3
4
|
// See README.md for the supported commands and usage. Operation construction
|
|
4
5
|
// stays beside its command; the shared session owns URLs, credentials, paging,
|
|
@@ -236,6 +237,7 @@ const ACCESS_REVIEW_DENIAL_HINTS = [
|
|
|
236
237
|
const DEFINITION_LIST = {
|
|
237
238
|
command: "entra access-review definition list",
|
|
238
239
|
key: "definitions",
|
|
240
|
+
noun: "access-review definitions",
|
|
239
241
|
known: DEFINITION_KNOWN,
|
|
240
242
|
knownList: KNOWN_DEFINITION_FIELDS,
|
|
241
243
|
defaultSelect: DEFAULT_DEFINITION_LIST_SELECT,
|
|
@@ -249,6 +251,7 @@ const DEFINITION_LIST = {
|
|
|
249
251
|
const INSTANCE_LIST = {
|
|
250
252
|
command: "entra access-review instance list",
|
|
251
253
|
key: "instances",
|
|
254
|
+
noun: "access-review instances",
|
|
252
255
|
known: INSTANCE_KNOWN,
|
|
253
256
|
knownList: KNOWN_INSTANCE_FIELDS,
|
|
254
257
|
defaultSelect: DEFAULT_INSTANCE_LIST_SELECT,
|
|
@@ -261,6 +264,7 @@ const INSTANCE_LIST = {
|
|
|
261
264
|
const DECISION_LIST = {
|
|
262
265
|
command: "entra access-review decision list",
|
|
263
266
|
key: "decisions",
|
|
267
|
+
noun: "access-review decisions",
|
|
264
268
|
known: DECISION_KNOWN,
|
|
265
269
|
knownList: KNOWN_DECISION_FIELDS,
|
|
266
270
|
defaultSelect: DEFAULT_DECISION_SELECT,
|
|
@@ -273,6 +277,7 @@ const DECISION_LIST = {
|
|
|
273
277
|
const CONTACTED_REVIEWER_LIST = {
|
|
274
278
|
command: "entra access-review contacted-reviewer list",
|
|
275
279
|
key: "contactedReviewers",
|
|
280
|
+
noun: "contacted reviewers",
|
|
276
281
|
known: CONTACTED_REVIEWER_KNOWN,
|
|
277
282
|
knownList: KNOWN_CONTACTED_REVIEWER_FIELDS,
|
|
278
283
|
defaultSelect: DEFAULT_CONTACTED_REVIEWER_LIST_SELECT,
|
|
@@ -286,6 +291,7 @@ const CONTACTED_REVIEWER_LIST = {
|
|
|
286
291
|
const STAGE_LIST = {
|
|
287
292
|
command: "entra access-review stage list",
|
|
288
293
|
key: "stages",
|
|
294
|
+
noun: "access-review stages",
|
|
289
295
|
known: STAGE_KNOWN,
|
|
290
296
|
knownList: KNOWN_STAGE_FIELDS,
|
|
291
297
|
defaultSelect: DEFAULT_STAGE_LIST_SELECT,
|
|
@@ -347,16 +353,17 @@ async function listCollection(shape, session, flags, profile, operation, help, p
|
|
|
347
353
|
if (!result.complete) {
|
|
348
354
|
return {
|
|
349
355
|
[shape.key]: rows,
|
|
350
|
-
|
|
356
|
+
...listTotals(rows.length, result.total, shape.noun, false),
|
|
357
|
+
complete: false,
|
|
358
|
+
reason: result.reason,
|
|
351
359
|
cursor: result.cursor,
|
|
352
360
|
help: [...truncationHints, resumeHint, ...standing],
|
|
353
361
|
};
|
|
354
362
|
}
|
|
355
|
-
const count = { returned: rows.length, complete: true };
|
|
356
363
|
if (!rows.length) {
|
|
357
|
-
return { [shape.key]: rows,
|
|
364
|
+
return { [shape.key]: rows, ...listTotals(rows.length, result.total, shape.noun, true), complete: true, help: [shape.emptyNote, ...standing] };
|
|
358
365
|
}
|
|
359
|
-
return { [shape.key]: rows,
|
|
366
|
+
return { [shape.key]: rows, ...listTotals(rows.length, result.total, shape.noun, true), complete: true, help: [...truncationHints, ...standing] };
|
|
360
367
|
}
|
|
361
368
|
async function showOne(key, command, known, knownList, defaultSelect, session, flags, profile, operation, help, profileName, params, malformed) {
|
|
362
369
|
const { select, fields } = selectedFields(flags, defaultSelect, known, knownList, help);
|