@knowttl/mg-axi 0.1.6 → 0.1.7

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/api.js CHANGED
@@ -61,7 +61,7 @@ const AUTH_METHOD_FIELDS = ["id", "createdDateTime", "displayName", "phoneNumber
61
61
  const REGISTRATION_FIELDS = ["id", "userPrincipalName", "userDisplayName", "userType", "isAdmin", "isMfaRegistered", "isMfaCapable", "isPasswordlessCapable", "isSsprRegistered", "isSsprEnabled", "isSsprCapable", "userPreferredMethodForSecondaryAuthentication", "lastUpdatedDateTime"];
62
62
  const SIGNIN_FIELDS = ["id", "createdDateTime", "userId", "userPrincipalName", "userDisplayName", "appDisplayName", "appId", "ipAddress", "location", "status", "conditionalAccessStatus", "riskDetail", "riskLevelAggregated", "riskLevelDuringSignIn", "riskState", "resourceDisplayName", "resourceId", "clientAppUsed"];
63
63
  const AUDIT_FIELDS = ["id", "activityDateTime", "activityDisplayName", "category", "loggedByService", "operationType", "result", "resultReason", "correlationId", "initiatedBy", "targetResources"];
64
- const PROVISIONING_FIELDS = ["id", "activityDateTime", "tenantId", "jobId", "cycleId", "changeId", "action", "durationInMilliseconds", "sourceSystem", "sourceIdentity", "targetSystem", "targetIdentity", "provisioningStatusInfo", "provisioningSteps", "modifiedProperties", "servicePrincipal", "initiatedBy"];
64
+ const PROVISIONING_FIELDS = ["id", "activityDateTime", "tenantId", "jobId", "cycleId", "changeId", "provisioningAction", "durationInMilliseconds", "sourceSystem", "sourceIdentity", "targetSystem", "targetIdentity", "provisioningStatusInfo", "provisioningSteps", "modifiedProperties", "servicePrincipal", "initiatedBy"];
65
65
  const RISKY_USER_FIELDS = ["id", "userPrincipalName", "userDisplayName", "riskDetail", "riskLastUpdatedDateTime", "riskLevel", "riskState"];
66
66
  const RISK_DETECTION_FIELDS = ["id", "detectedDateTime", "activityDateTime", "userId", "userPrincipalName", "userDisplayName", "ipAddress", "location", "riskDetail", "riskLevel", "riskState", "riskEventType", "detectionTimingType", "lastUpdatedDateTime", "source"];
67
67
  const RISKY_SP_FIELDS = ["id", "displayName", "appId", "servicePrincipalType", "riskDetail", "riskLastUpdatedDateTime", "riskLevel", "riskState", "isEnabled", "isProcessing"];
@@ -117,7 +117,7 @@ export const REVIEWED_ROUTES = [
117
117
  access: "D/A Policy.Read.AuthenticationMethod. Delegated callers pass it as --scopes; a supported administrator role (Conditional Access Administrator, Security Administrator or Security Reader) is also required.",
118
118
  note: "Conditional Access needs P1.",
119
119
  sources: ["https://learn.microsoft.com/en-us/graph/api/authenticationstrengthroot-list-policies?view=graph-rest-1.0"] },
120
- { id: "v1.0:GET:/identity/conditionalAccess/authenticationStrength/policies/{authenticationStrengthPolicy-id}", kind: "single", query: SINGLE_QUERY, fields: KNOWN_STRENGTH_FIELDS,
120
+ { id: "v1.0:GET:/identity/conditionalAccess/authenticationStrength/policies/{authenticationStrengthPolicy-id}", kind: "single", query: [], fields: KNOWN_STRENGTH_FIELDS,
121
121
  access: "D/A Policy.Read.AuthenticationMethod. Delegated callers pass it as --scopes; a supported administrator role (Conditional Access Administrator, Security Administrator or Security Reader) is also required.",
122
122
  note: "Conditional Access needs P1.",
123
123
  sources: ["https://learn.microsoft.com/en-us/graph/api/authenticationstrengthpolicy-get?view=graph-rest-1.0"] },
@@ -145,7 +145,7 @@ export const REVIEWED_ROUTES = [
145
145
  access: "D/A Policy.Read.All. Delegated callers pass it as --scopes; a supported administrator role (for example Security Reader) is also required.",
146
146
  note: "Conditional Access needs P1.",
147
147
  sources: ["https://learn.microsoft.com/en-us/graph/api/conditionalaccesstemplate-get?view=graph-rest-1.0"] },
148
- { id: "v1.0:GET:/identity/conditionalAccess/authenticationContextClassReferences", kind: "collection", query: COLLECTION_QUERY, fields: KNOWN_AUTH_CONTEXT_FIELDS,
148
+ { id: "v1.0:GET:/identity/conditionalAccess/authenticationContextClassReferences", kind: "collection", query: ["$filter", "$orderby", "$top"], fields: KNOWN_AUTH_CONTEXT_FIELDS,
149
149
  access: "D/A AuthenticationContext.Read.All. Delegated callers pass it as --scopes; a supported administrator role (Conditional Access Administrator, Global Reader, Global Secure Access Administrator, Security Administrator or Security Reader) is also required.",
150
150
  note: "Conditional Access needs P1.",
151
151
  sources: ["https://learn.microsoft.com/graph/api/conditionalaccessroot-list-authenticationcontextclassreferences?view=graph-rest-1.0"] },
@@ -187,7 +187,7 @@ export const REVIEWED_ROUTES = [
187
187
  { id: "v1.0:GET:/auditLogs/directoryAudits", kind: "collection", query: COLLECTION_QUERY, fields: AUDIT_FIELDS,
188
188
  access: "D/A AuditLog.Read.All. Delegated callers pass it as --scopes.",
189
189
  sources: ["https://learn.microsoft.com/graph/api/directoryaudit-list?view=graph-rest-1.0"] },
190
- { id: "v1.0:GET:/auditLogs/provisioning", kind: "collection", query: COLLECTION_QUERY, fields: PROVISIONING_FIELDS,
190
+ { id: "v1.0:GET:/auditLogs/provisioning", kind: "collection", query: ["$filter", "$orderby", "$top"], fields: PROVISIONING_FIELDS,
191
191
  access: "D/A AuditLog.Read.All and Directory.Read.All. Delegated callers pass both as --scopes; Reports Reader is the least privileged directory role.",
192
192
  note: "Entra ID P1/P2 licence prerequisite; retention is 30 days on premium and 7 days on free.",
193
193
  sources: ["https://learn.microsoft.com/graph/api/provisioningobjectsummary-list?view=graph-rest-1.0"] },
@@ -313,14 +313,6 @@ export const REVIEWED_ROUTES = [
313
313
  access: "D/A RoleManagement.Read.Directory. Delegated callers pass it as --scopes.",
314
314
  note: "Administrative-unit scopes need P1.",
315
315
  sources: ["https://learn.microsoft.com/graph/api/scopedrolemembership-get?view=graph-rest-1.0"] },
316
- { id: "v1.0:GET:/scopedRoleMemberships", kind: "collection", query: COLLECTION_QUERY, fields: KNOWN_SCOPED_MEMBER_FIELDS,
317
- access: "D/A RoleManagement.Read.Directory. Delegated callers pass it as --scopes.",
318
- note: "Administrative-unit-scoped admin assignments; directory-wide assignments live on roleAssignments. Administrative-unit scopes need P1.",
319
- sources: ["https://learn.microsoft.com/graph/api/scopedrolemembership-list?view=graph-rest-1.0"] },
320
- { id: "v1.0:GET:/scopedRoleMemberships/{scopedRoleMembership-id}", kind: "single", query: SINGLE_QUERY, fields: KNOWN_SCOPED_MEMBER_FIELDS,
321
- access: "D/A RoleManagement.Read.Directory. Delegated callers pass it as --scopes.",
322
- note: "Administrative-unit scopes need P1.",
323
- sources: ["https://learn.microsoft.com/graph/api/scopedrolemembership-get?view=graph-rest-1.0"] },
324
316
  { id: "v1.0:GET:/identityGovernance/privilegedAccess/group/assignmentSchedules", kind: "collection", query: COLLECTION_QUERY, fields: KNOWN_ASSIGNMENT_SCHEDULE_FIELDS,
325
317
  access: "D/A PrivilegedAssignmentSchedule.Read.AzureADGroup. Delegated callers pass it as --scopes.",
326
318
  note: "Assignment schedules govern group membership or ownership over time; PIM for Groups needs P2 or ID Governance; lists require $filter on groupId or principalId. AssignmentScheduleRequests stay out: their least privilege is the write scope PrivilegedAssignmentSchedule.ReadWrite.AzureADGroup.",
@@ -415,10 +407,6 @@ export const REVIEWED_ROUTES = [
415
407
  access: "D/A Domain.Read.All. Delegated callers pass it as --scopes; Domain Name Administrator or Global Reader are the least-privileged delegated roles. No P1/P2 prerequisite is stated for DNS record reads.",
416
408
  note: "No operation-level documentation page; access follows the parent service-configuration contract and the domainDnsRecord resource reference.",
417
409
  sources: ["https://learn.microsoft.com/en-us/graph/api/domain-list-serviceconfigurationrecords?view=graph-rest-1.0", "https://learn.microsoft.com/en-us/graph/api/resources/domaindnsrecord?view=graph-rest-1.0"] },
418
- { id: "v1.0:GET:/domainDnsRecords", kind: "collection", query: COLLECTION_QUERY, fields: DNS_RECORD_FIELDS,
419
- access: "D/A Domain.Read.All. Delegated callers pass it as --scopes; Domain Name Administrator or Global Reader are the least-privileged delegated roles. No P1/P2 prerequisite is stated for DNS record reads.",
420
- note: "No operation-level documentation page; access follows the documented domain/DNS-read contract and the domainDnsRecord resource reference.",
421
- sources: ["https://learn.microsoft.com/en-us/graph/api/domain-list?view=graph-rest-1.0", "https://learn.microsoft.com/en-us/graph/api/resources/domaindnsrecord?view=graph-rest-1.0"] },
422
410
  { id: "v1.0:GET:/domainDnsRecords/{domainDnsRecord-id}", kind: "single", query: SINGLE_QUERY, fields: DNS_RECORD_FIELDS,
423
411
  access: "D/A Domain.Read.All. Delegated callers pass it as --scopes; Domain Name Administrator or Global Reader are the least-privileged delegated roles. No P1/P2 prerequisite is stated for DNS record reads.",
424
412
  note: "No operation-level documentation page; access follows the documented domain/DNS-read contract and the domainDnsRecord resource reference.",
@@ -489,10 +477,6 @@ export const REVIEWED_ROUTES = [
489
477
  delegatedOnly: true,
490
478
  note: "No single-acceptance operation page; access follows the parent acceptance-list contract and the agreementAcceptance resource reference. Acceptance records are personal data: default rows carry id, agreementId, state and recordedDateTime only.",
491
479
  sources: ["https://learn.microsoft.com/en-us/graph/api/agreement-list-acceptances?view=graph-rest-1.0", "https://learn.microsoft.com/en-us/graph/api/resources/agreementacceptance?view=graph-rest-1.0"] },
492
- { id: "v1.0:GET:/directoryObjects", kind: "collection", query: ["$select"], fields: KNOWN_DIRECTORY_OBJECT_FIELDS, defaultFields: ["id"], keepODataType: true,
493
- access: "D/A Directory.Read.All. Delegated callers pass it as --scopes; no delegated role prerequisite is stated for directory-object reads. Personal Microsoft accounts are not supported.",
494
- note: "No List operation page exists; access follows the sibling directoryobject-get contract and the directoryObject resource reference. Graph documents no collection filter contract here, so only $select is reviewed. Rows are polymorphic: the @odata.type discriminator rides along automatically and subtype properties need the subtype's named reads, never raw $select of unreviewed fields.",
495
- sources: ["https://learn.microsoft.com/en-us/graph/api/directoryobject-get?view=graph-rest-1.0", "https://learn.microsoft.com/en-us/graph/api/resources/directoryobject?view=graph-rest-1.0"] },
496
480
  { id: "v1.0:GET:/directoryObjects/{directoryObject-id}", kind: "single", query: SINGLE_QUERY, fields: KNOWN_DIRECTORY_OBJECT_FIELDS, defaultFields: ["id", "deletedDateTime"], keepODataType: true,
497
481
  access: "D/A Directory.Read.All. Delegated callers pass it as --scopes; no delegated role prerequisite is stated for this read. Personal Microsoft accounts are not supported.",
498
482
  note: "Single reads return the base-type properties plus the @odata.type discriminator; subtype detail needs the subtype's named reads. Only base properties are projected, so subtype secrets or credentials can never appear.",
@@ -605,7 +589,7 @@ export const REVIEWED_ROUTES = [
605
589
  access: "D/A OrgContact.Read.All and Group.Read.All together least-privileged (Directory.Read.All is the documented higher-privileged alternative). Delegated callers pass both as --scopes; delegated access additionally needs a supported Entra role (Directory Readers reads basic properties; Global Reader, Directory Writers, Intune Administrator or User Administrator also work). Personal Microsoft accounts are not supported.",
606
590
  note: "No operation-level documentation page; OData cast selects the administrativeUnit subtype and access follows the parent transitiveMemberOf contract and the administrativeUnit resource reference.",
607
591
  sources: ["https://learn.microsoft.com/en-us/graph/api/orgcontact-list-transitivememberof?view=graph-rest-1.0", "https://learn.microsoft.com/en-us/graph/api/resources/administrativeunit?view=graph-rest-1.0"] },
608
- { id: "v1.0:GET:/groupLifecyclePolicies", kind: "collection", query: COLLECTION_QUERY, fields: KNOWN_LIFECYCLE_FIELDS,
592
+ { id: "v1.0:GET:/groupLifecyclePolicies", kind: "collection", query: ["$filter", "$orderby", "$top"], fields: KNOWN_LIFECYCLE_FIELDS,
609
593
  access: "D/A Directory.Read.All. Delegated callers pass it as --scopes; no delegated role prerequisite is stated for lifecycle-policy reads. Personal Microsoft accounts are not supported.",
610
594
  note: "Filtering uses plain $filter with no $count or ConsistencyLevel contract; no P1/P2 prerequisite is stated for lifecycle-policy reads.",
611
595
  sources: ["https://learn.microsoft.com/graph/api/grouplifecyclepolicy-list?view=graph-rest-1.0", "https://learn.microsoft.com/graph/api/resources/grouplifecyclepolicy?view=graph-rest-1.0"] },
package/dist/catalogue.js CHANGED
@@ -677,7 +677,7 @@ export const LEAVES = [
677
677
  cursor: roleRead.cursor,
678
678
  scopes: { value: "comma-separated-Graph-scopes", description: "Delegated only: explicit Graph scopes using full https://graph.microsoft.com/ names; defaults to RoleManagement.Read.Directory; delegated callers also need a supported directory role" },
679
679
  }, examples: ["mg-axi entra directory-role member list --role <role-id> --profile soc", "mg-axi entra directory-role member list --role <role-id> --profile soc --filter \"displayName eq 'Adele Vance'\""] },
680
- { path: "entra directory-role scoped-member list", description: "List the scoped members of one activated directory role (id, principalId, roleId, directoryScopeId); administrative-unit scopes need P1", operation: "GET:/directoryRoles/{directoryRole-id}/scopedMembers", flags: {
680
+ { path: "entra directory-role scoped-member list", description: "List the scoped members of one activated directory role (id, roleMemberInfo, roleId, administrativeUnitId); administrative-unit scopes need P1", operation: "GET:/directoryRoles/{directoryRole-id}/scopedMembers", flags: {
681
681
  ...common, role: { value: "role-id", required: true, description: "Activated directory-role object ID whose scoped members are listed" },
682
682
  limit: { value: "positive-integer", default: "100", description: "Cap returned rows; the remainder is buffered into an opaque cursor, never discarded; incompatible with --all" },
683
683
  all: { description: "Follow pages within request, byte and deadline budgets" },
@@ -687,7 +687,7 @@ export const LEAVES = [
687
687
  full: roleRead.full,
688
688
  cursor: roleRead.cursor,
689
689
  scopes: { value: "comma-separated-Graph-scopes", description: "Delegated only: explicit Graph scopes using full https://graph.microsoft.com/ names; defaults to RoleManagement.Read.Directory; delegated callers also need a supported directory role; administrative-unit scopes need P1" },
690
- }, examples: ["mg-axi entra directory-role scoped-member list --role <role-id> --profile soc", "mg-axi entra directory-role scoped-member list --role <role-id> --profile soc --filter \"principalId eq '<principal-id>'\""] },
690
+ }, examples: ["mg-axi entra directory-role scoped-member list --role <role-id> --profile soc", "mg-axi entra directory-role scoped-member list --role <role-id> --profile soc --filter \"administrativeUnitId eq '<unit-id>'\""] },
691
691
  { path: "entra directory-role scoped-member show", description: "Show one scoped member of an activated directory role with richer default properties", operation: "GET:/directoryRoles/{directoryRole-id}/scopedMembers/{scopedRoleMembership-id}", flags: {
692
692
  ...common, role: { value: "role-id", required: true, description: "Activated directory-role object ID owning the scoped member" },
693
693
  id: { value: "scoped-membership-id", required: true, description: "Scoped membership object ID" },
@@ -696,7 +696,7 @@ export const LEAVES = [
696
696
  full: roleRead.full,
697
697
  scopes: { value: "comma-separated-Graph-scopes", description: "Delegated only: explicit Graph scopes using full https://graph.microsoft.com/ names; defaults to RoleManagement.Read.Directory; delegated callers also need a supported directory role; administrative-unit scopes need P1" },
698
698
  }, examples: ["mg-axi entra directory-role scoped-member show --role <role-id> --id <scoped-membership-id> --profile soc", "mg-axi entra directory-role scoped-member show --role <role-id> --id <scoped-membership-id> --profile soc --full"] },
699
- { path: "entra scoped-role-membership list", description: "List administrative-unit-scoped role memberships (id, principalId, roleId, directoryScopeId); directory-wide assignments live on roleAssignments", operation: "GET:/scopedRoleMemberships", flags: {
699
+ { path: "entra scoped-role-membership list", description: "Unsupported by Graph v1.0; use directory-role scoped-member list --role <role-id>", operation: "GET:/scopedRoleMemberships", flags: {
700
700
  ...common,
701
701
  limit: { value: "positive-integer", default: "100", description: "Cap returned rows; the remainder is buffered into an opaque cursor, never discarded; incompatible with --all" },
702
702
  all: { description: "Follow pages within request, byte and deadline budgets" },
@@ -707,7 +707,7 @@ export const LEAVES = [
707
707
  cursor: roleRead.cursor,
708
708
  scopes: { value: "comma-separated-Graph-scopes", description: "Delegated only: explicit Graph scopes using full https://graph.microsoft.com/ names; defaults to RoleManagement.Read.Directory; delegated callers also need a supported directory role; administrative-unit scopes need P1" },
709
709
  }, examples: ["mg-axi entra scoped-role-membership list --profile soc", "mg-axi entra scoped-role-membership list --profile soc --filter \"roleId eq '<role-id>'\""] },
710
- { path: "entra scoped-role-membership show", description: "Show one administrative-unit-scoped role membership with richer default properties", operation: "GET:/scopedRoleMemberships/{scopedRoleMembership-id}", flags: {
710
+ { path: "entra scoped-role-membership show", description: "Unsupported by Graph v1.0; use directory-role scoped-member show --role <role-id> --id <scoped-membership-id>", operation: "GET:/scopedRoleMemberships/{scopedRoleMembership-id}", flags: {
711
711
  ...common, id: { value: "scoped-membership-id", required: true, description: "Scoped membership object ID" },
712
712
  select: roleRead.select,
713
713
  fields: roleRead.fields,
@@ -981,7 +981,7 @@ export const LEAVES = [
981
981
  full: dnsRead.full,
982
982
  scopes: dnsRead.scopes,
983
983
  }, examples: ["mg-axi entra domain service-configuration-record show --domain contoso.com --id <record-id> --profile soc"] },
984
- { path: "entra domain-dns-record list", description: "List domain DNS records from the top-level record set; rows carry @odata.type naming the derived record kind", operation: "GET:/domainDnsRecords", flags: {
984
+ { path: "entra domain-dns-record list", description: "Unsupported by Graph v1.0; use domain verification-dns-record list or domain service-configuration-record list with --domain <domain-id>", operation: "GET:/domainDnsRecords", flags: {
985
985
  ...common,
986
986
  limit: { value: "positive-integer", default: "100", description: "Cap returned rows; the remainder is buffered into an opaque cursor, never discarded; incompatible with --all" },
987
987
  all: { description: "Follow pages within request, byte and deadline budgets" },
@@ -1145,7 +1145,7 @@ export const LEAVES = [
1145
1145
  full: acceptanceRead.full,
1146
1146
  scopes: acceptanceRead.scopes,
1147
1147
  }, examples: ["mg-axi entra agreement-acceptance show --id <acceptance-id> --profile soc"] },
1148
- { path: "entra directory-object list", description: "List directory objects with compact rows (id plus the @odata.type subtype discriminator); Graph documents no List operation page, so no --filter", operation: "GET:/directoryObjects", flags: {
1148
+ { path: "entra directory-object list", description: "Unsupported by Graph v1.0; use directory-object show --id <object-id> or a named user, group, device, application or service-principal list", operation: "GET:/directoryObjects", flags: {
1149
1149
  ...common,
1150
1150
  limit: { value: "positive-integer", default: "100", description: "Cap returned rows; the remainder is buffered into an opaque cursor, never discarded; incompatible with --all" },
1151
1151
  all: { description: "Follow pages within request, byte and deadline budgets" },
@@ -1254,7 +1254,7 @@ export const LEAVES = [
1254
1254
  limit: { value: "positive-integer", default: "100", description: "Cap returned rows; the remainder is buffered into an opaque cursor, never discarded; incompatible with --all" },
1255
1255
  all: { description: "Follow pages within request, byte and deadline budgets" },
1256
1256
  filter: lifecycleRead.filter,
1257
- select: lifecycleRead.select,
1257
+ select: { value: "comma-separated-properties", description: "Select output properties locally; no $select is sent to Graph" },
1258
1258
  fields: lifecycleRead.fields,
1259
1259
  full: lifecycleRead.full,
1260
1260
  cursor: lifecycleRead.cursor,
@@ -1599,7 +1599,7 @@ export const LEAVES = [
1599
1599
  since: { ...logRead.since, description: "Required for a new query (resume with --cursor instead): earliest activityDateTime bounding the server time range" },
1600
1600
  until: logRead.until,
1601
1601
  filter: logRead.filter,
1602
- select: { value: "comma-separated-properties", description: "Request server properties from the supported provisioning-log property set" },
1602
+ select: { value: "comma-separated-properties", description: "Select output properties locally from the provisioning-log set, including provisioningAction; no $select is sent to Graph" },
1603
1603
  fields: logRead.fields,
1604
1604
  full: logRead.full,
1605
1605
  cursor: logRead.cursor,
@@ -1921,7 +1921,7 @@ export const LEAVES = [
1921
1921
  }, examples: ["mg-axi entra conditional-access auth-strength-policy list --profile soc", "mg-axi entra conditional-access auth-strength-policy list --profile soc --limit 10", "mg-axi entra conditional-access auth-strength-policy list --profile soc --filter \"policyType eq 'builtIn'\" --select id,displayName,policyType"] },
1922
1922
  { path: "entra conditional-access auth-strength-policy show", description: "Show one authentication-strength policy with the full reviewed property set", operation: "GET:/identity/conditionalAccess/authenticationStrength/policies/{authenticationStrengthPolicy-id}", flags: {
1923
1923
  ...common, id: { value: "auth-strength-policy-id", required: true, description: "Authentication-strength policy object ID" },
1924
- select: caStrengthRead.select,
1924
+ select: { value: "comma-separated-properties", description: "Select output properties locally; no $select is sent to Graph" },
1925
1925
  fields: caStrengthRead.fields,
1926
1926
  full: caStrengthRead.full,
1927
1927
  scopes: caStrengthRead.scopes,
@@ -1987,7 +1987,7 @@ export const LEAVES = [
1987
1987
  limit: { value: "positive-integer", default: "100", description: "Cap returned rows; the remainder is buffered into an opaque cursor, never discarded; incompatible with --all" },
1988
1988
  all: { description: "Follow pages within request, byte and deadline budgets" },
1989
1989
  filter: caAuthContextRead.filter,
1990
- select: caAuthContextRead.select,
1990
+ select: { value: "comma-separated-properties", description: "Select output properties locally; no $select is sent to Graph" },
1991
1991
  fields: caAuthContextRead.fields,
1992
1992
  full: caAuthContextRead.full,
1993
1993
  cursor: caAuthContextRead.cursor,
package/dist/docs.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { readFileSync } from "node:fs";
2
2
  import { DESCRIPTION, HOME_HELP, LEAVES } from "./catalogue.js";
3
+ const UNSUPPORTED_READ_NOTE = "Graph v1.0 does not support the legacy `entra scoped-role-membership list/show`, `entra domain-dns-record list` or `entra directory-object list` routes; those commands fail locally with supported alternatives and their root routes are not available through `api get`.";
3
4
  // PACK-01: single source for generated release records. The committed
4
5
  // skills/mg-axi/SKILL.md and docs/coverage.md are generated from the templates
5
6
  // below, the executable catalogue and the discovery inventory, so help, skill
@@ -174,6 +175,7 @@ export function skillDocument() {
174
175
  "Its effect is `read` for Graph read leaves and the doctor health check, `write` for the gated named writes, or `local` for home, profile, auth, config path, login-alias and setup views.",
175
176
  "Doctor acquires credentials silently and contacts Graph; only its help view stays offline.",
176
177
  "The list below records current executable leaves; it makes no coverage claim for other Graph operations.",
178
+ UNSUPPORTED_READ_NOTE,
177
179
  "See `docs/coverage.md` for the per-operation disposition records.",
178
180
  "`docs/coverage.md` and this file are generated from the same catalogue and inventory sources; `node tools/generate-docs.mjs --check` fails when they are stale.",
179
181
  "",
@@ -419,9 +421,12 @@ export function capabilityDocument() {
419
421
  "| `mg-axi api get` | reviewed raw reads (see src/api.ts) | reviewed-raw-read catalogue | API-01 |",
420
422
  "| `mg-axi doctor` | bounded `GET:/users` health check | uses the named user-list read | PACK-01 |",
421
423
  "",
424
+ UNSUPPORTED_READ_NOTE,
425
+ "",
422
426
  "## EXT-01 domain scope decisions",
423
427
  "",
424
428
  "Firstmate decision R1: approve narrowing this change to the eight v1.0 domain reads above.",
429
+ "The root domain-dns-record list is now explicitly unsupported; use verification-dns-record or service-configuration-record list with a parent domain.",
425
430
  "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.",
426
431
  "Federation stays out because it can carry signing-certificate material.",
427
432
  "Firstmate decision R2: keep the write-family status section as the coverage fix added to scope (resolved-kept).",
@@ -560,10 +565,10 @@ export function capabilityDocument() {
560
565
  "",
561
566
  "## EXT-01 directory-objects scope decisions",
562
567
  "",
563
- "This change covers the three v1.0 directory-object reads above (directory-object list, show and count).",
568
+ "Directory-object show and count are available. Graph v1.0 rejects directory-object list; the legacy command fails locally and points to show or a typed list command.",
564
569
  "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.",
565
570
  "Directory-object reads support v1.0 only; beta needs its own review.",
566
- "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.",
571
+ "Delta-token sync needs its own change-tracking contract beyond the approved show/count reads; the POST lookup and validation actions need their own request and projection review.",
567
572
  "Rows are polymorphic: only the base-type properties plus the @odata.type discriminator are projected, so subtype secrets or credentials can never appear.",
568
573
  "",
569
574
  "| Inventory operation | Disposition | Owning slice | Deferral reason |",
@@ -72,7 +72,7 @@ export const KNOWN_PROVISIONING_FIELDS = [
72
72
  "jobId",
73
73
  "cycleId",
74
74
  "changeId",
75
- "action",
75
+ "provisioningAction",
76
76
  "durationInMilliseconds",
77
77
  "sourceSystem",
78
78
  "sourceIdentity",
@@ -88,7 +88,7 @@ const PROVISIONING_KNOWN = new Set(KNOWN_PROVISIONING_FIELDS);
88
88
  // Compact list rows: identifier, time, who/what happened, and outcome.
89
89
  const DEFAULT_SIGNIN_LIST_SELECT = ["id", "createdDateTime", "userPrincipalName", "appDisplayName"];
90
90
  const DEFAULT_AUDIT_LIST_SELECT = ["id", "activityDateTime", "activityDisplayName", "result"];
91
- const DEFAULT_PROVISIONING_LIST_SELECT = ["id", "activityDateTime", "action", "provisioningStatusInfo"];
91
+ const DEFAULT_PROVISIONING_LIST_SELECT = ["id", "activityDateTime", "provisioningAction", "provisioningStatusInfo"];
92
92
  // Show rows: the full reviewed server set for one object.
93
93
  const DEFAULT_SIGNIN_SHOW_SELECT = [...KNOWN_SIGNIN_FIELDS];
94
94
  const DEFAULT_AUDIT_SHOW_SELECT = [...KNOWN_AUDIT_FIELDS];
@@ -344,7 +344,8 @@ async function listLogs(shape, session, flags, profile, operation, help, profile
344
344
  const scopes = scopesFor(flags, profile, help, shape.defaultScopes);
345
345
  const full = flags.full === true;
346
346
  const filter = boundedFilter(shape.dateField, flags, help);
347
- const query = { $select: select.join(",") };
347
+ // Provisioning lists reject $select; selection remains a local projection.
348
+ const query = shape.noun === "provisioning" ? {} : { $select: select.join(",") };
348
349
  const explicitFilter = filter !== undefined;
349
350
  if (explicitFilter)
350
351
  query.$filter = filter;
@@ -362,7 +363,7 @@ async function listLogs(shape, session, flags, profile, operation, help, profile
362
363
  // The hint replays the caller's own time flags (or the cursor alone, which
363
364
  // restores the saved query); copying back the composed $filter would wrap
364
365
  // it a second time and conflict with the cursor context.
365
- const effectiveFlags = { ...flags, select: result.query.$select ?? shape.defaultSelect.join(",") };
366
+ const effectiveFlags = { ...flags, select: result.query.$select ?? select.join(",") };
366
367
  const rows = [];
367
368
  let truncated = false;
368
369
  for (const row of result.value) {
@@ -376,7 +376,8 @@ async function listCollection(shape, session, flags, profile, operation, help, p
376
376
  const { select, fields } = selectedFields(flags, savedSelect === undefined ? shape.defaultSelect : fieldList(savedSelect, "select", shape.known, shape.knownList, shape.noun, help), shape.known, shape.knownList, shape.noun, help);
377
377
  const scopes = scopesFor(flags, profile, help, shape.defaultScopes);
378
378
  const full = flags.full === true;
379
- const query = { $select: select.join(",") };
379
+ // Authentication contexts reject $select; selection remains a local projection.
380
+ const query = shape.noun === "auth-context" ? {} : { $select: select.join(",") };
380
381
  if (flags.filter !== undefined)
381
382
  query.$filter = String(flags.filter);
382
383
  const args = { profile, operation, query, scopes };
@@ -397,7 +398,7 @@ async function listCollection(shape, session, flags, profile, operation, help, p
397
398
  args.limit = flags.limit === undefined ? 100 : Number(flags.limit);
398
399
  }
399
400
  const result = await withGuidance(shape.denialHints, () => session.collect(args));
400
- const effectiveFlags = { ...flags, select: result.query.$select ?? shape.defaultSelect.join(",") };
401
+ const effectiveFlags = { ...flags, select: result.query.$select ?? select.join(",") };
401
402
  if (result.query.$filter !== undefined)
402
403
  effectiveFlags.filter = result.query.$filter;
403
404
  const rows = [];
@@ -474,7 +475,7 @@ async function showSingle(shape, session, flags, profile, operation, help, profi
474
475
  profile,
475
476
  operation,
476
477
  params,
477
- query: { $select: select.join(",") },
478
+ query: shape.noun === "auth-strength-policy" ? {} : { $select: select.join(",") },
478
479
  scopes,
479
480
  }));
480
481
  if (raw === null || typeof raw !== "object" || Array.isArray(raw)) {
@@ -1,7 +1,7 @@
1
1
  import { AxiError } from "axi-sdk-js";
2
2
  import { listTotals } from "./list-totals.js";
3
3
  // EXT-01 directory-objects subfamily: the read mapping behind
4
- // `mg-axi entra directory-object list/show/count`. Operation construction
4
+ // `mg-axi entra directory-object show/count`. Operation construction
5
5
  // stays beside its command; the shared session owns URLs, credentials,
6
6
  // paging, retries and error translation, and the SDK owns TOON rendering.
7
7
  // This module only maps flags to session calls and projects rows for compact
@@ -10,10 +10,9 @@ import { listTotals } from "./list-totals.js";
10
10
  // Reviewed against the v1.0 directoryobject-get operation documentation and
11
11
  // the directoryObject resource reference on 2026-10-05. There is no dedicated
12
12
  // List operation page: the resource Methods table documents Get, delta,
13
- // Delete and the lookup/validation functions only, so the collection GET
14
- // follows the sibling get contract and the resource reference (both name
15
- // Directory.Read.All), with $select-only queries because collection filter
16
- // support is unreviewed. Both reads take delegated or application
13
+ // Delete and the lookup/validation functions only. Graph rejects the root
14
+ // collection GET, so the session refuses the legacy list command with
15
+ // supported alternatives. Detail and count reads take delegated or application
17
16
  // Directory.Read.All, the documented least privilege in each mode; personal
18
17
  // Microsoft accounts are not supported. No delegated role and no P1/P2
19
18
  // prerequisite are stated for these reads. The $count scalar carries no
@@ -2,7 +2,7 @@ import { AxiError } from "axi-sdk-js";
2
2
  import { listTotals } from "./list-totals.js";
3
3
  // EXT-01 domains subfamily: the read mapping behind `mg-axi entra domain
4
4
  // list/show`, the per-domain verification and service-configuration DNS
5
- // record reads, and the top-level domain DNS record reads. Operation
5
+ // record reads, and the top-level domain DNS record show. Operation
6
6
  // construction stays beside its command; the shared session owns URLs,
7
7
  // credentials, paging, retries and error translation, and the SDK owns TOON
8
8
  // rendering. This module only maps flags to session calls and projects rows
@@ -17,7 +17,7 @@ import { listTotals } from "./list-totals.js";
17
17
  // domain list/get support those plus Directory Readers and other directory
18
18
  // roles). Personal Microsoft accounts are not supported. No P1/P2
19
19
  // prerequisite is stated for these reads. The single-record GETs and the
20
- // top-level /domainDnsRecords GETs carry no operation-level documentation
20
+ // top-level /domainDnsRecords/{id} GET carries no operation-level documentation
21
21
  // page; they read the same domainDnsRecord resource through the same
22
22
  // permission contract, so the review cites the parent list documentation
23
23
  // alongside the domainDnsRecord resource reference. Domain ids are fully
@@ -28,6 +28,8 @@ import { listTotals } from "./list-totals.js";
28
28
  // no $count or ConsistencyLevel contract. No domain mutation exists in this
29
29
  // slice: verify, forceDelete, promote, federation and shared-email surfaces
30
30
  // belong to later pieces.
31
+ // Graph rejects the root /domainDnsRecords collection. The session refuses
32
+ // its legacy list command and points to the parent-domain DNS collections.
31
33
  // Every domain property this slice may request or display, matching the
32
34
  // reviewed raw surface. Anything else fails before credentials.
33
35
  export const KNOWN_DOMAIN_FIELDS = [
@@ -186,7 +186,8 @@ function collectionCommon(session, flags, known, knownList, defaults, defaultSco
186
186
  return { cursor, select, fields, scopes: scopesFor(flags, defaultScopes, profile, help), full: flags.full === true, filter };
187
187
  }
188
188
  function collectArgs(profile, operation, common, flags, help) {
189
- const query = { $select: common.select.join(",") };
189
+ // Lifecycle-policy lists reject $select; keep the selected output fields local.
190
+ const query = operation.path === "/groupLifecyclePolicies" ? {} : { $select: common.select.join(",") };
190
191
  if (common.filter !== undefined)
191
192
  query.$filter = common.filter;
192
193
  const args = { profile, operation, query, scopes: common.scopes };
@@ -227,7 +228,7 @@ function singleResult(raw, fields, full, kind) {
227
228
  export async function listLifecyclePolicies(session, flags, profile, operation, help, profileName) {
228
229
  const common = collectionCommon(session, flags, KNOWN_LIFECYCLE, KNOWN_LIFECYCLE_FIELDS, DEFAULT_LIFECYCLE_LIST_SELECT, DEFAULT_LIFECYCLE_SCOPES, operation, help, profile, true);
229
230
  const result = await withGuidance(LIFECYCLE_DENIAL_HINTS, () => session.collect(collectArgs(profile, operation, common, flags, help)));
230
- const effectiveFlags = { ...flags, select: result.query.$select ?? DEFAULT_LIFECYCLE_LIST_SELECT.join(",") };
231
+ const effectiveFlags = { ...flags, select: result.query.$select ?? common.select.join(",") };
231
232
  if (result.query.$filter !== undefined)
232
233
  effectiveFlags.filter = result.query.$filter;
233
234
  const policies = [];
@@ -3,8 +3,8 @@ import { listTotals } from "./list-totals.js";
3
3
  // READ-09: the Entra directory-role and PIM read mapping behind
4
4
  // `mg-axi entra directory-role list/show`, `mg-axi entra directory-role-template
5
5
  // list/show`, `mg-axi entra directory-role member list`, `mg-axi entra
6
- // directory-role scoped-member list/show`, `mg-axi entra scoped-role-membership
7
- // list/show`, `mg-axi entra role-assignment list`, `mg-axi entra pim eligible
6
+ // directory-role scoped-member list/show`, `mg-axi entra role-assignment
7
+ // list`, `mg-axi entra pim eligible
8
8
  // list` and `mg-axi entra pim active list`.
9
9
  // Operation construction stays beside its command; the shared session owns
10
10
  // URLs, credentials, paging, retries and error translation, and the SDK owns
@@ -44,6 +44,8 @@ import { listTotals } from "./list-totals.js";
44
44
  // every other PIM mutation belongs to later write slices, never to these
45
45
  // reads. Built-in roles are base inventory; custom role assignments need P1;
46
46
  // PIM reads need P2 or ID Governance.
47
+ // Direct /scopedRoleMemberships queries are unsupported by Graph v1.0;
48
+ // the session refuses the legacy commands with parent-role alternatives.
47
49
  // Eligible and active instances need separate reviewed property sets:
48
50
  // only active instances expose assignmentType.
49
51
  export const KNOWN_ROLE_FIELDS = [
@@ -84,9 +86,8 @@ export const KNOWN_ROLE_MEMBER_FIELDS = [
84
86
  ];
85
87
  export const KNOWN_SCOPED_MEMBER_FIELDS = [
86
88
  "id",
87
- "principalId",
89
+ "roleMemberInfo",
88
90
  "roleId",
89
- "directoryScopeId",
90
91
  "administrativeUnitId",
91
92
  ];
92
93
  const ROLE_KNOWN = new Set(KNOWN_ROLE_FIELDS);
@@ -106,7 +107,7 @@ const DEFAULT_ACTIVE_SELECT = ["id", "principalId", "roleDefinitionId", "assignm
106
107
  const DEFAULT_TEMPLATE_LIST_SELECT = ["id", "displayName", "description"];
107
108
  const DEFAULT_TEMPLATE_SHOW_SELECT = [...KNOWN_TEMPLATE_FIELDS];
108
109
  const DEFAULT_MEMBER_SELECT = ["id", "displayName"];
109
- const DEFAULT_SCOPED_LIST_SELECT = ["id", "principalId", "roleId", "directoryScopeId"];
110
+ const DEFAULT_SCOPED_LIST_SELECT = ["id", "roleMemberInfo", "roleId", "administrativeUnitId"];
110
111
  const DEFAULT_SCOPED_SHOW_SELECT = [...KNOWN_SCOPED_MEMBER_FIELDS];
111
112
  export const DEFAULT_ROLE_SCOPES = ["https://graph.microsoft.com/RoleManagement.Read.Directory"];
112
113
  export const DEFAULT_ELIGIBLE_SCOPES = ["https://graph.microsoft.com/RoleEligibilitySchedule.Read.Directory"];
@@ -228,6 +228,15 @@ function checkOperation(operation) {
228
228
  "Resolve the operation from inventory/operations.json before executing it",
229
229
  ]);
230
230
  }
231
+ const unsupported = {
232
+ "/domainDnsRecords": ["mg-axi entra domain verification-dns-record list --domain <domain-id>", "mg-axi entra domain service-configuration-record list --domain <domain-id>"],
233
+ "/directoryObjects": ["mg-axi entra directory-object show --id <object-id>", "Use the named user, group, device, application or service-principal list command"],
234
+ "/scopedRoleMemberships": ["mg-axi entra directory-role scoped-member list --role <role-id>"],
235
+ "/scopedRoleMemberships/{scopedRoleMembership-id}": ["mg-axi entra directory-role scoped-member show --role <role-id> --id <scoped-membership-id>"],
236
+ };
237
+ const alternatives = unsupported[operation.path];
238
+ if (alternatives)
239
+ throw new AxiError(`GET ${operation.path} is not supported by Microsoft Graph v1.0`, "VALIDATION_ERROR", alternatives);
231
240
  }
232
241
  const UNSAFE_PARAM = /[%/?\\{}()]/;
233
242
  const CONTROL = /[\s\x00-\x1f\x7f]/;
@@ -446,10 +455,10 @@ function faultBody(body) {
446
455
  try {
447
456
  const parsed = redact(JSON.parse(body));
448
457
  const code = typeof parsed?.error?.code === "string" ? parsed.error.code : null;
449
- if (!code)
450
- return "";
451
458
  const message = typeof parsed?.error?.message === "string" ? parsed.error.message.slice(0, 300) : "";
452
- return `: graph ${code}${message ? ` ${message}` : ""}`;
459
+ if (!code && !message)
460
+ return "";
461
+ return `: graph${code ? ` ${code}` : ""}${message ? ` ${message}` : ""}`;
453
462
  }
454
463
  catch {
455
464
  return "";
@@ -625,6 +634,7 @@ export class GraphSession {
625
634
  this.files = files;
626
635
  }
627
636
  cursorQuery(operation, cursor) {
637
+ checkOperation(operation);
628
638
  return decodeCursor(operation, cursor).query;
629
639
  }
630
640
  async execute(args) {
@@ -757,6 +767,7 @@ export class GraphSession {
757
767
  const headersFor = () => ({
758
768
  Authorization: `Bearer ${token}`,
759
769
  Accept: "application/json",
770
+ "Accept-Language": "en-US",
760
771
  "client-request-id": randomUUID(),
761
772
  ...(consistencyLevel === "eventual" ? { ConsistencyLevel: "eventual" } : {}),
762
773
  });
@@ -942,6 +953,7 @@ export class GraphSession {
942
953
  const headersFor = () => ({
943
954
  Authorization: `Bearer ${token}`,
944
955
  Accept: "application/json",
956
+ "Accept-Language": "en-US",
945
957
  "client-request-id": randomUUID(),
946
958
  ...(opts.consistencyLevel === "eventual" ? { ConsistencyLevel: "eventual" } : {}),
947
959
  // The branding GET documents Accept-Language as required; 0 selects
package/docs/coverage.md CHANGED
@@ -316,9 +316,12 @@ The declined EXT-02c access-review history reads (ReadWrite least privilege, SAS
316
316
  | `mg-axi api get` | reviewed raw reads (see src/api.ts) | reviewed-raw-read catalogue | API-01 |
317
317
  | `mg-axi doctor` | bounded `GET:/users` health check | uses the named user-list read | PACK-01 |
318
318
 
319
+ Graph v1.0 does not support the legacy `entra scoped-role-membership list/show`, `entra domain-dns-record list` or `entra directory-object list` routes; those commands fail locally with supported alternatives and their root routes are not available through `api get`.
320
+
319
321
  ## EXT-01 domain scope decisions
320
322
 
321
323
  Firstmate decision R1: approve narrowing this change to the eight v1.0 domain reads above.
324
+ The root domain-dns-record list is now explicitly unsupported; use verification-dns-record or service-configuration-record list with a parent domain.
322
325
  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.
323
326
  Federation stays out because it can carry signing-certificate material.
324
327
  Firstmate decision R2: keep the write-family status section as the coverage fix added to scope (resolved-kept).
@@ -644,10 +647,10 @@ Scalar counts need a separate query and response contract from the approved list
644
647
 
645
648
  ## EXT-01 directory-objects scope decisions
646
649
 
647
- This change covers the three v1.0 directory-object reads above (directory-object list, show and count).
650
+ Directory-object show and count are available. Graph v1.0 rejects directory-object list; the legacy command fails locally and points to show or a typed list command.
648
651
  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.
649
652
  Directory-object reads support v1.0 only; beta needs its own review.
650
- 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.
653
+ Delta-token sync needs its own change-tracking contract beyond the approved show/count reads; the POST lookup and validation actions need their own request and projection review.
651
654
  Rows are polymorphic: only the base-type properties plus the @odata.type discriminator are projected, so subtype secrets or credentials can never appear.
652
655
 
653
656
  | Inventory operation | Disposition | Owning slice | Deferral reason |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@knowttl/mg-axi",
3
- "version": "0.1.6",
3
+ "version": "0.1.7",
4
4
  "description": "Agent-facing Microsoft Graph CLI, read-only by default",
5
5
  "type": "module",
6
6
  "bin": {
@@ -26,6 +26,7 @@ Its capability label is `native` (implemented by an mg-axi handler).
26
26
  Its effect is `read` for Graph read leaves and the doctor health check, `write` for the gated named writes, or `local` for home, profile, auth, config path, login-alias and setup views.
27
27
  Doctor acquires credentials silently and contacts Graph; only its help view stays offline.
28
28
  The list below records current executable leaves; it makes no coverage claim for other Graph operations.
29
+ Graph v1.0 does not support the legacy `entra scoped-role-membership list/show`, `entra domain-dns-record list` or `entra directory-object list` routes; those commands fail locally with supported alternatives and their root routes are not available through `api get`.
29
30
  See `docs/coverage.md` for the per-operation disposition records.
30
31
  `docs/coverage.md` and this file are generated from the same catalogue and inventory sources; `node tools/generate-docs.mjs --check` fails when they are stale.
31
32