@cookiemunch/cli 0.5.0 → 0.6.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 +13 -0
- package/dist/bin.js +2 -2
- package/dist/bin.js.map +1 -1
- package/dist/chunk-LJ433UVC.js +1785 -0
- package/dist/chunk-LJ433UVC.js.map +1 -0
- package/dist/index.d.ts +73 -5
- package/dist/index.js +15 -1
- package/package.json +2 -2
- package/dist/chunk-2ADK3C47.js +0 -921
- package/dist/chunk-2ADK3C47.js.map +0 -1
package/dist/chunk-2ADK3C47.js
DELETED
|
@@ -1,921 +0,0 @@
|
|
|
1
|
-
// src/diff.ts
|
|
2
|
-
var READ_ONLY_CONFIG_FIELDS = ["effective", "warnings", "designWarnings", "region", "regulation", "copy", "rightsFormUrl", "version", "observeToken"];
|
|
3
|
-
function withoutReadOnly(cfg) {
|
|
4
|
-
const out2 = { ...cfg };
|
|
5
|
-
for (const k of READ_ONLY_CONFIG_FIELDS) delete out2[k];
|
|
6
|
-
return out2;
|
|
7
|
-
}
|
|
8
|
-
function isPlainObject(v) {
|
|
9
|
-
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
10
|
-
}
|
|
11
|
-
function mergeLikeServer(base, patch) {
|
|
12
|
-
const out2 = { ...base };
|
|
13
|
-
for (const [k, v] of Object.entries(patch)) {
|
|
14
|
-
const b = base[k];
|
|
15
|
-
if (v === null) delete out2[k];
|
|
16
|
-
else out2[k] = isPlainObject(v) ? mergeLikeServer(isPlainObject(b) ? b : {}, v) : v;
|
|
17
|
-
}
|
|
18
|
-
return out2;
|
|
19
|
-
}
|
|
20
|
-
function join(path, key) {
|
|
21
|
-
return path ? `${path}.${key}` : key;
|
|
22
|
-
}
|
|
23
|
-
function diffJson(before, after, path = "") {
|
|
24
|
-
if (isPlainObject(before) && isPlainObject(after)) {
|
|
25
|
-
const keys = [.../* @__PURE__ */ new Set([...Object.keys(before), ...Object.keys(after)])].sort();
|
|
26
|
-
return keys.flatMap((k) => {
|
|
27
|
-
const p = join(path, k);
|
|
28
|
-
if (!(k in after)) return [{ path: p, kind: "removed", from: before[k] }];
|
|
29
|
-
if (!(k in before)) return [{ path: p, kind: "added", to: after[k] }];
|
|
30
|
-
return diffJson(before[k], after[k], p);
|
|
31
|
-
});
|
|
32
|
-
}
|
|
33
|
-
if (Array.isArray(before) && Array.isArray(after)) {
|
|
34
|
-
const out2 = [];
|
|
35
|
-
const n = Math.max(before.length, after.length);
|
|
36
|
-
for (let i = 0; i < n; i++) {
|
|
37
|
-
const p = `${path}[${i}]`;
|
|
38
|
-
if (i >= after.length) out2.push({ path: p, kind: "removed", from: before[i] });
|
|
39
|
-
else if (i >= before.length) out2.push({ path: p, kind: "added", to: after[i] });
|
|
40
|
-
else out2.push(...diffJson(before[i], after[i], p));
|
|
41
|
-
}
|
|
42
|
-
return out2;
|
|
43
|
-
}
|
|
44
|
-
if (JSON.stringify(before) === JSON.stringify(after)) return [];
|
|
45
|
-
return [{ path: path || "(root)", kind: "changed", from: before, to: after }];
|
|
46
|
-
}
|
|
47
|
-
function show(v) {
|
|
48
|
-
const s = JSON.stringify(v);
|
|
49
|
-
return s === void 0 ? "undefined" : s.length > 120 ? `${s.slice(0, 117)}...` : s;
|
|
50
|
-
}
|
|
51
|
-
function formatChanges(changes) {
|
|
52
|
-
return changes.map(
|
|
53
|
-
(c) => c.kind === "added" ? `+ ${c.path}: ${show(c.to)}` : c.kind === "removed" ? `- ${c.path}: ${show(c.from)}` : `~ ${c.path}: ${show(c.from)} -> ${show(c.to)}`
|
|
54
|
-
).join("\n");
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
// src/rights-page.ts
|
|
58
|
-
import { CookieMunchApiError } from "@cookiemunch/sdk";
|
|
59
|
-
var isConflict = (e) => e instanceof CookieMunchApiError && e.status === 409;
|
|
60
|
-
function baseFrom(flag, read, h) {
|
|
61
|
-
if (flag === void 0) return read ?? null;
|
|
62
|
-
if (typeof flag === "string" && /^\d+$/.test(flag)) return Number(flag);
|
|
63
|
-
if (typeof flag === "string" && /^\d{4}-\d{2}-\d{2}T/.test(flag) && Number.isFinite(Date.parse(flag))) return flag;
|
|
64
|
-
throw h.usage("--base must be the draft\u2019s updatedAt (epoch ms, as pull prints it, or an ISO date)");
|
|
65
|
-
}
|
|
66
|
-
function issuesOf(e) {
|
|
67
|
-
if (!(e instanceof CookieMunchApiError) || e.status !== 422) return null;
|
|
68
|
-
const body = e.body;
|
|
69
|
-
return body?.issues ?? body?.problems ?? [];
|
|
70
|
-
}
|
|
71
|
-
async function rightsPageCommand(sub, p, io, client, h) {
|
|
72
|
-
const cbid = h.requireSite();
|
|
73
|
-
const report = (list) => io.stderr(`${list.map((i) => ` ${i.path || "(document)"}: ${i.message}`).join("\n")}
|
|
74
|
-
`);
|
|
75
|
-
if (sub === "pull") {
|
|
76
|
-
const { draft, updatedAt } = await client.rightsPage.get(cbid);
|
|
77
|
-
const text = `${JSON.stringify(draft, null, 2)}
|
|
78
|
-
`;
|
|
79
|
-
const out2 = typeof p.flags.out === "string" ? p.flags.out : void 0;
|
|
80
|
-
if (out2) {
|
|
81
|
-
await io.writeFile(out2, text);
|
|
82
|
-
io.stderr(`Wrote ${cbid}'s rights page draft to ${out2}
|
|
83
|
-
`);
|
|
84
|
-
} else io.stdout(text);
|
|
85
|
-
if (updatedAt != null) io.stderr(`To refuse a push if anyone saves the draft after this pull, push with: --base ${updatedAt}
|
|
86
|
-
`);
|
|
87
|
-
return 0;
|
|
88
|
-
}
|
|
89
|
-
if (sub === "diff" || sub === "push") {
|
|
90
|
-
const file = await h.readJson(p.positional[2]);
|
|
91
|
-
const read = await client.rightsPage.get(cbid);
|
|
92
|
-
const current = read.draft;
|
|
93
|
-
const base = baseFrom(p.flags.base, read.updatedAt, h);
|
|
94
|
-
let proposed2;
|
|
95
|
-
try {
|
|
96
|
-
proposed2 = (await client.rightsPage.dryRun(cbid, file, { mode: "put" })).draft;
|
|
97
|
-
} catch (e) {
|
|
98
|
-
const list = issuesOf(e);
|
|
99
|
-
if (!list) throw e;
|
|
100
|
-
io.stderr(`${p.positional[2]} is not a valid rights page:
|
|
101
|
-
`);
|
|
102
|
-
report(list);
|
|
103
|
-
return 1;
|
|
104
|
-
}
|
|
105
|
-
const changes = diffJson(current, proposed2);
|
|
106
|
-
if (!changes.length) {
|
|
107
|
-
io.stderr(`No changes: ${cbid}'s rights page draft already matches.
|
|
108
|
-
`);
|
|
109
|
-
return 0;
|
|
110
|
-
}
|
|
111
|
-
io.stdout(`${formatChanges(changes)}
|
|
112
|
-
`);
|
|
113
|
-
if (sub === "diff") {
|
|
114
|
-
io.stderr(`${changes.length} change${changes.length === 1 ? "" : "s"} (nothing saved). Apply with: cookiemunch rights-page push --site ${cbid} ${p.positional[2]}
|
|
115
|
-
`);
|
|
116
|
-
return p.flags["exit-code"] === true ? 1 : 0;
|
|
117
|
-
}
|
|
118
|
-
let saved;
|
|
119
|
-
try {
|
|
120
|
-
saved = await client.rightsPage.put(cbid, file, { baseUpdatedAt: base });
|
|
121
|
-
} catch (e) {
|
|
122
|
-
if (!isConflict(e)) throw e;
|
|
123
|
-
io.stderr(`Not saved: ${cbid}'s rights page draft changed since ${p.flags.base !== void 0 ? "the --base you gave" : "this diff"} (someone else saved it).
|
|
124
|
-
See what changed with: cookiemunch rights-page diff --site ${cbid} ${p.positional[2]}
|
|
125
|
-
then push again.
|
|
126
|
-
`);
|
|
127
|
-
return 1;
|
|
128
|
-
}
|
|
129
|
-
io.stderr(`Saved the draft (${changes.length} change${changes.length === 1 ? "" : "s"}). Nothing is live until: cookiemunch rights-page publish --site ${cbid}
|
|
130
|
-
`);
|
|
131
|
-
if (saved.warnings.length) {
|
|
132
|
-
io.stderr("It cannot be published yet:\n");
|
|
133
|
-
report(saved.warnings);
|
|
134
|
-
}
|
|
135
|
-
return 0;
|
|
136
|
-
}
|
|
137
|
-
if (sub === "publish") {
|
|
138
|
-
const base = p.flags.base !== void 0 ? baseFrom(p.flags.base, null, h) : (await client.rightsPage.get(cbid)).updatedAt ?? null;
|
|
139
|
-
if (p.flags.yes !== true) {
|
|
140
|
-
if (!io.confirm) throw h.usage("rights-page publish puts the draft live and there is no terminal to confirm in. Pass --yes to publish without a prompt.");
|
|
141
|
-
if (!await io.confirm(`Publish ${cbid}'s rights page draft? It goes live at once. [y/N] `)) {
|
|
142
|
-
io.stderr("Not published.\n");
|
|
143
|
-
return 1;
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
try {
|
|
147
|
-
const r = await client.rightsPage.publish(cbid, { baseUpdatedAt: base });
|
|
148
|
-
io.stderr(`Published version ${r.version} of ${cbid}'s rights page.
|
|
149
|
-
`);
|
|
150
|
-
return 0;
|
|
151
|
-
} catch (e) {
|
|
152
|
-
if (isConflict(e)) {
|
|
153
|
-
io.stderr(`Nothing was published: ${cbid}'s rights page draft changed since it was read (someone else saved it).
|
|
154
|
-
Review it with: cookiemunch rights-page pull --site ${cbid}
|
|
155
|
-
then publish again.
|
|
156
|
-
`);
|
|
157
|
-
return 1;
|
|
158
|
-
}
|
|
159
|
-
const list = issuesOf(e);
|
|
160
|
-
if (!list) throw e;
|
|
161
|
-
io.stderr("The rights page cannot be published:\n");
|
|
162
|
-
report(list);
|
|
163
|
-
return 1;
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
throw h.usage(`unknown rights-page command: ${sub ?? "(none)"} \u2014 use pull, diff, push or publish`);
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
// src/cli.ts
|
|
170
|
-
import { createCookieMunch, CookieMunchApiError as CookieMunchApiError2, mintConsentSyncToken } from "@cookiemunch/sdk";
|
|
171
|
-
|
|
172
|
-
// src/integrations.ts
|
|
173
|
-
var out = (io, v) => io.stdout(`${JSON.stringify(v, null, 2)}
|
|
174
|
-
`);
|
|
175
|
-
async function siteCommand(sub, io, client, h) {
|
|
176
|
-
if (sub !== "update") throw h.usage("usage: cookiemunch site update --site <cbid> [--domain <host>] [--name <name>] [--aliases a,b] [--dry-run]");
|
|
177
|
-
const cbid = h.requireSite();
|
|
178
|
-
const domain = h.flag("domain");
|
|
179
|
-
const name = h.flag("name");
|
|
180
|
-
const aliases = h.flag("aliases");
|
|
181
|
-
const patch = {};
|
|
182
|
-
if (typeof domain === "string") patch.domain = domain;
|
|
183
|
-
if (h.flag("clear-name") === true) patch.name = null;
|
|
184
|
-
else if (typeof name === "string") patch.name = name;
|
|
185
|
-
if (typeof aliases === "string") patch.aliases = aliases.split(",").map((a) => a.trim()).filter(Boolean);
|
|
186
|
-
if (Object.keys(patch).length === 0) throw h.usage("nothing to change: give --domain, --name, --clear-name or --aliases");
|
|
187
|
-
const r = await client.sites.update(cbid, patch, { dryRun: h.flag("dry-run") === true });
|
|
188
|
-
out(io, r);
|
|
189
|
-
if (r.effects.verification === "reset") io.stderr("The domain moved: verify the new domain (GET /v1/sites/{cbid}/verify/challenge).\n");
|
|
190
|
-
for (const w of r.warnings ?? []) io.stderr(`warning (${w.field}): ${w.reason}
|
|
191
|
-
`);
|
|
192
|
-
return 0;
|
|
193
|
-
}
|
|
194
|
-
async function healthCommand(io, client, h) {
|
|
195
|
-
const r = await client.integrationHealth(h.site());
|
|
196
|
-
if (h.flag("json") === true) out(io, r);
|
|
197
|
-
else {
|
|
198
|
-
for (const c of r.checks) {
|
|
199
|
-
const mark = c.ok === true ? "ok " : c.ok === false ? c.severity === "error" ? "FAIL" : "WARN" : "-- ";
|
|
200
|
-
io.stdout(`${mark} ${c.check} \u2014 ${c.subject}${c.detail ? `: ${c.detail}` : ""}
|
|
201
|
-
`);
|
|
202
|
-
if (c.ok === false && c.fixHint) io.stdout(` fix: ${c.fixHint}
|
|
203
|
-
`);
|
|
204
|
-
}
|
|
205
|
-
io.stdout(`${r.counts.error} error(s), ${r.counts.warning} warning(s), ${r.counts.notChecked} not checked
|
|
206
|
-
`);
|
|
207
|
-
}
|
|
208
|
-
return r.ok ? 0 : 1;
|
|
209
|
-
}
|
|
210
|
-
async function provisionCommand(file, io, client, h) {
|
|
211
|
-
const input = await h.readJson(file);
|
|
212
|
-
const withSecrets = { ...input };
|
|
213
|
-
if (input.atlas || io.env.COOKIEMUNCH_ATLAS_SECRET_KEY) {
|
|
214
|
-
const secretKey = io.env.COOKIEMUNCH_ATLAS_SECRET_KEY ?? input.atlas?.secretKey;
|
|
215
|
-
if (!secretKey) throw h.usage("set COOKIEMUNCH_ATLAS_SECRET_KEY for the atlas step");
|
|
216
|
-
withSecrets.atlas = { ...input.atlas ?? {}, secretKey };
|
|
217
|
-
}
|
|
218
|
-
if (input.forumulate) {
|
|
219
|
-
const token = io.env.COOKIEMUNCH_FORUMULATE_TOKEN ?? input.forumulate.token;
|
|
220
|
-
if (!token) throw h.usage("set COOKIEMUNCH_FORUMULATE_TOKEN for the forumulate step");
|
|
221
|
-
withSecrets.forumulate = { ...input.forumulate, token };
|
|
222
|
-
}
|
|
223
|
-
const r = await client.provision(withSecrets, { dryRun: h.flag("dry-run") === true });
|
|
224
|
-
for (const s of r.steps) {
|
|
225
|
-
io.stdout(`${s.status.padEnd(12)} ${s.step}${s.detail ? ` \u2014 ${s.detail}` : ""}${s.waitingOn ? ` (waiting on ${s.waitingOn})` : ""}
|
|
226
|
-
`);
|
|
227
|
-
}
|
|
228
|
-
io.stdout(`${r.dryRun ? "dry run: " : ""}${r.summary.changed} changed, ${r.summary.unchanged} unchanged, ${r.summary.waiting} waiting, ${r.summary.failed} failed
|
|
229
|
-
`);
|
|
230
|
-
return r.summary.failed ? 1 : 0;
|
|
231
|
-
}
|
|
232
|
-
async function partnersCommand(sub, rest, io, client, h) {
|
|
233
|
-
const cbid = h.requireSite();
|
|
234
|
-
if (sub === "list") {
|
|
235
|
-
out(io, await client.sites.listPartners(cbid));
|
|
236
|
-
return 0;
|
|
237
|
-
}
|
|
238
|
-
if (sub === "link") {
|
|
239
|
-
const partner = h.flag("partner");
|
|
240
|
-
const externalId = h.flag("external-id");
|
|
241
|
-
if (partner !== "forumulate" && partner !== "atlas") throw h.usage("--partner forumulate|atlas is required");
|
|
242
|
-
if (typeof externalId !== "string") throw h.usage("--external-id <the partner\u2019s account id> is required");
|
|
243
|
-
const label = h.flag("label");
|
|
244
|
-
const input = { partner, externalId, ...typeof label === "string" ? { label } : {} };
|
|
245
|
-
out(io, await client.sites.linkPartner(cbid, input, { dryRun: h.flag("dry-run") === true }));
|
|
246
|
-
return 0;
|
|
247
|
-
}
|
|
248
|
-
if (sub === "unlink") {
|
|
249
|
-
const id = rest[0];
|
|
250
|
-
if (!id) throw h.usage("usage: cookiemunch partners unlink <linkId> --site <cbid>");
|
|
251
|
-
out(io, await client.sites.unlinkPartner(cbid, id));
|
|
252
|
-
return 0;
|
|
253
|
-
}
|
|
254
|
-
throw h.usage("usage: cookiemunch partners list|link|unlink --site <cbid>");
|
|
255
|
-
}
|
|
256
|
-
async function keysCommand(sub, io, client, h) {
|
|
257
|
-
if (sub !== "report") throw h.usage("usage: cookiemunch keys report [--unused-days <n>] [--expiring-days <n>] [--json]");
|
|
258
|
-
const n = (name) => {
|
|
259
|
-
const v = h.flag(name);
|
|
260
|
-
if (v === void 0) return void 0;
|
|
261
|
-
const x = Number(v);
|
|
262
|
-
if (!Number.isInteger(x) || x < 1 || x > 3650) throw h.usage(`--${name} must be whole days from 1 to 3650`);
|
|
263
|
-
return x;
|
|
264
|
-
};
|
|
265
|
-
const unusedDays = n("unused-days");
|
|
266
|
-
const expiringDays = n("expiring-days");
|
|
267
|
-
const r = await client.keys.report({ ...unusedDays ? { unusedDays } : {}, ...expiringDays ? { expiringDays } : {} });
|
|
268
|
-
if (h.flag("json") === true) {
|
|
269
|
-
out(io, r);
|
|
270
|
-
return 0;
|
|
271
|
-
}
|
|
272
|
-
const section = (title, rows) => {
|
|
273
|
-
io.stdout(`${title}: ${rows.length}
|
|
274
|
-
`);
|
|
275
|
-
for (const k of rows) io.stdout(` ${k.prefix}${k.name ? ` (${k.name})` : ""} \u2014 ${k.reason}
|
|
276
|
-
`);
|
|
277
|
-
};
|
|
278
|
-
section(`Unused for ${r.unusedDays}+ days`, r.unused);
|
|
279
|
-
section("Broad access", r.broad);
|
|
280
|
-
section(`Expiring within ${r.expiringDays} days`, r.expiringSoon);
|
|
281
|
-
io.stdout(`${r.total} key(s), ${r.noExpiry} with no expiry
|
|
282
|
-
`);
|
|
283
|
-
return 0;
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
// src/design-warnings.ts
|
|
287
|
-
function where(f) {
|
|
288
|
-
const w = f.where ?? {};
|
|
289
|
-
return [
|
|
290
|
-
f.layer === "banner" ? "first layer" : `${f.layer} layer`,
|
|
291
|
-
w.view ? `view "${w.view}"` : "",
|
|
292
|
-
w.variant && w.variant !== "base" ? `variant ${w.variant}` : "",
|
|
293
|
-
w.theme === "dark" ? "dark theme" : "",
|
|
294
|
-
w.breakpoint === "mobile" ? "mobile" : "",
|
|
295
|
-
w.language ? `language ${w.language}` : ""
|
|
296
|
-
].filter(Boolean).join(", ");
|
|
297
|
-
}
|
|
298
|
-
function formatDesignWarnings(findings) {
|
|
299
|
-
return findings.map((f) => `${f.severity === "warning" ? "warning" : "notice "} ${f.code} ${where(f)}
|
|
300
|
-
${f.detail}
|
|
301
|
-
fix: ${f.fix}`).join("\n");
|
|
302
|
-
}
|
|
303
|
-
function countBySeverity(findings) {
|
|
304
|
-
const warnings = findings.filter((f) => f.severity === "warning").length;
|
|
305
|
-
return { warnings, notices: findings.length - warnings };
|
|
306
|
-
}
|
|
307
|
-
function summaryLine(findings) {
|
|
308
|
-
const { warnings, notices } = countBySeverity(findings);
|
|
309
|
-
if (warnings + notices === 0) return "No design warnings: Accept all and Reject all have equal weight.";
|
|
310
|
-
if (warnings === 0) {
|
|
311
|
-
return `Design: 0 warnings, ${notices} notice${notices === 1 ? "" : "s"} \u2014 worth a look, not a finding that Accept is favoured. See https://cookiemunch.net/docs/fair-consent-buttons`;
|
|
312
|
-
}
|
|
313
|
-
return `Design: ${warnings} warning${warnings === 1 ? "" : "s"}, ${notices} notice${notices === 1 ? "" : "s"} \u2014 Accept is pushed harder than Reject. Regulators treat that as a dark pattern; make both buttons the same style. See https://cookiemunch.net/docs/fair-consent-buttons`;
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
// src/cli.ts
|
|
317
|
-
var DEFAULT_BASE_URL = "https://api.cookiemunch.net";
|
|
318
|
-
var USAGE = `cookiemunch \u2014 Cookie Munch from the command line
|
|
319
|
-
|
|
320
|
-
Usage:
|
|
321
|
-
cookiemunch config pull --site <cbid> [--out <file>]
|
|
322
|
-
Write the site's config as JSON (to stdout, or to <file>).
|
|
323
|
-
cookiemunch config diff --site <cbid> <file> [--replace] [--exit-code]
|
|
324
|
-
Show what pushing <file> would change (the server's dry run; nothing is stored).
|
|
325
|
-
Read-only fields a pulled file carries (effective, designWarnings, ...) are ignored.
|
|
326
|
-
cookiemunch config push --site <cbid> <file> [--replace] [--strict]
|
|
327
|
-
Apply <file>. Default: merged over the stored config (PATCH) \u2014 fields the file
|
|
328
|
-
leaves out keep their value, and a field set to null is removed (it takes its
|
|
329
|
-
default). --replace sends it whole (PUT): a top-level field the file leaves out
|
|
330
|
-
reverts to its default. Prints any design warnings the server found. --strict
|
|
331
|
-
checks the file first and does not push it (exit 1) when Accept all is pushed
|
|
332
|
-
harder than Reject all anywhere \u2014 for CI.
|
|
333
|
-
cookiemunch config validate --site <cbid> <file> [--replace] [--strict]
|
|
334
|
-
Check <file> for consent-button parity on the server, without writing: is Accept
|
|
335
|
-
all more prominent than Reject all (colour, size, placement, copy, custom CSS)?
|
|
336
|
-
Merged over the stored config like push; --replace checks it as sent. --strict
|
|
337
|
-
exits 1 when there is any warning (notices never fail).
|
|
338
|
-
cookiemunch preview --site <cbid> [--url <page>] [--region <eu|de|us-ca\u2026>] [--lang <de\u2026>]
|
|
339
|
-
[--ttl <seconds>] [--open]
|
|
340
|
-
Print (and with --open, open) a link showing the banner as a visitor in that
|
|
341
|
-
region and language sees it. Works in headless browsers; nothing is recorded.
|
|
342
|
-
cookiemunch executors test <id> (--email <email> | --canary) [--operation <locate|export|erase>]
|
|
343
|
-
Try a connected system against one person without filing a request.
|
|
344
|
-
--operation defaults to locate (lookup is accepted as another name for it).
|
|
345
|
-
--canary uses the organisation's canary address instead: no real person, and
|
|
346
|
-
a canary locate also updates the connection's health. Exits 1 when not ok.
|
|
347
|
-
cookiemunch executors webhook-test <id> --headers-file <json> --body-file <file>
|
|
348
|
-
Would this webhook delivery verify? Sends the headers (a JSON object) and the
|
|
349
|
-
body file byte for byte to the receiver's check; nothing is acted on. Prints
|
|
350
|
-
{ verified, stage, reason } and exits 1 when it does not verify.
|
|
351
|
-
cookiemunch executors deliveries <id>
|
|
352
|
-
The last 50 deliveries to that connection's webhook, each with its reason.
|
|
353
|
-
cookiemunch executors presets
|
|
354
|
-
The versioned presets, which are usable now and which wait on another
|
|
355
|
-
platform, and the organisation's canary address.
|
|
356
|
-
cookiemunch executors upgrade-preset <id> [--preset <name>] [--version <n>]
|
|
357
|
-
[--org-id <id>] [--atlas-id-app-id <id>] [--dry-run]
|
|
358
|
-
Rebuild a connection from its preset's latest version (or --version), keeping
|
|
359
|
-
its id and credentials; --preset adopts one on a hand-written connection.
|
|
360
|
-
Prints the diff; --dry-run changes nothing.
|
|
361
|
-
cookiemunch rights-page pull --site <cbid> [--out <file>]
|
|
362
|
-
Write the site's rights page draft as JSON, and print its updatedAt for --base.
|
|
363
|
-
cookiemunch rights-page diff --site <cbid> <file> [--exit-code]
|
|
364
|
-
Validate <file> on the server (dry run) and show what pushing it would change.
|
|
365
|
-
cookiemunch rights-page push --site <cbid> <file> [--base <updatedAt>]
|
|
366
|
-
Replace the draft with <file>. Nothing is live until publish. Refused (exit 1)
|
|
367
|
-
if the draft changed since the diff, or since --base (the updatedAt pull printed).
|
|
368
|
-
cookiemunch rights-page publish --site <cbid> [--yes] [--base <updatedAt>]
|
|
369
|
-
Publish the draft (it goes live at once). Asks first; --yes skips the question
|
|
370
|
-
and is required when there is no terminal to ask in. Prints why when the page
|
|
371
|
-
cannot go live. Refused (exit 1) if the draft changed after it was read (or
|
|
372
|
-
since --base); --yes never overrides that.
|
|
373
|
-
|
|
374
|
-
cookiemunch scan --site <cbid> [--wait] [--timeout <seconds>]
|
|
375
|
-
Start a cookie crawl of the site and print its state (JSON). --wait holds on
|
|
376
|
-
until it is done or failed (default up to 120 s, --timeout to change) and exits
|
|
377
|
-
1 when it failed or is still running at the end. A scan already running is
|
|
378
|
-
waited on rather than started twice.
|
|
379
|
-
|
|
380
|
-
cookiemunch cookies presets --site <cbid>
|
|
381
|
-
The declaration presets (atlas, forumulate, chatly, cookiemunch, referally): what each takes,
|
|
382
|
-
what it declared on this site, and whether the vendor's own manifest exists yet.
|
|
383
|
-
cookiemunch cookies preset <vendor> --site <cbid> [--param k=v \u2026] [--dry-run]
|
|
384
|
-
Declare what that platform writes (cookies and browser storage), marked as
|
|
385
|
-
declared by it; run again to re-sync. Atlas: --param frontendApiHost=auth.example.com
|
|
386
|
-
[--param crossSubdomainCookies=false]. Chatly: --param publicId=ch_web_\u2026 .
|
|
387
|
-
Referally: [--param cookieDomain=.example.com] [--param cookieDays=60] [--param category=\u2026].
|
|
388
|
-
cookiemunch cookies unpreset <vendor> --site <cbid> [--dry-run]
|
|
389
|
-
Remove every declaration that vendor's preset made.
|
|
390
|
-
cookiemunch cookies delete-observed <name> --site <cbid> [--dry-run]
|
|
391
|
-
Remove every stored row of that name (a name the site never sets). A declaration stays.
|
|
392
|
-
cookiemunch cookies purge-observed --site <cbid> --since <time> [--until <time>] [--dry-run]
|
|
393
|
-
Remove every row a real visit first reported in that window (ISO-8601 or epoch ms).
|
|
394
|
-
Crawled rows and declarations stay. Run with --dry-run first.
|
|
395
|
-
cookiemunch site update --site <cbid> [--domain <host>] [--name <name> | --clear-name]
|
|
396
|
-
[--aliases <a,b>] [--dry-run]
|
|
397
|
-
Rename or move a site. The cbid and consent ledger stay; a domain move clears
|
|
398
|
-
verification. --dry-run prints the diff and its effects without saving.
|
|
399
|
-
cookiemunch consent-sync status --site <cbid>
|
|
400
|
-
Whether hosted consent sync is set up (the secret's last four characters, when it was minted).
|
|
401
|
-
cookiemunch consent-sync rotate --site <cbid>
|
|
402
|
-
Mint the site's consent-sync signing secret, or replace it (the old one stops at once).
|
|
403
|
-
Prints the secret alone on stdout, so it can be piped into your secret store: it is shown once.
|
|
404
|
-
cookiemunch consent-sync remove --site <cbid> --yes
|
|
405
|
-
Remove it: hosted consent sync stops for every visitor.
|
|
406
|
-
cookiemunch consent-sync token --site <cbid> --subject <id> [--ttl <seconds>]
|
|
407
|
-
Sign a test token locally with COOKIEMUNCH_CONSENT_SYNC_SECRET (nothing is sent), e.g. to try
|
|
408
|
-
GET /api/v1/<cbid>/profile with curl.
|
|
409
|
-
cookiemunch health [--site <cbid>] [--json]
|
|
410
|
-
Every link the site's privacy wiring depends on, with a fix hint for each that
|
|
411
|
-
fails. Exits 1 when a check fails (checks that could not run do not count).
|
|
412
|
-
cookiemunch provision <file> [--dry-run]
|
|
413
|
-
Wire a product's privacy in one call (site, connections, Atlas, Forumulate) from
|
|
414
|
-
a JSON file without secrets: the Atlas key and Forumulate token come from
|
|
415
|
-
COOKIEMUNCH_ATLAS_SECRET_KEY and COOKIEMUNCH_FORUMULATE_TOKEN. Re-running is a
|
|
416
|
-
no-op. Exits 1 when a step failed.
|
|
417
|
-
cookiemunch keys report [--unused-days <n>] [--expiring-days <n>] [--json]
|
|
418
|
-
Key hygiene: keys unused for n days (default 90), keys with broad access, and
|
|
419
|
-
keys expiring within n days (default 14). Needs a full-access key.
|
|
420
|
-
cookiemunch partners list|link|unlink --site <cbid> [--partner forumulate|atlas]
|
|
421
|
-
[--external-id <id>] [--label <text>] [<linkId>]
|
|
422
|
-
Grant a partner platform the site's consent events (no secret is created), list
|
|
423
|
-
the grants, or revoke one.
|
|
424
|
-
|
|
425
|
-
Environment:
|
|
426
|
-
COOKIEMUNCH_API_KEY an API key (fck_\u2026). Required.
|
|
427
|
-
COOKIEMUNCH_BASE_URL the API origin. Default ${DEFAULT_BASE_URL}.
|
|
428
|
-
COOKIEMUNCH_SITE default for --site.
|
|
429
|
-
COOKIEMUNCH_STRICT_FIELDS=1 send Prefer: strict-unknown-fields on writes: a field the
|
|
430
|
-
API does not know is refused (400 unknown_field), not ignored.
|
|
431
|
-
`;
|
|
432
|
-
var UsageError = class extends Error {
|
|
433
|
-
};
|
|
434
|
-
var BOOLEAN_FLAGS = /* @__PURE__ */ new Set(["replace", "exit-code", "open", "help", "json", "yes", "strict", "wait", "canary", "dry-run", "clear-name"]);
|
|
435
|
-
function parseArgs(argv) {
|
|
436
|
-
const positional = [];
|
|
437
|
-
const flags = {};
|
|
438
|
-
for (let i = 0; i < argv.length; i++) {
|
|
439
|
-
const a = argv[i];
|
|
440
|
-
if (a === "-h") {
|
|
441
|
-
flags.help = true;
|
|
442
|
-
} else if (a.startsWith("--")) {
|
|
443
|
-
const eq = a.indexOf("=");
|
|
444
|
-
const name = eq > 0 ? a.slice(2, eq) : a.slice(2);
|
|
445
|
-
let value;
|
|
446
|
-
if (eq > 0) value = a.slice(eq + 1);
|
|
447
|
-
else if (BOOLEAN_FLAGS.has(name)) value = true;
|
|
448
|
-
else {
|
|
449
|
-
const v = argv[i + 1];
|
|
450
|
-
if (v === void 0 || v.startsWith("--")) throw new UsageError(`--${name} needs a value`);
|
|
451
|
-
value = v;
|
|
452
|
-
i++;
|
|
453
|
-
}
|
|
454
|
-
if (name === "param" && typeof value === "string" && typeof flags.param === "string") flags.param = `${flags.param}
|
|
455
|
-
${value}`;
|
|
456
|
-
else flags[name] = value;
|
|
457
|
-
} else {
|
|
458
|
-
positional.push(a);
|
|
459
|
-
}
|
|
460
|
-
}
|
|
461
|
-
return { positional, flags };
|
|
462
|
-
}
|
|
463
|
-
function str(flags, name) {
|
|
464
|
-
const v = flags[name];
|
|
465
|
-
return typeof v === "string" ? v : void 0;
|
|
466
|
-
}
|
|
467
|
-
function requireSite(p, io) {
|
|
468
|
-
const site = str(p.flags, "site") ?? io.env.COOKIEMUNCH_SITE;
|
|
469
|
-
if (!site) throw new UsageError("--site <cbid> is required (or set COOKIEMUNCH_SITE)");
|
|
470
|
-
return site;
|
|
471
|
-
}
|
|
472
|
-
async function readConfigFile(io, path) {
|
|
473
|
-
if (!path) throw new UsageError("a config file is required");
|
|
474
|
-
let text;
|
|
475
|
-
try {
|
|
476
|
-
text = await io.readFile(path);
|
|
477
|
-
} catch (e) {
|
|
478
|
-
throw new UsageError(`cannot read ${path}: ${e.message}`);
|
|
479
|
-
}
|
|
480
|
-
let parsed;
|
|
481
|
-
try {
|
|
482
|
-
parsed = JSON.parse(text);
|
|
483
|
-
} catch (e) {
|
|
484
|
-
throw new UsageError(`${path} is not valid JSON: ${e.message}`);
|
|
485
|
-
}
|
|
486
|
-
if (!isPlainObject(parsed)) throw new UsageError(`${path} must hold a JSON object`);
|
|
487
|
-
return parsed;
|
|
488
|
-
}
|
|
489
|
-
function proposed(current, file, replace, cbid) {
|
|
490
|
-
return replace ? { ...file, cbid } : mergeLikeServer(current, file);
|
|
491
|
-
}
|
|
492
|
-
var CLI_VERSION = "0.5.0";
|
|
493
|
-
function userAgent(io) {
|
|
494
|
-
return io.env.COOKIEMUNCH_USER_AGENT || `cookiemunch-cli/${CLI_VERSION}`;
|
|
495
|
-
}
|
|
496
|
-
async function rawRequest(io, base, key, method, path, body) {
|
|
497
|
-
const res = await io.fetch(`${base.replace(/\/+$/, "")}/v1${path}`, {
|
|
498
|
-
method,
|
|
499
|
-
headers: { Authorization: `Bearer ${key}`, "User-Agent": userAgent(io), ...body !== void 0 ? { "Content-Type": "application/json" } : {} },
|
|
500
|
-
...body !== void 0 ? { body: JSON.stringify(body) } : {}
|
|
501
|
-
});
|
|
502
|
-
const text = await res.text();
|
|
503
|
-
let data = text;
|
|
504
|
-
try {
|
|
505
|
-
data = text ? JSON.parse(text) : void 0;
|
|
506
|
-
} catch {
|
|
507
|
-
}
|
|
508
|
-
if (!res.ok) {
|
|
509
|
-
const err = isPlainObject(data) ? data : {};
|
|
510
|
-
throw new CookieMunchApiError2(
|
|
511
|
-
res.status,
|
|
512
|
-
typeof err.error === "string" ? err.error : `request failed with status ${res.status}`,
|
|
513
|
-
typeof err.code === "string" ? err.code : void 0
|
|
514
|
-
);
|
|
515
|
-
}
|
|
516
|
-
return data;
|
|
517
|
-
}
|
|
518
|
-
async function configChanges(client, cbid, file, replace) {
|
|
519
|
-
const body = withoutReadOnly(file);
|
|
520
|
-
try {
|
|
521
|
-
const r = await client.sites.dryRunConfig(cbid, body, { mode: replace ? "put" : "patch" });
|
|
522
|
-
if (r && r.dryRun === true && Array.isArray(r.diff)) {
|
|
523
|
-
return r.diff.map((c) => {
|
|
524
|
-
const ch = { path: c.path, kind: c.from === void 0 ? "added" : c.to === void 0 ? "removed" : "changed" };
|
|
525
|
-
if (c.from !== void 0) ch.from = c.from;
|
|
526
|
-
if (c.to !== void 0) ch.to = c.to;
|
|
527
|
-
return ch;
|
|
528
|
-
});
|
|
529
|
-
}
|
|
530
|
-
} catch {
|
|
531
|
-
}
|
|
532
|
-
const current = withoutReadOnly(await client.sites.getConfig(cbid));
|
|
533
|
-
return diffJson(current, proposed(current, body, replace, cbid));
|
|
534
|
-
}
|
|
535
|
-
async function configCommand(sub, p, io, client) {
|
|
536
|
-
const cbid = requireSite(p, io);
|
|
537
|
-
if (sub === "pull") {
|
|
538
|
-
const cfg = await client.sites.getConfig(cbid);
|
|
539
|
-
const text = `${JSON.stringify(cfg, null, 2)}
|
|
540
|
-
`;
|
|
541
|
-
const out2 = str(p.flags, "out");
|
|
542
|
-
if (out2) {
|
|
543
|
-
await io.writeFile(out2, text);
|
|
544
|
-
io.stderr(`Wrote ${cbid}'s config to ${out2}
|
|
545
|
-
`);
|
|
546
|
-
} else {
|
|
547
|
-
io.stdout(text);
|
|
548
|
-
}
|
|
549
|
-
return 0;
|
|
550
|
-
}
|
|
551
|
-
if (sub === "validate") {
|
|
552
|
-
const file = await readConfigFile(io, p.positional[2]);
|
|
553
|
-
const r = await client.sites.lintConfig(cbid, file, p.flags.replace === true ? { replace: true } : {});
|
|
554
|
-
if (r.designWarnings.length) io.stdout(`${formatDesignWarnings(r.designWarnings)}
|
|
555
|
-
`);
|
|
556
|
-
io.stderr(`${summaryLine(r.designWarnings)}
|
|
557
|
-
`);
|
|
558
|
-
return p.flags.strict === true && countBySeverity(r.designWarnings).warnings > 0 ? 1 : 0;
|
|
559
|
-
}
|
|
560
|
-
if (sub === "diff" || sub === "push") {
|
|
561
|
-
const file = await readConfigFile(io, p.positional[2]);
|
|
562
|
-
const replace = p.flags.replace === true;
|
|
563
|
-
if (sub === "push" && p.flags.strict === true) {
|
|
564
|
-
const lint = await client.sites.lintConfig(cbid, file, replace ? { replace: true } : {});
|
|
565
|
-
if (countBySeverity(lint.designWarnings).warnings > 0) {
|
|
566
|
-
io.stderr(`${formatDesignWarnings(lint.designWarnings)}
|
|
567
|
-
${summaryLine(lint.designWarnings)}
|
|
568
|
-
not pushed (--strict).
|
|
569
|
-
`);
|
|
570
|
-
return 1;
|
|
571
|
-
}
|
|
572
|
-
}
|
|
573
|
-
const changes = await configChanges(client, cbid, file, replace);
|
|
574
|
-
if (changes.length === 0) {
|
|
575
|
-
io.stderr(`No changes: ${cbid}'s config already matches.
|
|
576
|
-
`);
|
|
577
|
-
return 0;
|
|
578
|
-
}
|
|
579
|
-
io.stdout(`${formatChanges(changes)}
|
|
580
|
-
`);
|
|
581
|
-
if (sub === "diff") {
|
|
582
|
-
io.stderr(`${changes.length} change${changes.length === 1 ? "" : "s"} (nothing sent). Apply with: cookiemunch config push --site ${cbid} ${p.positional[2]}${replace ? " --replace" : ""}
|
|
583
|
-
`);
|
|
584
|
-
return p.flags["exit-code"] === true ? 1 : 0;
|
|
585
|
-
}
|
|
586
|
-
const saved = replace ? await client.sites.putConfig(cbid, { ...file, cbid }) : await client.sites.patchConfig(cbid, file);
|
|
587
|
-
io.stderr(`Applied ${changes.length} change${changes.length === 1 ? "" : "s"} to ${cbid}.
|
|
588
|
-
`);
|
|
589
|
-
const found = Array.isArray(saved?.designWarnings) ? saved.designWarnings : [];
|
|
590
|
-
if (found.length) io.stderr(`${formatDesignWarnings(found)}
|
|
591
|
-
${summaryLine(found)}
|
|
592
|
-
`);
|
|
593
|
-
return 0;
|
|
594
|
-
}
|
|
595
|
-
throw new UsageError(`unknown config command: ${sub ?? "(none)"} \u2014 use pull, diff, push or validate`);
|
|
596
|
-
}
|
|
597
|
-
async function previewCommand(p, io, client) {
|
|
598
|
-
const cbid = requireSite(p, io);
|
|
599
|
-
const ttl = str(p.flags, "ttl");
|
|
600
|
-
const ttlSeconds = ttl === void 0 ? void 0 : Number(ttl);
|
|
601
|
-
if (ttlSeconds !== void 0 && (!Number.isInteger(ttlSeconds) || ttlSeconds < 1 || ttlSeconds > 3600)) {
|
|
602
|
-
throw new UsageError("--ttl must be a whole number of seconds from 1 to 3600");
|
|
603
|
-
}
|
|
604
|
-
const r = await client.sites.previewToken(cbid, {
|
|
605
|
-
...str(p.flags, "region") ? { region: str(p.flags, "region") } : {},
|
|
606
|
-
...str(p.flags, "lang") ? { lang: str(p.flags, "lang") } : {},
|
|
607
|
-
...str(p.flags, "url") ? { url: str(p.flags, "url") } : {},
|
|
608
|
-
...ttlSeconds !== void 0 ? { ttlSeconds } : {}
|
|
609
|
-
});
|
|
610
|
-
if (p.flags.json === true) {
|
|
611
|
-
io.stdout(`${JSON.stringify(r, null, 2)}
|
|
612
|
-
`);
|
|
613
|
-
} else if (r.previewUrl) {
|
|
614
|
-
io.stdout(`${r.previewUrl}
|
|
615
|
-
`);
|
|
616
|
-
} else {
|
|
617
|
-
io.stdout(`${r.token}
|
|
618
|
-
`);
|
|
619
|
-
io.stderr("Add ?cm_preview=<token>&cm_region=..&cm_lang=.. to any page of the site.\n");
|
|
620
|
-
}
|
|
621
|
-
io.stderr(`Valid until ${new Date(r.expiresAt).toISOString()}.
|
|
622
|
-
`);
|
|
623
|
-
if (p.flags.open === true && r.previewUrl) await io.openUrl(r.previewUrl);
|
|
624
|
-
return 0;
|
|
625
|
-
}
|
|
626
|
-
async function consentSyncCommand(sub, p, io, client) {
|
|
627
|
-
const cbid = requireSite(p, io);
|
|
628
|
-
if (sub === "status") {
|
|
629
|
-
io.stdout(`${JSON.stringify(await client.consentSyncSecret.get(cbid), null, 2)}
|
|
630
|
-
`);
|
|
631
|
-
return 0;
|
|
632
|
-
}
|
|
633
|
-
if (sub === "rotate") {
|
|
634
|
-
const r = await client.consentSyncSecret.rotate(cbid);
|
|
635
|
-
io.stdout(`${r.secret}
|
|
636
|
-
`);
|
|
637
|
-
io.stderr("This secret is shown once: store it on your server (never in a page). A previous secret stopped working.\n");
|
|
638
|
-
return 0;
|
|
639
|
-
}
|
|
640
|
-
if (sub === "remove") {
|
|
641
|
-
if (p.flags.yes !== true) throw new UsageError("consent-sync remove stops hosted consent sync for every visitor: add --yes");
|
|
642
|
-
await client.consentSyncSecret.remove(cbid);
|
|
643
|
-
io.stderr(`Hosted consent sync is off for ${cbid}.
|
|
644
|
-
`);
|
|
645
|
-
return 0;
|
|
646
|
-
}
|
|
647
|
-
if (sub === "token") {
|
|
648
|
-
const secret = io.env.COOKIEMUNCH_CONSENT_SYNC_SECRET;
|
|
649
|
-
if (!secret) throw new UsageError("set COOKIEMUNCH_CONSENT_SYNC_SECRET to the site's consent-sync secret");
|
|
650
|
-
const subject = str(p.flags, "subject");
|
|
651
|
-
if (!subject) throw new UsageError("--subject <id> is required");
|
|
652
|
-
const ttl = str(p.flags, "ttl");
|
|
653
|
-
try {
|
|
654
|
-
io.stdout(`${mintConsentSyncToken({ secret, cbid, subject, ...ttl !== void 0 ? { ttlSeconds: Number(ttl) } : {} })}
|
|
655
|
-
`);
|
|
656
|
-
} catch (e) {
|
|
657
|
-
throw new UsageError(e.message);
|
|
658
|
-
}
|
|
659
|
-
return 0;
|
|
660
|
-
}
|
|
661
|
-
throw new UsageError(`unknown consent-sync command: ${sub ?? "(none)"} \u2014 use status, rotate, remove or token`);
|
|
662
|
-
}
|
|
663
|
-
async function executorsCommand(sub, p, io, base, key, client) {
|
|
664
|
-
if (sub === "webhook-test") {
|
|
665
|
-
const id2 = p.positional[2];
|
|
666
|
-
if (!id2) throw new UsageError("executors webhook-test <id> needs the connection id");
|
|
667
|
-
const headersFile = str(p.flags, "headers-file");
|
|
668
|
-
const bodyFile = str(p.flags, "body-file");
|
|
669
|
-
if (!headersFile || !bodyFile) throw new UsageError("--headers-file and --body-file are both required");
|
|
670
|
-
let headers;
|
|
671
|
-
try {
|
|
672
|
-
headers = JSON.parse(await io.readFile(headersFile));
|
|
673
|
-
} catch {
|
|
674
|
-
throw new UsageError(`${headersFile} is not a JSON object of header name to value`);
|
|
675
|
-
}
|
|
676
|
-
if (!isPlainObject(headers)) throw new UsageError(`${headersFile} is not a JSON object of header name to value`);
|
|
677
|
-
const body = await io.readFile(bodyFile);
|
|
678
|
-
const r = await client.fulfillment.testExecutorWebhook(id2, { headers, body });
|
|
679
|
-
io.stdout(`${JSON.stringify(r, null, 2)}
|
|
680
|
-
`);
|
|
681
|
-
return r.verified ? 0 : 1;
|
|
682
|
-
}
|
|
683
|
-
if (sub === "deliveries") {
|
|
684
|
-
const id2 = p.positional[2];
|
|
685
|
-
if (!id2) throw new UsageError("executors deliveries <id> needs the connection id");
|
|
686
|
-
io.stdout(`${JSON.stringify(await client.fulfillment.executorWebhookDeliveries(id2), null, 2)}
|
|
687
|
-
`);
|
|
688
|
-
return 0;
|
|
689
|
-
}
|
|
690
|
-
if (sub === "presets") {
|
|
691
|
-
io.stdout(`${JSON.stringify(await client.fulfillment.executorPresets(), null, 2)}
|
|
692
|
-
`);
|
|
693
|
-
return 0;
|
|
694
|
-
}
|
|
695
|
-
if (sub === "upgrade-preset") {
|
|
696
|
-
const id2 = p.positional[2];
|
|
697
|
-
if (!id2) throw new UsageError("executors upgrade-preset <id> needs the connection id");
|
|
698
|
-
const version = str(p.flags, "version");
|
|
699
|
-
if (version !== void 0 && !/^[1-9]\d*$/.test(version)) throw new UsageError("--version must be a positive whole number");
|
|
700
|
-
const preset = str(p.flags, "preset");
|
|
701
|
-
const orgId = str(p.flags, "org-id");
|
|
702
|
-
const atlasIdAppId = str(p.flags, "atlas-id-app-id");
|
|
703
|
-
const input = {
|
|
704
|
-
...preset ? { preset } : {},
|
|
705
|
-
...version ? { presetVersion: Number(version) } : {},
|
|
706
|
-
...orgId ? { orgId } : {},
|
|
707
|
-
...atlasIdAppId ? { atlasIdAppId } : {}
|
|
708
|
-
};
|
|
709
|
-
const r = p.flags["dry-run"] === true ? await client.fulfillment.dryRunUpgradeExecutorPreset(id2, input) : await client.fulfillment.upgradeExecutorPreset(id2, input);
|
|
710
|
-
io.stdout(`${JSON.stringify(r, null, 2)}
|
|
711
|
-
`);
|
|
712
|
-
return 0;
|
|
713
|
-
}
|
|
714
|
-
if (sub !== "test") throw new UsageError(`unknown executors command: ${sub ?? "(none)"} \u2014 use test, webhook-test, deliveries, presets or upgrade-preset`);
|
|
715
|
-
const id = p.positional[2];
|
|
716
|
-
if (!id) throw new UsageError("executors test <id> needs the connection id");
|
|
717
|
-
const canary = p.flags.canary === true;
|
|
718
|
-
const email = str(p.flags, "email");
|
|
719
|
-
if (canary && email) throw new UsageError("use --email or --canary, not both");
|
|
720
|
-
if (!email && !canary) throw new UsageError("--email (or --canary) is required");
|
|
721
|
-
const asked = str(p.flags, "operation") ?? "locate";
|
|
722
|
-
const operation = asked === "lookup" ? "locate" : asked;
|
|
723
|
-
try {
|
|
724
|
-
const r = await rawRequest(io, base, key, "POST", `/dsar/executors/${encodeURIComponent(id)}/test`, canary ? { canary: true, operation } : { email, operation });
|
|
725
|
-
io.stdout(`${JSON.stringify(r, null, 2)}
|
|
726
|
-
`);
|
|
727
|
-
return canary && r.ok === false ? 1 : 0;
|
|
728
|
-
} catch (e) {
|
|
729
|
-
if (e instanceof CookieMunchApiError2 && e.status === 404 && e.message === "not found") {
|
|
730
|
-
io.stderr("This server does not offer connection tests yet (POST /v1/dsar/executors/{id}/test).\n");
|
|
731
|
-
return 1;
|
|
732
|
-
}
|
|
733
|
-
throw e;
|
|
734
|
-
}
|
|
735
|
-
}
|
|
736
|
-
async function scanCommand(p, io, client) {
|
|
737
|
-
const cbid = requireSite(p, io);
|
|
738
|
-
const timeout = str(p.flags, "timeout");
|
|
739
|
-
const timeoutS = timeout === void 0 ? 120 : Number(timeout);
|
|
740
|
-
if (!Number.isFinite(timeoutS) || timeoutS < 1 || timeoutS > 3600) throw new UsageError("--timeout must be a number of seconds from 1 to 3600");
|
|
741
|
-
let started = null;
|
|
742
|
-
try {
|
|
743
|
-
started = await client.sites.scan(cbid);
|
|
744
|
-
} catch (e) {
|
|
745
|
-
if (!(e instanceof CookieMunchApiError2 && e.status === 409)) throw e;
|
|
746
|
-
io.stderr(`A scan of ${cbid} is already running${p.flags.wait === true ? "; waiting for it" : ""}.
|
|
747
|
-
`);
|
|
748
|
-
if (p.flags.wait !== true) return 0;
|
|
749
|
-
}
|
|
750
|
-
if (p.flags.wait !== true) {
|
|
751
|
-
io.stdout(`${JSON.stringify(started, null, 2)}
|
|
752
|
-
`);
|
|
753
|
-
return 0;
|
|
754
|
-
}
|
|
755
|
-
const r = await client.sites.waitForScan(cbid, { timeoutMs: timeoutS * 1e3 });
|
|
756
|
-
io.stdout(`${JSON.stringify(r, null, 2)}
|
|
757
|
-
`);
|
|
758
|
-
if (r.state === "failed") {
|
|
759
|
-
io.stderr(`Scan ${r.scanId ?? ""} failed: ${r.error ?? "no reason given"}
|
|
760
|
-
`);
|
|
761
|
-
return 1;
|
|
762
|
-
}
|
|
763
|
-
if (r.status === "scanning") {
|
|
764
|
-
io.stderr(`Scan ${r.scanId ?? ""} is still running after ${timeoutS} s.
|
|
765
|
-
`);
|
|
766
|
-
return 1;
|
|
767
|
-
}
|
|
768
|
-
const n = r.cookieCount ?? 0;
|
|
769
|
-
const pages = r.pagesVisited !== void 0 ? ` on ${r.pagesVisited} page${r.pagesVisited === 1 ? "" : "s"}` : "";
|
|
770
|
-
const kept = r.keptFromEarlier ? `, ${r.keptFromEarlier} kept from earlier crawls and visits` : "";
|
|
771
|
-
const took = r.durationMs !== void 0 ? ` in ${Math.round(r.durationMs / 1e3)} s` : "";
|
|
772
|
-
const landed = r.finalUrl ? ` (redirected to ${r.finalUrl})` : "";
|
|
773
|
-
io.stderr(`Scan ${r.scanId ?? ""} done${took}: ${n} cookie${n === 1 ? "" : "s"} seen${pages}${kept}${landed}.
|
|
774
|
-
`);
|
|
775
|
-
return 0;
|
|
776
|
-
}
|
|
777
|
-
var PRESET_VENDORS = ["atlas", "forumulate", "chatly", "cookiemunch", "referally"];
|
|
778
|
-
function vendorArg(v) {
|
|
779
|
-
if (!v || !PRESET_VENDORS.includes(v)) throw new UsageError(`a vendor is required: ${PRESET_VENDORS.join(", ")}`);
|
|
780
|
-
return v;
|
|
781
|
-
}
|
|
782
|
-
function presetParams(p) {
|
|
783
|
-
const raw = str(p.flags, "param");
|
|
784
|
-
const out2 = {};
|
|
785
|
-
for (const line of raw ? raw.split("\n") : []) {
|
|
786
|
-
const eq = line.indexOf("=");
|
|
787
|
-
if (eq <= 0) throw new UsageError(`--param takes key=value, got "${line}"`);
|
|
788
|
-
const v = line.slice(eq + 1);
|
|
789
|
-
const k = line.slice(0, eq);
|
|
790
|
-
out2[k] = v === "true" ? true : v === "false" ? false : k === "cookieDays" && /^\d+$/.test(v) ? Number(v) : v;
|
|
791
|
-
}
|
|
792
|
-
return out2;
|
|
793
|
-
}
|
|
794
|
-
async function cookiesCommand(sub, p, io, client) {
|
|
795
|
-
const dry = p.flags["dry-run"] === true ? { dryRun: true } : void 0;
|
|
796
|
-
const print = (v) => io.stdout(`${JSON.stringify(v, null, 2)}
|
|
797
|
-
`);
|
|
798
|
-
if (sub === "presets") {
|
|
799
|
-
print(await client.sites.cookiePresets(requireSite(p, io)));
|
|
800
|
-
return 0;
|
|
801
|
-
}
|
|
802
|
-
if (sub === "preset" || sub === "unpreset") {
|
|
803
|
-
const cbid = requireSite(p, io);
|
|
804
|
-
const vendor = vendorArg(p.positional[2]);
|
|
805
|
-
if (sub === "unpreset") {
|
|
806
|
-
print(await client.sites.removeCookiePreset(cbid, vendor, dry));
|
|
807
|
-
return 0;
|
|
808
|
-
}
|
|
809
|
-
const r = await client.sites.applyCookiePreset(cbid, vendor, presetParams(p), dry);
|
|
810
|
-
print(r);
|
|
811
|
-
if (r.note) io.stderr(`${r.note}
|
|
812
|
-
`);
|
|
813
|
-
for (const s of r.skipped ?? []) io.stderr(`skipped ${s.name}: ${s.reason}
|
|
814
|
-
`);
|
|
815
|
-
if (r.manifest.status === "waiting_on_vendor") io.stderr(`Built-in rows: ${r.manifest.waitingOn} does not publish a manifest yet (${r.manifest.item}).
|
|
816
|
-
`);
|
|
817
|
-
return 0;
|
|
818
|
-
}
|
|
819
|
-
if (sub === "delete-observed") {
|
|
820
|
-
const cbid = requireSite(p, io);
|
|
821
|
-
const name = p.positional[2];
|
|
822
|
-
if (!name) throw new UsageError("a cookie name is required");
|
|
823
|
-
if (dry) print(await client.sites.deleteObservedCookie(cbid, name, dry));
|
|
824
|
-
else {
|
|
825
|
-
await client.sites.deleteObservedCookie(cbid, name);
|
|
826
|
-
io.stderr(`Removed ${name}.
|
|
827
|
-
`);
|
|
828
|
-
}
|
|
829
|
-
return 0;
|
|
830
|
-
}
|
|
831
|
-
if (sub === "purge-observed") {
|
|
832
|
-
const cbid = requireSite(p, io);
|
|
833
|
-
const since = str(p.flags, "since");
|
|
834
|
-
if (!since) throw new UsageError("--since is required (ISO-8601 or epoch ms)");
|
|
835
|
-
const until = str(p.flags, "until");
|
|
836
|
-
const r = await client.sites.purgeObservedCookies(cbid, { since, ...until ? { until } : {} }, dry);
|
|
837
|
-
print(r);
|
|
838
|
-
io.stderr(`${dry ? "Would remove" : "Removed"} ${r.count} row${r.count === 1 ? "" : "s"}.
|
|
839
|
-
`);
|
|
840
|
-
return 0;
|
|
841
|
-
}
|
|
842
|
-
throw new UsageError(`unknown cookies command: ${sub ?? "(none)"}`);
|
|
843
|
-
}
|
|
844
|
-
async function run(argv, io) {
|
|
845
|
-
let p;
|
|
846
|
-
try {
|
|
847
|
-
p = parseArgs(argv);
|
|
848
|
-
} catch (e) {
|
|
849
|
-
io.stderr(`${e.message}
|
|
850
|
-
|
|
851
|
-
${USAGE}`);
|
|
852
|
-
return 2;
|
|
853
|
-
}
|
|
854
|
-
const [cmd, sub] = p.positional;
|
|
855
|
-
if (!cmd || p.flags.help === true || cmd === "help") {
|
|
856
|
-
io.stdout(USAGE);
|
|
857
|
-
return cmd || p.flags.help === true ? 0 : 2;
|
|
858
|
-
}
|
|
859
|
-
const key = io.env.COOKIEMUNCH_API_KEY;
|
|
860
|
-
if (!key) {
|
|
861
|
-
io.stderr("COOKIEMUNCH_API_KEY is not set. Create a key under Developers in the dashboard.\n");
|
|
862
|
-
return 2;
|
|
863
|
-
}
|
|
864
|
-
const base = io.env.COOKIEMUNCH_BASE_URL || DEFAULT_BASE_URL;
|
|
865
|
-
const client = createCookieMunch({ apiKey: key, baseUrl: base, fetch: io.fetch, userAgent: userAgent(io), ...io.env.COOKIEMUNCH_STRICT_FIELDS === "1" ? { strictUnknownFields: true } : {} });
|
|
866
|
-
try {
|
|
867
|
-
if (cmd === "config") return await configCommand(sub, p, io, client);
|
|
868
|
-
if (cmd === "preview") return await previewCommand(p, io, client);
|
|
869
|
-
if (cmd === "scan") return await scanCommand(p, io, client);
|
|
870
|
-
if (cmd === "consent-sync") return await consentSyncCommand(sub, p, io, client);
|
|
871
|
-
if (cmd === "cookies") return await cookiesCommand(sub, p, io, client);
|
|
872
|
-
if (cmd === "executors") return await executorsCommand(sub, p, io, base, key, client);
|
|
873
|
-
const helpers = {
|
|
874
|
-
requireSite: () => requireSite(p, io),
|
|
875
|
-
site: () => str(p.flags, "site") ?? io.env.COOKIEMUNCH_SITE,
|
|
876
|
-
readJson: (path) => readConfigFile(io, path),
|
|
877
|
-
usage: (m) => new UsageError(m),
|
|
878
|
-
flag: (name) => p.flags[name]
|
|
879
|
-
};
|
|
880
|
-
if (cmd === "site") return await siteCommand(sub, io, client, helpers);
|
|
881
|
-
if (cmd === "health") return await healthCommand(io, client, helpers);
|
|
882
|
-
if (cmd === "provision") return await provisionCommand(sub, io, client, helpers);
|
|
883
|
-
if (cmd === "keys") return await keysCommand(sub, io, client, helpers);
|
|
884
|
-
if (cmd === "partners") return await partnersCommand(sub, p.positional.slice(2), io, client, helpers);
|
|
885
|
-
if (cmd === "rights-page") {
|
|
886
|
-
return await rightsPageCommand(sub, p, io, client, {
|
|
887
|
-
requireSite: () => requireSite(p, io),
|
|
888
|
-
readJson: (path) => readConfigFile(io, path),
|
|
889
|
-
usage: (m) => new UsageError(m)
|
|
890
|
-
});
|
|
891
|
-
}
|
|
892
|
-
throw new UsageError(`unknown command: ${cmd}`);
|
|
893
|
-
} catch (e) {
|
|
894
|
-
if (e instanceof UsageError) {
|
|
895
|
-
io.stderr(`${e.message}
|
|
896
|
-
|
|
897
|
-
${USAGE}`);
|
|
898
|
-
return 2;
|
|
899
|
-
}
|
|
900
|
-
if (e instanceof CookieMunchApiError2) {
|
|
901
|
-
io.stderr(`API error ${e.status}${e.code ? ` (${e.code})` : ""}: ${e.message}
|
|
902
|
-
`);
|
|
903
|
-
return 1;
|
|
904
|
-
}
|
|
905
|
-
io.stderr(`${e.message ?? String(e)}
|
|
906
|
-
`);
|
|
907
|
-
return 1;
|
|
908
|
-
}
|
|
909
|
-
}
|
|
910
|
-
|
|
911
|
-
export {
|
|
912
|
-
mergeLikeServer,
|
|
913
|
-
diffJson,
|
|
914
|
-
formatChanges,
|
|
915
|
-
rightsPageCommand,
|
|
916
|
-
DEFAULT_BASE_URL,
|
|
917
|
-
USAGE,
|
|
918
|
-
parseArgs,
|
|
919
|
-
run
|
|
920
|
-
};
|
|
921
|
-
//# sourceMappingURL=chunk-2ADK3C47.js.map
|