@revfleet/hscli 0.8.11 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +186 -0
- package/README.md +223 -10
- package/dist/cli.js +29 -3
- package/dist/cli.js.map +1 -1
- package/dist/commands/agent/contracts.d.ts +3 -0
- package/dist/commands/agent/contracts.js +139 -0
- package/dist/commands/agent/contracts.js.map +1 -0
- package/dist/commands/agent/index.d.ts +3 -0
- package/dist/commands/agent/index.js +188 -0
- package/dist/commands/agent/index.js.map +1 -0
- package/dist/commands/agent/recipes.d.ts +13 -0
- package/dist/commands/agent/recipes.js +108 -0
- package/dist/commands/agent/recipes.js.map +1 -0
- package/dist/commands/agent/runner.d.ts +16 -0
- package/dist/commands/agent/runner.js +165 -0
- package/dist/commands/agent/runner.js.map +1 -0
- package/dist/commands/auth/index.js +64 -7
- package/dist/commands/auth/index.js.map +1 -1
- package/dist/commands/automation/index.js +30 -0
- package/dist/commands/automation/index.js.map +1 -1
- package/dist/commands/backup/index.d.ts +12 -0
- package/dist/commands/backup/index.js +459 -0
- package/dist/commands/backup/index.js.map +1 -0
- package/dist/commands/cms/content-ops.js +1 -1
- package/dist/commands/cms/content-ops.js.map +1 -1
- package/dist/commands/communication-preferences/index.js +207 -7
- package/dist/commands/communication-preferences/index.js.map +1 -1
- package/dist/commands/content-permissions/index.d.ts +22 -0
- package/dist/commands/content-permissions/index.js +141 -0
- package/dist/commands/content-permissions/index.js.map +1 -0
- package/dist/commands/context/index.d.ts +3 -0
- package/dist/commands/context/index.js +182 -0
- package/dist/commands/context/index.js.map +1 -0
- package/dist/commands/crm/associations.js +38 -0
- package/dist/commands/crm/associations.js.map +1 -1
- package/dist/commands/crm/custom-objects.js +39 -2
- package/dist/commands/crm/custom-objects.js.map +1 -1
- package/dist/commands/crm/dated.js +3 -3
- package/dist/commands/crm/dated.js.map +1 -1
- package/dist/commands/crm/migration.js +210 -2
- package/dist/commands/crm/migration.js.map +1 -1
- package/dist/commands/crm/properties.js +2 -1
- package/dist/commands/crm/properties.js.map +1 -1
- package/dist/commands/crm/property-batch.d.ts +1 -0
- package/dist/commands/crm/property-batch.js +44 -5
- package/dist/commands/crm/property-batch.js.map +1 -1
- package/dist/commands/crm/shared.js +21 -2
- package/dist/commands/crm/shared.js.map +1 -1
- package/dist/commands/crm/sync.js +1 -1
- package/dist/commands/crm/sync.js.map +1 -1
- package/dist/commands/doctor/index.js +193 -0
- package/dist/commands/doctor/index.js.map +1 -1
- package/dist/commands/email-tracking/index.d.ts +16 -0
- package/dist/commands/email-tracking/index.js +314 -0
- package/dist/commands/email-tracking/index.js.map +1 -0
- package/dist/commands/extensions/index.js +1 -1
- package/dist/commands/extensions/index.js.map +1 -1
- package/dist/commands/forms/index.js +15 -2
- package/dist/commands/forms/index.js.map +1 -1
- package/dist/commands/forms/legacy-v2.d.ts +7 -2
- package/dist/commands/forms/legacy-v2.js +103 -7
- package/dist/commands/forms/legacy-v2.js.map +1 -1
- package/dist/commands/guide/index.d.ts +3 -0
- package/dist/commands/guide/index.js +75 -16
- package/dist/commands/guide/index.js.map +1 -1
- package/dist/commands/internal/catalog.d.ts +1 -0
- package/dist/commands/internal/catalog.js +2 -0
- package/dist/commands/internal/catalog.js.map +1 -0
- package/dist/commands/internal/index.d.ts +26 -0
- package/dist/commands/internal/index.js +823 -0
- package/dist/commands/internal/index.js.map +1 -0
- package/dist/commands/internal-session.d.ts +15 -0
- package/dist/commands/internal-session.js +152 -0
- package/dist/commands/internal-session.js.map +1 -0
- package/dist/commands/lists/index.js +1 -1
- package/dist/commands/lists/index.js.map +1 -1
- package/dist/commands/marketing/email-audit.d.ts +32 -0
- package/dist/commands/marketing/email-audit.js +121 -0
- package/dist/commands/marketing/email-audit.js.map +1 -0
- package/dist/commands/marketing/index.js +2 -0
- package/dist/commands/marketing/index.js.map +1 -1
- package/dist/commands/mcp.js +5 -3
- package/dist/commands/mcp.js.map +1 -1
- package/dist/commands/playbooks/index.d.ts +3 -0
- package/dist/commands/playbooks/index.js +228 -0
- package/dist/commands/playbooks/index.js.map +1 -0
- package/dist/commands/portals/index.d.ts +3 -0
- package/dist/commands/portals/index.js +132 -0
- package/dist/commands/portals/index.js.map +1 -0
- package/dist/commands/reports/index.d.ts +3 -0
- package/dist/commands/reports/index.js +479 -0
- package/dist/commands/reports/index.js.map +1 -0
- package/dist/commands/sales-content/index.d.ts +13 -0
- package/dist/commands/sales-content/index.js +59 -0
- package/dist/commands/sales-content/index.js.map +1 -0
- package/dist/commands/sales-templates/index.d.ts +3 -0
- package/dist/commands/sales-templates/index.js +88 -0
- package/dist/commands/sales-templates/index.js.map +1 -0
- package/dist/commands/seed/cms.js +28 -45
- package/dist/commands/seed/cms.js.map +1 -1
- package/dist/commands/sequences/index.d.ts +18 -0
- package/dist/commands/sequences/index.js +263 -0
- package/dist/commands/sequences/index.js.map +1 -0
- package/dist/commands/settings/index.js +450 -102
- package/dist/commands/settings/index.js.map +1 -1
- package/dist/commands/settings/site-settings.d.ts +3 -0
- package/dist/commands/settings/site-settings.js +106 -0
- package/dist/commands/settings/site-settings.js.map +1 -0
- package/dist/commands/ui/index.d.ts +3 -0
- package/dist/commands/ui/index.js +422 -0
- package/dist/commands/ui/index.js.map +1 -0
- package/dist/commands/workflows/index.js +2 -0
- package/dist/commands/workflows/index.js.map +1 -1
- package/dist/commands/workflows/preflight.d.ts +23 -0
- package/dist/commands/workflows/preflight.js +295 -0
- package/dist/commands/workflows/preflight.js.map +1 -0
- package/dist/core/action-contract.d.ts +43 -0
- package/dist/core/action-contract.js +207 -0
- package/dist/core/action-contract.js.map +1 -0
- package/dist/core/auth.d.ts +6 -0
- package/dist/core/auth.js +85 -0
- package/dist/core/auth.js.map +1 -1
- package/dist/core/http.d.ts +8 -0
- package/dist/core/http.js +37 -5
- package/dist/core/http.js.map +1 -1
- package/dist/core/id-maps.d.ts +27 -0
- package/dist/core/id-maps.js +204 -0
- package/dist/core/id-maps.js.map +1 -0
- package/dist/core/output.d.ts +2 -1
- package/dist/core/output.js +34 -0
- package/dist/core/output.js.map +1 -1
- package/dist/mcp/profiles.d.ts +14 -0
- package/dist/mcp/profiles.js +68 -0
- package/dist/mcp/profiles.js.map +1 -0
- package/dist/mcp/server.d.ts +5 -3
- package/dist/mcp/server.js +22 -5
- package/dist/mcp/server.js.map +1 -1
- package/docs/CAPABILITY_LIBRARY.md +29 -8
- package/docs/COMMAND_TREE.md +98 -17
- package/docs/COMPARISON.md +8 -5
- package/docs/COOKBOOK.md +127 -5
- package/docs/INTERNAL-ENDPOINTS-DISCOVERY.md +813 -0
- package/docs/INTERNAL-ENDPOINTS.md +371 -0
- package/docs/INTERNAL_ENDPOINTS.md +243 -0
- package/docs/MARKETING_SETUP.md +21 -1
- package/docs/MCP.md +28 -89
- package/docs/MCP_TOOL_CATALOG.md +192 -0
- package/docs/MIGRATION_ADAPTERS.md +115 -0
- package/docs/PORTAL_BACKUP.md +212 -0
- package/docs/PUBLISHING.md +1 -1
- package/docs/SALES_CONTENT_INTERNAL_PUNCH_LIST.md +504 -0
- package/docs/TUTORIALS/secure-agent-writes.md +49 -0
- package/docs/WHY_HOW_WHAT.md +10 -4
- package/package.json +4 -2
|
@@ -1,41 +1,14 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { getProfile } from "../../core/auth.js";
|
|
3
|
-
import { createBrowserSessionClient, createClient } from "../../core/http.js";
|
|
1
|
+
import { createClient } from "../../core/http.js";
|
|
4
2
|
import { CliError, printResult } from "../../core/output.js";
|
|
5
3
|
import { encodePathSegment, maybeWrite, parseJsonPayload, parseNumberFlag } from "../crm/shared.js";
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
.option("--portal-id <id>", "HubSpot portal ID (or HSCLI_PORTAL_ID / profile.portalId)")
|
|
9
|
-
.option("--ui-domain <domain>", "HubSpot app domain, e.g. app.hubspot.com or app-eu1.hubspot.com")
|
|
10
|
-
.option("--cookie <header>", "Browser Cookie header for app.hubspot.com")
|
|
11
|
-
.option("--cookie-file <path>", "Cookie header, Netscape cookie jar, or JSON cookie export")
|
|
12
|
-
.option("--csrf <token>", "x-hubspot-csrf-hubspotapi header value");
|
|
13
|
-
}
|
|
4
|
+
import { addBrowserSessionOptions, resolveBrowserSession } from "../internal-session.js";
|
|
5
|
+
import { registerSiteSettings } from "./site-settings.js";
|
|
14
6
|
function resolvePermissionSetSession(ctx, opts) {
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
const uiDomain = normalizeUiDomain(firstString(opts.uiDomain, process.env.HSCLI_HUBSPOT_UI_DOMAIN, profile?.uiDomain, "app.hubspot.com") ?? "app.hubspot.com");
|
|
21
|
-
const cookie = firstString(opts.cookie, process.env.HSCLI_HUBSPOT_COOKIE) ?? readCookieFile(firstString(opts.cookieFile, process.env.HSCLI_HUBSPOT_COOKIE_FILE));
|
|
22
|
-
if (!cookie) {
|
|
23
|
-
throw new CliError("SESSION_COOKIE_REQUIRED", "Permission-set commands require --cookie, --cookie-file, HSCLI_HUBSPOT_COOKIE, or HSCLI_HUBSPOT_COOKIE_FILE.");
|
|
24
|
-
}
|
|
25
|
-
const csrfToken = firstString(opts.csrf, process.env.HSCLI_HUBSPOT_CSRF, extractCsrfToken(cookie));
|
|
26
|
-
if (!csrfToken) {
|
|
27
|
-
throw new CliError("SESSION_CSRF_REQUIRED", "Permission-set commands require --csrf or HSCLI_HUBSPOT_CSRF. A csrf.app cookie is also accepted when present.");
|
|
28
|
-
}
|
|
29
|
-
return {
|
|
30
|
-
portalId,
|
|
31
|
-
client: createBrowserSessionClient(ctx.profile, {
|
|
32
|
-
apiBaseUrl: `https://${uiDomain}`,
|
|
33
|
-
cookie,
|
|
34
|
-
csrfToken,
|
|
35
|
-
telemetryFile: ctx.telemetryFile,
|
|
36
|
-
strictCapabilities: ctx.strictCapabilities,
|
|
37
|
-
}),
|
|
38
|
-
};
|
|
7
|
+
const session = resolveBrowserSession(ctx, opts, "Permission-set commands", true);
|
|
8
|
+
return { client: session.client, portalId: session.portalId };
|
|
9
|
+
}
|
|
10
|
+
function resolveBusinessUnitSession(ctx, opts, requirePortalId = false) {
|
|
11
|
+
return resolveBrowserSession(ctx, opts, "Business-unit internal commands", requirePortalId);
|
|
39
12
|
}
|
|
40
13
|
function permissionSetsPath(portalId) {
|
|
41
14
|
return `/api/app-users/v1/permission-sets?portalId=${encodeURIComponent(portalId)}`;
|
|
@@ -43,6 +16,87 @@ function permissionSetsPath(portalId) {
|
|
|
43
16
|
function permissionSetPath(portalId, id) {
|
|
44
17
|
return `/api/app-users/v1/permission-sets/${encodePathSegment(id, "permissionSetId")}?portalId=${encodeURIComponent(portalId)}`;
|
|
45
18
|
}
|
|
19
|
+
function permissionSetClonePath(portalId, id) {
|
|
20
|
+
return `/api/app-users/v1/permission-sets/${encodePathSegment(id, "permissionSetId")}/clone?portalId=${encodeURIComponent(portalId)}`;
|
|
21
|
+
}
|
|
22
|
+
function appUserPath(portalId, userId) {
|
|
23
|
+
return `/api/app-users/v1/users/${encodePathSegment(userId, "userId")}?portalId=${encodeURIComponent(portalId)}`;
|
|
24
|
+
}
|
|
25
|
+
function appUsersPath(portalId, limit, offset) {
|
|
26
|
+
const params = new URLSearchParams();
|
|
27
|
+
params.set("portalId", portalId);
|
|
28
|
+
if (limit)
|
|
29
|
+
params.set("limit", String(parseNumberFlag(limit, "--limit")));
|
|
30
|
+
if (offset)
|
|
31
|
+
params.set("offset", offset);
|
|
32
|
+
return `/api/app-users/v1/users?${params.toString()}`;
|
|
33
|
+
}
|
|
34
|
+
function appUsersBatchPath(portalId) {
|
|
35
|
+
return `/api/app-users/v1/users/partial/batch?portalId=${encodeURIComponent(portalId)}`;
|
|
36
|
+
}
|
|
37
|
+
function roleCatalogPath(portalId) {
|
|
38
|
+
return `/api/app-users/v1/roles?portalId=${encodeURIComponent(portalId)}`;
|
|
39
|
+
}
|
|
40
|
+
function roleCatalogItemPath(portalId, roleName) {
|
|
41
|
+
return `/api/app-users/v1/roles/${encodePathSegment(roleName, "roleName")}?portalId=${encodeURIComponent(portalId)}`;
|
|
42
|
+
}
|
|
43
|
+
function roleCategoriesPath(portalId) {
|
|
44
|
+
return `/api/app-users/v1/role-categories?portalId=${encodeURIComponent(portalId)}`;
|
|
45
|
+
}
|
|
46
|
+
function roleScopesPath(portalId) {
|
|
47
|
+
return `/api/app-users/v1/scopes?portalId=${encodeURIComponent(portalId)}`;
|
|
48
|
+
}
|
|
49
|
+
function seatsPath(portalId) {
|
|
50
|
+
return `/api/app-users/v1/seats?portalId=${encodeURIComponent(portalId)}`;
|
|
51
|
+
}
|
|
52
|
+
function seatAssignmentsPath(portalId) {
|
|
53
|
+
return `/api/app-users/v1/seat-assignments?portalId=${encodeURIComponent(portalId)}`;
|
|
54
|
+
}
|
|
55
|
+
function seatAssignmentPath(portalId, userId) {
|
|
56
|
+
return `/api/app-users/v1/seat-assignments/${encodePathSegment(userId, "userId")}?portalId=${encodeURIComponent(portalId)}`;
|
|
57
|
+
}
|
|
58
|
+
function permissionTemplatesPath(portalId) {
|
|
59
|
+
return `/api/permission-templates/v1/templates?portalId=${encodeURIComponent(portalId)}`;
|
|
60
|
+
}
|
|
61
|
+
function permissionTemplatePath(portalId, templateId) {
|
|
62
|
+
return `/api/permission-templates/v1/templates/${encodePathSegment(templateId, "templateId")}?portalId=${encodeURIComponent(portalId)}`;
|
|
63
|
+
}
|
|
64
|
+
function permissionSetAssignmentsPath(portalId) {
|
|
65
|
+
return `/api/app-users/v1/permission-sets/permission-set-user-assignments?portalId=${encodeURIComponent(portalId)}`;
|
|
66
|
+
}
|
|
67
|
+
function permissionSetAssignUserRpcPath(portalId) {
|
|
68
|
+
return `/api/chirp-frontend-app/v1/gateway/com.hubspot.appusers.rpc.PermissionSetAssignmentsRpc/assignUserToPermissionSets?portalId=${encodeURIComponent(portalId)}`;
|
|
69
|
+
}
|
|
70
|
+
const PERMISSION_AUDIT_RPC = {
|
|
71
|
+
"assignment-history": "getPermissionSetAssignmentHistory",
|
|
72
|
+
"role-change-history": "getRoleChangeHistory",
|
|
73
|
+
"user-access": "getUserAccessAudit",
|
|
74
|
+
};
|
|
75
|
+
function permissionAuditRpcPath(name) {
|
|
76
|
+
return `/api/chirp-frontend-app/v1/gateway/com.hubspot.app.user.audits.service.rpc.AppUserAuditsRpc/${PERMISSION_AUDIT_RPC[name]}`;
|
|
77
|
+
}
|
|
78
|
+
function flpAllowlistingsPath() {
|
|
79
|
+
return "/api/crm-permissions/v1/field-level-permissions/allowlistings";
|
|
80
|
+
}
|
|
81
|
+
function flpObjectAllowlistingsPath(typeId) {
|
|
82
|
+
return `/api/crm-permissions/v1/field-level-permissions/allowlistings/${encodePathSegment(typeId, "typeId")}`;
|
|
83
|
+
}
|
|
84
|
+
function flpPropertyAllowlistingsPath(typeId, propertyName) {
|
|
85
|
+
return `${flpObjectAllowlistingsPath(typeId)}/${encodePathSegment(propertyName, "propertyName")}`;
|
|
86
|
+
}
|
|
87
|
+
function flpAccessStatePath() {
|
|
88
|
+
return "/api/crm-permissions/v1/field-level-permissions/allowlistings/access-state/multi-get";
|
|
89
|
+
}
|
|
90
|
+
function flpBlocklistPath() {
|
|
91
|
+
return "/api/crm-permissions/v1/field-level-permissions/blocklist/including-global";
|
|
92
|
+
}
|
|
93
|
+
function flpBypassUsersPath() {
|
|
94
|
+
return "/api/permissions-skynet/v1/scopes/app/field-level-permissions-bypass/user-ids";
|
|
95
|
+
}
|
|
96
|
+
function businessUnitsInternalPath(portalId) {
|
|
97
|
+
const query = portalId ? `?portalId=${encodeURIComponent(portalId)}` : "";
|
|
98
|
+
return `/api/business-units/v1/business-units${query}`;
|
|
99
|
+
}
|
|
46
100
|
async function maybeWritePermissionSetWithRoleRetry(ctx, client, method, path, body, stripUnknownRoles) {
|
|
47
101
|
try {
|
|
48
102
|
return await maybeWrite(ctx, client, method, path, body);
|
|
@@ -75,6 +129,65 @@ function extractPermissionSets(response) {
|
|
|
75
129
|
}
|
|
76
130
|
return [];
|
|
77
131
|
}
|
|
132
|
+
function buildBusinessUnitIdMapSeed(response) {
|
|
133
|
+
const seed = {};
|
|
134
|
+
for (const unit of extractBusinessUnits(response)) {
|
|
135
|
+
const id = firstString(stringFromUnknown(unit.id), stringFromUnknown(unit.businessUnitId), stringFromUnknown(unit.unitId));
|
|
136
|
+
if (!id)
|
|
137
|
+
continue;
|
|
138
|
+
seed[id] = {
|
|
139
|
+
sourceName: firstString(stringFromUnknown(unit.name), stringFromUnknown(unit.label)),
|
|
140
|
+
target_id: "",
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
return seed;
|
|
144
|
+
}
|
|
145
|
+
function extractBusinessUnits(response) {
|
|
146
|
+
if (Array.isArray(response))
|
|
147
|
+
return response.filter(isRecord);
|
|
148
|
+
if (!isRecord(response))
|
|
149
|
+
return [];
|
|
150
|
+
for (const key of ["results", "businessUnits", "items"]) {
|
|
151
|
+
const value = response[key];
|
|
152
|
+
if (Array.isArray(value))
|
|
153
|
+
return value.filter(isRecord);
|
|
154
|
+
}
|
|
155
|
+
const data = isRecord(response.data) ? response.data : undefined;
|
|
156
|
+
if (data) {
|
|
157
|
+
for (const key of ["results", "businessUnits", "items"]) {
|
|
158
|
+
const value = data[key];
|
|
159
|
+
if (Array.isArray(value))
|
|
160
|
+
return value.filter(isRecord);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
return [];
|
|
164
|
+
}
|
|
165
|
+
async function findExistingBusinessUnitByName(client, name, portalId) {
|
|
166
|
+
const response = await client.request(businessUnitsInternalPath(portalId));
|
|
167
|
+
return extractBusinessUnits(response).find((unit) => unit.name === name);
|
|
168
|
+
}
|
|
169
|
+
function normalizeBusinessUnitCreatePayload(raw) {
|
|
170
|
+
if (!isRecord(raw)) {
|
|
171
|
+
throw new CliError("INVALID_BUSINESS_UNIT_PAYLOAD", "Business-unit create payload must be a JSON object.");
|
|
172
|
+
}
|
|
173
|
+
const name = typeof raw.name === "string" ? raw.name.trim() : "";
|
|
174
|
+
if (!name) {
|
|
175
|
+
throw new CliError("BUSINESS_UNIT_NAME_REQUIRED", "Business-unit create payload requires a non-empty name.");
|
|
176
|
+
}
|
|
177
|
+
const payload = { name };
|
|
178
|
+
for (const key of ["label", "description", "logoMetadata"]) {
|
|
179
|
+
if (raw[key] !== undefined && raw[key] !== null && raw[key] !== "")
|
|
180
|
+
payload[key] = raw[key];
|
|
181
|
+
}
|
|
182
|
+
return payload;
|
|
183
|
+
}
|
|
184
|
+
function stringFromUnknown(value) {
|
|
185
|
+
if (typeof value === "string")
|
|
186
|
+
return value;
|
|
187
|
+
if (typeof value === "number" && Number.isFinite(value))
|
|
188
|
+
return String(value);
|
|
189
|
+
return undefined;
|
|
190
|
+
}
|
|
78
191
|
function extractMissingRoles(error) {
|
|
79
192
|
if (!(error instanceof CliError) || error.status !== 400)
|
|
80
193
|
return [];
|
|
@@ -97,14 +210,6 @@ function stripRoles(body, rolesToStrip) {
|
|
|
97
210
|
return undefined;
|
|
98
211
|
return { ...body, roleNames: kept };
|
|
99
212
|
}
|
|
100
|
-
function safeProfile(profile) {
|
|
101
|
-
try {
|
|
102
|
-
return getProfile(profile);
|
|
103
|
-
}
|
|
104
|
-
catch {
|
|
105
|
-
return undefined;
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
213
|
function firstString(...values) {
|
|
109
214
|
for (const value of values) {
|
|
110
215
|
const trimmed = value?.trim();
|
|
@@ -113,73 +218,87 @@ function firstString(...values) {
|
|
|
113
218
|
}
|
|
114
219
|
return undefined;
|
|
115
220
|
}
|
|
116
|
-
function
|
|
117
|
-
return
|
|
221
|
+
function isRecord(value) {
|
|
222
|
+
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
118
223
|
}
|
|
119
|
-
function
|
|
120
|
-
if (!
|
|
121
|
-
|
|
122
|
-
const raw = readFileSync(path, "utf8").trim();
|
|
123
|
-
if (!raw)
|
|
124
|
-
return undefined;
|
|
125
|
-
return normalizeCookieFile(raw);
|
|
126
|
-
}
|
|
127
|
-
function normalizeCookieFile(raw) {
|
|
128
|
-
const parsed = parseCookieJson(raw);
|
|
129
|
-
if (parsed)
|
|
130
|
-
return parsed;
|
|
131
|
-
const lines = raw.split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
|
|
132
|
-
const netscapeCookies = lines
|
|
133
|
-
.filter((line) => !line.startsWith("#"))
|
|
134
|
-
.map((line) => line.split(/\t+/))
|
|
135
|
-
.filter((parts) => parts.length >= 7)
|
|
136
|
-
.map((parts) => `${parts[5]}=${parts.slice(6).join("\t")}`);
|
|
137
|
-
if (netscapeCookies.length > 0)
|
|
138
|
-
return netscapeCookies.join("; ");
|
|
139
|
-
return lines.filter((line) => !line.startsWith("#")).join("; ");
|
|
140
|
-
}
|
|
141
|
-
function parseCookieJson(raw) {
|
|
142
|
-
try {
|
|
143
|
-
const parsed = JSON.parse(raw);
|
|
144
|
-
if (typeof parsed === "string")
|
|
145
|
-
return parsed;
|
|
146
|
-
if (isRecord(parsed) && typeof parsed.cookie === "string")
|
|
147
|
-
return parsed.cookie;
|
|
148
|
-
const cookies = Array.isArray(parsed) ? parsed : isRecord(parsed) && Array.isArray(parsed.cookies) ? parsed.cookies : undefined;
|
|
149
|
-
if (!cookies)
|
|
150
|
-
return undefined;
|
|
151
|
-
const pairs = cookies
|
|
152
|
-
.filter(isRecord)
|
|
153
|
-
.map((cookie) => {
|
|
154
|
-
const name = typeof cookie.name === "string" ? cookie.name.trim() : "";
|
|
155
|
-
const value = typeof cookie.value === "string" ? cookie.value : "";
|
|
156
|
-
return name ? `${name}=${value}` : "";
|
|
157
|
-
})
|
|
158
|
-
.filter(Boolean);
|
|
159
|
-
return pairs.length > 0 ? pairs.join("; ") : undefined;
|
|
224
|
+
function normalizeUserCreatePayload(raw, allowInviteEmail) {
|
|
225
|
+
if (!isRecord(raw)) {
|
|
226
|
+
throw new CliError("INVALID_USER_PAYLOAD", "User create payload must be a JSON object.");
|
|
160
227
|
}
|
|
161
|
-
|
|
162
|
-
|
|
228
|
+
const payload = { ...raw };
|
|
229
|
+
const inviteFields = [
|
|
230
|
+
payload.sendWelcomeEmail === true ? "sendWelcomeEmail=true" : undefined,
|
|
231
|
+
payload.sendInviteEmail === true ? "sendInviteEmail=true" : undefined,
|
|
232
|
+
payload.skipInviteEmail === false ? "skipInviteEmail=false" : undefined,
|
|
233
|
+
].filter(Boolean);
|
|
234
|
+
if (!allowInviteEmail && inviteFields.length > 0) {
|
|
235
|
+
throw new CliError("USER_INVITE_EMAIL_BLOCKED", `User invite email blocked by default (${inviteFields.join(", ")}). Set sendWelcomeEmail:false or pass --allow-invite-email.`);
|
|
236
|
+
}
|
|
237
|
+
if (!allowInviteEmail) {
|
|
238
|
+
payload.sendWelcomeEmail = false;
|
|
163
239
|
}
|
|
240
|
+
return payload;
|
|
164
241
|
}
|
|
165
|
-
function
|
|
166
|
-
const
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
continue;
|
|
171
|
-
if (candidates.has(rawName.trim().toLowerCase())) {
|
|
172
|
-
const value = rawValue.join("=").trim();
|
|
173
|
-
return value ? decodeURIComponent(value) : undefined;
|
|
174
|
-
}
|
|
242
|
+
function permissionSetIdsFromOptions(opts) {
|
|
243
|
+
const single = firstString(opts.permissionSetId);
|
|
244
|
+
const many = firstString(opts.permissionSetIds);
|
|
245
|
+
if (single && many) {
|
|
246
|
+
throw new CliError("INVALID_PERMISSION_SET_ASSIGNMENT", "Use either --permission-set-id or --permission-set-ids, not both.");
|
|
175
247
|
}
|
|
176
|
-
|
|
248
|
+
if (single)
|
|
249
|
+
return [single];
|
|
250
|
+
if (many) {
|
|
251
|
+
const ids = many.split(",").map((id) => id.trim()).filter(Boolean);
|
|
252
|
+
if (ids.length === 0)
|
|
253
|
+
throw new CliError("INVALID_PERMISSION_SET_ASSIGNMENT", "--permission-set-ids must contain at least one id.");
|
|
254
|
+
return ids;
|
|
255
|
+
}
|
|
256
|
+
throw new CliError("INVALID_PERMISSION_SET_ASSIGNMENT", "Assign requires --permission-set-id or --permission-set-ids.");
|
|
177
257
|
}
|
|
178
|
-
function
|
|
179
|
-
|
|
258
|
+
function idStringFromAssignmentValue(value, fieldName) {
|
|
259
|
+
const id = stringFromUnknown(value)?.trim();
|
|
260
|
+
if (!id) {
|
|
261
|
+
throw new CliError("INVALID_PERMISSION_SET_ASSIGNMENT", `${fieldName} must be a non-empty string or number.`);
|
|
262
|
+
}
|
|
263
|
+
return id;
|
|
264
|
+
}
|
|
265
|
+
function buildPermissionSetAssignmentPayload(userId, opts) {
|
|
266
|
+
return {
|
|
267
|
+
assignments: [
|
|
268
|
+
{
|
|
269
|
+
userId,
|
|
270
|
+
permissionSetIds: permissionSetIdsFromOptions(opts),
|
|
271
|
+
},
|
|
272
|
+
],
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
function normalizePermissionSetAssignmentsPayload(raw) {
|
|
276
|
+
if (!isRecord(raw)) {
|
|
277
|
+
throw new CliError("INVALID_PERMISSION_SET_ASSIGNMENT", "Assignment payload must be a JSON object.");
|
|
278
|
+
}
|
|
279
|
+
const assignments = raw.assignments;
|
|
280
|
+
if (!Array.isArray(assignments) || assignments.length === 0) {
|
|
281
|
+
throw new CliError("INVALID_PERMISSION_SET_ASSIGNMENT", "Assignment payload requires a non-empty assignments array.");
|
|
282
|
+
}
|
|
283
|
+
return {
|
|
284
|
+
assignments: assignments.map((assignment, index) => {
|
|
285
|
+
if (!isRecord(assignment)) {
|
|
286
|
+
throw new CliError("INVALID_PERMISSION_SET_ASSIGNMENT", `assignments[${index}] must be a JSON object.`);
|
|
287
|
+
}
|
|
288
|
+
const permissionSetIds = assignment.permissionSetIds;
|
|
289
|
+
if (!Array.isArray(permissionSetIds) || permissionSetIds.length === 0) {
|
|
290
|
+
throw new CliError("INVALID_PERMISSION_SET_ASSIGNMENT", `assignments[${index}].permissionSetIds must be a non-empty array.`);
|
|
291
|
+
}
|
|
292
|
+
return {
|
|
293
|
+
userId: idStringFromAssignmentValue(assignment.userId, `assignments[${index}].userId`),
|
|
294
|
+
permissionSetIds: permissionSetIds.map((id, idIndex) => idStringFromAssignmentValue(id, `assignments[${index}].permissionSetIds[${idIndex}]`)),
|
|
295
|
+
};
|
|
296
|
+
}),
|
|
297
|
+
};
|
|
180
298
|
}
|
|
181
299
|
export function registerSettings(program, getCtx) {
|
|
182
300
|
const settings = program.command("settings").description("HubSpot Settings APIs");
|
|
301
|
+
registerSiteSettings(settings, getCtx);
|
|
183
302
|
// Users
|
|
184
303
|
const users = settings.command("users").description("User provisioning");
|
|
185
304
|
users
|
|
@@ -205,10 +324,11 @@ export function registerSettings(program, getCtx) {
|
|
|
205
324
|
users
|
|
206
325
|
.command("create")
|
|
207
326
|
.requiredOption("--data <payload>", "User creation payload JSON")
|
|
327
|
+
.option("--allow-invite-email", "Allow the create payload to send welcome/invite email")
|
|
208
328
|
.action(async (opts) => {
|
|
209
329
|
const ctx = getCtx();
|
|
210
330
|
const client = createClient(ctx.profile);
|
|
211
|
-
const payload = parseJsonPayload(opts.data);
|
|
331
|
+
const payload = normalizeUserCreatePayload(parseJsonPayload(opts.data), Boolean(opts.allowInviteEmail));
|
|
212
332
|
const res = await maybeWrite(ctx, client, "POST", "/settings/v3/users/", payload);
|
|
213
333
|
printResult(ctx, res);
|
|
214
334
|
});
|
|
@@ -257,6 +377,33 @@ export function registerSettings(program, getCtx) {
|
|
|
257
377
|
const res = await client.request(`/settings/v3/business-units/?${params.toString()}`);
|
|
258
378
|
printResult(ctx, res);
|
|
259
379
|
});
|
|
380
|
+
addBrowserSessionOptions(businessUnits
|
|
381
|
+
.command("capture")
|
|
382
|
+
.description("Capture full business-unit metadata via internal HubSpot session-auth endpoint")
|
|
383
|
+
.option("--include-id-map-seed", "Include a source-ID keyed business-unit map seed")).action(async (opts) => {
|
|
384
|
+
const ctx = getCtx();
|
|
385
|
+
const { client, portalId } = resolveBusinessUnitSession(ctx, opts);
|
|
386
|
+
const res = await client.request(businessUnitsInternalPath(portalId));
|
|
387
|
+
printResult(ctx, opts.includeIdMapSeed ? { result: res, idMapSeed: buildBusinessUnitIdMapSeed(res) } : res);
|
|
388
|
+
});
|
|
389
|
+
addBrowserSessionOptions(businessUnits
|
|
390
|
+
.command("create-internal")
|
|
391
|
+
.description("Create a business unit via the internal browser-session endpoint")
|
|
392
|
+
.requiredOption("--data <payload>", "Business-unit payload JSON: { name, label?, description?, logoMetadata? }")
|
|
393
|
+
.option("--skip-existing", "Skip create when a target business unit with the same name already exists")).action(async (opts) => {
|
|
394
|
+
const ctx = getCtx();
|
|
395
|
+
const { client, portalId } = resolveBusinessUnitSession(ctx, opts, true);
|
|
396
|
+
const payload = normalizeBusinessUnitCreatePayload(parseJsonPayload(opts.data));
|
|
397
|
+
if (!ctx.dryRun && opts.skipExisting) {
|
|
398
|
+
const existing = await findExistingBusinessUnitByName(client, String(payload.name), portalId);
|
|
399
|
+
if (existing) {
|
|
400
|
+
printResult(ctx, { skipped: true, reason: "business-unit-exists", matchKey: "name", existing });
|
|
401
|
+
return;
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
const res = await maybeWrite(ctx, client, "POST", businessUnitsInternalPath(portalId), payload);
|
|
405
|
+
printResult(ctx, res);
|
|
406
|
+
});
|
|
260
407
|
// Teams
|
|
261
408
|
const teams = settings.command("teams").description("Team management");
|
|
262
409
|
teams
|
|
@@ -326,6 +473,207 @@ export function registerSettings(program, getCtx) {
|
|
|
326
473
|
const res = await maybeWrite(ctx, client, "DELETE", permissionSetPath(portalId, id));
|
|
327
474
|
printResult(ctx, res);
|
|
328
475
|
});
|
|
476
|
+
addBrowserSessionOptions(permissionSets.command("clone")
|
|
477
|
+
.argument("<id>")
|
|
478
|
+
.description("Clone a permission set through the internal browser-session endpoint")
|
|
479
|
+
.option("--data <payload>", "Optional clone payload JSON, e.g. { name }")).action(async (id, opts) => {
|
|
480
|
+
const ctx = getCtx();
|
|
481
|
+
const { client, portalId } = resolvePermissionSetSession(ctx, opts);
|
|
482
|
+
const payload = opts.data ? parseJsonPayload(opts.data) : {};
|
|
483
|
+
const res = await maybeWrite(ctx, client, "POST", permissionSetClonePath(portalId, id), payload);
|
|
484
|
+
printResult(ctx, res);
|
|
485
|
+
});
|
|
486
|
+
const permissionSetRoles = permissionSets.command("roles").description("Internal atomic role catalog used by permission sets");
|
|
487
|
+
addBrowserSessionOptions(permissionSetRoles.command("list"))
|
|
488
|
+
.action(async (opts) => {
|
|
489
|
+
const ctx = getCtx();
|
|
490
|
+
const { client, portalId } = resolvePermissionSetSession(ctx, opts);
|
|
491
|
+
const res = await client.request(roleCatalogPath(portalId));
|
|
492
|
+
printResult(ctx, res);
|
|
493
|
+
});
|
|
494
|
+
addBrowserSessionOptions(permissionSetRoles.command("get").argument("<roleName>"))
|
|
495
|
+
.action(async (roleName, opts) => {
|
|
496
|
+
const ctx = getCtx();
|
|
497
|
+
const { client, portalId } = resolvePermissionSetSession(ctx, opts);
|
|
498
|
+
const res = await client.request(roleCatalogItemPath(portalId, roleName));
|
|
499
|
+
printResult(ctx, res);
|
|
500
|
+
});
|
|
501
|
+
const permissionSetAssignments = permissionSets.command("assignments").description("Internal permission-set assignment map");
|
|
502
|
+
addBrowserSessionOptions(permissionSetAssignments.command("list"))
|
|
503
|
+
.action(async (opts) => {
|
|
504
|
+
const ctx = getCtx();
|
|
505
|
+
const { client, portalId } = resolvePermissionSetSession(ctx, opts);
|
|
506
|
+
const res = await client.request(permissionSetAssignmentsPath(portalId));
|
|
507
|
+
printResult(ctx, res);
|
|
508
|
+
});
|
|
509
|
+
const permissionSetAudit = permissionSets.command("audit").description("Read-only internal permission audit RPCs");
|
|
510
|
+
for (const name of Object.keys(PERMISSION_AUDIT_RPC)) {
|
|
511
|
+
addBrowserSessionOptions(permissionSetAudit.command(name)
|
|
512
|
+
.requiredOption("--data <payload>", "Audit RPC payload JSON")).action(async (opts) => {
|
|
513
|
+
const ctx = getCtx();
|
|
514
|
+
const { client } = resolvePermissionSetSession(ctx, opts);
|
|
515
|
+
const payload = parseJsonPayload(opts.data);
|
|
516
|
+
const res = await client.request(permissionAuditRpcPath(name), {
|
|
517
|
+
method: "POST",
|
|
518
|
+
body: payload,
|
|
519
|
+
permissionMode: "read",
|
|
520
|
+
});
|
|
521
|
+
printResult(ctx, res);
|
|
522
|
+
});
|
|
523
|
+
}
|
|
524
|
+
addBrowserSessionOptions(permissionSets.command("role-categories").description("List internal permission role categories"))
|
|
525
|
+
.action(async (opts) => {
|
|
526
|
+
const ctx = getCtx();
|
|
527
|
+
const { client, portalId } = resolvePermissionSetSession(ctx, opts);
|
|
528
|
+
const res = await client.request(roleCategoriesPath(portalId));
|
|
529
|
+
printResult(ctx, res);
|
|
530
|
+
});
|
|
531
|
+
addBrowserSessionOptions(permissionSets.command("scopes").description("List internal permission role scope buckets"))
|
|
532
|
+
.action(async (opts) => {
|
|
533
|
+
const ctx = getCtx();
|
|
534
|
+
const { client, portalId } = resolvePermissionSetSession(ctx, opts);
|
|
535
|
+
const res = await client.request(roleScopesPath(portalId));
|
|
536
|
+
printResult(ctx, res);
|
|
537
|
+
});
|
|
538
|
+
const permissionSetUsers = permissionSets.command("users").description("Internal app-users reads and permission-set assignments");
|
|
539
|
+
addBrowserSessionOptions(permissionSetUsers.command("list")
|
|
540
|
+
.option("--limit <n>", "Max records", "200")
|
|
541
|
+
.option("--offset <n>", "Offset cursor")).action(async (opts) => {
|
|
542
|
+
const ctx = getCtx();
|
|
543
|
+
const { client, portalId } = resolvePermissionSetSession(ctx, opts);
|
|
544
|
+
const res = await client.request(appUsersPath(portalId, opts.limit, opts.offset));
|
|
545
|
+
printResult(ctx, res);
|
|
546
|
+
});
|
|
547
|
+
addBrowserSessionOptions(permissionSetUsers.command("get").argument("<userId>"))
|
|
548
|
+
.action(async (userId, opts) => {
|
|
549
|
+
const ctx = getCtx();
|
|
550
|
+
const { client, portalId } = resolvePermissionSetSession(ctx, opts);
|
|
551
|
+
const res = await client.request(appUserPath(portalId, userId));
|
|
552
|
+
printResult(ctx, res);
|
|
553
|
+
});
|
|
554
|
+
addBrowserSessionOptions(permissionSetUsers.command("batch-read")
|
|
555
|
+
.requiredOption("--data <payload>", "Batch-read payload JSON")).action(async (opts) => {
|
|
556
|
+
const ctx = getCtx();
|
|
557
|
+
const { client, portalId } = resolvePermissionSetSession(ctx, opts);
|
|
558
|
+
const payload = parseJsonPayload(opts.data);
|
|
559
|
+
const res = await maybeWrite(ctx, client, "POST", appUsersBatchPath(portalId), payload);
|
|
560
|
+
printResult(ctx, res);
|
|
561
|
+
});
|
|
562
|
+
addBrowserSessionOptions(permissionSetUsers.command("assign")
|
|
563
|
+
.argument("<userId>")
|
|
564
|
+
.option("--permission-set-id <id>", "Assign one permission set id")
|
|
565
|
+
.option("--permission-set-ids <ids>", "Assign multiple permission set ids, comma-separated")).action(async (userId, opts) => {
|
|
566
|
+
const ctx = getCtx();
|
|
567
|
+
const { client, portalId } = resolvePermissionSetSession(ctx, opts);
|
|
568
|
+
const payload = buildPermissionSetAssignmentPayload(userId, opts);
|
|
569
|
+
const res = await maybeWrite(ctx, client, "POST", permissionSetAssignUserRpcPath(portalId), payload);
|
|
570
|
+
printResult(ctx, res);
|
|
571
|
+
});
|
|
572
|
+
addBrowserSessionOptions(permissionSetUsers.command("assign-batch")
|
|
573
|
+
.description("Assign permission sets to one or more users through the allowlisted Chirp RPC")
|
|
574
|
+
.requiredOption("--data <payload>", "Assignment payload JSON: { assignments: [{ userId, permissionSetIds }] }")).action(async (opts) => {
|
|
575
|
+
const ctx = getCtx();
|
|
576
|
+
const { client, portalId } = resolvePermissionSetSession(ctx, opts);
|
|
577
|
+
const payload = normalizePermissionSetAssignmentsPayload(parseJsonPayload(opts.data));
|
|
578
|
+
const res = await maybeWrite(ctx, client, "POST", permissionSetAssignUserRpcPath(portalId), payload);
|
|
579
|
+
printResult(ctx, res);
|
|
580
|
+
});
|
|
581
|
+
const permissionSetSeatAssignments = permissionSets.command("seat-assignments").description("Internal seat assignment inventory and updates");
|
|
582
|
+
addBrowserSessionOptions(permissionSetSeatAssignments.command("list"))
|
|
583
|
+
.action(async (opts) => {
|
|
584
|
+
const ctx = getCtx();
|
|
585
|
+
const { client, portalId } = resolvePermissionSetSession(ctx, opts);
|
|
586
|
+
const res = await client.request(seatAssignmentsPath(portalId));
|
|
587
|
+
printResult(ctx, res);
|
|
588
|
+
});
|
|
589
|
+
addBrowserSessionOptions(permissionSetSeatAssignments.command("update")
|
|
590
|
+
.argument("<userId>")
|
|
591
|
+
.requiredOption("--data <payload>", "Seat assignment update payload JSON")).action(async (userId, opts) => {
|
|
592
|
+
const ctx = getCtx();
|
|
593
|
+
const { client, portalId } = resolvePermissionSetSession(ctx, opts);
|
|
594
|
+
const payload = parseJsonPayload(opts.data);
|
|
595
|
+
const res = await maybeWrite(ctx, client, "PATCH", seatAssignmentPath(portalId, userId), payload);
|
|
596
|
+
printResult(ctx, res);
|
|
597
|
+
});
|
|
598
|
+
addBrowserSessionOptions(permissionSets.command("seats").description("List internal seat catalog"))
|
|
599
|
+
.action(async (opts) => {
|
|
600
|
+
const ctx = getCtx();
|
|
601
|
+
const { client, portalId } = resolvePermissionSetSession(ctx, opts);
|
|
602
|
+
const res = await client.request(seatsPath(portalId));
|
|
603
|
+
printResult(ctx, res);
|
|
604
|
+
});
|
|
605
|
+
const permissionSetTemplates = permissionSets.command("templates").description("Internal permission template catalog");
|
|
606
|
+
addBrowserSessionOptions(permissionSetTemplates.command("list"))
|
|
607
|
+
.action(async (opts) => {
|
|
608
|
+
const ctx = getCtx();
|
|
609
|
+
const { client, portalId } = resolvePermissionSetSession(ctx, opts);
|
|
610
|
+
const res = await client.request(permissionTemplatesPath(portalId));
|
|
611
|
+
printResult(ctx, res);
|
|
612
|
+
});
|
|
613
|
+
addBrowserSessionOptions(permissionSetTemplates.command("get").argument("<templateId>"))
|
|
614
|
+
.action(async (templateId, opts) => {
|
|
615
|
+
const ctx = getCtx();
|
|
616
|
+
const { client, portalId } = resolvePermissionSetSession(ctx, opts);
|
|
617
|
+
const res = await client.request(permissionTemplatePath(portalId, templateId));
|
|
618
|
+
printResult(ctx, res);
|
|
619
|
+
});
|
|
620
|
+
addBrowserSessionOptions(permissionSetTemplates.command("create")
|
|
621
|
+
.requiredOption("--data <payload>", "Permission template payload JSON")).action(async (opts) => {
|
|
622
|
+
const ctx = getCtx();
|
|
623
|
+
const { client, portalId } = resolvePermissionSetSession(ctx, opts);
|
|
624
|
+
const payload = parseJsonPayload(opts.data);
|
|
625
|
+
const res = await maybeWrite(ctx, client, "POST", permissionTemplatesPath(portalId), payload);
|
|
626
|
+
printResult(ctx, res);
|
|
627
|
+
});
|
|
628
|
+
const flp = settings.command("flp").description("Field-level permission inventory via internal HubSpot session-auth endpoint");
|
|
629
|
+
addBrowserSessionOptions(flp.command("list").description("List all field-level permission allowlistings"))
|
|
630
|
+
.action(async (opts) => {
|
|
631
|
+
const ctx = getCtx();
|
|
632
|
+
const { client } = resolvePermissionSetSession(ctx, opts);
|
|
633
|
+
const res = await client.request(flpAllowlistingsPath());
|
|
634
|
+
printResult(ctx, res);
|
|
635
|
+
});
|
|
636
|
+
addBrowserSessionOptions(flp.command("object").argument("<typeId>").description("List field-level permission allowlistings for one object type"))
|
|
637
|
+
.action(async (typeId, opts) => {
|
|
638
|
+
const ctx = getCtx();
|
|
639
|
+
const { client } = resolvePermissionSetSession(ctx, opts);
|
|
640
|
+
const res = await client.request(flpObjectAllowlistingsPath(typeId));
|
|
641
|
+
printResult(ctx, res);
|
|
642
|
+
});
|
|
643
|
+
addBrowserSessionOptions(flp.command("property").argument("<typeId>").argument("<propertyName>").description("Get field-level permission allowlisting for one property"))
|
|
644
|
+
.action(async (typeId, propertyName, opts) => {
|
|
645
|
+
const ctx = getCtx();
|
|
646
|
+
const { client } = resolvePermissionSetSession(ctx, opts);
|
|
647
|
+
const res = await client.request(flpPropertyAllowlistingsPath(typeId, propertyName));
|
|
648
|
+
printResult(ctx, res);
|
|
649
|
+
});
|
|
650
|
+
addBrowserSessionOptions(flp.command("access-state")
|
|
651
|
+
.description("Bulk-read field-level permission access state")
|
|
652
|
+
.requiredOption("--data <payload>", "Access-state payload JSON")).action(async (opts) => {
|
|
653
|
+
const ctx = getCtx();
|
|
654
|
+
const { client } = resolvePermissionSetSession(ctx, opts);
|
|
655
|
+
const payload = parseJsonPayload(opts.data);
|
|
656
|
+
const res = await client.request(flpAccessStatePath(), {
|
|
657
|
+
method: "POST",
|
|
658
|
+
body: payload,
|
|
659
|
+
permissionMode: "read",
|
|
660
|
+
});
|
|
661
|
+
printResult(ctx, res);
|
|
662
|
+
});
|
|
663
|
+
addBrowserSessionOptions(flp.command("blocklist").description("List field-level permission blocklist including global entries"))
|
|
664
|
+
.action(async (opts) => {
|
|
665
|
+
const ctx = getCtx();
|
|
666
|
+
const { client } = resolvePermissionSetSession(ctx, opts);
|
|
667
|
+
const res = await client.request(flpBlocklistPath());
|
|
668
|
+
printResult(ctx, res);
|
|
669
|
+
});
|
|
670
|
+
addBrowserSessionOptions(flp.command("bypass-users").description("List users who bypass field-level permissions"))
|
|
671
|
+
.action(async (opts) => {
|
|
672
|
+
const ctx = getCtx();
|
|
673
|
+
const { client } = resolvePermissionSetSession(ctx, opts);
|
|
674
|
+
const res = await client.request(flpBypassUsersPath());
|
|
675
|
+
printResult(ctx, res);
|
|
676
|
+
});
|
|
329
677
|
// Currencies
|
|
330
678
|
const currencies = settings.command("currencies").description("Multi-currency management");
|
|
331
679
|
currencies
|