@canonry/canonry 4.159.0 → 4.161.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 +1 -1
- package/assets/agent-workspace/skills/aero/SKILL.md +16 -1
- package/assets/agent-workspace/skills/canonry/SKILL.md +17 -5
- package/assets/agent-workspace/skills/canonry/references/canonry-cli.md +66 -0
- package/assets/agent-workspace/skills/canonry/references/server-side-traffic.md +231 -62
- package/assets/assets/{AuditHistoryPanel-BiloGcQA.js → AuditHistoryPanel-C_1twF00.js} +1 -1
- package/assets/assets/{BacklinksPage-CPM1O2bC.js → BacklinksPage-CfAuMCaN.js} +1 -1
- package/assets/assets/{HistoryPage-1YOjtdJy.js → HistoryPage-DrRxOL3j.js} +1 -1
- package/assets/assets/{MeasurementPropertyPage-CVDTY_gC.js → MeasurementPropertyPage-BFtxx5Jw.js} +1 -1
- package/assets/assets/{ProjectPage-DnGXMx1n.js → ProjectPage-Djvhx4HD.js} +9 -9
- package/assets/assets/{RunRow-fIaadH4h.js → RunRow-DVZXibR2.js} +1 -1
- package/assets/assets/{RunsPage-DfifdM5z.js → RunsPage-CAUMy7By.js} +1 -1
- package/assets/assets/{SettingsPage-efA2EEmS.js → SettingsPage-CcSOmQAl.js} +1 -1
- package/assets/assets/{SiteHealthSection-BROtQGfu.js → SiteHealthSection-DYjEWd1I.js} +3 -3
- package/assets/assets/{TrafficPage-Wk43Bm_q.js → TrafficPage-BWM33jnJ.js} +1 -1
- package/assets/assets/{TrafficSourceDetailPage-BhBvoLWu.js → TrafficSourceDetailPage-DvEZ_JGY.js} +1 -1
- package/assets/assets/{extract-error-message-YGzZFvWs.js → extract-error-message-CnKOJkYB.js} +1 -1
- package/assets/assets/{index-DmsIZQAm.js → index-CQGAqmDx.js} +22 -22
- package/assets/assets/{react-sigma_core.esm.min-DM9XptbM.js → react-sigma_core.esm.min-CHCAIMBW.js} +1 -1
- package/assets/index.html +1 -1
- package/dist/{chunk-ZL3VY435.js → chunk-EXDO7EPL.js} +104 -9
- package/dist/{chunk-ZEG43TY2.js → chunk-HCGTKQCG.js} +21 -1
- package/dist/{chunk-5AFWHKKI.js → chunk-J27J2DF6.js} +2095 -703
- package/dist/{chunk-BZPBVOAM.js → chunk-W6OWEPZG.js} +70 -30
- package/dist/cli.js +308 -124
- package/dist/index.d.ts +28 -14
- package/dist/index.js +4 -4
- package/dist/{intelligence-service-DHY5CYWX.js → intelligence-service-HKDIVTZB.js} +2 -2
- package/dist/mcp.js +2 -2
- package/package.json +8 -7
package/dist/cli.js
CHANGED
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
detectAndTrackUpgrade,
|
|
17
17
|
emitInstallSummary,
|
|
18
18
|
formatAuditFactorScore,
|
|
19
|
-
|
|
19
|
+
getCloudflareTrafficConnectionBySourceId,
|
|
20
20
|
getMissingUserSkillsNudge,
|
|
21
21
|
getOrCreateAnonymousId,
|
|
22
22
|
installSkills,
|
|
@@ -31,7 +31,7 @@ import {
|
|
|
31
31
|
showFirstRunNotice,
|
|
32
32
|
trackCliCommandFinished,
|
|
33
33
|
trackEvent
|
|
34
|
-
} from "./chunk-
|
|
34
|
+
} from "./chunk-W6OWEPZG.js";
|
|
35
35
|
import {
|
|
36
36
|
CliError,
|
|
37
37
|
EXIT_SYSTEM_ERROR,
|
|
@@ -51,7 +51,7 @@ import {
|
|
|
51
51
|
saveConfigPatch,
|
|
52
52
|
systemError,
|
|
53
53
|
usageError
|
|
54
|
-
} from "./chunk-
|
|
54
|
+
} from "./chunk-HCGTKQCG.js";
|
|
55
55
|
import {
|
|
56
56
|
CLOUDFLARE_WORKER_BINDINGS,
|
|
57
57
|
CLOUDFLARE_WORKER_GENERATED_MARKER,
|
|
@@ -65,7 +65,7 @@ import {
|
|
|
65
65
|
projects,
|
|
66
66
|
queries,
|
|
67
67
|
renderReportHtml
|
|
68
|
-
} from "./chunk-
|
|
68
|
+
} from "./chunk-J27J2DF6.js";
|
|
69
69
|
import {
|
|
70
70
|
AdsDeliverySnapshotStatuses,
|
|
71
71
|
AdsHistoricalCampaignRollupStatuses,
|
|
@@ -129,7 +129,7 @@ import {
|
|
|
129
129
|
providerQuotaPolicySchema,
|
|
130
130
|
resolveProviderInput,
|
|
131
131
|
winnabilityClassSchema
|
|
132
|
-
} from "./chunk-
|
|
132
|
+
} from "./chunk-EXDO7EPL.js";
|
|
133
133
|
|
|
134
134
|
// src/cli.ts
|
|
135
135
|
import { pathToFileURL } from "url";
|
|
@@ -3875,9 +3875,9 @@ async function gaConnect(project, opts) {
|
|
|
3875
3875
|
propertyId: opts.propertyId
|
|
3876
3876
|
};
|
|
3877
3877
|
if (opts.keyFile) {
|
|
3878
|
-
const
|
|
3878
|
+
const fs19 = await import("fs");
|
|
3879
3879
|
try {
|
|
3880
|
-
const content =
|
|
3880
|
+
const content = fs19.readFileSync(opts.keyFile, "utf-8");
|
|
3881
3881
|
JSON.parse(content);
|
|
3882
3882
|
body.keyJson = content;
|
|
3883
3883
|
} catch (e) {
|
|
@@ -5292,6 +5292,9 @@ var GET_CLI_COMMANDS = [
|
|
|
5292
5292
|
}
|
|
5293
5293
|
];
|
|
5294
5294
|
|
|
5295
|
+
// src/commands/traffic.ts
|
|
5296
|
+
import fs5 from "fs";
|
|
5297
|
+
|
|
5295
5298
|
// src/cloudflare-worker-deploy.ts
|
|
5296
5299
|
import { spawn } from "child_process";
|
|
5297
5300
|
import { randomUUID } from "crypto";
|
|
@@ -5455,7 +5458,8 @@ function writeCloudflareWorkerArtifacts(artifacts, contents) {
|
|
|
5455
5458
|
}
|
|
5456
5459
|
function assertCloudflareArtifactsDoNotContainSecrets(contents, secrets) {
|
|
5457
5460
|
const artifactText = [contents.workerScript, contents.wranglerToml, contents.instructions ?? ""];
|
|
5458
|
-
|
|
5461
|
+
const secretValues = "apiToken" in secrets ? [secrets.apiToken] : [secrets.bearerToken, secrets.hmacSecret];
|
|
5462
|
+
for (const secret of secretValues) {
|
|
5459
5463
|
if (secret && artifactText.some((value) => value.includes(secret))) {
|
|
5460
5464
|
throw new Error("Cloudflare setup response unexpectedly contained a cleartext secret");
|
|
5461
5465
|
}
|
|
@@ -5463,7 +5467,8 @@ function assertCloudflareArtifactsDoNotContainSecrets(contents, secrets) {
|
|
|
5463
5467
|
}
|
|
5464
5468
|
function redactCloudflareSecrets(message, secrets) {
|
|
5465
5469
|
let redacted = message;
|
|
5466
|
-
|
|
5470
|
+
const secretValues = "apiToken" in secrets ? [secrets.apiToken] : [secrets.bearerToken, secrets.hmacSecret];
|
|
5471
|
+
for (const secret of secretValues) {
|
|
5467
5472
|
if (secret) redacted = redacted.replaceAll(secret, "[REDACTED]");
|
|
5468
5473
|
}
|
|
5469
5474
|
return redacted;
|
|
@@ -5520,12 +5525,14 @@ ${diagnostic}` : "";
|
|
|
5520
5525
|
}
|
|
5521
5526
|
async function preflightCloudflareWrangler(opts = {}) {
|
|
5522
5527
|
const runner = opts.run ?? runWranglerCapture;
|
|
5528
|
+
const deliveryMode = opts.deliveryMode ?? "direct-push";
|
|
5523
5529
|
const help = await runner(
|
|
5524
5530
|
"wrangler",
|
|
5525
5531
|
["deploy", "--help"],
|
|
5526
5532
|
{ cwd: path2.resolve(opts.cwd ?? process.cwd()) }
|
|
5527
5533
|
);
|
|
5528
|
-
const
|
|
5534
|
+
const requiredFlags = deliveryMode === "direct-push" ? ["--dry-run", "--secrets-file", "--strict"] : ["--dry-run", "--strict"];
|
|
5535
|
+
const missing = requiredFlags.filter((flag) => !help.includes(flag));
|
|
5529
5536
|
if (missing.length > 0) {
|
|
5530
5537
|
throw new Error(`Wrangler deploy does not support required flags: ${missing.join(", ")}`);
|
|
5531
5538
|
}
|
|
@@ -5539,29 +5546,38 @@ async function preflightCloudflareWrangler(opts = {}) {
|
|
|
5539
5546
|
try {
|
|
5540
5547
|
fs4.chmodSync(tempDirectory, 448);
|
|
5541
5548
|
fs4.writeFileSync(workerScriptPath, generateWorkerScript({
|
|
5542
|
-
deliveryMode
|
|
5549
|
+
deliveryMode,
|
|
5543
5550
|
workerVersion: "preflight",
|
|
5544
5551
|
botList: DEFAULT_BOT_LIST
|
|
5545
5552
|
}), { encoding: "utf-8", flag: "wx", mode: 384 });
|
|
5546
|
-
fs4.writeFileSync(wranglerTomlPath, generateWranglerToml(
|
|
5547
|
-
deliveryMode
|
|
5548
|
-
|
|
5549
|
-
|
|
5550
|
-
|
|
5551
|
-
|
|
5552
|
-
|
|
5553
|
-
|
|
5554
|
-
|
|
5555
|
-
|
|
5556
|
-
|
|
5553
|
+
fs4.writeFileSync(wranglerTomlPath, generateWranglerToml(
|
|
5554
|
+
deliveryMode === "queue-pull" ? {
|
|
5555
|
+
deliveryMode,
|
|
5556
|
+
sourceId: "preflight",
|
|
5557
|
+
hostname: "example.invalid",
|
|
5558
|
+
queueName: "canonry-preflight",
|
|
5559
|
+
workerVersion: "preflight"
|
|
5560
|
+
} : {
|
|
5561
|
+
deliveryMode,
|
|
5562
|
+
sourceId: "preflight",
|
|
5563
|
+
hostname: "example.invalid",
|
|
5564
|
+
ingestUrl: "https://canonry.example.invalid/api/v1/projects/preflight/traffic/cloudflare/ingest",
|
|
5565
|
+
workerVersion: "preflight"
|
|
5566
|
+
}
|
|
5567
|
+
), { encoding: "utf-8", flag: "wx", mode: 384 });
|
|
5568
|
+
if (deliveryMode === "direct-push") {
|
|
5569
|
+
fs4.writeFileSync(secretsPath, JSON.stringify({
|
|
5570
|
+
[CLOUDFLARE_WORKER_BINDINGS.bearerToken]: "preflight-bearer",
|
|
5571
|
+
[CLOUDFLARE_WORKER_BINDINGS.hmacSecret]: "preflight-hmac"
|
|
5572
|
+
}), { encoding: "utf-8", flag: "wx", mode: 384 });
|
|
5573
|
+
}
|
|
5557
5574
|
const validationOutput = await runner(
|
|
5558
5575
|
"wrangler",
|
|
5559
5576
|
[
|
|
5560
5577
|
"deploy",
|
|
5561
5578
|
"--config",
|
|
5562
5579
|
wranglerTomlPath,
|
|
5563
|
-
"--secrets-file",
|
|
5564
|
-
secretsPath,
|
|
5580
|
+
...deliveryMode === "direct-push" ? ["--secrets-file", secretsPath] : [],
|
|
5565
5581
|
"--strict",
|
|
5566
5582
|
"--dry-run",
|
|
5567
5583
|
"--outdir",
|
|
@@ -5582,6 +5598,18 @@ ${wranglerDiagnostic(validationOutput)}`
|
|
|
5582
5598
|
}
|
|
5583
5599
|
}
|
|
5584
5600
|
async function deployCloudflareWorker(opts) {
|
|
5601
|
+
const deliveryMode = opts.deliveryMode ?? "direct-push";
|
|
5602
|
+
if (deliveryMode === "queue-pull") {
|
|
5603
|
+
const runner = opts.run ?? runWrangler;
|
|
5604
|
+
await runner(
|
|
5605
|
+
"wrangler",
|
|
5606
|
+
["deploy", "--config", path2.resolve(opts.wranglerTomlPath), "--strict"],
|
|
5607
|
+
{ cwd: path2.dirname(path2.resolve(opts.wranglerTomlPath)) }
|
|
5608
|
+
);
|
|
5609
|
+
return;
|
|
5610
|
+
}
|
|
5611
|
+
if (!opts.secrets) throw new Error("Direct-push Worker deploy requires bearer and HMAC secrets");
|
|
5612
|
+
const secrets = opts.secrets;
|
|
5585
5613
|
const tempDirectory = fs4.mkdtempSync(path2.join(opts.tempRoot ?? os.tmpdir(), "canonry-cloudflare-secrets-"));
|
|
5586
5614
|
const secretsPath = path2.join(tempDirectory, "secrets.json");
|
|
5587
5615
|
try {
|
|
@@ -5589,8 +5617,8 @@ async function deployCloudflareWorker(opts) {
|
|
|
5589
5617
|
fs4.writeFileSync(
|
|
5590
5618
|
secretsPath,
|
|
5591
5619
|
JSON.stringify({
|
|
5592
|
-
[CLOUDFLARE_WORKER_BINDINGS.bearerToken]:
|
|
5593
|
-
[CLOUDFLARE_WORKER_BINDINGS.hmacSecret]:
|
|
5620
|
+
[CLOUDFLARE_WORKER_BINDINGS.bearerToken]: secrets.bearerToken,
|
|
5621
|
+
[CLOUDFLARE_WORKER_BINDINGS.hmacSecret]: secrets.hmacSecret
|
|
5594
5622
|
}),
|
|
5595
5623
|
{ encoding: "utf-8", flag: "wx", mode: 384 }
|
|
5596
5624
|
);
|
|
@@ -5618,6 +5646,9 @@ async function deployCloudflareWorker(opts) {
|
|
|
5618
5646
|
}
|
|
5619
5647
|
|
|
5620
5648
|
// src/commands/traffic.ts
|
|
5649
|
+
var DEFAULT_CLOUDFLARE_QUEUE_RETENTION_SECONDS = 345600;
|
|
5650
|
+
var CLOUDFLARE_QUEUE_RETENTION_MIN_SECONDS = 60;
|
|
5651
|
+
var CLOUDFLARE_QUEUE_RETENTION_MAX_SECONDS = 1209600;
|
|
5621
5652
|
function getClient9() {
|
|
5622
5653
|
return createApiClient();
|
|
5623
5654
|
}
|
|
@@ -5627,7 +5658,60 @@ function configString(value, fallback = "(unset)") {
|
|
|
5627
5658
|
function errorMessage2(error) {
|
|
5628
5659
|
return error instanceof Error ? error.message : String(error);
|
|
5629
5660
|
}
|
|
5661
|
+
function requireCloudflareQueueOption(project, value, option) {
|
|
5662
|
+
if (value?.trim()) return value.trim();
|
|
5663
|
+
const code = option === "--account-id" ? "TRAFFIC_CLOUDFLARE_QUEUE_ACCOUNT_REQUIRED" : option === "--queue-id" ? "TRAFFIC_CLOUDFLARE_QUEUE_ID_REQUIRED" : option === "--queue-name" ? "TRAFFIC_CLOUDFLARE_QUEUE_NAME_REQUIRED" : "TRAFFIC_CLOUDFLARE_QUEUE_TOKEN_FILE_REQUIRED";
|
|
5664
|
+
throw new CliError({
|
|
5665
|
+
code,
|
|
5666
|
+
message: `${option} is required with --delivery-mode queue-pull`,
|
|
5667
|
+
displayMessage: `Error: ${option} is required with --delivery-mode queue-pull`,
|
|
5668
|
+
details: { project }
|
|
5669
|
+
});
|
|
5670
|
+
}
|
|
5671
|
+
function readCloudflareQueueApiToken(project, tokenFile) {
|
|
5672
|
+
try {
|
|
5673
|
+
const stat = fs5.lstatSync(tokenFile);
|
|
5674
|
+
if (!stat.isFile() || stat.isSymbolicLink()) {
|
|
5675
|
+
throw new Error("not a regular file");
|
|
5676
|
+
}
|
|
5677
|
+
const apiToken = fs5.readFileSync(tokenFile, "utf-8").trim();
|
|
5678
|
+
if (!apiToken) throw new Error("file is empty");
|
|
5679
|
+
return apiToken;
|
|
5680
|
+
} catch (error) {
|
|
5681
|
+
const message = errorMessage2(error);
|
|
5682
|
+
throw new CliError({
|
|
5683
|
+
code: "TRAFFIC_CLOUDFLARE_QUEUE_TOKEN_FILE_READ_ERROR",
|
|
5684
|
+
message: `Failed to read --api-token-file: ${message}`,
|
|
5685
|
+
displayMessage: `Error: failed to read --api-token-file "${tokenFile}": ${message}`,
|
|
5686
|
+
details: { project, tokenFile }
|
|
5687
|
+
});
|
|
5688
|
+
}
|
|
5689
|
+
}
|
|
5630
5690
|
async function trafficConnectCloudflare(project, opts, deps = {}) {
|
|
5691
|
+
const deliveryMode = opts.deliveryMode ?? "direct-push";
|
|
5692
|
+
let queuePull;
|
|
5693
|
+
if (deliveryMode === "queue-pull") {
|
|
5694
|
+
const accountId = requireCloudflareQueueOption(project, opts.accountId, "--account-id");
|
|
5695
|
+
const queueId = requireCloudflareQueueOption(project, opts.queueId, "--queue-id");
|
|
5696
|
+
const queueName = requireCloudflareQueueOption(project, opts.queueName, "--queue-name");
|
|
5697
|
+
const tokenFile = requireCloudflareQueueOption(project, opts.apiTokenFile, "--api-token-file");
|
|
5698
|
+
const retentionSeconds = opts.retentionSeconds ?? DEFAULT_CLOUDFLARE_QUEUE_RETENTION_SECONDS;
|
|
5699
|
+
if (!Number.isInteger(retentionSeconds) || retentionSeconds < CLOUDFLARE_QUEUE_RETENTION_MIN_SECONDS || retentionSeconds > CLOUDFLARE_QUEUE_RETENTION_MAX_SECONDS) {
|
|
5700
|
+
throw new CliError({
|
|
5701
|
+
code: "TRAFFIC_CLOUDFLARE_QUEUE_RETENTION_INVALID",
|
|
5702
|
+
message: "--retention-seconds must be an integer between 60 and 1209600",
|
|
5703
|
+
displayMessage: "Error: --retention-seconds must be an integer between 60 and 1209600",
|
|
5704
|
+
details: { project }
|
|
5705
|
+
});
|
|
5706
|
+
}
|
|
5707
|
+
queuePull = {
|
|
5708
|
+
accountId,
|
|
5709
|
+
queueId,
|
|
5710
|
+
queueName,
|
|
5711
|
+
apiToken: readCloudflareQueueApiToken(project, tokenFile),
|
|
5712
|
+
retentionSeconds
|
|
5713
|
+
};
|
|
5714
|
+
}
|
|
5631
5715
|
if (opts.deploy && !opts.zoneId) {
|
|
5632
5716
|
throw new CliError({
|
|
5633
5717
|
code: "TRAFFIC_CLOUDFLARE_DEPLOY_ZONE_REQUIRED",
|
|
@@ -5654,7 +5738,7 @@ async function trafficConnectCloudflare(project, opts, deps = {}) {
|
|
|
5654
5738
|
}
|
|
5655
5739
|
if (opts.deploy) {
|
|
5656
5740
|
try {
|
|
5657
|
-
await (deps.preflightWrangler ?? preflightCloudflareWrangler)();
|
|
5741
|
+
await (deps.preflightWrangler ?? preflightCloudflareWrangler)({ deliveryMode });
|
|
5658
5742
|
} catch (error) {
|
|
5659
5743
|
const message = errorMessage2(error);
|
|
5660
5744
|
throw new CliError({
|
|
@@ -5680,18 +5764,27 @@ ${message}`,
|
|
|
5680
5764
|
});
|
|
5681
5765
|
}
|
|
5682
5766
|
const client = deps.client ?? getClient9();
|
|
5683
|
-
const result = await client.trafficConnectCloudflare(
|
|
5684
|
-
|
|
5685
|
-
|
|
5686
|
-
|
|
5687
|
-
|
|
5688
|
-
|
|
5767
|
+
const result = await client.trafficConnectCloudflare(
|
|
5768
|
+
project,
|
|
5769
|
+
deliveryMode === "queue-pull" ? {
|
|
5770
|
+
deliveryMode,
|
|
5771
|
+
displayName: opts.displayName,
|
|
5772
|
+
zoneId: opts.zoneId,
|
|
5773
|
+
...queuePull
|
|
5774
|
+
} : {
|
|
5775
|
+
deliveryMode,
|
|
5776
|
+
displayName: opts.displayName,
|
|
5777
|
+
zoneId: opts.zoneId,
|
|
5778
|
+
accountId: opts.accountId
|
|
5779
|
+
}
|
|
5780
|
+
);
|
|
5689
5781
|
const localConfig = (deps.loadLocalConfig ?? loadConfigRaw)();
|
|
5690
|
-
const localCredential = localConfig ?
|
|
5691
|
-
const deploymentCredential = localCredential?.
|
|
5692
|
-
|
|
5782
|
+
const localCredential = localConfig ? getCloudflareTrafficConnectionBySourceId(localConfig, result.sourceId) : void 0;
|
|
5783
|
+
const deploymentCredential = localCredential?.deliveryMode === result.deliveryMode ? localCredential : void 0;
|
|
5784
|
+
const artifactSecrets = deploymentCredential ?? (queuePull ? { apiToken: queuePull.apiToken } : void 0);
|
|
5785
|
+
if (artifactSecrets) {
|
|
5693
5786
|
try {
|
|
5694
|
-
assertCloudflareArtifactsDoNotContainSecrets(result,
|
|
5787
|
+
assertCloudflareArtifactsDoNotContainSecrets(result, artifactSecrets);
|
|
5695
5788
|
} catch {
|
|
5696
5789
|
throw new CliError({
|
|
5697
5790
|
code: "TRAFFIC_CLOUDFLARE_SECRET_EXPOSURE",
|
|
@@ -5712,7 +5805,7 @@ ${message}`,
|
|
|
5712
5805
|
details: { project, sourceId: result.sourceId, outputDirectory }
|
|
5713
5806
|
});
|
|
5714
5807
|
}
|
|
5715
|
-
if (opts.deploy && !deploymentCredential) {
|
|
5808
|
+
if (opts.deploy && result.deliveryMode === "direct-push" && !deploymentCredential) {
|
|
5716
5809
|
throw new CliError({
|
|
5717
5810
|
code: "TRAFFIC_CLOUDFLARE_DEPLOY_CONFIG_UNSHARED",
|
|
5718
5811
|
message: "Cloudflare auto-deploy requires the CLI and Canonry server to share the same local config",
|
|
@@ -5724,7 +5817,7 @@ ${message}`,
|
|
|
5724
5817
|
}
|
|
5725
5818
|
});
|
|
5726
5819
|
}
|
|
5727
|
-
if (opts.deploy && deploymentCredential) {
|
|
5820
|
+
if (opts.deploy && result.deliveryMode === "direct-push" && deploymentCredential?.deliveryMode === "direct-push") {
|
|
5728
5821
|
try {
|
|
5729
5822
|
await (deps.deployWorker ?? deployCloudflareWorker)({
|
|
5730
5823
|
wranglerTomlPath: artifacts.wranglerTomlPath,
|
|
@@ -5740,16 +5833,40 @@ ${message}`,
|
|
|
5740
5833
|
});
|
|
5741
5834
|
}
|
|
5742
5835
|
}
|
|
5836
|
+
if (opts.deploy && result.deliveryMode === "queue-pull") {
|
|
5837
|
+
try {
|
|
5838
|
+
await (deps.deployWorker ?? deployCloudflareWorker)({
|
|
5839
|
+
wranglerTomlPath: artifacts.wranglerTomlPath,
|
|
5840
|
+
deliveryMode: "queue-pull"
|
|
5841
|
+
});
|
|
5842
|
+
} catch (error) {
|
|
5843
|
+
const message = artifactSecrets ? redactCloudflareSecrets(errorMessage2(error), artifactSecrets) : errorMessage2(error);
|
|
5844
|
+
throw new CliError({
|
|
5845
|
+
code: "TRAFFIC_CLOUDFLARE_DEPLOY_FAILED",
|
|
5846
|
+
message,
|
|
5847
|
+
displayMessage: `Error: Cloudflare source connected and artifacts written, but Wrangler deploy failed: ${message}`,
|
|
5848
|
+
details: { project, sourceId: result.sourceId, outputDirectory }
|
|
5849
|
+
});
|
|
5850
|
+
}
|
|
5851
|
+
}
|
|
5743
5852
|
const summary = {
|
|
5744
5853
|
sourceId: result.sourceId,
|
|
5745
5854
|
deliveryMode: result.deliveryMode,
|
|
5855
|
+
activationRequired: result.activationRequired === true,
|
|
5746
5856
|
workerVersion: result.workerVersion,
|
|
5747
5857
|
outputDirectory: artifacts.outputDirectory,
|
|
5748
5858
|
files: {
|
|
5749
5859
|
workerScript: artifacts.workerScriptPath,
|
|
5750
5860
|
wranglerConfig: artifacts.wranglerTomlPath
|
|
5751
5861
|
},
|
|
5752
|
-
autoDeployAvailable: Boolean(deploymentCredential),
|
|
5862
|
+
autoDeployAvailable: result.deliveryMode === "queue-pull" || Boolean(deploymentCredential),
|
|
5863
|
+
...result.deliveryMode === "queue-pull" ? {
|
|
5864
|
+
queuePullConsumer: {
|
|
5865
|
+
required: true,
|
|
5866
|
+
queueName: result.queueName,
|
|
5867
|
+
configuredBy: "wrangler-or-cloudflare-dashboard"
|
|
5868
|
+
}
|
|
5869
|
+
} : {},
|
|
5753
5870
|
deployment: opts.deploy ? "worker-deployed-route-unattached" : "not-requested",
|
|
5754
5871
|
routeAttachment: "required-manual",
|
|
5755
5872
|
requestLimitFailureMode: {
|
|
@@ -5764,7 +5881,7 @@ ${message}`,
|
|
|
5764
5881
|
console.log(JSON.stringify(summary, null, 2));
|
|
5765
5882
|
return;
|
|
5766
5883
|
}
|
|
5767
|
-
console.log(`Cloudflare
|
|
5884
|
+
console.log(`Cloudflare ${result.deliveryMode} traffic source connected for project "${project}".`);
|
|
5768
5885
|
console.log(` Source ID: ${result.sourceId}`);
|
|
5769
5886
|
console.log(` Worker version: ${result.workerVersion}`);
|
|
5770
5887
|
console.log(` Worker script: ${artifacts.workerScriptPath}`);
|
|
@@ -5776,17 +5893,42 @@ ${message}`,
|
|
|
5776
5893
|
console.log("Required: set the route Request limit failure mode to Fail open before route activation.");
|
|
5777
5894
|
console.log("Wrangler cannot configure this toggle. Workers Free includes 100,000 requests per day for the account.");
|
|
5778
5895
|
console.log("Use one authoritative server-traffic source for this site. Overlapping adapters double-count project totals.");
|
|
5896
|
+
if (result.deliveryMode === "queue-pull") {
|
|
5897
|
+
console.log(`Required: enable HTTP pull with \`wrangler queues consumer http add ${result.queueName}\` before the first sync.`);
|
|
5898
|
+
}
|
|
5899
|
+
if (result.activationRequired) {
|
|
5900
|
+
console.log(`Activation required: run \`canonry traffic activate ${project} --source ${result.sourceId}\`. This command does not activate delivery.`);
|
|
5901
|
+
}
|
|
5779
5902
|
if (opts.deploy) {
|
|
5780
5903
|
console.log("");
|
|
5781
5904
|
console.log("Required Cloudflare Dashboard steps:");
|
|
5782
5905
|
console.log(result.instructions);
|
|
5783
|
-
|
|
5906
|
+
if (result.deliveryMode === "direct-push") {
|
|
5907
|
+
console.log("Keep the Canonry public HTTPS URL reachable so the Worker can push events.");
|
|
5908
|
+
}
|
|
5784
5909
|
} else if (deploymentCredential) {
|
|
5785
5910
|
console.log("Next: install and authenticate Wrangler. Inspect the route, then rerun with --deploy --confirm-route --confirm-fail-open and --zone-id <id>.");
|
|
5911
|
+
} else if (result.deliveryMode === "queue-pull") {
|
|
5912
|
+
console.log("Next: install and authenticate Wrangler externally, then rerun with --deploy --confirm-route --confirm-fail-open and --zone-id <id>.");
|
|
5786
5913
|
} else {
|
|
5787
5914
|
console.log("Auto-deploy is unavailable because this CLI and the Canonry server do not share local config. Ask the server operator to deploy and install the Worker secrets.");
|
|
5788
5915
|
}
|
|
5789
5916
|
}
|
|
5917
|
+
async function trafficActivate(project, opts, deps = {}) {
|
|
5918
|
+
const client = deps.client ?? getClient9();
|
|
5919
|
+
const source = await client.trafficActivate(project, opts.sourceId);
|
|
5920
|
+
if (isMachineFormat(opts.format)) {
|
|
5921
|
+
console.log(JSON.stringify(source, null, 2));
|
|
5922
|
+
return;
|
|
5923
|
+
}
|
|
5924
|
+
const sourceMode = typeof source.config.deliveryMode === "string" ? source.config.deliveryMode : source.sourceType;
|
|
5925
|
+
const trafficSync2 = sourceMode === "direct-push" ? "not required for direct-push delivery" : "enabled for this pull source";
|
|
5926
|
+
console.log(`Traffic source activated for project "${project}".`);
|
|
5927
|
+
console.log(` Source ID: ${source.id}`);
|
|
5928
|
+
console.log(` Source mode: ${sourceMode}`);
|
|
5929
|
+
console.log(` Status: ${source.status}`);
|
|
5930
|
+
console.log(` Traffic sync: ${trafficSync2}`);
|
|
5931
|
+
}
|
|
5790
5932
|
async function trafficConnectWordpress(project, opts) {
|
|
5791
5933
|
if (!opts.url) {
|
|
5792
5934
|
throw new CliError({
|
|
@@ -5814,9 +5956,9 @@ async function trafficConnectWordpress(project, opts) {
|
|
|
5814
5956
|
}
|
|
5815
5957
|
let applicationPassword = opts.appPassword?.trim() ?? "";
|
|
5816
5958
|
if (!applicationPassword && opts.appPasswordFile) {
|
|
5817
|
-
const
|
|
5959
|
+
const fs19 = await import("fs");
|
|
5818
5960
|
try {
|
|
5819
|
-
applicationPassword =
|
|
5961
|
+
applicationPassword = fs19.readFileSync(opts.appPasswordFile, "utf-8").trim();
|
|
5820
5962
|
} catch (e) {
|
|
5821
5963
|
const msg = e instanceof Error ? e.message : String(e);
|
|
5822
5964
|
throw new CliError({
|
|
@@ -5872,10 +6014,10 @@ async function trafficConnectCloudRun(project, opts) {
|
|
|
5872
6014
|
details: { project }
|
|
5873
6015
|
});
|
|
5874
6016
|
}
|
|
5875
|
-
const
|
|
6017
|
+
const fs19 = await import("fs");
|
|
5876
6018
|
let keyJson;
|
|
5877
6019
|
try {
|
|
5878
|
-
keyJson =
|
|
6020
|
+
keyJson = fs19.readFileSync(opts.serviceAccountKey, "utf-8");
|
|
5879
6021
|
JSON.parse(keyJson);
|
|
5880
6022
|
} catch (e) {
|
|
5881
6023
|
const msg = e instanceof Error ? e.message : String(e);
|
|
@@ -5935,9 +6077,9 @@ async function trafficConnectVercel(project, opts) {
|
|
|
5935
6077
|
}
|
|
5936
6078
|
let token = opts.token?.trim() ?? "";
|
|
5937
6079
|
if (!token && opts.tokenFile) {
|
|
5938
|
-
const
|
|
6080
|
+
const fs19 = await import("fs");
|
|
5939
6081
|
try {
|
|
5940
|
-
token =
|
|
6082
|
+
token = fs19.readFileSync(opts.tokenFile, "utf-8").trim();
|
|
5941
6083
|
} catch (e) {
|
|
5942
6084
|
const msg = e instanceof Error ? e.message : String(e);
|
|
5943
6085
|
throw new CliError({
|
|
@@ -6288,11 +6430,16 @@ async function trafficEvents(project, opts) {
|
|
|
6288
6430
|
var TRAFFIC_CLI_COMMANDS = [
|
|
6289
6431
|
{
|
|
6290
6432
|
path: ["traffic", "connect", "cloudflare"],
|
|
6291
|
-
usage: "canonry traffic connect cloudflare <project> [--display-name <name>] [--zone-id <id>] [--account-id <id>] [--output-dir <dir>] [--deploy --confirm-route --confirm-fail-open] [--format json]",
|
|
6433
|
+
usage: "canonry traffic connect cloudflare <project> [--delivery-mode direct-push|queue-pull] [--display-name <name>] [--zone-id <id>] [--account-id <id>] [--queue-id <id> --queue-name <name> --api-token-file <path> --retention-seconds 345600] [--output-dir <dir>] [--deploy --confirm-route --confirm-fail-open] [--format json]",
|
|
6292
6434
|
options: {
|
|
6293
6435
|
"display-name": stringOption(),
|
|
6294
6436
|
"zone-id": stringOption(),
|
|
6295
6437
|
"account-id": stringOption(),
|
|
6438
|
+
"delivery-mode": stringOption(),
|
|
6439
|
+
"queue-id": stringOption(),
|
|
6440
|
+
"queue-name": stringOption(),
|
|
6441
|
+
"api-token-file": stringOption(),
|
|
6442
|
+
"retention-seconds": stringOption(),
|
|
6296
6443
|
"output-dir": stringOption(),
|
|
6297
6444
|
deploy: { type: "boolean" },
|
|
6298
6445
|
"confirm-route": { type: "boolean" },
|
|
@@ -6302,12 +6449,26 @@ var TRAFFIC_CLI_COMMANDS = [
|
|
|
6302
6449
|
const project = requireProject(
|
|
6303
6450
|
input,
|
|
6304
6451
|
"traffic.connect.cloudflare",
|
|
6305
|
-
"canonry traffic connect cloudflare <project> [--zone-id <id>] [--output-dir <dir>] [--deploy --confirm-route --confirm-fail-open]"
|
|
6452
|
+
"canonry traffic connect cloudflare <project> [--delivery-mode direct-push|queue-pull] [--zone-id <id>] [--output-dir <dir>] [--deploy --confirm-route --confirm-fail-open]"
|
|
6306
6453
|
);
|
|
6454
|
+
const deliveryMode = getString(input.values, "delivery-mode") ?? "direct-push";
|
|
6455
|
+
if (deliveryMode !== "direct-push" && deliveryMode !== "queue-pull") {
|
|
6456
|
+
throw new Error("--delivery-mode must be direct-push or queue-pull");
|
|
6457
|
+
}
|
|
6458
|
+
const retentionSeconds = parseIntegerOption(input, "retention-seconds", {
|
|
6459
|
+
command: "traffic.connect.cloudflare",
|
|
6460
|
+
usage: "canonry traffic connect cloudflare <project> --delivery-mode queue-pull --retention-seconds 345600",
|
|
6461
|
+
message: "--retention-seconds must be an integer"
|
|
6462
|
+
});
|
|
6307
6463
|
await trafficConnectCloudflare(project, {
|
|
6464
|
+
deliveryMode,
|
|
6308
6465
|
displayName: getString(input.values, "display-name"),
|
|
6309
6466
|
zoneId: getString(input.values, "zone-id"),
|
|
6310
6467
|
accountId: getString(input.values, "account-id"),
|
|
6468
|
+
queueId: getString(input.values, "queue-id"),
|
|
6469
|
+
queueName: getString(input.values, "queue-name"),
|
|
6470
|
+
apiTokenFile: getString(input.values, "api-token-file"),
|
|
6471
|
+
retentionSeconds: retentionSeconds ?? DEFAULT_CLOUDFLARE_QUEUE_RETENTION_SECONDS,
|
|
6311
6472
|
outputDirectory: getString(input.values, "output-dir"),
|
|
6312
6473
|
deploy: getBoolean(input.values, "deploy"),
|
|
6313
6474
|
confirmRoute: getBoolean(input.values, "confirm-route"),
|
|
@@ -6419,6 +6580,23 @@ var TRAFFIC_CLI_COMMANDS = [
|
|
|
6419
6580
|
});
|
|
6420
6581
|
}
|
|
6421
6582
|
},
|
|
6583
|
+
{
|
|
6584
|
+
path: ["traffic", "activate"],
|
|
6585
|
+
usage: "canonry traffic activate <project> --source <id> [--format json]",
|
|
6586
|
+
options: {
|
|
6587
|
+
source: stringOption()
|
|
6588
|
+
},
|
|
6589
|
+
run: async (input) => {
|
|
6590
|
+
const project = requireProject(
|
|
6591
|
+
input,
|
|
6592
|
+
"traffic.activate",
|
|
6593
|
+
"canonry traffic activate <project> --source <id>"
|
|
6594
|
+
);
|
|
6595
|
+
const sourceId = getString(input.values, "source");
|
|
6596
|
+
if (!sourceId) throw new Error("--source <id> is required");
|
|
6597
|
+
await trafficActivate(project, { sourceId, format: input.format });
|
|
6598
|
+
}
|
|
6599
|
+
},
|
|
6422
6600
|
{
|
|
6423
6601
|
path: ["traffic", "sync"],
|
|
6424
6602
|
usage: "canonry traffic sync <project> --source <id> [--since-minutes 43200] [--format json]",
|
|
@@ -6568,7 +6746,7 @@ var TRAFFIC_CLI_COMMANDS = [
|
|
|
6568
6746
|
unknownSubcommand(input.positionals[0], {
|
|
6569
6747
|
command: "traffic",
|
|
6570
6748
|
usage: "canonry traffic <subcommand> <project> [args]",
|
|
6571
|
-
available: ["connect", "sync", "backfill", "status", "sources", "events"]
|
|
6749
|
+
available: ["connect", "activate", "sync", "backfill", "status", "sources", "events"]
|
|
6572
6750
|
});
|
|
6573
6751
|
}
|
|
6574
6752
|
}
|
|
@@ -6914,7 +7092,7 @@ async function googlePerformanceDaily(project, opts) {
|
|
|
6914
7092
|
console.log(JSON.stringify(data, null, 2));
|
|
6915
7093
|
return;
|
|
6916
7094
|
} else if (opts.format === "jsonl") {
|
|
6917
|
-
emitJsonl(data.daily.map((row) => ({ project, ...row })));
|
|
7095
|
+
emitJsonl(data.daily.map((row) => ({ project, window: data.window, ...row })));
|
|
6918
7096
|
return;
|
|
6919
7097
|
}
|
|
6920
7098
|
if (data.daily.length === 0) {
|
|
@@ -6922,8 +7100,18 @@ async function googlePerformanceDaily(project, opts) {
|
|
|
6922
7100
|
return;
|
|
6923
7101
|
}
|
|
6924
7102
|
const { clicks, impressions, ctr, days } = data.totals;
|
|
6925
|
-
|
|
7103
|
+
const win = data.window;
|
|
7104
|
+
const range = win?.startDate && win.endDate ? ` ${win.startDate} to ${win.endDate}` : "";
|
|
7105
|
+
console.log(`GSC daily summary (${days} day${days === 1 ? "" : "s"}${range}):
|
|
6926
7106
|
`);
|
|
7107
|
+
const endsAtFrontier = win?.endDate !== void 0 && win.endDate === win.latestDataDate;
|
|
7108
|
+
if (endsAtFrontier && (win.daysSinceLatestData ?? 0) > 0) {
|
|
7109
|
+
console.log(` Search Console data through ${win.endDate} (${win.daysSinceLatestData} day${win.daysSinceLatestData === 1 ? "" : "s"} ago).`);
|
|
7110
|
+
console.log();
|
|
7111
|
+
} else if (win?.latestDataDate && win.endDate !== win.latestDataDate) {
|
|
7112
|
+
console.log(` Showing a fixed range. Latest Search Console data is ${win.latestDataDate}.`);
|
|
7113
|
+
console.log();
|
|
7114
|
+
}
|
|
6927
7115
|
console.log(` Clicks: ${clicks.toLocaleString()}`);
|
|
6928
7116
|
console.log(` Impressions: ${impressions.toLocaleString()}`);
|
|
6929
7117
|
console.log(` CTR: ${(ctr * 100).toFixed(2)}%`);
|
|
@@ -6988,11 +7176,7 @@ async function googlePerformance(project, opts) {
|
|
|
6988
7176
|
if (opts.startDate) params.startDate = opts.startDate;
|
|
6989
7177
|
if (opts.endDate) params.endDate = opts.endDate;
|
|
6990
7178
|
} else if (opts.days) {
|
|
6991
|
-
|
|
6992
|
-
const start = /* @__PURE__ */ new Date();
|
|
6993
|
-
start.setDate(start.getDate() - opts.days);
|
|
6994
|
-
params.startDate = start.toISOString().split("T")[0];
|
|
6995
|
-
params.endDate = end.toISOString().split("T")[0];
|
|
7179
|
+
params.days = String(opts.days);
|
|
6996
7180
|
}
|
|
6997
7181
|
if (opts.keyword) params.query = opts.keyword;
|
|
6998
7182
|
if (opts.page) params.page = opts.page;
|
|
@@ -8310,7 +8494,7 @@ var USERS_CLI_COMMANDS = [
|
|
|
8310
8494
|
];
|
|
8311
8495
|
|
|
8312
8496
|
// src/commands/keyword.ts
|
|
8313
|
-
import
|
|
8497
|
+
import fs6 from "fs";
|
|
8314
8498
|
function getClient14() {
|
|
8315
8499
|
return createApiClient();
|
|
8316
8500
|
}
|
|
@@ -8378,7 +8562,7 @@ async function listKeywords(project, format) {
|
|
|
8378
8562
|
}
|
|
8379
8563
|
}
|
|
8380
8564
|
async function importKeywords(project, filePath, format) {
|
|
8381
|
-
if (!
|
|
8565
|
+
if (!fs6.existsSync(filePath)) {
|
|
8382
8566
|
throw new CliError({
|
|
8383
8567
|
code: "KEYWORD_IMPORT_FILE_NOT_FOUND",
|
|
8384
8568
|
message: `File not found: ${filePath}`,
|
|
@@ -8389,7 +8573,7 @@ async function importKeywords(project, filePath, format) {
|
|
|
8389
8573
|
}
|
|
8390
8574
|
});
|
|
8391
8575
|
}
|
|
8392
|
-
const content =
|
|
8576
|
+
const content = fs6.readFileSync(filePath, "utf-8");
|
|
8393
8577
|
const keywords = content.split("\n").map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#"));
|
|
8394
8578
|
if (keywords.length === 0) {
|
|
8395
8579
|
if (isMachineFormat(format)) {
|
|
@@ -8590,7 +8774,7 @@ var KEYWORD_CLI_COMMANDS = [
|
|
|
8590
8774
|
];
|
|
8591
8775
|
|
|
8592
8776
|
// src/commands/query.ts
|
|
8593
|
-
import
|
|
8777
|
+
import fs7 from "fs";
|
|
8594
8778
|
function getClient15() {
|
|
8595
8779
|
return createApiClient();
|
|
8596
8780
|
}
|
|
@@ -8681,7 +8865,7 @@ async function listQueries(project, format) {
|
|
|
8681
8865
|
}
|
|
8682
8866
|
}
|
|
8683
8867
|
async function importQueries(project, filePath, format) {
|
|
8684
|
-
if (!
|
|
8868
|
+
if (!fs7.existsSync(filePath)) {
|
|
8685
8869
|
throw new CliError({
|
|
8686
8870
|
code: "QUERY_IMPORT_FILE_NOT_FOUND",
|
|
8687
8871
|
message: `File not found: ${filePath}`,
|
|
@@ -8692,7 +8876,7 @@ async function importQueries(project, filePath, format) {
|
|
|
8692
8876
|
}
|
|
8693
8877
|
});
|
|
8694
8878
|
}
|
|
8695
|
-
const content =
|
|
8879
|
+
const content = fs7.readFileSync(filePath, "utf-8");
|
|
8696
8880
|
const queries2 = content.split("\n").map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#"));
|
|
8697
8881
|
if (queries2.length === 0) {
|
|
8698
8882
|
if (isMachineFormat(format)) {
|
|
@@ -8894,7 +9078,7 @@ var QUERY_CLI_COMMANDS = [
|
|
|
8894
9078
|
];
|
|
8895
9079
|
|
|
8896
9080
|
// src/commands/mcp.ts
|
|
8897
|
-
import
|
|
9081
|
+
import fs8 from "fs";
|
|
8898
9082
|
import path4 from "path";
|
|
8899
9083
|
import { createRequire } from "module";
|
|
8900
9084
|
|
|
@@ -9010,8 +9194,8 @@ function renderClientSnippet(client, serverName, entry) {
|
|
|
9010
9194
|
return renderJsonSnippet(serverName, entry, client.format);
|
|
9011
9195
|
}
|
|
9012
9196
|
function readJsonConfig(configPath) {
|
|
9013
|
-
if (!
|
|
9014
|
-
const raw =
|
|
9197
|
+
if (!fs8.existsSync(configPath)) return {};
|
|
9198
|
+
const raw = fs8.readFileSync(configPath, "utf-8").trim();
|
|
9015
9199
|
if (!raw) return {};
|
|
9016
9200
|
try {
|
|
9017
9201
|
const parsed = JSON.parse(raw);
|
|
@@ -9029,14 +9213,14 @@ function readJsonConfig(configPath) {
|
|
|
9029
9213
|
}
|
|
9030
9214
|
}
|
|
9031
9215
|
function writeJsonConfig(configPath, value) {
|
|
9032
|
-
|
|
9033
|
-
|
|
9216
|
+
fs8.mkdirSync(path4.dirname(configPath), { recursive: true });
|
|
9217
|
+
fs8.writeFileSync(configPath, `${JSON.stringify(value, null, 2)}
|
|
9034
9218
|
`, "utf-8");
|
|
9035
9219
|
}
|
|
9036
9220
|
function backupConfigIfPresent(configPath) {
|
|
9037
|
-
if (!
|
|
9221
|
+
if (!fs8.existsSync(configPath)) return void 0;
|
|
9038
9222
|
const backupPath = `${configPath}.canonry.bak`;
|
|
9039
|
-
|
|
9223
|
+
fs8.copyFileSync(configPath, backupPath);
|
|
9040
9224
|
return backupPath;
|
|
9041
9225
|
}
|
|
9042
9226
|
function findClientOrThrow(id) {
|
|
@@ -9392,13 +9576,13 @@ var NOTIFY_CLI_COMMANDS = [
|
|
|
9392
9576
|
];
|
|
9393
9577
|
|
|
9394
9578
|
// src/commands/apply.ts
|
|
9395
|
-
import
|
|
9579
|
+
import fs9 from "fs";
|
|
9396
9580
|
import { parseAllDocuments } from "yaml";
|
|
9397
9581
|
async function applyConfigFile(filePath) {
|
|
9398
|
-
if (!
|
|
9582
|
+
if (!fs9.existsSync(filePath)) {
|
|
9399
9583
|
throw new Error(`File not found: ${filePath}`);
|
|
9400
9584
|
}
|
|
9401
|
-
const content =
|
|
9585
|
+
const content = fs9.readFileSync(filePath, "utf-8");
|
|
9402
9586
|
const docs = parseAllDocuments(content);
|
|
9403
9587
|
const client = createApiClient();
|
|
9404
9588
|
const errors = [];
|
|
@@ -9801,7 +9985,7 @@ async function loadLatestRunForExport(client, project) {
|
|
|
9801
9985
|
}
|
|
9802
9986
|
|
|
9803
9987
|
// src/commands/results-export.ts
|
|
9804
|
-
import
|
|
9988
|
+
import fs10 from "fs";
|
|
9805
9989
|
import path5 from "path";
|
|
9806
9990
|
async function exportResults(project, opts) {
|
|
9807
9991
|
const { output, ...request } = opts;
|
|
@@ -9811,8 +9995,8 @@ async function exportResults(project, opts) {
|
|
|
9811
9995
|
return;
|
|
9812
9996
|
}
|
|
9813
9997
|
const target = output ? path5.resolve(output) : path5.resolve(process.cwd(), path5.basename(artifact.filename));
|
|
9814
|
-
|
|
9815
|
-
|
|
9998
|
+
fs10.mkdirSync(path5.dirname(target), { recursive: true });
|
|
9999
|
+
fs10.writeFileSync(target, artifact.content, "utf8");
|
|
9816
10000
|
console.log(`Results export written to ${target}`);
|
|
9817
10001
|
}
|
|
9818
10002
|
|
|
@@ -10528,7 +10712,7 @@ function parseProviderModelAssignments(assignments) {
|
|
|
10528
10712
|
}
|
|
10529
10713
|
|
|
10530
10714
|
// src/commands/report.ts
|
|
10531
|
-
import
|
|
10715
|
+
import fs11 from "fs";
|
|
10532
10716
|
import path6 from "path";
|
|
10533
10717
|
function defaultOutputPath(project, audience) {
|
|
10534
10718
|
const date = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
@@ -10545,10 +10729,10 @@ async function runReportCommand(project, opts = {}) {
|
|
|
10545
10729
|
const html = renderReportHtml(report, { audience });
|
|
10546
10730
|
const targetPath = opts.output ? path6.resolve(opts.output) : defaultOutputPath(project, audience);
|
|
10547
10731
|
const dir = path6.dirname(targetPath);
|
|
10548
|
-
if (!
|
|
10549
|
-
|
|
10732
|
+
if (!fs11.existsSync(dir)) {
|
|
10733
|
+
fs11.mkdirSync(dir, { recursive: true });
|
|
10550
10734
|
}
|
|
10551
|
-
|
|
10735
|
+
fs11.writeFileSync(targetPath, html, "utf-8");
|
|
10552
10736
|
console.log(`Report written to ${targetPath}`);
|
|
10553
10737
|
}
|
|
10554
10738
|
|
|
@@ -11523,11 +11707,11 @@ var SKILLS_CLI_COMMANDS = [
|
|
|
11523
11707
|
];
|
|
11524
11708
|
|
|
11525
11709
|
// src/commands/snapshot.ts
|
|
11526
|
-
import
|
|
11710
|
+
import fs13 from "fs";
|
|
11527
11711
|
import path8 from "path";
|
|
11528
11712
|
|
|
11529
11713
|
// src/snapshot-pdf.ts
|
|
11530
|
-
import
|
|
11714
|
+
import fs12 from "fs";
|
|
11531
11715
|
import path7 from "path";
|
|
11532
11716
|
import { PDFDocument, StandardFonts, rgb } from "pdf-lib";
|
|
11533
11717
|
var PAGE_WIDTH = 612;
|
|
@@ -11741,8 +11925,8 @@ async function writeSnapshotPdf(report, outputPath) {
|
|
|
11741
11925
|
renderQueries(pdf, report);
|
|
11742
11926
|
const bytes = await doc.save();
|
|
11743
11927
|
const resolvedPath = path7.resolve(outputPath);
|
|
11744
|
-
|
|
11745
|
-
|
|
11928
|
+
fs12.mkdirSync(path7.dirname(resolvedPath), { recursive: true });
|
|
11929
|
+
fs12.writeFileSync(resolvedPath, bytes);
|
|
11746
11930
|
return resolvedPath;
|
|
11747
11931
|
}
|
|
11748
11932
|
function renderCover(pdf, report) {
|
|
@@ -11901,8 +12085,8 @@ PDF saved: ${savedPdfPath}`);
|
|
|
11901
12085
|
}
|
|
11902
12086
|
function writeSnapshotMarkdown(report, outputPath) {
|
|
11903
12087
|
const resolvedPath = path8.resolve(outputPath);
|
|
11904
|
-
|
|
11905
|
-
|
|
12088
|
+
fs13.mkdirSync(path8.dirname(resolvedPath), { recursive: true });
|
|
12089
|
+
fs13.writeFileSync(resolvedPath, formatSnapshotMarkdown(report), "utf-8");
|
|
11906
12090
|
return resolvedPath;
|
|
11907
12091
|
}
|
|
11908
12092
|
function formatSnapshotMarkdown(report) {
|
|
@@ -13130,7 +13314,7 @@ async function bootstrapCommand(_opts) {
|
|
|
13130
13314
|
|
|
13131
13315
|
// src/commands/daemon.ts
|
|
13132
13316
|
import { spawn as spawn2 } from "child_process";
|
|
13133
|
-
import
|
|
13317
|
+
import fs14 from "fs";
|
|
13134
13318
|
import path10 from "path";
|
|
13135
13319
|
function getPidPath() {
|
|
13136
13320
|
return path10.join(getConfigDir(), "canonry.pid");
|
|
@@ -13173,8 +13357,8 @@ function buildServeForwardArgs(opts) {
|
|
|
13173
13357
|
async function startDaemon(opts) {
|
|
13174
13358
|
const pidPath = getPidPath();
|
|
13175
13359
|
const format = opts.format ?? "text";
|
|
13176
|
-
if (
|
|
13177
|
-
const existingPid = parseInt(
|
|
13360
|
+
if (fs14.existsSync(pidPath)) {
|
|
13361
|
+
const existingPid = parseInt(fs14.readFileSync(pidPath, "utf-8").trim(), 10);
|
|
13178
13362
|
if (!isNaN(existingPid) && isProcessAlive(existingPid)) {
|
|
13179
13363
|
throw new CliError({
|
|
13180
13364
|
code: "DAEMON_ALREADY_RUNNING",
|
|
@@ -13185,7 +13369,7 @@ async function startDaemon(opts) {
|
|
|
13185
13369
|
}
|
|
13186
13370
|
});
|
|
13187
13371
|
}
|
|
13188
|
-
|
|
13372
|
+
fs14.unlinkSync(pidPath);
|
|
13189
13373
|
}
|
|
13190
13374
|
const cliPath = path10.resolve(new URL(import.meta.url).pathname);
|
|
13191
13375
|
const inSourceMode = new URL(import.meta.url).pathname.endsWith(".ts");
|
|
@@ -13204,10 +13388,10 @@ async function startDaemon(opts) {
|
|
|
13204
13388
|
});
|
|
13205
13389
|
}
|
|
13206
13390
|
const configDir = getConfigDir();
|
|
13207
|
-
if (!
|
|
13208
|
-
|
|
13391
|
+
if (!fs14.existsSync(configDir)) {
|
|
13392
|
+
fs14.mkdirSync(configDir, { recursive: true });
|
|
13209
13393
|
}
|
|
13210
|
-
|
|
13394
|
+
fs14.writeFileSync(pidPath, String(child.pid), "utf-8");
|
|
13211
13395
|
const port = opts.port ?? "4100";
|
|
13212
13396
|
const host = opts.host ?? "127.0.0.1";
|
|
13213
13397
|
if (!isMachineFormat(format)) {
|
|
@@ -13216,7 +13400,7 @@ async function startDaemon(opts) {
|
|
|
13216
13400
|
const ready = await waitForReady(host, port);
|
|
13217
13401
|
if (!ready) {
|
|
13218
13402
|
try {
|
|
13219
|
-
|
|
13403
|
+
fs14.unlinkSync(pidPath);
|
|
13220
13404
|
} catch {
|
|
13221
13405
|
}
|
|
13222
13406
|
throw new CliError({
|
|
@@ -13248,7 +13432,7 @@ async function startDaemon(opts) {
|
|
|
13248
13432
|
}
|
|
13249
13433
|
function stopDaemon(format = "text") {
|
|
13250
13434
|
const pidPath = getPidPath();
|
|
13251
|
-
if (!
|
|
13435
|
+
if (!fs14.existsSync(pidPath)) {
|
|
13252
13436
|
if (isMachineFormat(format)) {
|
|
13253
13437
|
console.log(JSON.stringify({
|
|
13254
13438
|
stopped: false,
|
|
@@ -13259,7 +13443,7 @@ function stopDaemon(format = "text") {
|
|
|
13259
13443
|
console.log("Canonry is not running (no PID file found)");
|
|
13260
13444
|
return;
|
|
13261
13445
|
}
|
|
13262
|
-
const pid = parseInt(
|
|
13446
|
+
const pid = parseInt(fs14.readFileSync(pidPath, "utf-8").trim(), 10);
|
|
13263
13447
|
if (isNaN(pid)) {
|
|
13264
13448
|
if (isMachineFormat(format)) {
|
|
13265
13449
|
console.log(JSON.stringify({
|
|
@@ -13270,7 +13454,7 @@ function stopDaemon(format = "text") {
|
|
|
13270
13454
|
} else {
|
|
13271
13455
|
console.error("Invalid PID file. Removing it.");
|
|
13272
13456
|
}
|
|
13273
|
-
|
|
13457
|
+
fs14.unlinkSync(pidPath);
|
|
13274
13458
|
return;
|
|
13275
13459
|
}
|
|
13276
13460
|
if (!isProcessAlive(pid)) {
|
|
@@ -13284,12 +13468,12 @@ function stopDaemon(format = "text") {
|
|
|
13284
13468
|
} else {
|
|
13285
13469
|
console.log(`Canonry is not running (stale PID: ${pid}). Cleaning up.`);
|
|
13286
13470
|
}
|
|
13287
|
-
|
|
13471
|
+
fs14.unlinkSync(pidPath);
|
|
13288
13472
|
return;
|
|
13289
13473
|
}
|
|
13290
13474
|
try {
|
|
13291
13475
|
process.kill(pid, "SIGTERM");
|
|
13292
|
-
|
|
13476
|
+
fs14.unlinkSync(pidPath);
|
|
13293
13477
|
if (isMachineFormat(format)) {
|
|
13294
13478
|
console.log(JSON.stringify({
|
|
13295
13479
|
stopped: true,
|
|
@@ -13313,7 +13497,7 @@ function stopDaemon(format = "text") {
|
|
|
13313
13497
|
|
|
13314
13498
|
// src/commands/init.ts
|
|
13315
13499
|
import crypto2 from "crypto";
|
|
13316
|
-
import
|
|
13500
|
+
import fs15 from "fs";
|
|
13317
13501
|
import readline2 from "readline";
|
|
13318
13502
|
import path11 from "path";
|
|
13319
13503
|
var pendingServeHandoff = false;
|
|
@@ -13351,7 +13535,7 @@ var PROJECT_MARKERS = [".git", "canonry.yaml", "canonry.yml", "package.json"];
|
|
|
13351
13535
|
function cwdLooksLikeProject(dir) {
|
|
13352
13536
|
const home = process.env.HOME ?? "";
|
|
13353
13537
|
if (home && path11.resolve(dir) === path11.resolve(home)) return false;
|
|
13354
|
-
return PROJECT_MARKERS.some((marker) =>
|
|
13538
|
+
return PROJECT_MARKERS.some((marker) => fs15.existsSync(path11.join(dir, marker)));
|
|
13355
13539
|
}
|
|
13356
13540
|
var DEFAULT_AGENT_MODELS = {
|
|
13357
13541
|
anthropic: "anthropic/claude-sonnet-4-6",
|
|
@@ -13382,8 +13566,8 @@ async function initCommand(opts) {
|
|
|
13382
13566
|
return void 0;
|
|
13383
13567
|
}
|
|
13384
13568
|
const configDir = getConfigDir();
|
|
13385
|
-
if (!
|
|
13386
|
-
|
|
13569
|
+
if (!fs15.existsSync(configDir)) {
|
|
13570
|
+
fs15.mkdirSync(configDir, { recursive: true });
|
|
13387
13571
|
}
|
|
13388
13572
|
const bootstrapEnv = getBootstrapEnv(process.env, {
|
|
13389
13573
|
GEMINI_API_KEY: opts?.geminiKey,
|
|
@@ -13684,7 +13868,7 @@ function encodeLegacySetupState(state) {
|
|
|
13684
13868
|
}
|
|
13685
13869
|
|
|
13686
13870
|
// src/agent-plugin.ts
|
|
13687
|
-
import
|
|
13871
|
+
import fs16 from "fs";
|
|
13688
13872
|
import path12 from "path";
|
|
13689
13873
|
var CANONRY_PLUGIN_ID = "canonry@canonry";
|
|
13690
13874
|
var REQUIRED_SKILLS = ["canonry", "aero"];
|
|
@@ -13729,7 +13913,7 @@ function isCanonryPluginId(value) {
|
|
|
13729
13913
|
}
|
|
13730
13914
|
function readJson(filePath) {
|
|
13731
13915
|
try {
|
|
13732
|
-
return JSON.parse(
|
|
13916
|
+
return JSON.parse(fs16.readFileSync(filePath, "utf8"));
|
|
13733
13917
|
} catch {
|
|
13734
13918
|
return null;
|
|
13735
13919
|
}
|
|
@@ -13748,7 +13932,7 @@ function readCodexPluginConfigured(filePath) {
|
|
|
13748
13932
|
try {
|
|
13749
13933
|
let currentPlugin = null;
|
|
13750
13934
|
let inPluginsTable = false;
|
|
13751
|
-
for (const line of
|
|
13935
|
+
for (const line of fs16.readFileSync(filePath, "utf8").split(/\r?\n/)) {
|
|
13752
13936
|
const section = /^\s*\[plugins\.(?:"([^"]+)"|'([^']+)'|(\S+))\]\s*(?:#.*)?$/.exec(line);
|
|
13753
13937
|
if (section) {
|
|
13754
13938
|
const captures = section.slice(1, 4);
|
|
@@ -13781,7 +13965,7 @@ function readCodexPluginConfigured(filePath) {
|
|
|
13781
13965
|
}
|
|
13782
13966
|
}
|
|
13783
13967
|
function verifiedPluginVersion(root, client) {
|
|
13784
|
-
if (client === "codex" &&
|
|
13968
|
+
if (client === "codex" && fs16.existsSync(path12.join(root, "plugin.json"))) {
|
|
13785
13969
|
const manifest2 = readJson(path12.join(root, "plugin.json"));
|
|
13786
13970
|
if (manifest2 && typeof manifest2 === "object") {
|
|
13787
13971
|
const plugin2 = manifest2;
|
|
@@ -13795,7 +13979,7 @@ function verifiedPluginVersion(root, client) {
|
|
|
13795
13979
|
if (portableMcp.$schema !== AGENT_PLUGINS_MCP_SCHEMA || portableMcp.mcpServers?.canonry?.type !== "stdio" || portableMcp.mcpServers.canonry.command !== "canonry-mcp") {
|
|
13796
13980
|
return null;
|
|
13797
13981
|
}
|
|
13798
|
-
return REQUIRED_SKILLS.every((name) =>
|
|
13982
|
+
return REQUIRED_SKILLS.every((name) => fs16.existsSync(path12.join(root, "skills", name, "SKILL.md"))) ? plugin2.version : null;
|
|
13799
13983
|
}
|
|
13800
13984
|
if (typeof plugin2.$schema === "string" && plugin2.$schema.startsWith("https://agent-plugins.org/schemas/")) {
|
|
13801
13985
|
return null;
|
|
@@ -13813,7 +13997,7 @@ function verifiedPluginVersion(root, client) {
|
|
|
13813
13997
|
if (!mcp || typeof mcp !== "object") return null;
|
|
13814
13998
|
const canonryServer = mcp.mcpServers?.canonry;
|
|
13815
13999
|
if (canonryServer?.command !== "canonry-mcp") return null;
|
|
13816
|
-
return REQUIRED_SKILLS.every((name) =>
|
|
14000
|
+
return REQUIRED_SKILLS.every((name) => fs16.existsSync(path12.join(root, "skills", name, "SKILL.md"))) ? plugin.version : null;
|
|
13817
14001
|
}
|
|
13818
14002
|
function readClaudeUserInstallPaths(claudeConfigDir) {
|
|
13819
14003
|
const parsed = readJson(path12.join(claudeConfigDir, "plugins", "installed_plugins.json"));
|
|
@@ -13830,7 +14014,7 @@ function readClaudeUserInstallPaths(claudeConfigDir) {
|
|
|
13830
14014
|
}
|
|
13831
14015
|
function listDirectories(dir) {
|
|
13832
14016
|
try {
|
|
13833
|
-
return
|
|
14017
|
+
return fs16.readdirSync(dir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => path12.join(dir, entry.name));
|
|
13834
14018
|
} catch {
|
|
13835
14019
|
return [];
|
|
13836
14020
|
}
|
|
@@ -15483,7 +15667,7 @@ var VISIBILITY_STATS_CLI_COMMANDS = [
|
|
|
15483
15667
|
];
|
|
15484
15668
|
|
|
15485
15669
|
// src/cli-commands/wordpress.ts
|
|
15486
|
-
import
|
|
15670
|
+
import fs17 from "fs";
|
|
15487
15671
|
|
|
15488
15672
|
// src/commands/wordpress.ts
|
|
15489
15673
|
function getClient28() {
|
|
@@ -15722,12 +15906,12 @@ async function wordpressSetMeta(project, body) {
|
|
|
15722
15906
|
printPageDetail(result);
|
|
15723
15907
|
}
|
|
15724
15908
|
async function wordpressBulkSetMeta(project, opts) {
|
|
15725
|
-
const
|
|
15909
|
+
const fs19 = await import("fs/promises");
|
|
15726
15910
|
const path13 = await import("path");
|
|
15727
15911
|
const filePath = path13.resolve(opts.from);
|
|
15728
15912
|
let raw;
|
|
15729
15913
|
try {
|
|
15730
|
-
raw = await
|
|
15914
|
+
raw = await fs19.readFile(filePath, "utf8");
|
|
15731
15915
|
} catch {
|
|
15732
15916
|
throw new CliError({
|
|
15733
15917
|
code: "FILE_READ_ERROR",
|
|
@@ -15824,13 +16008,13 @@ async function wordpressSetSchema(project, body) {
|
|
|
15824
16008
|
printManualAssist(`Schema update for "${body.slug}"`, result);
|
|
15825
16009
|
}
|
|
15826
16010
|
async function wordpressSchemaDeploy(project, opts) {
|
|
15827
|
-
const
|
|
16011
|
+
const fs19 = await import("fs/promises");
|
|
15828
16012
|
const path13 = await import("path");
|
|
15829
16013
|
const yaml = await loadYamlModule();
|
|
15830
16014
|
const filePath = path13.resolve(opts.profile);
|
|
15831
16015
|
let raw;
|
|
15832
16016
|
try {
|
|
15833
|
-
raw = await
|
|
16017
|
+
raw = await fs19.readFile(filePath, "utf8");
|
|
15834
16018
|
} catch {
|
|
15835
16019
|
throw new CliError({
|
|
15836
16020
|
code: "FILE_READ_ERROR",
|
|
@@ -15935,13 +16119,13 @@ async function wordpressOnboard(project, opts) {
|
|
|
15935
16119
|
}
|
|
15936
16120
|
let profileData;
|
|
15937
16121
|
if (opts.profile) {
|
|
15938
|
-
const
|
|
16122
|
+
const fs19 = await import("fs/promises");
|
|
15939
16123
|
const path13 = await import("path");
|
|
15940
16124
|
const yaml = await loadYamlModule();
|
|
15941
16125
|
const filePath = path13.resolve(opts.profile);
|
|
15942
16126
|
let raw;
|
|
15943
16127
|
try {
|
|
15944
|
-
raw = await
|
|
16128
|
+
raw = await fs19.readFile(filePath, "utf8");
|
|
15945
16129
|
} catch {
|
|
15946
16130
|
throw new CliError({
|
|
15947
16131
|
code: "FILE_READ_ERROR",
|
|
@@ -16090,7 +16274,7 @@ function resolveContent(input, command, usage, options) {
|
|
|
16090
16274
|
}
|
|
16091
16275
|
if (contentFile) {
|
|
16092
16276
|
try {
|
|
16093
|
-
return
|
|
16277
|
+
return fs17.readFileSync(contentFile, "utf-8");
|
|
16094
16278
|
} catch (error) {
|
|
16095
16279
|
const message = error instanceof Error ? error.message : String(error);
|
|
16096
16280
|
throw usageError(`Error: could not read --content-file "${contentFile}": ${message}`, {
|
|
@@ -17057,16 +17241,16 @@ Usage: ${usage}`, {
|
|
|
17057
17241
|
|
|
17058
17242
|
// src/commands/measurement-plan.ts
|
|
17059
17243
|
import crypto3 from "crypto";
|
|
17060
|
-
import
|
|
17244
|
+
import fs18 from "fs";
|
|
17061
17245
|
import { parse } from "yaml";
|
|
17062
17246
|
import { z as z2 } from "zod";
|
|
17063
17247
|
function readPlan(source) {
|
|
17064
|
-
const content = source === "-" ?
|
|
17248
|
+
const content = source === "-" ? fs18.readFileSync(0, "utf8") : fs18.readFileSync(source, "utf8");
|
|
17065
17249
|
const parsed = source.endsWith(".json") ? JSON.parse(content) : parse(content);
|
|
17066
17250
|
return measurementPlanInputSchema.parse(parsed);
|
|
17067
17251
|
}
|
|
17068
17252
|
function readDiscoveryRule(source) {
|
|
17069
|
-
const content = source === "-" ?
|
|
17253
|
+
const content = source === "-" ? fs18.readFileSync(0, "utf8") : fs18.readFileSync(source, "utf8");
|
|
17070
17254
|
const parsed = source.endsWith(".json") ? JSON.parse(content) : parse(content);
|
|
17071
17255
|
return measurementDiscoveryRuleSchema.parse(parsed);
|
|
17072
17256
|
}
|
|
@@ -17115,7 +17299,7 @@ var advancedDeactivateInputSchema = z2.object({
|
|
|
17115
17299
|
}).strict();
|
|
17116
17300
|
function readAdvancedMeasurementInput(source) {
|
|
17117
17301
|
if (source === void 0) return {};
|
|
17118
|
-
const content = source === "-" ?
|
|
17302
|
+
const content = source === "-" ? fs18.readFileSync(0, "utf8") : fs18.readFileSync(source, "utf8");
|
|
17119
17303
|
return JSON.parse(content);
|
|
17120
17304
|
}
|
|
17121
17305
|
var advancedMeasurementCollectionKeys = {
|
|
@@ -17297,7 +17481,7 @@ async function replaceMeasurementPlanAssignments(project, opts) {
|
|
|
17297
17481
|
console.log(JSON.stringify({ preview, result }, null, 2));
|
|
17298
17482
|
}
|
|
17299
17483
|
function readGroupMembershipCsv(source) {
|
|
17300
|
-
return source === "-" ?
|
|
17484
|
+
return source === "-" ? fs18.readFileSync(0, "utf8") : fs18.readFileSync(source, "utf8");
|
|
17301
17485
|
}
|
|
17302
17486
|
async function previewMeasurementPlanGroups(project, source) {
|
|
17303
17487
|
const preview = await createApiClient().previewMeasurementDraftGroupMembership(project, {
|