@siteoshq/cli 1.1.1 → 1.2.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/README.md +21 -2
- package/dist/cli.js +514 -19
- package/dist/cli.js.map +1 -1
- package/package.json +19 -20
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# SiteOS CLI
|
|
2
2
|
|
|
3
3
|
`@siteoshq/cli` exposes one `siteos` binary for Auth, common Projects, Pulse, Cookie, Forms,
|
|
4
|
-
Search, Trace and Integrations. This source prepares version 1.
|
|
4
|
+
Search, Trace and Integrations. This source prepares version 1.2.0; publishing is a separate release.
|
|
5
5
|
Node.js 22 or newer is required.
|
|
6
6
|
|
|
7
7
|
## Install and authenticate
|
|
@@ -22,7 +22,7 @@ grants; the durable session never reaches a product endpoint.
|
|
|
22
22
|
|
|
23
23
|
## Hosted origins
|
|
24
24
|
|
|
25
|
-
CLI 1.
|
|
25
|
+
CLI 1.2.0 defaults to `https://app.siteos.sh` for Auth and every service. Existing credentials
|
|
26
26
|
and repository bindings retain their selected origin; sign in again when moving installations.
|
|
27
27
|
Never copy private state between production and staging.
|
|
28
28
|
|
|
@@ -183,3 +183,22 @@ pnpm --dir packages/cli pack:check
|
|
|
183
183
|
```
|
|
184
184
|
|
|
185
185
|
`pack:check` creates the publishable tarball locally. It does not publish the package.
|
|
186
|
+
|
|
187
|
+
## Cookie verification
|
|
188
|
+
|
|
189
|
+
CLI 1.2.0 adds `cookie schema`, `cookie validate --input draft.json`,
|
|
190
|
+
`cookie regions resolve --country GB --source published` and `cookie restore --input restore.json`.
|
|
191
|
+
Restore changes the draft only. Validation makes no write and reports a stale draft version.
|
|
192
|
+
|
|
193
|
+
From the selected website repository, run `siteos cookie verify --json` or
|
|
194
|
+
`siteos cookie verify --url /pricing --browser webkit --json`. Install the matching local browser
|
|
195
|
+
when requested with `npx playwright@1.61.1 install chromium` or `npx playwright@1.61.1 install webkit`.
|
|
196
|
+
The check opens fresh contexts, exercises the public consent API and reports request origins and
|
|
197
|
+
storage names without values, query strings, request bodies or authenticated sessions. These are
|
|
198
|
+
real visits; normal installation/aggregate signals may be recorded. Reports remain local.
|
|
199
|
+
|
|
200
|
+
A report records the revision, runtime, observed Edge location, scenario scope and 24-hour expiry.
|
|
201
|
+
`passed` exits 0; `needs-review` and `failed` exit 1. Unknown origins/storage require classification.
|
|
202
|
+
The check covers one route and a bounded observation window, not hidden first-party/server-side
|
|
203
|
+
tracking, every delayed interaction, visual accessibility or legal compliance. Repeat after
|
|
204
|
+
website/publication changes and preserve a separate GTM Tag Assistant check where applicable.
|
package/dist/cli.js
CHANGED
|
@@ -1,4 +1,438 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
+
var __esm = (fn, res) => function __init() {
|
|
5
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
6
|
+
};
|
|
7
|
+
var __export = (target, all) => {
|
|
8
|
+
for (var name in all)
|
|
9
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
// src/services/cookie-verification.ts
|
|
13
|
+
var cookie_verification_exports = {};
|
|
14
|
+
__export(cookie_verification_exports, {
|
|
15
|
+
VerificationInstallation: () => VerificationInstallation,
|
|
16
|
+
assessCookieObservations: () => assessCookieObservations,
|
|
17
|
+
verificationTarget: () => verificationTarget,
|
|
18
|
+
verifyCookieWebsite: () => verifyCookieWebsite
|
|
19
|
+
});
|
|
20
|
+
import { z as z14 } from "zod";
|
|
21
|
+
function verificationTarget(websiteUrl, candidate) {
|
|
22
|
+
const website = new URL(websiteUrl);
|
|
23
|
+
const target = new URL(candidate ?? websiteUrl, website);
|
|
24
|
+
if (target.origin !== website.origin || target.username || target.password || target.hash || !["https:", "http:"].includes(target.protocol))
|
|
25
|
+
throw new Error(
|
|
26
|
+
"Verify a URL on the selected Project environment's origin, without credentials or fragments."
|
|
27
|
+
);
|
|
28
|
+
return target.toString();
|
|
29
|
+
}
|
|
30
|
+
function matchesName(value, names) {
|
|
31
|
+
return Boolean(
|
|
32
|
+
names && (names.exact.includes(value) || names.prefixes.some((prefix) => value.startsWith(prefix)))
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
function assessCookieObservations(input) {
|
|
36
|
+
const issues = [];
|
|
37
|
+
const unknownOrigins = /* @__PURE__ */ new Set();
|
|
38
|
+
const unknownStorage = /* @__PURE__ */ new Set();
|
|
39
|
+
const services = input.envelope.config.services;
|
|
40
|
+
for (const request of input.requests) {
|
|
41
|
+
const matches = services.filter(
|
|
42
|
+
(service) => service.lifecycle && [
|
|
43
|
+
...service.lifecycle.scriptOrigins,
|
|
44
|
+
...service.lifecycle.iframeOrigins,
|
|
45
|
+
...service.lifecycle.pixelOrigins
|
|
46
|
+
].some((url) => new URL(url).origin === request.origin)
|
|
47
|
+
);
|
|
48
|
+
const state = input.observations.find(
|
|
49
|
+
(item) => item.scenario === request.scenario
|
|
50
|
+
)?.state;
|
|
51
|
+
if (matches.length) {
|
|
52
|
+
const allowed = matches.some(
|
|
53
|
+
(service) => state?.services.find((item) => item.key === service.key)?.allowed
|
|
54
|
+
);
|
|
55
|
+
const advancedGoogle = input.envelope.config.integrations.googleConsentMode === "advanced" && matches.every(
|
|
56
|
+
(service) => ["google-analytics", "google-ads"].includes(service.key)
|
|
57
|
+
);
|
|
58
|
+
if (!allowed && !advancedGoogle)
|
|
59
|
+
issues.push({
|
|
60
|
+
code: "request_without_permission",
|
|
61
|
+
scenario: request.scenario,
|
|
62
|
+
detail: request.origin
|
|
63
|
+
});
|
|
64
|
+
} else if (request.origin !== input.firstPartyOrigin && !input.deliveryOrigins.includes(request.origin))
|
|
65
|
+
unknownOrigins.add(request.origin);
|
|
66
|
+
}
|
|
67
|
+
for (const observation of input.observations) {
|
|
68
|
+
for (const service of services) {
|
|
69
|
+
const allowed = observation.state.services.find(
|
|
70
|
+
(item) => item.key === service.key
|
|
71
|
+
)?.allowed;
|
|
72
|
+
const stored = observation.cookies.some(
|
|
73
|
+
(name) => matchesName(name, service.lifecycle?.firstPartyCookies)
|
|
74
|
+
) || observation.localStorage.some(
|
|
75
|
+
(name) => matchesName(name, service.lifecycle?.localStorage)
|
|
76
|
+
);
|
|
77
|
+
if (!allowed && stored)
|
|
78
|
+
issues.push({
|
|
79
|
+
code: "storage_without_permission",
|
|
80
|
+
scenario: observation.scenario,
|
|
81
|
+
detail: service.key
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
for (const name of observation.cookies) {
|
|
85
|
+
if (name !== "siteos_consent" && !services.some(
|
|
86
|
+
(service) => matchesName(name, service.lifecycle?.firstPartyCookies)
|
|
87
|
+
))
|
|
88
|
+
unknownStorage.add(`cookie:${name}`);
|
|
89
|
+
}
|
|
90
|
+
for (const name of observation.localStorage) {
|
|
91
|
+
if (!name.startsWith("siteos-cookie:") && !services.some(
|
|
92
|
+
(service) => matchesName(name, service.lifecycle?.localStorage)
|
|
93
|
+
))
|
|
94
|
+
unknownStorage.add(`localStorage:${name}`);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return {
|
|
98
|
+
issues,
|
|
99
|
+
unknownOrigins: [...unknownOrigins].sort(),
|
|
100
|
+
unknownStorage: [...unknownStorage].sort()
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
async function verifyCookieWebsite(input) {
|
|
104
|
+
const startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
105
|
+
const issues = [];
|
|
106
|
+
const requests = [];
|
|
107
|
+
const observations = [];
|
|
108
|
+
let envelope = null;
|
|
109
|
+
let scenario = "first-visit";
|
|
110
|
+
let overflow = false;
|
|
111
|
+
const installation = input.installation;
|
|
112
|
+
const browser = await (input.launch ?? (async () => {
|
|
113
|
+
const playwright = await import("playwright");
|
|
114
|
+
try {
|
|
115
|
+
return await playwright[input.browserName].launch({ headless: true });
|
|
116
|
+
} catch {
|
|
117
|
+
throw new Error(
|
|
118
|
+
`The ${input.browserName} browser is unavailable. Run npx playwright@1.61.1 install ${input.browserName}, then retry.`
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
}))();
|
|
122
|
+
const readState = async (page) => State.parse(
|
|
123
|
+
await page.evaluate(
|
|
124
|
+
() => window.SiteOSCookie.getDebugState()
|
|
125
|
+
)
|
|
126
|
+
);
|
|
127
|
+
const waitForRuntime = async (page) => {
|
|
128
|
+
await page.waitForFunction(
|
|
129
|
+
() => Boolean(
|
|
130
|
+
window.SiteOSCookie?.getDebugState().resolved
|
|
131
|
+
),
|
|
132
|
+
void 0,
|
|
133
|
+
{ timeout: 15e3 }
|
|
134
|
+
);
|
|
135
|
+
};
|
|
136
|
+
try {
|
|
137
|
+
for (const gpc of [false, true]) {
|
|
138
|
+
const context = await browser.newContext({
|
|
139
|
+
serviceWorkers: "block",
|
|
140
|
+
...gpc ? { extraHTTPHeaders: { "Sec-GPC": "1" } } : {}
|
|
141
|
+
});
|
|
142
|
+
if (gpc)
|
|
143
|
+
await context.addInitScript(
|
|
144
|
+
() => Object.defineProperty(navigator, "globalPrivacyControl", {
|
|
145
|
+
get: () => true
|
|
146
|
+
})
|
|
147
|
+
);
|
|
148
|
+
const page = await context.newPage();
|
|
149
|
+
page.setDefaultTimeout(15e3);
|
|
150
|
+
page.setDefaultNavigationTimeout(25e3);
|
|
151
|
+
const pending = /* @__PURE__ */ new Set();
|
|
152
|
+
page.on("request", (request) => {
|
|
153
|
+
if (!/^https?:/u.test(request.url())) return;
|
|
154
|
+
if (requests.length >= 2e3) {
|
|
155
|
+
overflow = true;
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
requests.push({
|
|
159
|
+
origin: new URL(request.url()).origin,
|
|
160
|
+
type: request.resourceType(),
|
|
161
|
+
scenario
|
|
162
|
+
});
|
|
163
|
+
});
|
|
164
|
+
page.on("response", (response) => {
|
|
165
|
+
const expectedUrl = new URL(installation.delivery.configUrl);
|
|
166
|
+
const responseUrl = new URL(response.url());
|
|
167
|
+
if (responseUrl.origin !== expectedUrl.origin || responseUrl.pathname !== expectedUrl.pathname)
|
|
168
|
+
return;
|
|
169
|
+
const responseScenario = scenario;
|
|
170
|
+
const reading = (async () => {
|
|
171
|
+
let timeout;
|
|
172
|
+
try {
|
|
173
|
+
const raw = await Promise.race([
|
|
174
|
+
response.body(),
|
|
175
|
+
new Promise((_, reject) => {
|
|
176
|
+
timeout = setTimeout(() => reject(new Error("timeout")), 5e3);
|
|
177
|
+
})
|
|
178
|
+
]);
|
|
179
|
+
if (raw.byteLength > 512 * 1024) throw new Error("size");
|
|
180
|
+
const parsed = Envelope.parse(JSON.parse(raw.toString()));
|
|
181
|
+
if (parsed.publicKey !== installation.publicKey || parsed.revision !== installation.currentPublishedRevision)
|
|
182
|
+
throw new Error("revision");
|
|
183
|
+
envelope = parsed;
|
|
184
|
+
} catch {
|
|
185
|
+
issues.push({
|
|
186
|
+
code: "configuration_mismatch",
|
|
187
|
+
scenario: responseScenario,
|
|
188
|
+
detail: "Configuration is unavailable, invalid, or differs from the published revision."
|
|
189
|
+
});
|
|
190
|
+
} finally {
|
|
191
|
+
clearTimeout(timeout);
|
|
192
|
+
}
|
|
193
|
+
})();
|
|
194
|
+
pending.add(reading);
|
|
195
|
+
void reading.finally(() => pending.delete(reading));
|
|
196
|
+
});
|
|
197
|
+
scenario = gpc ? "gpc-first-visit" : "first-visit";
|
|
198
|
+
try {
|
|
199
|
+
await page.goto(input.url, { waitUntil: "domcontentloaded" });
|
|
200
|
+
if (new URL(page.url()).origin !== new URL(input.url).origin)
|
|
201
|
+
throw new Error("The website redirected to another origin.");
|
|
202
|
+
await waitForRuntime(page);
|
|
203
|
+
await Promise.all([...pending]);
|
|
204
|
+
const capture = async () => {
|
|
205
|
+
await page.waitForTimeout(1500);
|
|
206
|
+
if (new URL(page.url()).origin !== new URL(input.url).origin)
|
|
207
|
+
throw new Error("The website navigated to another origin.");
|
|
208
|
+
await waitForRuntime(page);
|
|
209
|
+
const state = await readState(page);
|
|
210
|
+
if (state.preview || state.resolved?.publicKey !== installation.publicKey || state.resolved?.revision !== installation.currentPublishedRevision)
|
|
211
|
+
issues.push({
|
|
212
|
+
code: "runtime_mismatch",
|
|
213
|
+
scenario,
|
|
214
|
+
detail: "The loaded runtime is a preview or uses another publication."
|
|
215
|
+
});
|
|
216
|
+
if (Number(state.runtimeVersion.split(".")[0]) < 11)
|
|
217
|
+
issues.push({
|
|
218
|
+
code: "runtime_outdated",
|
|
219
|
+
scenario,
|
|
220
|
+
detail: state.runtimeVersion
|
|
221
|
+
});
|
|
222
|
+
observations.push({
|
|
223
|
+
scenario,
|
|
224
|
+
state,
|
|
225
|
+
cookies: [
|
|
226
|
+
...new Set(
|
|
227
|
+
(await context.cookies()).map((cookie) => cookie.name)
|
|
228
|
+
)
|
|
229
|
+
].sort(),
|
|
230
|
+
localStorage: await page.evaluate(
|
|
231
|
+
() => Object.keys(localStorage).sort()
|
|
232
|
+
)
|
|
233
|
+
});
|
|
234
|
+
};
|
|
235
|
+
await capture();
|
|
236
|
+
if (gpc) {
|
|
237
|
+
const state = observations.at(-1).state;
|
|
238
|
+
if (!state.consent.globalPrivacyControl || !state.consent.privacyChoices.saleOrShareOptOut || !state.consent.privacyChoices.targetedAdvertisingOptOut)
|
|
239
|
+
issues.push({
|
|
240
|
+
code: "gpc_not_applied",
|
|
241
|
+
scenario,
|
|
242
|
+
detail: "GPC must be visible and both privacy opt-outs applied."
|
|
243
|
+
});
|
|
244
|
+
} else {
|
|
245
|
+
for (const action of [
|
|
246
|
+
"reject",
|
|
247
|
+
"accept",
|
|
248
|
+
"granular",
|
|
249
|
+
"withdraw"
|
|
250
|
+
]) {
|
|
251
|
+
scenario = action;
|
|
252
|
+
try {
|
|
253
|
+
await page.evaluate(async (choice) => {
|
|
254
|
+
const api = window.SiteOSCookie;
|
|
255
|
+
if (choice === "accept") await api.acceptAll();
|
|
256
|
+
else if (choice === "granular")
|
|
257
|
+
await api.updateConsent(
|
|
258
|
+
api.getConsent().categories.filter((key) => key !== "necessary").slice(0, 1)
|
|
259
|
+
);
|
|
260
|
+
else await api.rejectAll();
|
|
261
|
+
}, action);
|
|
262
|
+
} catch (error) {
|
|
263
|
+
if (!(error instanceof Error) || !/Execution context was destroyed/u.test(error.message))
|
|
264
|
+
throw error;
|
|
265
|
+
}
|
|
266
|
+
await capture();
|
|
267
|
+
const state = observations.at(-1).state;
|
|
268
|
+
if ((action === "reject" || action === "withdraw") && state.consent.categories.some((key) => key !== "necessary"))
|
|
269
|
+
issues.push({
|
|
270
|
+
code: "refusal_not_applied",
|
|
271
|
+
scenario,
|
|
272
|
+
detail: "Optional categories remain granted."
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
scenario = "returning-after-refusal";
|
|
276
|
+
await page.reload({ waitUntil: "domcontentloaded" });
|
|
277
|
+
await capture();
|
|
278
|
+
if (observations.at(-1).state.consent.categories.some((key) => key !== "necessary"))
|
|
279
|
+
issues.push({
|
|
280
|
+
code: "refusal_not_persisted",
|
|
281
|
+
scenario,
|
|
282
|
+
detail: "Refusal was not preserved across reload."
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
} catch {
|
|
286
|
+
issues.push({
|
|
287
|
+
code: "scenario_incomplete",
|
|
288
|
+
scenario,
|
|
289
|
+
detail: "The page or Cookie runtime did not complete this scenario within the time limit."
|
|
290
|
+
});
|
|
291
|
+
} finally {
|
|
292
|
+
await context.close();
|
|
293
|
+
await Promise.all([...pending]);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
} finally {
|
|
297
|
+
await browser.close();
|
|
298
|
+
}
|
|
299
|
+
const assessment = envelope ? assessCookieObservations({
|
|
300
|
+
envelope,
|
|
301
|
+
observations,
|
|
302
|
+
requests,
|
|
303
|
+
firstPartyOrigin: new URL(input.url).origin,
|
|
304
|
+
deliveryOrigins: Object.values(installation.delivery).filter((url) => typeof url === "string").map((url) => new URL(url).origin)
|
|
305
|
+
}) : { issues: [], unknownOrigins: [], unknownStorage: [] };
|
|
306
|
+
issues.push(...assessment.issues);
|
|
307
|
+
if (!envelope)
|
|
308
|
+
issues.push({
|
|
309
|
+
code: "configuration_not_observed",
|
|
310
|
+
scenario: "all",
|
|
311
|
+
detail: "No matching public configuration response was observed."
|
|
312
|
+
});
|
|
313
|
+
if (overflow)
|
|
314
|
+
issues.push({
|
|
315
|
+
code: "request_limit",
|
|
316
|
+
scenario: "all",
|
|
317
|
+
detail: "The request limit was reached; this run is incomplete."
|
|
318
|
+
});
|
|
319
|
+
const config = envelope;
|
|
320
|
+
const status = issues.length ? "failed" : assessment.unknownOrigins.length || assessment.unknownStorage.length || config?.config.integrations.googleConsentMode === "advanced" ? "needs-review" : "passed";
|
|
321
|
+
return {
|
|
322
|
+
schemaVersion: 1,
|
|
323
|
+
status,
|
|
324
|
+
startedAt,
|
|
325
|
+
completedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
326
|
+
expiresAt: new Date(Date.now() + 24 * 60 * 60 * 1e3).toISOString(),
|
|
327
|
+
target: {
|
|
328
|
+
origin: new URL(input.url).origin,
|
|
329
|
+
path: new URL(input.url).pathname
|
|
330
|
+
},
|
|
331
|
+
browser: input.browserName,
|
|
332
|
+
publicKey: installation.publicKey,
|
|
333
|
+
revision: installation.currentPublishedRevision,
|
|
334
|
+
region: config?.trustedSignals?.region ?? null,
|
|
335
|
+
issues: [
|
|
336
|
+
...new Map(
|
|
337
|
+
issues.map((issue) => [JSON.stringify(issue), issue])
|
|
338
|
+
).values()
|
|
339
|
+
],
|
|
340
|
+
unknownOrigins: assessment.unknownOrigins,
|
|
341
|
+
unknownStorage: assessment.unknownStorage,
|
|
342
|
+
observations,
|
|
343
|
+
requestCounts: [
|
|
344
|
+
...new Set(requests.map((item) => `${item.scenario} ${item.origin}`))
|
|
345
|
+
].map((key) => ({
|
|
346
|
+
scenario: key.split(" ")[0],
|
|
347
|
+
origin: key.split(" ")[1],
|
|
348
|
+
count: requests.filter(
|
|
349
|
+
(item) => `${item.scenario} ${item.origin}` === key
|
|
350
|
+
).length
|
|
351
|
+
})),
|
|
352
|
+
limits: [
|
|
353
|
+
"One route, fresh local browser, 1.5-second observation per scenario, at most 2000 requests.",
|
|
354
|
+
"Consent actions use the public runtime API. Visual, keyboard and GTM Tag Assistant acceptance remain separate.",
|
|
355
|
+
"Unknown origins/storage need classification. First-party or server-side tracking and delayed/interaction-only resources require a source audit.",
|
|
356
|
+
"Geography is this browser's actual Edge location. Use regions resolve for simulations; do not spoof production geography.",
|
|
357
|
+
"No cookie values, request query strings, bodies, credentials or visitor identifiers are included. A pass covers only the observed scope, not legal compliance."
|
|
358
|
+
]
|
|
359
|
+
};
|
|
360
|
+
}
|
|
361
|
+
var Names, Service, Envelope, State, VerificationInstallation;
|
|
362
|
+
var init_cookie_verification = __esm({
|
|
363
|
+
"src/services/cookie-verification.ts"() {
|
|
364
|
+
"use strict";
|
|
365
|
+
Names = z14.object({
|
|
366
|
+
exact: z14.array(z14.string()),
|
|
367
|
+
prefixes: z14.array(z14.string())
|
|
368
|
+
});
|
|
369
|
+
Service = z14.object({
|
|
370
|
+
key: z14.string(),
|
|
371
|
+
purposeKey: z14.string(),
|
|
372
|
+
lifecycle: z14.object({
|
|
373
|
+
scriptOrigins: z14.array(z14.string()),
|
|
374
|
+
iframeOrigins: z14.array(z14.string()),
|
|
375
|
+
pixelOrigins: z14.array(z14.string()),
|
|
376
|
+
firstPartyCookies: Names,
|
|
377
|
+
localStorage: Names
|
|
378
|
+
}).optional()
|
|
379
|
+
});
|
|
380
|
+
Envelope = z14.object({
|
|
381
|
+
publicKey: z14.string(),
|
|
382
|
+
revision: z14.number(),
|
|
383
|
+
ruleKey: z14.string(),
|
|
384
|
+
profileKey: z14.string(),
|
|
385
|
+
trustedSignals: z14.object({
|
|
386
|
+
region: z14.object({
|
|
387
|
+
countryCode: z14.string().nullable(),
|
|
388
|
+
subdivisionCode: z14.string().nullable()
|
|
389
|
+
}).optional()
|
|
390
|
+
}).optional(),
|
|
391
|
+
config: z14.object({
|
|
392
|
+
services: z14.array(Service),
|
|
393
|
+
categories: z14.array(z14.object({ key: z14.string(), required: z14.boolean() })),
|
|
394
|
+
integrations: z14.object({ googleConsentMode: z14.string() })
|
|
395
|
+
})
|
|
396
|
+
});
|
|
397
|
+
State = z14.object({
|
|
398
|
+
runtimeVersion: z14.string(),
|
|
399
|
+
preview: z14.boolean(),
|
|
400
|
+
resolved: z14.object({
|
|
401
|
+
publicKey: z14.string(),
|
|
402
|
+
revision: z14.number(),
|
|
403
|
+
ruleKey: z14.string(),
|
|
404
|
+
profileKey: z14.string()
|
|
405
|
+
}).nullable(),
|
|
406
|
+
consent: z14.object({
|
|
407
|
+
categories: z14.array(z14.string()),
|
|
408
|
+
decision: z14.string(),
|
|
409
|
+
globalPrivacyControl: z14.boolean(),
|
|
410
|
+
privacyChoices: z14.object({
|
|
411
|
+
saleOrShareOptOut: z14.boolean(),
|
|
412
|
+
targetedAdvertisingOptOut: z14.boolean()
|
|
413
|
+
})
|
|
414
|
+
}),
|
|
415
|
+
services: z14.array(
|
|
416
|
+
z14.object({
|
|
417
|
+
key: z14.string(),
|
|
418
|
+
allowed: z14.boolean(),
|
|
419
|
+
footprintPresent: z14.boolean()
|
|
420
|
+
})
|
|
421
|
+
)
|
|
422
|
+
});
|
|
423
|
+
VerificationInstallation = z14.object({
|
|
424
|
+
publicKey: z14.string(),
|
|
425
|
+
currentPublishedRevision: z14.number().int().positive(),
|
|
426
|
+
delivery: z14.object({
|
|
427
|
+
runtimeUrl: z14.string().url(),
|
|
428
|
+
configUrl: z14.string().url(),
|
|
429
|
+
analyticsUrl: z14.string().url().nullable(),
|
|
430
|
+
receiptsUrl: z14.string().url(),
|
|
431
|
+
handshakeUrl: z14.string().url()
|
|
432
|
+
})
|
|
433
|
+
});
|
|
434
|
+
}
|
|
435
|
+
});
|
|
2
436
|
|
|
3
437
|
// src/health-check.ts
|
|
4
438
|
import path10 from "path";
|
|
@@ -8563,13 +8997,18 @@ async function runEnvironmentCommand3(options) {
|
|
|
8563
8997
|
import { readFile as readFile16, stat } from "fs/promises";
|
|
8564
8998
|
import path28 from "path";
|
|
8565
8999
|
import { parseArgs as parseArgs3 } from "util";
|
|
8566
|
-
import { z as
|
|
9000
|
+
import { z as z15 } from "zod";
|
|
8567
9001
|
var SERVICE_HELP = {
|
|
8568
9002
|
cookie: `Manage Cookie for the selected SiteOS Project.
|
|
8569
9003
|
|
|
8570
9004
|
Usage:
|
|
8571
9005
|
siteos cookie status [--json]
|
|
8572
9006
|
siteos cookie installation [--json]
|
|
9007
|
+
siteos cookie schema [--json]
|
|
9008
|
+
siteos cookie validate --input <draft.json> [--json]
|
|
9009
|
+
siteos cookie regions resolve [--country <ISO>] [--subdivision <code>] [--source <draft|published>] [--json]
|
|
9010
|
+
siteos cookie verify [--url <same-origin-url>] [--browser <chromium|webkit>] [--json]
|
|
9011
|
+
siteos cookie restore --input <restore.json> [--json]
|
|
8573
9012
|
siteos cookie draft get [--json]
|
|
8574
9013
|
siteos cookie draft save --input <draft.json> [--json]
|
|
8575
9014
|
siteos cookie publish --input <publication.json> [--json]
|
|
@@ -8618,7 +9057,12 @@ async function runServiceCommand(service, options) {
|
|
|
8618
9057
|
"range-days": { type: "string" },
|
|
8619
9058
|
query: { type: "string" },
|
|
8620
9059
|
cursor: { type: "string" },
|
|
8621
|
-
channel: { type: "string" }
|
|
9060
|
+
channel: { type: "string" },
|
|
9061
|
+
country: { type: "string" },
|
|
9062
|
+
subdivision: { type: "string" },
|
|
9063
|
+
source: { type: "string" },
|
|
9064
|
+
url: { type: "string" },
|
|
9065
|
+
browser: { type: "string" }
|
|
8622
9066
|
}
|
|
8623
9067
|
});
|
|
8624
9068
|
const [action, subaction] = positionals;
|
|
@@ -8627,6 +9071,11 @@ async function runServiceCommand(service, options) {
|
|
|
8627
9071
|
cookie: {
|
|
8628
9072
|
status: [],
|
|
8629
9073
|
installation: [],
|
|
9074
|
+
schema: [],
|
|
9075
|
+
validate: ["input"],
|
|
9076
|
+
"regions resolve": ["country", "subdivision", "source"],
|
|
9077
|
+
verify: ["url", "browser"],
|
|
9078
|
+
restore: ["input"],
|
|
8630
9079
|
"draft get": [],
|
|
8631
9080
|
"draft save": ["input"],
|
|
8632
9081
|
publish: ["input"],
|
|
@@ -8706,8 +9155,8 @@ async function runServiceCommand(service, options) {
|
|
|
8706
9155
|
);
|
|
8707
9156
|
const result2 = await response.json();
|
|
8708
9157
|
if (!response.ok) {
|
|
8709
|
-
const error =
|
|
8710
|
-
error:
|
|
9158
|
+
const error = z15.object({
|
|
9159
|
+
error: z15.object({ code: z15.string(), message: z15.string().max(500) })
|
|
8711
9160
|
}).safeParse(result2);
|
|
8712
9161
|
throw new SiteOSAuthApiError({
|
|
8713
9162
|
code: error.success ? error.data.error.code : "SERVICE_REQUEST_FAILED",
|
|
@@ -8731,14 +9180,60 @@ async function runServiceCommand(service, options) {
|
|
|
8731
9180
|
if (service === "cookie") {
|
|
8732
9181
|
if (action === "status" && positionals.length === 1)
|
|
8733
9182
|
result = await request(site);
|
|
8734
|
-
else if (action === "
|
|
9183
|
+
else if (action === "schema") result = await request(`${site}/schema`);
|
|
9184
|
+
else if (action === "validate") {
|
|
9185
|
+
result = await request(
|
|
9186
|
+
`${site}/validate`,
|
|
9187
|
+
"POST",
|
|
9188
|
+
await inputFile(),
|
|
9189
|
+
"cookie:workspace:read"
|
|
9190
|
+
);
|
|
9191
|
+
const validation = z15.object({
|
|
9192
|
+
valid: z15.boolean(),
|
|
9193
|
+
draftVersionMatches: z15.boolean().optional()
|
|
9194
|
+
}).parse(result);
|
|
9195
|
+
if (!validation.valid || validation.draftVersionMatches === false)
|
|
9196
|
+
return { exitCode: 2, stdout: JSON.stringify(result, null, 2) };
|
|
9197
|
+
} else if (action === "restore")
|
|
9198
|
+
result = await request(`${site}/restore`, "POST", await inputFile());
|
|
9199
|
+
else if (action === "regions") {
|
|
9200
|
+
const query = new URLSearchParams();
|
|
9201
|
+
for (const key of ["country", "subdivision", "source"])
|
|
9202
|
+
if (values[key]) query.set(key, values[key]);
|
|
9203
|
+
result = await request(`${site}/regions?${query}`);
|
|
9204
|
+
} else if (action === "verify") {
|
|
9205
|
+
const {
|
|
9206
|
+
VerificationInstallation: VerificationInstallation2,
|
|
9207
|
+
verificationTarget: verificationTarget2,
|
|
9208
|
+
verifyCookieWebsite: verifyCookieWebsite2
|
|
9209
|
+
} = await Promise.resolve().then(() => (init_cookie_verification(), cookie_verification_exports));
|
|
9210
|
+
const browserName = values.browser ?? "chromium";
|
|
9211
|
+
if (browserName !== "chromium" && browserName !== "webkit")
|
|
9212
|
+
throw new Error("Choose chromium or webkit.");
|
|
9213
|
+
if (!common.environment.url)
|
|
9214
|
+
throw new Error(
|
|
9215
|
+
"Set the selected Project environment URL before verification."
|
|
9216
|
+
);
|
|
9217
|
+
const url = verificationTarget2(common.environment.url, values.url);
|
|
9218
|
+
result = await verifyCookieWebsite2({
|
|
9219
|
+
installation: VerificationInstallation2.parse(
|
|
9220
|
+
await request(`${site}/installation`)
|
|
9221
|
+
),
|
|
9222
|
+
url,
|
|
9223
|
+
browserName
|
|
9224
|
+
});
|
|
9225
|
+
return {
|
|
9226
|
+
exitCode: result.status === "passed" ? 0 : 1,
|
|
9227
|
+
stdout: JSON.stringify(result, null, 2)
|
|
9228
|
+
};
|
|
9229
|
+
} else if (action === "installation")
|
|
8735
9230
|
result = await request(`${site}/installation`);
|
|
8736
9231
|
else if (action === "draft" && subaction === "get" && positionals.length === 2) {
|
|
8737
|
-
const response =
|
|
8738
|
-
name:
|
|
8739
|
-
hostname:
|
|
8740
|
-
draftVersion:
|
|
8741
|
-
draft:
|
|
9232
|
+
const response = z15.object({
|
|
9233
|
+
name: z15.string(),
|
|
9234
|
+
hostname: z15.string(),
|
|
9235
|
+
draftVersion: z15.number(),
|
|
9236
|
+
draft: z15.unknown()
|
|
8742
9237
|
}).parse(await request(site));
|
|
8743
9238
|
result = {
|
|
8744
9239
|
name: response.name,
|
|
@@ -8770,9 +9265,9 @@ async function runServiceCommand(service, options) {
|
|
|
8770
9265
|
else if (action === "environments" && positionals.length === 1)
|
|
8771
9266
|
result = await request(`${site}/environments`);
|
|
8772
9267
|
else if (action === "report" || action === "installation" || action === "tracking-plan") {
|
|
8773
|
-
const response =
|
|
8774
|
-
environments:
|
|
8775
|
-
|
|
9268
|
+
const response = z15.object({
|
|
9269
|
+
environments: z15.array(
|
|
9270
|
+
z15.object({ id: z15.string(), slug: z15.string() })
|
|
8776
9271
|
)
|
|
8777
9272
|
}).parse(await request(`${site}/environments`));
|
|
8778
9273
|
const environment = common.overview.project.environments.find(
|
|
@@ -8797,11 +9292,11 @@ async function runServiceCommand(service, options) {
|
|
|
8797
9292
|
);
|
|
8798
9293
|
const base = `/environments/${encodeURIComponent(matches[0].id)}`;
|
|
8799
9294
|
if (action === "report" && positionals.length === 1) {
|
|
8800
|
-
const detail =
|
|
8801
|
-
site:
|
|
8802
|
-
id:
|
|
8803
|
-
environments:
|
|
8804
|
-
|
|
9295
|
+
const detail = z15.object({
|
|
9296
|
+
site: z15.object({
|
|
9297
|
+
id: z15.literal(common.resourceId),
|
|
9298
|
+
environments: z15.array(
|
|
9299
|
+
z15.object({ id: z15.string(), evidence: z15.unknown() })
|
|
8805
9300
|
)
|
|
8806
9301
|
})
|
|
8807
9302
|
}).parse(await request(site));
|
|
@@ -8859,7 +9354,7 @@ async function runServiceCommand(service, options) {
|
|
|
8859
9354
|
} catch (cause) {
|
|
8860
9355
|
return {
|
|
8861
9356
|
exitCode: cause instanceof SiteOSAuthApiError ? 1 : 2,
|
|
8862
|
-
stderr: cause instanceof SiteOSAuthApiError || !(cause instanceof
|
|
9357
|
+
stderr: cause instanceof SiteOSAuthApiError || !(cause instanceof z15.ZodError) && cause instanceof Error ? cause.message : "The service returned an invalid response."
|
|
8863
9358
|
};
|
|
8864
9359
|
}
|
|
8865
9360
|
}
|