@canonry/canonry 4.156.0 → 4.158.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 +2 -2
- package/assets/agent-workspace/skills/canonry/SKILL.md +8 -3
- package/assets/agent-workspace/skills/canonry/references/server-side-traffic.md +454 -21
- package/assets/assets/{AuditHistoryPanel-gmJMVlXX.js → AuditHistoryPanel-McQfjoU9.js} +1 -1
- package/assets/assets/{BacklinksPage-Bv2sTCyN.js → BacklinksPage-Da8y1Wv8.js} +1 -1
- package/assets/assets/{HistoryPage-13q7GkiI.js → HistoryPage-9MJfhRyT.js} +1 -1
- package/assets/assets/{MeasurementPropertyPage-BgBqXT1p.js → MeasurementPropertyPage-BiNAHEJW.js} +1 -1
- package/assets/assets/ProjectPage-mWSMjXrR.js +15 -0
- package/assets/assets/{RunRow-bPzapIGJ.js → RunRow-DCG2LBlS.js} +1 -1
- package/assets/assets/{RunsPage-Dqfz18lH.js → RunsPage-ByWA_FSk.js} +1 -1
- package/assets/assets/{SettingsPage-_wMecn-V.js → SettingsPage-DdSeKAKF.js} +1 -1
- package/assets/assets/{TrafficPage-RYBdsOLB.js → TrafficPage-5u8rPcBt.js} +1 -1
- package/assets/assets/{TrafficSourceDetailPage-DpeMMyKU.js → TrafficSourceDetailPage-Dm-fK87u.js} +1 -1
- package/assets/assets/{extract-error-message-DUarAlyD.js → extract-error-message-CtxkAd6E.js} +1 -1
- package/assets/assets/{index-DP-fT2uI.js → index-DUfg50Pl.js} +28 -28
- package/assets/assets/index-cXb3mfeQ.css +1 -0
- package/assets/assets/{react-sigma_core.esm.min-DnypJl5w.js → react-sigma_core.esm.min-ComIyDKi.js} +1 -1
- package/assets/index.html +2 -2
- package/dist/{chunk-ZWESOFA5.js → chunk-4VZP64GZ.js} +273 -15
- package/dist/{chunk-AMPT2EP7.js → chunk-DEVAEC2S.js} +121 -1
- package/dist/{chunk-CPASKGUE.js → chunk-LK6YFHG4.js} +35 -1
- package/dist/{chunk-ABNN2ZVR.js → chunk-YP4K2KXH.js} +1933 -451
- package/dist/cli.js +667 -142
- package/dist/index.d.ts +32 -0
- package/dist/index.js +4 -4
- package/dist/{intelligence-service-WHC7B32V.js → intelligence-service-LQ6ZRWMF.js} +2 -2
- package/dist/mcp.js +2 -2
- package/package.json +12 -10
- package/assets/assets/ProjectPage-EsGwKhot.js +0 -15
- package/assets/assets/index-BxegEwic.css +0 -1
package/dist/cli.js
CHANGED
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
detectAndTrackUpgrade,
|
|
17
17
|
emitInstallSummary,
|
|
18
18
|
formatAuditFactorScore,
|
|
19
|
+
getCloudflareTrafficConnection,
|
|
19
20
|
getMissingUserSkillsNudge,
|
|
20
21
|
getOrCreateAnonymousId,
|
|
21
22
|
installSkills,
|
|
@@ -30,7 +31,7 @@ import {
|
|
|
30
31
|
showFirstRunNotice,
|
|
31
32
|
trackCliCommandFinished,
|
|
32
33
|
trackEvent
|
|
33
|
-
} from "./chunk-
|
|
34
|
+
} from "./chunk-4VZP64GZ.js";
|
|
34
35
|
import {
|
|
35
36
|
CliError,
|
|
36
37
|
EXIT_SYSTEM_ERROR,
|
|
@@ -50,15 +51,21 @@ import {
|
|
|
50
51
|
saveConfigPatch,
|
|
51
52
|
systemError,
|
|
52
53
|
usageError
|
|
53
|
-
} from "./chunk-
|
|
54
|
+
} from "./chunk-LK6YFHG4.js";
|
|
54
55
|
import {
|
|
56
|
+
CLOUDFLARE_WORKER_BINDINGS,
|
|
57
|
+
CLOUDFLARE_WORKER_GENERATED_MARKER,
|
|
58
|
+
CLOUDFLARE_WRANGLER_GENERATED_MARKER,
|
|
59
|
+
DEFAULT_BOT_LIST,
|
|
55
60
|
apiKeys,
|
|
56
61
|
createClient,
|
|
62
|
+
generateWorkerScript,
|
|
63
|
+
generateWranglerToml,
|
|
57
64
|
migrate,
|
|
58
65
|
projects,
|
|
59
66
|
queries,
|
|
60
67
|
renderReportHtml
|
|
61
|
-
} from "./chunk-
|
|
68
|
+
} from "./chunk-YP4K2KXH.js";
|
|
62
69
|
import {
|
|
63
70
|
AdsDeliverySnapshotStatuses,
|
|
64
71
|
AdsHistoricalCampaignRollupStatuses,
|
|
@@ -122,7 +129,7 @@ import {
|
|
|
122
129
|
providerQuotaPolicySchema,
|
|
123
130
|
resolveProviderInput,
|
|
124
131
|
winnabilityClassSchema
|
|
125
|
-
} from "./chunk-
|
|
132
|
+
} from "./chunk-DEVAEC2S.js";
|
|
126
133
|
|
|
127
134
|
// src/cli.ts
|
|
128
135
|
import { pathToFileURL } from "url";
|
|
@@ -168,9 +175,9 @@ import { parseArgs } from "util";
|
|
|
168
175
|
function commandId(spec) {
|
|
169
176
|
return spec.path.join(".");
|
|
170
177
|
}
|
|
171
|
-
function matchesPath(args,
|
|
172
|
-
if (args.length <
|
|
173
|
-
return
|
|
178
|
+
function matchesPath(args, path13) {
|
|
179
|
+
if (args.length < path13.length) return false;
|
|
180
|
+
return path13.every((segment, index) => args[index] === segment);
|
|
174
181
|
}
|
|
175
182
|
function withGlobalOptions(options) {
|
|
176
183
|
const base = options ? { ...options } : {};
|
|
@@ -3868,9 +3875,9 @@ async function gaConnect(project, opts) {
|
|
|
3868
3875
|
propertyId: opts.propertyId
|
|
3869
3876
|
};
|
|
3870
3877
|
if (opts.keyFile) {
|
|
3871
|
-
const
|
|
3878
|
+
const fs18 = await import("fs");
|
|
3872
3879
|
try {
|
|
3873
|
-
const content =
|
|
3880
|
+
const content = fs18.readFileSync(opts.keyFile, "utf-8");
|
|
3874
3881
|
JSON.parse(content);
|
|
3875
3882
|
body.keyJson = content;
|
|
3876
3883
|
} catch (e) {
|
|
@@ -5224,9 +5231,9 @@ async function getCommand(opts) {
|
|
|
5224
5231
|
console.log(JSON.stringify(leaf, null, 2));
|
|
5225
5232
|
}
|
|
5226
5233
|
}
|
|
5227
|
-
function walkPath(value,
|
|
5228
|
-
if (!
|
|
5229
|
-
const segments =
|
|
5234
|
+
function walkPath(value, path13) {
|
|
5235
|
+
if (!path13 || path13 === ".") return value;
|
|
5236
|
+
const segments = path13.split(".").flatMap((part) => {
|
|
5230
5237
|
const tokens = [];
|
|
5231
5238
|
let i = 0;
|
|
5232
5239
|
const bracketStart = part.indexOf("[");
|
|
@@ -5274,17 +5281,342 @@ var GET_CLI_COMMANDS = [
|
|
|
5274
5281
|
},
|
|
5275
5282
|
run: async (input) => {
|
|
5276
5283
|
const project = requireProject(input, "get", USAGE2);
|
|
5277
|
-
const
|
|
5284
|
+
const path13 = requirePositional(input, 1, {
|
|
5278
5285
|
command: "get",
|
|
5279
5286
|
usage: USAGE2,
|
|
5280
5287
|
message: 'path is required (e.g. "scores.mentionShare.value")'
|
|
5281
5288
|
});
|
|
5282
5289
|
const from = getString(input.values, "from");
|
|
5283
|
-
await getCommand({ project, path:
|
|
5290
|
+
await getCommand({ project, path: path13, from, format: input.format });
|
|
5284
5291
|
}
|
|
5285
5292
|
}
|
|
5286
5293
|
];
|
|
5287
5294
|
|
|
5295
|
+
// src/cloudflare-worker-deploy.ts
|
|
5296
|
+
import { spawn } from "child_process";
|
|
5297
|
+
import { randomUUID } from "crypto";
|
|
5298
|
+
import fs4 from "fs";
|
|
5299
|
+
import os from "os";
|
|
5300
|
+
import path2 from "path";
|
|
5301
|
+
import { stripVTControlCharacters } from "util";
|
|
5302
|
+
var MAX_WRANGLER_DIAGNOSTIC_LENGTH = 2e3;
|
|
5303
|
+
function wranglerDiagnostic(output) {
|
|
5304
|
+
const plain = stripVTControlCharacters(output).trim();
|
|
5305
|
+
if (plain.length <= MAX_WRANGLER_DIAGNOSTIC_LENGTH) return plain;
|
|
5306
|
+
const half = Math.floor(MAX_WRANGLER_DIAGNOSTIC_LENGTH / 2);
|
|
5307
|
+
return `${plain.slice(0, half)}
|
|
5308
|
+
...
|
|
5309
|
+
${plain.slice(-half)}`;
|
|
5310
|
+
}
|
|
5311
|
+
function projectSlug(project) {
|
|
5312
|
+
return project.trim().toLowerCase().replace(/[^a-z0-9._-]+/g, "-").replace(/^-+|-+$/g, "") || "project";
|
|
5313
|
+
}
|
|
5314
|
+
function lstatIfPresent(filePath) {
|
|
5315
|
+
try {
|
|
5316
|
+
return fs4.lstatSync(filePath);
|
|
5317
|
+
} catch (error) {
|
|
5318
|
+
if (error.code === "ENOENT") return null;
|
|
5319
|
+
throw error;
|
|
5320
|
+
}
|
|
5321
|
+
}
|
|
5322
|
+
function resolveCloudflareWorkerOutputDirectory(project, outputDirectory, cwd = process.cwd()) {
|
|
5323
|
+
return path2.resolve(cwd, outputDirectory ?? `canonry-cloudflare-${projectSlug(project)}`);
|
|
5324
|
+
}
|
|
5325
|
+
function prepareCloudflareWorkerOutputDirectory(outputDirectory) {
|
|
5326
|
+
const resolved = path2.resolve(outputDirectory);
|
|
5327
|
+
const outputStat = lstatIfPresent(resolved);
|
|
5328
|
+
if (outputStat) {
|
|
5329
|
+
const stat = outputStat;
|
|
5330
|
+
if (!stat.isDirectory() || stat.isSymbolicLink()) {
|
|
5331
|
+
throw new Error(`output directory is not a regular directory: ${resolved}`);
|
|
5332
|
+
}
|
|
5333
|
+
} else {
|
|
5334
|
+
fs4.mkdirSync(resolved, { recursive: true, mode: 448 });
|
|
5335
|
+
}
|
|
5336
|
+
const artifacts = {
|
|
5337
|
+
outputDirectory: resolved,
|
|
5338
|
+
workerScriptPath: path2.join(resolved, "worker.js"),
|
|
5339
|
+
wranglerTomlPath: path2.join(resolved, "wrangler.toml")
|
|
5340
|
+
};
|
|
5341
|
+
for (const artifactPath of [artifacts.workerScriptPath, artifacts.wranglerTomlPath]) {
|
|
5342
|
+
const stat = lstatIfPresent(artifactPath);
|
|
5343
|
+
if (!stat) continue;
|
|
5344
|
+
if (!stat.isFile() || stat.isSymbolicLink()) {
|
|
5345
|
+
throw new Error(`artifact path is not a regular file: ${artifactPath}`);
|
|
5346
|
+
}
|
|
5347
|
+
const contents = fs4.readFileSync(artifactPath, "utf-8");
|
|
5348
|
+
if (!isRecognizablyCanonryGenerated(artifactPath, contents)) {
|
|
5349
|
+
throw new Error(`refusing to overwrite an operator-owned artifact: ${artifactPath}`);
|
|
5350
|
+
}
|
|
5351
|
+
}
|
|
5352
|
+
return artifacts;
|
|
5353
|
+
}
|
|
5354
|
+
function readRegularArtifact(filePath) {
|
|
5355
|
+
const stat = lstatIfPresent(filePath);
|
|
5356
|
+
if (!stat) return null;
|
|
5357
|
+
if (!stat.isFile() || stat.isSymbolicLink()) {
|
|
5358
|
+
throw new Error(`artifact path is not a regular file: ${filePath}`);
|
|
5359
|
+
}
|
|
5360
|
+
return fs4.readFileSync(filePath, "utf-8");
|
|
5361
|
+
}
|
|
5362
|
+
function isRecognizablyCanonryGenerated(filePath, contents) {
|
|
5363
|
+
if (path2.basename(filePath) === "worker.js") {
|
|
5364
|
+
return contents.startsWith(`${CLOUDFLARE_WORKER_GENERATED_MARKER}
|
|
5365
|
+
`);
|
|
5366
|
+
}
|
|
5367
|
+
if (path2.basename(filePath) !== "wrangler.toml") return false;
|
|
5368
|
+
if (contents.startsWith(`${CLOUDFLARE_WRANGLER_GENERATED_MARKER}
|
|
5369
|
+
`)) return true;
|
|
5370
|
+
return /^name = "canonry-traffic-[^"]+"$/m.test(contents) && /^main = "worker\.js"$/m.test(contents) && /^CANONRY_DELIVERY_MODE = "direct-push"$/m.test(contents) && /^CANONRY_SOURCE_ID = /m.test(contents) && /^CANONRY_INGEST_URL = /m.test(contents) && /^CANONRY_WORKER_VERSION = /m.test(contents) && contents.includes("[secrets]") && contents.includes(CLOUDFLARE_WORKER_BINDINGS.bearerToken) && contents.includes(CLOUDFLARE_WORKER_BINDINGS.hmacSecret);
|
|
5371
|
+
}
|
|
5372
|
+
function writePrivateTemporaryFile(filePath, contents) {
|
|
5373
|
+
const temporaryPath = path2.join(
|
|
5374
|
+
path2.dirname(filePath),
|
|
5375
|
+
`.${path2.basename(filePath)}.${randomUUID()}.tmp`
|
|
5376
|
+
);
|
|
5377
|
+
const fd = fs4.openSync(temporaryPath, "wx", 384);
|
|
5378
|
+
let closed = false;
|
|
5379
|
+
try {
|
|
5380
|
+
fs4.writeFileSync(fd, contents, "utf-8");
|
|
5381
|
+
fs4.fsyncSync(fd);
|
|
5382
|
+
fs4.closeSync(fd);
|
|
5383
|
+
closed = true;
|
|
5384
|
+
} catch (error) {
|
|
5385
|
+
if (!closed) {
|
|
5386
|
+
try {
|
|
5387
|
+
fs4.closeSync(fd);
|
|
5388
|
+
} catch {
|
|
5389
|
+
}
|
|
5390
|
+
}
|
|
5391
|
+
try {
|
|
5392
|
+
fs4.unlinkSync(temporaryPath);
|
|
5393
|
+
} catch {
|
|
5394
|
+
}
|
|
5395
|
+
throw error;
|
|
5396
|
+
}
|
|
5397
|
+
return temporaryPath;
|
|
5398
|
+
}
|
|
5399
|
+
function writeCloudflareWorkerArtifacts(artifacts, contents) {
|
|
5400
|
+
const requested = [
|
|
5401
|
+
{ filePath: artifacts.workerScriptPath, contents: contents.workerScript },
|
|
5402
|
+
{ filePath: artifacts.wranglerTomlPath, contents: contents.wranglerToml }
|
|
5403
|
+
];
|
|
5404
|
+
const changes = requested.flatMap((artifact) => {
|
|
5405
|
+
const previous = readRegularArtifact(artifact.filePath);
|
|
5406
|
+
if (previous === artifact.contents) return [];
|
|
5407
|
+
if (previous !== null && !isRecognizablyCanonryGenerated(artifact.filePath, previous)) {
|
|
5408
|
+
throw new Error(`refusing to overwrite an operator-owned artifact: ${artifact.filePath}`);
|
|
5409
|
+
}
|
|
5410
|
+
return [{ ...artifact, previous }];
|
|
5411
|
+
});
|
|
5412
|
+
const pending = [];
|
|
5413
|
+
const applied = [];
|
|
5414
|
+
try {
|
|
5415
|
+
for (const change of changes) {
|
|
5416
|
+
pending.push({
|
|
5417
|
+
...change,
|
|
5418
|
+
temporaryPath: writePrivateTemporaryFile(change.filePath, change.contents)
|
|
5419
|
+
});
|
|
5420
|
+
}
|
|
5421
|
+
for (const change of pending) {
|
|
5422
|
+
if (readRegularArtifact(change.filePath) !== change.previous) {
|
|
5423
|
+
throw new Error(`artifact changed during update: ${change.filePath}`);
|
|
5424
|
+
}
|
|
5425
|
+
if (change.previous === null) {
|
|
5426
|
+
fs4.linkSync(change.temporaryPath, change.filePath);
|
|
5427
|
+
fs4.unlinkSync(change.temporaryPath);
|
|
5428
|
+
} else {
|
|
5429
|
+
fs4.renameSync(change.temporaryPath, change.filePath);
|
|
5430
|
+
}
|
|
5431
|
+
applied.push(change);
|
|
5432
|
+
}
|
|
5433
|
+
} catch (error) {
|
|
5434
|
+
for (const change of applied.reverse()) {
|
|
5435
|
+
try {
|
|
5436
|
+
if (readRegularArtifact(change.filePath) !== change.contents) continue;
|
|
5437
|
+
if (change.previous === null) {
|
|
5438
|
+
fs4.unlinkSync(change.filePath);
|
|
5439
|
+
} else {
|
|
5440
|
+
const restorePath = writePrivateTemporaryFile(change.filePath, change.previous);
|
|
5441
|
+
fs4.renameSync(restorePath, change.filePath);
|
|
5442
|
+
}
|
|
5443
|
+
} catch {
|
|
5444
|
+
}
|
|
5445
|
+
}
|
|
5446
|
+
throw error;
|
|
5447
|
+
} finally {
|
|
5448
|
+
for (const change of pending) {
|
|
5449
|
+
try {
|
|
5450
|
+
if (fs4.existsSync(change.temporaryPath)) fs4.unlinkSync(change.temporaryPath);
|
|
5451
|
+
} catch {
|
|
5452
|
+
}
|
|
5453
|
+
}
|
|
5454
|
+
}
|
|
5455
|
+
}
|
|
5456
|
+
function assertCloudflareArtifactsDoNotContainSecrets(contents, secrets) {
|
|
5457
|
+
const artifactText = [contents.workerScript, contents.wranglerToml, contents.instructions ?? ""];
|
|
5458
|
+
for (const secret of [secrets.bearerToken, secrets.hmacSecret]) {
|
|
5459
|
+
if (secret && artifactText.some((value) => value.includes(secret))) {
|
|
5460
|
+
throw new Error("Cloudflare setup response unexpectedly contained a cleartext secret");
|
|
5461
|
+
}
|
|
5462
|
+
}
|
|
5463
|
+
}
|
|
5464
|
+
function redactCloudflareSecrets(message, secrets) {
|
|
5465
|
+
let redacted = message;
|
|
5466
|
+
for (const secret of [secrets.bearerToken, secrets.hmacSecret]) {
|
|
5467
|
+
if (secret) redacted = redacted.replaceAll(secret, "[REDACTED]");
|
|
5468
|
+
}
|
|
5469
|
+
return redacted;
|
|
5470
|
+
}
|
|
5471
|
+
async function runWrangler(command, args, opts) {
|
|
5472
|
+
await new Promise((resolve, reject) => {
|
|
5473
|
+
const child = spawn(command, [...args], {
|
|
5474
|
+
cwd: opts.cwd,
|
|
5475
|
+
shell: false,
|
|
5476
|
+
stdio: "inherit"
|
|
5477
|
+
});
|
|
5478
|
+
child.once("error", reject);
|
|
5479
|
+
child.once("exit", (code, signal) => {
|
|
5480
|
+
if (code === 0) {
|
|
5481
|
+
resolve();
|
|
5482
|
+
return;
|
|
5483
|
+
}
|
|
5484
|
+
reject(new Error(
|
|
5485
|
+
signal ? `Wrangler exited after signal ${signal}` : `Wrangler exited with code ${code ?? "unknown"}`
|
|
5486
|
+
));
|
|
5487
|
+
});
|
|
5488
|
+
});
|
|
5489
|
+
}
|
|
5490
|
+
async function runWranglerCapture(command, args, opts) {
|
|
5491
|
+
return new Promise((resolve, reject) => {
|
|
5492
|
+
const child = spawn(command, [...args], {
|
|
5493
|
+
cwd: opts.cwd,
|
|
5494
|
+
shell: false,
|
|
5495
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
5496
|
+
});
|
|
5497
|
+
let output = "";
|
|
5498
|
+
child.stdout.setEncoding("utf-8");
|
|
5499
|
+
child.stderr.setEncoding("utf-8");
|
|
5500
|
+
child.stdout.on("data", (chunk) => {
|
|
5501
|
+
output += String(chunk);
|
|
5502
|
+
});
|
|
5503
|
+
child.stderr.on("data", (chunk) => {
|
|
5504
|
+
output += String(chunk);
|
|
5505
|
+
});
|
|
5506
|
+
child.once("error", reject);
|
|
5507
|
+
child.once("exit", (code, signal) => {
|
|
5508
|
+
if (code === 0) {
|
|
5509
|
+
resolve(output);
|
|
5510
|
+
return;
|
|
5511
|
+
}
|
|
5512
|
+
const diagnostic = wranglerDiagnostic(output);
|
|
5513
|
+
const suffix = diagnostic ? `
|
|
5514
|
+
${diagnostic}` : "";
|
|
5515
|
+
reject(new Error(
|
|
5516
|
+
signal ? `Wrangler exited after signal ${signal}${suffix}` : `Wrangler exited with code ${code ?? "unknown"}${suffix}`
|
|
5517
|
+
));
|
|
5518
|
+
});
|
|
5519
|
+
});
|
|
5520
|
+
}
|
|
5521
|
+
async function preflightCloudflareWrangler(opts = {}) {
|
|
5522
|
+
const runner = opts.run ?? runWranglerCapture;
|
|
5523
|
+
const help = await runner(
|
|
5524
|
+
"wrangler",
|
|
5525
|
+
["deploy", "--help"],
|
|
5526
|
+
{ cwd: path2.resolve(opts.cwd ?? process.cwd()) }
|
|
5527
|
+
);
|
|
5528
|
+
const missing = ["--dry-run", "--secrets-file", "--strict"].filter((flag) => !help.includes(flag));
|
|
5529
|
+
if (missing.length > 0) {
|
|
5530
|
+
throw new Error(`Wrangler deploy does not support required flags: ${missing.join(", ")}`);
|
|
5531
|
+
}
|
|
5532
|
+
const tempDirectory = fs4.mkdtempSync(path2.join(
|
|
5533
|
+
opts.tempRoot ?? os.tmpdir(),
|
|
5534
|
+
"canonry-cloudflare-wrangler-preflight-"
|
|
5535
|
+
));
|
|
5536
|
+
const workerScriptPath = path2.join(tempDirectory, "worker.js");
|
|
5537
|
+
const wranglerTomlPath = path2.join(tempDirectory, "wrangler.toml");
|
|
5538
|
+
const secretsPath = path2.join(tempDirectory, "secrets.json");
|
|
5539
|
+
try {
|
|
5540
|
+
fs4.chmodSync(tempDirectory, 448);
|
|
5541
|
+
fs4.writeFileSync(workerScriptPath, generateWorkerScript({
|
|
5542
|
+
deliveryMode: "direct-push",
|
|
5543
|
+
workerVersion: "preflight",
|
|
5544
|
+
botList: DEFAULT_BOT_LIST
|
|
5545
|
+
}), { encoding: "utf-8", flag: "wx", mode: 384 });
|
|
5546
|
+
fs4.writeFileSync(wranglerTomlPath, generateWranglerToml({
|
|
5547
|
+
deliveryMode: "direct-push",
|
|
5548
|
+
sourceId: "preflight",
|
|
5549
|
+
hostname: "example.invalid",
|
|
5550
|
+
ingestUrl: "https://canonry.example.invalid/api/v1/projects/preflight/traffic/cloudflare/ingest",
|
|
5551
|
+
workerVersion: "preflight"
|
|
5552
|
+
}), { encoding: "utf-8", flag: "wx", mode: 384 });
|
|
5553
|
+
fs4.writeFileSync(secretsPath, JSON.stringify({
|
|
5554
|
+
[CLOUDFLARE_WORKER_BINDINGS.bearerToken]: "preflight-bearer",
|
|
5555
|
+
[CLOUDFLARE_WORKER_BINDINGS.hmacSecret]: "preflight-hmac"
|
|
5556
|
+
}), { encoding: "utf-8", flag: "wx", mode: 384 });
|
|
5557
|
+
const validationOutput = await runner(
|
|
5558
|
+
"wrangler",
|
|
5559
|
+
[
|
|
5560
|
+
"deploy",
|
|
5561
|
+
"--config",
|
|
5562
|
+
wranglerTomlPath,
|
|
5563
|
+
"--secrets-file",
|
|
5564
|
+
secretsPath,
|
|
5565
|
+
"--strict",
|
|
5566
|
+
"--dry-run",
|
|
5567
|
+
"--outdir",
|
|
5568
|
+
path2.join(tempDirectory, "dist")
|
|
5569
|
+
],
|
|
5570
|
+
{ cwd: tempDirectory }
|
|
5571
|
+
);
|
|
5572
|
+
const hasUnknownFields = /Unexpected fields found/i.test(validationOutput);
|
|
5573
|
+
const hasExperimentalSecrets = /\bexperimental\b/i.test(validationOutput) && /\bsecrets?\b/i.test(validationOutput);
|
|
5574
|
+
if (hasUnknownFields || hasExperimentalSecrets) {
|
|
5575
|
+
throw new Error(
|
|
5576
|
+
`Wrangler reported an incompatible generated configuration:
|
|
5577
|
+
${wranglerDiagnostic(validationOutput)}`
|
|
5578
|
+
);
|
|
5579
|
+
}
|
|
5580
|
+
} finally {
|
|
5581
|
+
fs4.rmSync(tempDirectory, { recursive: true, force: true });
|
|
5582
|
+
}
|
|
5583
|
+
}
|
|
5584
|
+
async function deployCloudflareWorker(opts) {
|
|
5585
|
+
const tempDirectory = fs4.mkdtempSync(path2.join(opts.tempRoot ?? os.tmpdir(), "canonry-cloudflare-secrets-"));
|
|
5586
|
+
const secretsPath = path2.join(tempDirectory, "secrets.json");
|
|
5587
|
+
try {
|
|
5588
|
+
fs4.chmodSync(tempDirectory, 448);
|
|
5589
|
+
fs4.writeFileSync(
|
|
5590
|
+
secretsPath,
|
|
5591
|
+
JSON.stringify({
|
|
5592
|
+
[CLOUDFLARE_WORKER_BINDINGS.bearerToken]: opts.secrets.bearerToken,
|
|
5593
|
+
[CLOUDFLARE_WORKER_BINDINGS.hmacSecret]: opts.secrets.hmacSecret
|
|
5594
|
+
}),
|
|
5595
|
+
{ encoding: "utf-8", flag: "wx", mode: 384 }
|
|
5596
|
+
);
|
|
5597
|
+
fs4.chmodSync(secretsPath, 384);
|
|
5598
|
+
const runner = opts.run ?? runWrangler;
|
|
5599
|
+
await runner(
|
|
5600
|
+
"wrangler",
|
|
5601
|
+
[
|
|
5602
|
+
"deploy",
|
|
5603
|
+
"--config",
|
|
5604
|
+
path2.resolve(opts.wranglerTomlPath),
|
|
5605
|
+
"--secrets-file",
|
|
5606
|
+
secretsPath,
|
|
5607
|
+
"--strict"
|
|
5608
|
+
],
|
|
5609
|
+
{ cwd: path2.dirname(path2.resolve(opts.wranglerTomlPath)) }
|
|
5610
|
+
);
|
|
5611
|
+
} finally {
|
|
5612
|
+
try {
|
|
5613
|
+
if (fs4.existsSync(secretsPath)) fs4.unlinkSync(secretsPath);
|
|
5614
|
+
} finally {
|
|
5615
|
+
fs4.rmSync(tempDirectory, { recursive: true, force: true });
|
|
5616
|
+
}
|
|
5617
|
+
}
|
|
5618
|
+
}
|
|
5619
|
+
|
|
5288
5620
|
// src/commands/traffic.ts
|
|
5289
5621
|
function getClient9() {
|
|
5290
5622
|
return createApiClient();
|
|
@@ -5292,6 +5624,169 @@ function getClient9() {
|
|
|
5292
5624
|
function configString(value, fallback = "(unset)") {
|
|
5293
5625
|
return typeof value === "string" ? value : fallback;
|
|
5294
5626
|
}
|
|
5627
|
+
function errorMessage2(error) {
|
|
5628
|
+
return error instanceof Error ? error.message : String(error);
|
|
5629
|
+
}
|
|
5630
|
+
async function trafficConnectCloudflare(project, opts, deps = {}) {
|
|
5631
|
+
if (opts.deploy && !opts.zoneId) {
|
|
5632
|
+
throw new CliError({
|
|
5633
|
+
code: "TRAFFIC_CLOUDFLARE_DEPLOY_ZONE_REQUIRED",
|
|
5634
|
+
message: "--zone-id is required with --deploy",
|
|
5635
|
+
displayMessage: "Error: --zone-id <id> is required with --deploy",
|
|
5636
|
+
details: { project }
|
|
5637
|
+
});
|
|
5638
|
+
}
|
|
5639
|
+
if (opts.deploy && !opts.confirmRoute) {
|
|
5640
|
+
throw new CliError({
|
|
5641
|
+
code: "TRAFFIC_CLOUDFLARE_ROUTE_CONFIRMATION_REQUIRED",
|
|
5642
|
+
message: "--confirm-route is required with --deploy",
|
|
5643
|
+
displayMessage: "Error: inspect existing Worker routes first. Pass --confirm-route to acknowledge the required manual route attachment after Worker deployment.",
|
|
5644
|
+
details: { project, zoneId: opts.zoneId }
|
|
5645
|
+
});
|
|
5646
|
+
}
|
|
5647
|
+
if (opts.deploy && !opts.confirmFailOpen) {
|
|
5648
|
+
throw new CliError({
|
|
5649
|
+
code: "TRAFFIC_CLOUDFLARE_FAIL_OPEN_CONFIRMATION_REQUIRED",
|
|
5650
|
+
message: "--confirm-fail-open is required with --deploy",
|
|
5651
|
+
displayMessage: "Error: pass --confirm-fail-open to acknowledge the required Fail open setting during manual route attachment after Worker deployment.",
|
|
5652
|
+
details: { project, zoneId: opts.zoneId }
|
|
5653
|
+
});
|
|
5654
|
+
}
|
|
5655
|
+
if (opts.deploy) {
|
|
5656
|
+
try {
|
|
5657
|
+
await (deps.preflightWrangler ?? preflightCloudflareWrangler)();
|
|
5658
|
+
} catch (error) {
|
|
5659
|
+
const message = errorMessage2(error);
|
|
5660
|
+
throw new CliError({
|
|
5661
|
+
code: "TRAFFIC_CLOUDFLARE_WRANGLER_UNSUPPORTED",
|
|
5662
|
+
message,
|
|
5663
|
+
displayMessage: `Error: Wrangler preflight failed before Canonry changed state.
|
|
5664
|
+
${message}`,
|
|
5665
|
+
details: { project }
|
|
5666
|
+
});
|
|
5667
|
+
}
|
|
5668
|
+
}
|
|
5669
|
+
const outputDirectory = resolveCloudflareWorkerOutputDirectory(project, opts.outputDirectory);
|
|
5670
|
+
let artifacts;
|
|
5671
|
+
try {
|
|
5672
|
+
artifacts = prepareCloudflareWorkerOutputDirectory(outputDirectory);
|
|
5673
|
+
} catch (error) {
|
|
5674
|
+
const message = errorMessage2(error);
|
|
5675
|
+
throw new CliError({
|
|
5676
|
+
code: "TRAFFIC_CLOUDFLARE_OUTPUT_INVALID",
|
|
5677
|
+
message,
|
|
5678
|
+
displayMessage: `Error: cannot use Cloudflare Worker output directory: ${message}`,
|
|
5679
|
+
details: { project, outputDirectory }
|
|
5680
|
+
});
|
|
5681
|
+
}
|
|
5682
|
+
const client = deps.client ?? getClient9();
|
|
5683
|
+
const result = await client.trafficConnectCloudflare(project, {
|
|
5684
|
+
deliveryMode: "direct-push",
|
|
5685
|
+
displayName: opts.displayName,
|
|
5686
|
+
zoneId: opts.zoneId,
|
|
5687
|
+
accountId: opts.accountId
|
|
5688
|
+
});
|
|
5689
|
+
const localConfig = (deps.loadLocalConfig ?? loadConfigRaw)();
|
|
5690
|
+
const localCredential = localConfig ? getCloudflareTrafficConnection(localConfig, project) : void 0;
|
|
5691
|
+
const deploymentCredential = localCredential?.sourceId === result.sourceId ? localCredential : void 0;
|
|
5692
|
+
if (deploymentCredential) {
|
|
5693
|
+
try {
|
|
5694
|
+
assertCloudflareArtifactsDoNotContainSecrets(result, deploymentCredential);
|
|
5695
|
+
} catch {
|
|
5696
|
+
throw new CliError({
|
|
5697
|
+
code: "TRAFFIC_CLOUDFLARE_SECRET_EXPOSURE",
|
|
5698
|
+
message: "Cloudflare setup response unexpectedly contained a cleartext secret",
|
|
5699
|
+
displayMessage: "Error: refused to write Cloudflare artifacts because the response contained a cleartext secret",
|
|
5700
|
+
details: { project, sourceId: result.sourceId }
|
|
5701
|
+
});
|
|
5702
|
+
}
|
|
5703
|
+
}
|
|
5704
|
+
try {
|
|
5705
|
+
writeCloudflareWorkerArtifacts(artifacts, result);
|
|
5706
|
+
} catch (error) {
|
|
5707
|
+
const message = errorMessage2(error);
|
|
5708
|
+
throw new CliError({
|
|
5709
|
+
code: "TRAFFIC_CLOUDFLARE_ARTIFACT_WRITE_FAILED",
|
|
5710
|
+
message,
|
|
5711
|
+
displayMessage: `Error: Cloudflare source connected, but artifact writing failed: ${message}`,
|
|
5712
|
+
details: { project, sourceId: result.sourceId, outputDirectory }
|
|
5713
|
+
});
|
|
5714
|
+
}
|
|
5715
|
+
if (opts.deploy && !deploymentCredential) {
|
|
5716
|
+
throw new CliError({
|
|
5717
|
+
code: "TRAFFIC_CLOUDFLARE_DEPLOY_CONFIG_UNSHARED",
|
|
5718
|
+
message: "Cloudflare auto-deploy requires the CLI and Canonry server to share the same local config",
|
|
5719
|
+
displayMessage: "Error: source connected and artifacts written, but auto-deploy is unavailable because this CLI cannot read the server-stored Worker credentials. Ask the Canonry server operator to deploy the Worker and install its secrets.",
|
|
5720
|
+
details: {
|
|
5721
|
+
project,
|
|
5722
|
+
sourceId: result.sourceId,
|
|
5723
|
+
outputDirectory
|
|
5724
|
+
}
|
|
5725
|
+
});
|
|
5726
|
+
}
|
|
5727
|
+
if (opts.deploy && deploymentCredential) {
|
|
5728
|
+
try {
|
|
5729
|
+
await (deps.deployWorker ?? deployCloudflareWorker)({
|
|
5730
|
+
wranglerTomlPath: artifacts.wranglerTomlPath,
|
|
5731
|
+
secrets: deploymentCredential
|
|
5732
|
+
});
|
|
5733
|
+
} catch (error) {
|
|
5734
|
+
const message = redactCloudflareSecrets(errorMessage2(error), deploymentCredential);
|
|
5735
|
+
throw new CliError({
|
|
5736
|
+
code: "TRAFFIC_CLOUDFLARE_DEPLOY_FAILED",
|
|
5737
|
+
message,
|
|
5738
|
+
displayMessage: `Error: Cloudflare source connected and artifacts written, but Wrangler deploy failed: ${message}`,
|
|
5739
|
+
details: { project, sourceId: result.sourceId, outputDirectory }
|
|
5740
|
+
});
|
|
5741
|
+
}
|
|
5742
|
+
}
|
|
5743
|
+
const summary = {
|
|
5744
|
+
sourceId: result.sourceId,
|
|
5745
|
+
deliveryMode: result.deliveryMode,
|
|
5746
|
+
workerVersion: result.workerVersion,
|
|
5747
|
+
outputDirectory: artifacts.outputDirectory,
|
|
5748
|
+
files: {
|
|
5749
|
+
workerScript: artifacts.workerScriptPath,
|
|
5750
|
+
wranglerConfig: artifacts.wranglerTomlPath
|
|
5751
|
+
},
|
|
5752
|
+
autoDeployAvailable: Boolean(deploymentCredential),
|
|
5753
|
+
deployment: opts.deploy ? "worker-deployed-route-unattached" : "not-requested",
|
|
5754
|
+
routeAttachment: "required-manual",
|
|
5755
|
+
requestLimitFailureMode: {
|
|
5756
|
+
required: "fail-open",
|
|
5757
|
+
status: "required-during-manual-route-attachment",
|
|
5758
|
+
configuredBy: "cloudflare-dashboard",
|
|
5759
|
+
configuredByWrangler: false
|
|
5760
|
+
},
|
|
5761
|
+
postDeployInstructions: result.instructions
|
|
5762
|
+
};
|
|
5763
|
+
if (isMachineFormat(opts.format)) {
|
|
5764
|
+
console.log(JSON.stringify(summary, null, 2));
|
|
5765
|
+
return;
|
|
5766
|
+
}
|
|
5767
|
+
console.log(`Cloudflare direct-push traffic source connected for project "${project}".`);
|
|
5768
|
+
console.log(` Source ID: ${result.sourceId}`);
|
|
5769
|
+
console.log(` Worker version: ${result.workerVersion}`);
|
|
5770
|
+
console.log(` Worker script: ${artifacts.workerScriptPath}`);
|
|
5771
|
+
console.log(` Wrangler config: ${artifacts.wranglerTomlPath}`);
|
|
5772
|
+
console.log(` Deployment: ${opts.deploy ? "Worker deployed; route not attached" : "not requested"}`);
|
|
5773
|
+
console.log("");
|
|
5774
|
+
console.log("The required manual route is a catch-all. Inspect existing Worker routes before deployment.");
|
|
5775
|
+
console.log("Wrangler deployment does not attach the route. Attach it manually after the Worker deploys.");
|
|
5776
|
+
console.log("Required: set the route Request limit failure mode to Fail open before route activation.");
|
|
5777
|
+
console.log("Wrangler cannot configure this toggle. Workers Free includes 100,000 requests per day for the account.");
|
|
5778
|
+
console.log("Use one authoritative server-traffic source for this site. Overlapping adapters double-count project totals.");
|
|
5779
|
+
if (opts.deploy) {
|
|
5780
|
+
console.log("");
|
|
5781
|
+
console.log("Required Cloudflare Dashboard steps:");
|
|
5782
|
+
console.log(result.instructions);
|
|
5783
|
+
console.log("Keep the Canonry public HTTPS URL reachable so the Worker can push events.");
|
|
5784
|
+
} else if (deploymentCredential) {
|
|
5785
|
+
console.log("Next: install and authenticate Wrangler. Inspect the route, then rerun with --deploy --confirm-route --confirm-fail-open and --zone-id <id>.");
|
|
5786
|
+
} else {
|
|
5787
|
+
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
|
+
}
|
|
5789
|
+
}
|
|
5295
5790
|
async function trafficConnectWordpress(project, opts) {
|
|
5296
5791
|
if (!opts.url) {
|
|
5297
5792
|
throw new CliError({
|
|
@@ -5319,9 +5814,9 @@ async function trafficConnectWordpress(project, opts) {
|
|
|
5319
5814
|
}
|
|
5320
5815
|
let applicationPassword = opts.appPassword?.trim() ?? "";
|
|
5321
5816
|
if (!applicationPassword && opts.appPasswordFile) {
|
|
5322
|
-
const
|
|
5817
|
+
const fs18 = await import("fs");
|
|
5323
5818
|
try {
|
|
5324
|
-
applicationPassword =
|
|
5819
|
+
applicationPassword = fs18.readFileSync(opts.appPasswordFile, "utf-8").trim();
|
|
5325
5820
|
} catch (e) {
|
|
5326
5821
|
const msg = e instanceof Error ? e.message : String(e);
|
|
5327
5822
|
throw new CliError({
|
|
@@ -5377,10 +5872,10 @@ async function trafficConnectCloudRun(project, opts) {
|
|
|
5377
5872
|
details: { project }
|
|
5378
5873
|
});
|
|
5379
5874
|
}
|
|
5380
|
-
const
|
|
5875
|
+
const fs18 = await import("fs");
|
|
5381
5876
|
let keyJson;
|
|
5382
5877
|
try {
|
|
5383
|
-
keyJson =
|
|
5878
|
+
keyJson = fs18.readFileSync(opts.serviceAccountKey, "utf-8");
|
|
5384
5879
|
JSON.parse(keyJson);
|
|
5385
5880
|
} catch (e) {
|
|
5386
5881
|
const msg = e instanceof Error ? e.message : String(e);
|
|
@@ -5440,9 +5935,9 @@ async function trafficConnectVercel(project, opts) {
|
|
|
5440
5935
|
}
|
|
5441
5936
|
let token = opts.token?.trim() ?? "";
|
|
5442
5937
|
if (!token && opts.tokenFile) {
|
|
5443
|
-
const
|
|
5938
|
+
const fs18 = await import("fs");
|
|
5444
5939
|
try {
|
|
5445
|
-
token =
|
|
5940
|
+
token = fs18.readFileSync(opts.tokenFile, "utf-8").trim();
|
|
5446
5941
|
} catch (e) {
|
|
5447
5942
|
const msg = e instanceof Error ? e.message : String(e);
|
|
5448
5943
|
throw new CliError({
|
|
@@ -5552,11 +6047,11 @@ async function trafficBackfill(project, opts) {
|
|
|
5552
6047
|
console.log(`Inspect rebuilt rollups: canonry traffic events ${project} --source ${opts.source} --since-minutes ${submitted.daysApplied * 24 * 60}`);
|
|
5553
6048
|
return;
|
|
5554
6049
|
}
|
|
5555
|
-
const
|
|
6050
|
+
const errorMessage3 = final.error?.message ?? null;
|
|
5556
6051
|
throw new CliError({
|
|
5557
6052
|
code: "TRAFFIC_BACKFILL_FAILED",
|
|
5558
|
-
message:
|
|
5559
|
-
displayMessage: `Error: backfill run ${final.id} ${final.status}${
|
|
6053
|
+
message: errorMessage3 ?? "backfill run did not complete successfully",
|
|
6054
|
+
displayMessage: `Error: backfill run ${final.id} ${final.status}${errorMessage3 ? ` \u2014 ${errorMessage3}` : ""}`,
|
|
5560
6055
|
details: { project, runId: final.id, status: final.status }
|
|
5561
6056
|
});
|
|
5562
6057
|
}
|
|
@@ -5791,6 +6286,36 @@ async function trafficEvents(project, opts) {
|
|
|
5791
6286
|
|
|
5792
6287
|
// src/cli-commands/traffic.ts
|
|
5793
6288
|
var TRAFFIC_CLI_COMMANDS = [
|
|
6289
|
+
{
|
|
6290
|
+
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]",
|
|
6292
|
+
options: {
|
|
6293
|
+
"display-name": stringOption(),
|
|
6294
|
+
"zone-id": stringOption(),
|
|
6295
|
+
"account-id": stringOption(),
|
|
6296
|
+
"output-dir": stringOption(),
|
|
6297
|
+
deploy: { type: "boolean" },
|
|
6298
|
+
"confirm-route": { type: "boolean" },
|
|
6299
|
+
"confirm-fail-open": { type: "boolean" }
|
|
6300
|
+
},
|
|
6301
|
+
run: async (input) => {
|
|
6302
|
+
const project = requireProject(
|
|
6303
|
+
input,
|
|
6304
|
+
"traffic.connect.cloudflare",
|
|
6305
|
+
"canonry traffic connect cloudflare <project> [--zone-id <id>] [--output-dir <dir>] [--deploy --confirm-route --confirm-fail-open]"
|
|
6306
|
+
);
|
|
6307
|
+
await trafficConnectCloudflare(project, {
|
|
6308
|
+
displayName: getString(input.values, "display-name"),
|
|
6309
|
+
zoneId: getString(input.values, "zone-id"),
|
|
6310
|
+
accountId: getString(input.values, "account-id"),
|
|
6311
|
+
outputDirectory: getString(input.values, "output-dir"),
|
|
6312
|
+
deploy: getBoolean(input.values, "deploy"),
|
|
6313
|
+
confirmRoute: getBoolean(input.values, "confirm-route"),
|
|
6314
|
+
confirmFailOpen: getBoolean(input.values, "confirm-fail-open"),
|
|
6315
|
+
format: input.format
|
|
6316
|
+
});
|
|
6317
|
+
}
|
|
6318
|
+
},
|
|
5794
6319
|
{
|
|
5795
6320
|
path: ["traffic", "connect", "cloud-run"],
|
|
5796
6321
|
usage: "canonry traffic connect cloud-run <project> --gcp-project <id> --service-account-key <path> [--service <name>] [--location <region>] [--display-name <name>] [--format json]",
|
|
@@ -5890,7 +6415,7 @@ var TRAFFIC_CLI_COMMANDS = [
|
|
|
5890
6415
|
unknownSubcommand(input.positionals[0], {
|
|
5891
6416
|
command: "traffic connect",
|
|
5892
6417
|
usage: "canonry traffic connect <provider> <project> [args]",
|
|
5893
|
-
available: ["cloud-run", "wordpress", "vercel"]
|
|
6418
|
+
available: ["cloud-run", "cloudflare", "wordpress", "vercel"]
|
|
5894
6419
|
});
|
|
5895
6420
|
}
|
|
5896
6421
|
},
|
|
@@ -5998,7 +6523,7 @@ var TRAFFIC_CLI_COMMANDS = [
|
|
|
5998
6523
|
},
|
|
5999
6524
|
{
|
|
6000
6525
|
path: ["traffic", "events"],
|
|
6001
|
-
usage: "canonry traffic events <project> [--kind crawler|ai-referral|all] [--source <id>] [--since-minutes 1440] [--since <iso>] [--until <iso>] [--limit 500] [--granularity hour|day] [--format json]",
|
|
6526
|
+
usage: "canonry traffic events <project> [--kind crawler|ai-user-fetch|ai-referral|all] [--source <id>] [--since-minutes 1440] [--since <iso>] [--until <iso>] [--limit 500] [--granularity hour|day] [--format json]",
|
|
6002
6527
|
options: {
|
|
6003
6528
|
kind: stringOption(),
|
|
6004
6529
|
source: stringOption(),
|
|
@@ -6270,10 +6795,10 @@ Open this URL in your browser to authorize Google ${opts.type.toUpperCase()} acc
|
|
|
6270
6795
|
console.log("(Ensure this URI is listed in your Google Cloud Console OAuth client's authorized redirect URIs)\n");
|
|
6271
6796
|
}
|
|
6272
6797
|
try {
|
|
6273
|
-
const { spawn:
|
|
6798
|
+
const { spawn: spawn3 } = await import("child_process");
|
|
6274
6799
|
const platform = process.platform;
|
|
6275
6800
|
const [cmd, ...extraArgs] = platform === "darwin" ? ["open", authUrl] : platform === "win32" ? ["cmd", "/c", "start", "", authUrl] : ["xdg-open", authUrl];
|
|
6276
|
-
|
|
6801
|
+
spawn3(cmd, [...extraArgs], { detached: true, stdio: "ignore" }).unref();
|
|
6277
6802
|
console.log("(Browser opened automatically)");
|
|
6278
6803
|
} catch {
|
|
6279
6804
|
console.log("(Could not open browser automatically \u2014 please copy the URL above)");
|
|
@@ -7785,7 +8310,7 @@ var USERS_CLI_COMMANDS = [
|
|
|
7785
8310
|
];
|
|
7786
8311
|
|
|
7787
8312
|
// src/commands/keyword.ts
|
|
7788
|
-
import
|
|
8313
|
+
import fs5 from "fs";
|
|
7789
8314
|
function getClient14() {
|
|
7790
8315
|
return createApiClient();
|
|
7791
8316
|
}
|
|
@@ -7853,7 +8378,7 @@ async function listKeywords(project, format) {
|
|
|
7853
8378
|
}
|
|
7854
8379
|
}
|
|
7855
8380
|
async function importKeywords(project, filePath, format) {
|
|
7856
|
-
if (!
|
|
8381
|
+
if (!fs5.existsSync(filePath)) {
|
|
7857
8382
|
throw new CliError({
|
|
7858
8383
|
code: "KEYWORD_IMPORT_FILE_NOT_FOUND",
|
|
7859
8384
|
message: `File not found: ${filePath}`,
|
|
@@ -7864,7 +8389,7 @@ async function importKeywords(project, filePath, format) {
|
|
|
7864
8389
|
}
|
|
7865
8390
|
});
|
|
7866
8391
|
}
|
|
7867
|
-
const content =
|
|
8392
|
+
const content = fs5.readFileSync(filePath, "utf-8");
|
|
7868
8393
|
const keywords = content.split("\n").map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#"));
|
|
7869
8394
|
if (keywords.length === 0) {
|
|
7870
8395
|
if (isMachineFormat(format)) {
|
|
@@ -8065,7 +8590,7 @@ var KEYWORD_CLI_COMMANDS = [
|
|
|
8065
8590
|
];
|
|
8066
8591
|
|
|
8067
8592
|
// src/commands/query.ts
|
|
8068
|
-
import
|
|
8593
|
+
import fs6 from "fs";
|
|
8069
8594
|
function getClient15() {
|
|
8070
8595
|
return createApiClient();
|
|
8071
8596
|
}
|
|
@@ -8156,7 +8681,7 @@ async function listQueries(project, format) {
|
|
|
8156
8681
|
}
|
|
8157
8682
|
}
|
|
8158
8683
|
async function importQueries(project, filePath, format) {
|
|
8159
|
-
if (!
|
|
8684
|
+
if (!fs6.existsSync(filePath)) {
|
|
8160
8685
|
throw new CliError({
|
|
8161
8686
|
code: "QUERY_IMPORT_FILE_NOT_FOUND",
|
|
8162
8687
|
message: `File not found: ${filePath}`,
|
|
@@ -8167,7 +8692,7 @@ async function importQueries(project, filePath, format) {
|
|
|
8167
8692
|
}
|
|
8168
8693
|
});
|
|
8169
8694
|
}
|
|
8170
|
-
const content =
|
|
8695
|
+
const content = fs6.readFileSync(filePath, "utf-8");
|
|
8171
8696
|
const queries2 = content.split("\n").map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#"));
|
|
8172
8697
|
if (queries2.length === 0) {
|
|
8173
8698
|
if (isMachineFormat(format)) {
|
|
@@ -8369,16 +8894,16 @@ var QUERY_CLI_COMMANDS = [
|
|
|
8369
8894
|
];
|
|
8370
8895
|
|
|
8371
8896
|
// src/commands/mcp.ts
|
|
8372
|
-
import
|
|
8373
|
-
import
|
|
8897
|
+
import fs7 from "fs";
|
|
8898
|
+
import path4 from "path";
|
|
8374
8899
|
import { createRequire } from "module";
|
|
8375
8900
|
|
|
8376
8901
|
// src/mcp-clients.ts
|
|
8377
|
-
import
|
|
8378
|
-
import
|
|
8902
|
+
import os2 from "os";
|
|
8903
|
+
import path3 from "path";
|
|
8379
8904
|
var CLAUDE_DESKTOP_CONFIG_FILENAME = "claude_desktop_config.json";
|
|
8380
8905
|
function homeRelative(...segments) {
|
|
8381
|
-
return
|
|
8906
|
+
return path3.join(os2.homedir(), ...segments);
|
|
8382
8907
|
}
|
|
8383
8908
|
function claudeDesktopConfigPath() {
|
|
8384
8909
|
switch (process.platform) {
|
|
@@ -8386,7 +8911,7 @@ function claudeDesktopConfigPath() {
|
|
|
8386
8911
|
return homeRelative("Library", "Application Support", "Claude", CLAUDE_DESKTOP_CONFIG_FILENAME);
|
|
8387
8912
|
case "win32": {
|
|
8388
8913
|
const appData = process.env.APPDATA ?? homeRelative("AppData", "Roaming");
|
|
8389
|
-
return
|
|
8914
|
+
return path3.join(appData, "Claude", CLAUDE_DESKTOP_CONFIG_FILENAME);
|
|
8390
8915
|
}
|
|
8391
8916
|
default:
|
|
8392
8917
|
return homeRelative(".config", "Claude", CLAUDE_DESKTOP_CONFIG_FILENAME);
|
|
@@ -8399,7 +8924,7 @@ function codexConfigPath() {
|
|
|
8399
8924
|
return homeRelative(".codex", "config.toml");
|
|
8400
8925
|
}
|
|
8401
8926
|
function claudeCodeProjectConfigPath() {
|
|
8402
|
-
return
|
|
8927
|
+
return path3.join(process.cwd(), ".mcp.json");
|
|
8403
8928
|
}
|
|
8404
8929
|
var SUPPORTED_MCP_CLIENTS = [
|
|
8405
8930
|
{
|
|
@@ -8442,7 +8967,7 @@ function listMcpClientIds() {
|
|
|
8442
8967
|
var _require = createRequire(import.meta.url);
|
|
8443
8968
|
function resolveCanonryMcpBin() {
|
|
8444
8969
|
const packageJsonPath = _require.resolve("../package.json");
|
|
8445
|
-
const packageRoot =
|
|
8970
|
+
const packageRoot = path4.dirname(packageJsonPath);
|
|
8446
8971
|
const pkg = _require("../package.json");
|
|
8447
8972
|
const relativeBin = pkg.bin?.["canonry-mcp"];
|
|
8448
8973
|
if (!relativeBin) {
|
|
@@ -8452,7 +8977,7 @@ function resolveCanonryMcpBin() {
|
|
|
8452
8977
|
exitCode: 2
|
|
8453
8978
|
});
|
|
8454
8979
|
}
|
|
8455
|
-
return
|
|
8980
|
+
return path4.resolve(packageRoot, relativeBin);
|
|
8456
8981
|
}
|
|
8457
8982
|
function buildEntry(opts) {
|
|
8458
8983
|
const target = opts.binPath ?? resolveCanonryMcpBin();
|
|
@@ -8485,8 +9010,8 @@ function renderClientSnippet(client, serverName, entry) {
|
|
|
8485
9010
|
return renderJsonSnippet(serverName, entry, client.format);
|
|
8486
9011
|
}
|
|
8487
9012
|
function readJsonConfig(configPath) {
|
|
8488
|
-
if (!
|
|
8489
|
-
const raw =
|
|
9013
|
+
if (!fs7.existsSync(configPath)) return {};
|
|
9014
|
+
const raw = fs7.readFileSync(configPath, "utf-8").trim();
|
|
8490
9015
|
if (!raw) return {};
|
|
8491
9016
|
try {
|
|
8492
9017
|
const parsed = JSON.parse(raw);
|
|
@@ -8504,14 +9029,14 @@ function readJsonConfig(configPath) {
|
|
|
8504
9029
|
}
|
|
8505
9030
|
}
|
|
8506
9031
|
function writeJsonConfig(configPath, value) {
|
|
8507
|
-
|
|
8508
|
-
|
|
9032
|
+
fs7.mkdirSync(path4.dirname(configPath), { recursive: true });
|
|
9033
|
+
fs7.writeFileSync(configPath, `${JSON.stringify(value, null, 2)}
|
|
8509
9034
|
`, "utf-8");
|
|
8510
9035
|
}
|
|
8511
9036
|
function backupConfigIfPresent(configPath) {
|
|
8512
|
-
if (!
|
|
9037
|
+
if (!fs7.existsSync(configPath)) return void 0;
|
|
8513
9038
|
const backupPath = `${configPath}.canonry.bak`;
|
|
8514
|
-
|
|
9039
|
+
fs7.copyFileSync(configPath, backupPath);
|
|
8515
9040
|
return backupPath;
|
|
8516
9041
|
}
|
|
8517
9042
|
function findClientOrThrow(id) {
|
|
@@ -8867,13 +9392,13 @@ var NOTIFY_CLI_COMMANDS = [
|
|
|
8867
9392
|
];
|
|
8868
9393
|
|
|
8869
9394
|
// src/commands/apply.ts
|
|
8870
|
-
import
|
|
9395
|
+
import fs8 from "fs";
|
|
8871
9396
|
import { parseAllDocuments } from "yaml";
|
|
8872
9397
|
async function applyConfigFile(filePath) {
|
|
8873
|
-
if (!
|
|
9398
|
+
if (!fs8.existsSync(filePath)) {
|
|
8874
9399
|
throw new Error(`File not found: ${filePath}`);
|
|
8875
9400
|
}
|
|
8876
|
-
const content =
|
|
9401
|
+
const content = fs8.readFileSync(filePath, "utf-8");
|
|
8877
9402
|
const docs = parseAllDocuments(content);
|
|
8878
9403
|
const client = createApiClient();
|
|
8879
9404
|
const errors = [];
|
|
@@ -9276,8 +9801,8 @@ async function loadLatestRunForExport(client, project) {
|
|
|
9276
9801
|
}
|
|
9277
9802
|
|
|
9278
9803
|
// src/commands/results-export.ts
|
|
9279
|
-
import
|
|
9280
|
-
import
|
|
9804
|
+
import fs9 from "fs";
|
|
9805
|
+
import path5 from "path";
|
|
9281
9806
|
async function exportResults(project, opts) {
|
|
9282
9807
|
const { output, ...request } = opts;
|
|
9283
9808
|
const artifact = await createApiClient().downloadResultsExport(project, request);
|
|
@@ -9285,9 +9810,9 @@ async function exportResults(project, opts) {
|
|
|
9285
9810
|
process.stdout.write(artifact.content);
|
|
9286
9811
|
return;
|
|
9287
9812
|
}
|
|
9288
|
-
const target = output ?
|
|
9289
|
-
|
|
9290
|
-
|
|
9813
|
+
const target = output ? path5.resolve(output) : path5.resolve(process.cwd(), path5.basename(artifact.filename));
|
|
9814
|
+
fs9.mkdirSync(path5.dirname(target), { recursive: true });
|
|
9815
|
+
fs9.writeFileSync(target, artifact.content, "utf8");
|
|
9291
9816
|
console.log(`Results export written to ${target}`);
|
|
9292
9817
|
}
|
|
9293
9818
|
|
|
@@ -10003,11 +10528,11 @@ function parseProviderModelAssignments(assignments) {
|
|
|
10003
10528
|
}
|
|
10004
10529
|
|
|
10005
10530
|
// src/commands/report.ts
|
|
10006
|
-
import
|
|
10007
|
-
import
|
|
10531
|
+
import fs10 from "fs";
|
|
10532
|
+
import path6 from "path";
|
|
10008
10533
|
function defaultOutputPath(project, audience) {
|
|
10009
10534
|
const date = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
10010
|
-
return
|
|
10535
|
+
return path6.resolve(process.cwd(), `canonry-report-${project}-${audience}-${date}.html`);
|
|
10011
10536
|
}
|
|
10012
10537
|
async function runReportCommand(project, opts = {}) {
|
|
10013
10538
|
const client = createApiClient();
|
|
@@ -10018,12 +10543,12 @@ async function runReportCommand(project, opts = {}) {
|
|
|
10018
10543
|
return;
|
|
10019
10544
|
}
|
|
10020
10545
|
const html = renderReportHtml(report, { audience });
|
|
10021
|
-
const targetPath = opts.output ?
|
|
10022
|
-
const dir =
|
|
10023
|
-
if (!
|
|
10024
|
-
|
|
10546
|
+
const targetPath = opts.output ? path6.resolve(opts.output) : defaultOutputPath(project, audience);
|
|
10547
|
+
const dir = path6.dirname(targetPath);
|
|
10548
|
+
if (!fs10.existsSync(dir)) {
|
|
10549
|
+
fs10.mkdirSync(dir, { recursive: true });
|
|
10025
10550
|
}
|
|
10026
|
-
|
|
10551
|
+
fs10.writeFileSync(targetPath, html, "utf-8");
|
|
10027
10552
|
console.log(`Report written to ${targetPath}`);
|
|
10028
10553
|
}
|
|
10029
10554
|
|
|
@@ -10998,12 +11523,12 @@ var SKILLS_CLI_COMMANDS = [
|
|
|
10998
11523
|
];
|
|
10999
11524
|
|
|
11000
11525
|
// src/commands/snapshot.ts
|
|
11001
|
-
import
|
|
11002
|
-
import
|
|
11526
|
+
import fs12 from "fs";
|
|
11527
|
+
import path8 from "path";
|
|
11003
11528
|
|
|
11004
11529
|
// src/snapshot-pdf.ts
|
|
11005
|
-
import
|
|
11006
|
-
import
|
|
11530
|
+
import fs11 from "fs";
|
|
11531
|
+
import path7 from "path";
|
|
11007
11532
|
import { PDFDocument, StandardFonts, rgb } from "pdf-lib";
|
|
11008
11533
|
var PAGE_WIDTH = 612;
|
|
11009
11534
|
var PAGE_HEIGHT = 792;
|
|
@@ -11215,9 +11740,9 @@ async function writeSnapshotPdf(report, outputPath) {
|
|
|
11215
11740
|
renderCompetitors(pdf, report);
|
|
11216
11741
|
renderQueries(pdf, report);
|
|
11217
11742
|
const bytes = await doc.save();
|
|
11218
|
-
const resolvedPath =
|
|
11219
|
-
|
|
11220
|
-
|
|
11743
|
+
const resolvedPath = path7.resolve(outputPath);
|
|
11744
|
+
fs11.mkdirSync(path7.dirname(resolvedPath), { recursive: true });
|
|
11745
|
+
fs11.writeFileSync(resolvedPath, bytes);
|
|
11221
11746
|
return resolvedPath;
|
|
11222
11747
|
}
|
|
11223
11748
|
function renderCover(pdf, report) {
|
|
@@ -11375,9 +11900,9 @@ Markdown saved: ${savedMdPath}`);
|
|
|
11375
11900
|
PDF saved: ${savedPdfPath}`);
|
|
11376
11901
|
}
|
|
11377
11902
|
function writeSnapshotMarkdown(report, outputPath) {
|
|
11378
|
-
const resolvedPath =
|
|
11379
|
-
|
|
11380
|
-
|
|
11903
|
+
const resolvedPath = path8.resolve(outputPath);
|
|
11904
|
+
fs12.mkdirSync(path8.dirname(resolvedPath), { recursive: true });
|
|
11905
|
+
fs12.writeFileSync(resolvedPath, formatSnapshotMarkdown(report), "utf-8");
|
|
11381
11906
|
return resolvedPath;
|
|
11382
11907
|
}
|
|
11383
11908
|
function formatSnapshotMarkdown(report) {
|
|
@@ -12360,7 +12885,7 @@ var CONTENT_CLI_COMMANDS = [
|
|
|
12360
12885
|
|
|
12361
12886
|
// src/commands/bootstrap.ts
|
|
12362
12887
|
import crypto from "crypto";
|
|
12363
|
-
import
|
|
12888
|
+
import path9 from "path";
|
|
12364
12889
|
import { eq } from "drizzle-orm";
|
|
12365
12890
|
|
|
12366
12891
|
// ../config/src/index.ts
|
|
@@ -12517,7 +13042,7 @@ async function bootstrapCommand(_opts) {
|
|
|
12517
13042
|
);
|
|
12518
13043
|
}
|
|
12519
13044
|
const configDir = getConfigDir();
|
|
12520
|
-
const databasePath = env.databasePath ||
|
|
13045
|
+
const databasePath = env.databasePath || path9.join(configDir, "data.db");
|
|
12521
13046
|
const existing = configExists();
|
|
12522
13047
|
const existingConfig = existing ? loadConfig() : void 0;
|
|
12523
13048
|
let rawApiKey;
|
|
@@ -12604,11 +13129,11 @@ async function bootstrapCommand(_opts) {
|
|
|
12604
13129
|
}
|
|
12605
13130
|
|
|
12606
13131
|
// src/commands/daemon.ts
|
|
12607
|
-
import { spawn } from "child_process";
|
|
12608
|
-
import
|
|
12609
|
-
import
|
|
13132
|
+
import { spawn as spawn2 } from "child_process";
|
|
13133
|
+
import fs13 from "fs";
|
|
13134
|
+
import path10 from "path";
|
|
12610
13135
|
function getPidPath() {
|
|
12611
|
-
return
|
|
13136
|
+
return path10.join(getConfigDir(), "canonry.pid");
|
|
12612
13137
|
}
|
|
12613
13138
|
function isProcessAlive(pid) {
|
|
12614
13139
|
try {
|
|
@@ -12648,8 +13173,8 @@ function buildServeForwardArgs(opts) {
|
|
|
12648
13173
|
async function startDaemon(opts) {
|
|
12649
13174
|
const pidPath = getPidPath();
|
|
12650
13175
|
const format = opts.format ?? "text";
|
|
12651
|
-
if (
|
|
12652
|
-
const existingPid = parseInt(
|
|
13176
|
+
if (fs13.existsSync(pidPath)) {
|
|
13177
|
+
const existingPid = parseInt(fs13.readFileSync(pidPath, "utf-8").trim(), 10);
|
|
12653
13178
|
if (!isNaN(existingPid) && isProcessAlive(existingPid)) {
|
|
12654
13179
|
throw new CliError({
|
|
12655
13180
|
code: "DAEMON_ALREADY_RUNNING",
|
|
@@ -12660,13 +13185,13 @@ async function startDaemon(opts) {
|
|
|
12660
13185
|
}
|
|
12661
13186
|
});
|
|
12662
13187
|
}
|
|
12663
|
-
|
|
13188
|
+
fs13.unlinkSync(pidPath);
|
|
12664
13189
|
}
|
|
12665
|
-
const cliPath =
|
|
13190
|
+
const cliPath = path10.resolve(new URL(import.meta.url).pathname);
|
|
12666
13191
|
const inSourceMode = new URL(import.meta.url).pathname.endsWith(".ts");
|
|
12667
13192
|
const args = inSourceMode ? ["--import", "tsx", cliPath, "serve"] : [cliPath, "serve"];
|
|
12668
13193
|
args.push(...buildServeForwardArgs(opts));
|
|
12669
|
-
const child =
|
|
13194
|
+
const child = spawn2(process.execPath, args, {
|
|
12670
13195
|
detached: true,
|
|
12671
13196
|
stdio: "ignore"
|
|
12672
13197
|
});
|
|
@@ -12679,10 +13204,10 @@ async function startDaemon(opts) {
|
|
|
12679
13204
|
});
|
|
12680
13205
|
}
|
|
12681
13206
|
const configDir = getConfigDir();
|
|
12682
|
-
if (!
|
|
12683
|
-
|
|
13207
|
+
if (!fs13.existsSync(configDir)) {
|
|
13208
|
+
fs13.mkdirSync(configDir, { recursive: true });
|
|
12684
13209
|
}
|
|
12685
|
-
|
|
13210
|
+
fs13.writeFileSync(pidPath, String(child.pid), "utf-8");
|
|
12686
13211
|
const port = opts.port ?? "4100";
|
|
12687
13212
|
const host = opts.host ?? "127.0.0.1";
|
|
12688
13213
|
if (!isMachineFormat(format)) {
|
|
@@ -12691,7 +13216,7 @@ async function startDaemon(opts) {
|
|
|
12691
13216
|
const ready = await waitForReady(host, port);
|
|
12692
13217
|
if (!ready) {
|
|
12693
13218
|
try {
|
|
12694
|
-
|
|
13219
|
+
fs13.unlinkSync(pidPath);
|
|
12695
13220
|
} catch {
|
|
12696
13221
|
}
|
|
12697
13222
|
throw new CliError({
|
|
@@ -12723,7 +13248,7 @@ async function startDaemon(opts) {
|
|
|
12723
13248
|
}
|
|
12724
13249
|
function stopDaemon(format = "text") {
|
|
12725
13250
|
const pidPath = getPidPath();
|
|
12726
|
-
if (!
|
|
13251
|
+
if (!fs13.existsSync(pidPath)) {
|
|
12727
13252
|
if (isMachineFormat(format)) {
|
|
12728
13253
|
console.log(JSON.stringify({
|
|
12729
13254
|
stopped: false,
|
|
@@ -12734,7 +13259,7 @@ function stopDaemon(format = "text") {
|
|
|
12734
13259
|
console.log("Canonry is not running (no PID file found)");
|
|
12735
13260
|
return;
|
|
12736
13261
|
}
|
|
12737
|
-
const pid = parseInt(
|
|
13262
|
+
const pid = parseInt(fs13.readFileSync(pidPath, "utf-8").trim(), 10);
|
|
12738
13263
|
if (isNaN(pid)) {
|
|
12739
13264
|
if (isMachineFormat(format)) {
|
|
12740
13265
|
console.log(JSON.stringify({
|
|
@@ -12745,7 +13270,7 @@ function stopDaemon(format = "text") {
|
|
|
12745
13270
|
} else {
|
|
12746
13271
|
console.error("Invalid PID file. Removing it.");
|
|
12747
13272
|
}
|
|
12748
|
-
|
|
13273
|
+
fs13.unlinkSync(pidPath);
|
|
12749
13274
|
return;
|
|
12750
13275
|
}
|
|
12751
13276
|
if (!isProcessAlive(pid)) {
|
|
@@ -12759,12 +13284,12 @@ function stopDaemon(format = "text") {
|
|
|
12759
13284
|
} else {
|
|
12760
13285
|
console.log(`Canonry is not running (stale PID: ${pid}). Cleaning up.`);
|
|
12761
13286
|
}
|
|
12762
|
-
|
|
13287
|
+
fs13.unlinkSync(pidPath);
|
|
12763
13288
|
return;
|
|
12764
13289
|
}
|
|
12765
13290
|
try {
|
|
12766
13291
|
process.kill(pid, "SIGTERM");
|
|
12767
|
-
|
|
13292
|
+
fs13.unlinkSync(pidPath);
|
|
12768
13293
|
if (isMachineFormat(format)) {
|
|
12769
13294
|
console.log(JSON.stringify({
|
|
12770
13295
|
stopped: true,
|
|
@@ -12788,9 +13313,9 @@ function stopDaemon(format = "text") {
|
|
|
12788
13313
|
|
|
12789
13314
|
// src/commands/init.ts
|
|
12790
13315
|
import crypto2 from "crypto";
|
|
12791
|
-
import
|
|
13316
|
+
import fs14 from "fs";
|
|
12792
13317
|
import readline2 from "readline";
|
|
12793
|
-
import
|
|
13318
|
+
import path11 from "path";
|
|
12794
13319
|
var pendingServeHandoff = false;
|
|
12795
13320
|
function consumePendingServeHandoff() {
|
|
12796
13321
|
const pending = pendingServeHandoff;
|
|
@@ -12825,8 +13350,8 @@ var DEFAULT_QUOTA = {
|
|
|
12825
13350
|
var PROJECT_MARKERS = [".git", "canonry.yaml", "canonry.yml", "package.json"];
|
|
12826
13351
|
function cwdLooksLikeProject(dir) {
|
|
12827
13352
|
const home = process.env.HOME ?? "";
|
|
12828
|
-
if (home &&
|
|
12829
|
-
return PROJECT_MARKERS.some((marker) =>
|
|
13353
|
+
if (home && path11.resolve(dir) === path11.resolve(home)) return false;
|
|
13354
|
+
return PROJECT_MARKERS.some((marker) => fs14.existsSync(path11.join(dir, marker)));
|
|
12830
13355
|
}
|
|
12831
13356
|
var DEFAULT_AGENT_MODELS = {
|
|
12832
13357
|
anthropic: "anthropic/claude-sonnet-4-6",
|
|
@@ -12857,8 +13382,8 @@ async function initCommand(opts) {
|
|
|
12857
13382
|
return void 0;
|
|
12858
13383
|
}
|
|
12859
13384
|
const configDir = getConfigDir();
|
|
12860
|
-
if (!
|
|
12861
|
-
|
|
13385
|
+
if (!fs14.existsSync(configDir)) {
|
|
13386
|
+
fs14.mkdirSync(configDir, { recursive: true });
|
|
12862
13387
|
}
|
|
12863
13388
|
const bootstrapEnv = getBootstrapEnv(process.env, {
|
|
12864
13389
|
GEMINI_API_KEY: opts?.geminiKey,
|
|
@@ -12973,7 +13498,7 @@ async function initCommand(opts) {
|
|
|
12973
13498
|
const rawApiKey = `cnry_${crypto2.randomBytes(16).toString("hex")}`;
|
|
12974
13499
|
const keyHash = crypto2.createHash("sha256").update(rawApiKey).digest("hex");
|
|
12975
13500
|
const keyPrefix = rawApiKey.slice(0, 9);
|
|
12976
|
-
const databasePath =
|
|
13501
|
+
const databasePath = path11.join(configDir, "data.db");
|
|
12977
13502
|
const db = createClient(databasePath);
|
|
12978
13503
|
migrate(db);
|
|
12979
13504
|
db.insert(apiKeys).values({
|
|
@@ -13159,8 +13684,8 @@ function encodeLegacySetupState(state) {
|
|
|
13159
13684
|
}
|
|
13160
13685
|
|
|
13161
13686
|
// src/agent-plugin.ts
|
|
13162
|
-
import
|
|
13163
|
-
import
|
|
13687
|
+
import fs15 from "fs";
|
|
13688
|
+
import path12 from "path";
|
|
13164
13689
|
var CANONRY_PLUGIN_ID = "canonry@canonry";
|
|
13165
13690
|
var REQUIRED_SKILLS = ["canonry", "aero"];
|
|
13166
13691
|
var AGENT_PLUGINS_SCHEMA = "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json";
|
|
@@ -13204,7 +13729,7 @@ function isCanonryPluginId(value) {
|
|
|
13204
13729
|
}
|
|
13205
13730
|
function readJson(filePath) {
|
|
13206
13731
|
try {
|
|
13207
|
-
return JSON.parse(
|
|
13732
|
+
return JSON.parse(fs15.readFileSync(filePath, "utf8"));
|
|
13208
13733
|
} catch {
|
|
13209
13734
|
return null;
|
|
13210
13735
|
}
|
|
@@ -13223,7 +13748,7 @@ function readCodexPluginConfigured(filePath) {
|
|
|
13223
13748
|
try {
|
|
13224
13749
|
let currentPlugin = null;
|
|
13225
13750
|
let inPluginsTable = false;
|
|
13226
|
-
for (const line of
|
|
13751
|
+
for (const line of fs15.readFileSync(filePath, "utf8").split(/\r?\n/)) {
|
|
13227
13752
|
const section = /^\s*\[plugins\.(?:"([^"]+)"|'([^']+)'|(\S+))\]\s*(?:#.*)?$/.exec(line);
|
|
13228
13753
|
if (section) {
|
|
13229
13754
|
const captures = section.slice(1, 4);
|
|
@@ -13256,21 +13781,21 @@ function readCodexPluginConfigured(filePath) {
|
|
|
13256
13781
|
}
|
|
13257
13782
|
}
|
|
13258
13783
|
function verifiedPluginVersion(root, client) {
|
|
13259
|
-
if (client === "codex" &&
|
|
13260
|
-
const manifest2 = readJson(
|
|
13784
|
+
if (client === "codex" && fs15.existsSync(path12.join(root, "plugin.json"))) {
|
|
13785
|
+
const manifest2 = readJson(path12.join(root, "plugin.json"));
|
|
13261
13786
|
if (manifest2 && typeof manifest2 === "object") {
|
|
13262
13787
|
const plugin2 = manifest2;
|
|
13263
13788
|
if (plugin2.$schema === AGENT_PLUGINS_SCHEMA) {
|
|
13264
13789
|
if (plugin2.name !== "canonry" || typeof plugin2.version !== "string" || !parseSemver(plugin2.version)) {
|
|
13265
13790
|
return null;
|
|
13266
13791
|
}
|
|
13267
|
-
const mcp2 = readJson(
|
|
13792
|
+
const mcp2 = readJson(path12.join(root, "mcp.json"));
|
|
13268
13793
|
if (!mcp2 || typeof mcp2 !== "object") return null;
|
|
13269
13794
|
const portableMcp = mcp2;
|
|
13270
13795
|
if (portableMcp.$schema !== AGENT_PLUGINS_MCP_SCHEMA || portableMcp.mcpServers?.canonry?.type !== "stdio" || portableMcp.mcpServers.canonry.command !== "canonry-mcp") {
|
|
13271
13796
|
return null;
|
|
13272
13797
|
}
|
|
13273
|
-
return REQUIRED_SKILLS.every((name) =>
|
|
13798
|
+
return REQUIRED_SKILLS.every((name) => fs15.existsSync(path12.join(root, "skills", name, "SKILL.md"))) ? plugin2.version : null;
|
|
13274
13799
|
}
|
|
13275
13800
|
if (typeof plugin2.$schema === "string" && plugin2.$schema.startsWith("https://agent-plugins.org/schemas/")) {
|
|
13276
13801
|
return null;
|
|
@@ -13278,20 +13803,20 @@ function verifiedPluginVersion(root, client) {
|
|
|
13278
13803
|
}
|
|
13279
13804
|
}
|
|
13280
13805
|
const manifestDir = client === "claude-code" ? ".claude-plugin" : ".codex-plugin";
|
|
13281
|
-
const manifest = readJson(
|
|
13806
|
+
const manifest = readJson(path12.join(root, manifestDir, "plugin.json"));
|
|
13282
13807
|
if (!manifest || typeof manifest !== "object") return null;
|
|
13283
13808
|
const plugin = manifest;
|
|
13284
13809
|
if (plugin.name !== "canonry" || typeof plugin.version !== "string" || !parseSemver(plugin.version) || plugin.skills !== "./skills/" || plugin.mcpServers !== "./.mcp.json") {
|
|
13285
13810
|
return null;
|
|
13286
13811
|
}
|
|
13287
|
-
const mcp = readJson(
|
|
13812
|
+
const mcp = readJson(path12.join(root, ".mcp.json"));
|
|
13288
13813
|
if (!mcp || typeof mcp !== "object") return null;
|
|
13289
13814
|
const canonryServer = mcp.mcpServers?.canonry;
|
|
13290
13815
|
if (canonryServer?.command !== "canonry-mcp") return null;
|
|
13291
|
-
return REQUIRED_SKILLS.every((name) =>
|
|
13816
|
+
return REQUIRED_SKILLS.every((name) => fs15.existsSync(path12.join(root, "skills", name, "SKILL.md"))) ? plugin.version : null;
|
|
13292
13817
|
}
|
|
13293
13818
|
function readClaudeUserInstallPaths(claudeConfigDir) {
|
|
13294
|
-
const parsed = readJson(
|
|
13819
|
+
const parsed = readJson(path12.join(claudeConfigDir, "plugins", "installed_plugins.json"));
|
|
13295
13820
|
if (!parsed || typeof parsed !== "object") return [];
|
|
13296
13821
|
const plugins = parsed.plugins;
|
|
13297
13822
|
if (!plugins || typeof plugins !== "object") return [];
|
|
@@ -13305,7 +13830,7 @@ function readClaudeUserInstallPaths(claudeConfigDir) {
|
|
|
13305
13830
|
}
|
|
13306
13831
|
function listDirectories(dir) {
|
|
13307
13832
|
try {
|
|
13308
|
-
return
|
|
13833
|
+
return fs15.readdirSync(dir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => path12.join(dir, entry.name));
|
|
13309
13834
|
} catch {
|
|
13310
13835
|
return [];
|
|
13311
13836
|
}
|
|
@@ -13317,8 +13842,8 @@ function verifiedClaudeVersion(claudeConfigDir) {
|
|
|
13317
13842
|
return newestVersion(readClaudeUserInstallPaths(claudeConfigDir).map((installPath) => verifiedPluginVersion(installPath, "claude-code")).filter((version) => version !== null));
|
|
13318
13843
|
}
|
|
13319
13844
|
function verifiedCodexVersion(codexHome) {
|
|
13320
|
-
const pluginCache =
|
|
13321
|
-
const versionedRoots = listDirectories(pluginCache).map((installPath) => ({ installPath, version:
|
|
13845
|
+
const pluginCache = path12.join(codexHome, "plugins", "cache", "canonry", "canonry");
|
|
13846
|
+
const versionedRoots = listDirectories(pluginCache).map((installPath) => ({ installPath, version: path12.basename(installPath) })).filter((candidate) => parseSemver(candidate.version) !== null).sort((left, right) => compareSemver(left.version, right.version));
|
|
13322
13847
|
const active = versionedRoots.at(-1);
|
|
13323
13848
|
if (!active) return verifiedPluginVersion(pluginCache, "codex");
|
|
13324
13849
|
const manifestVersion = verifiedPluginVersion(active.installPath, "codex");
|
|
@@ -13326,12 +13851,12 @@ function verifiedCodexVersion(codexHome) {
|
|
|
13326
13851
|
}
|
|
13327
13852
|
function detectCanonryAgentPlugin(opts = {}) {
|
|
13328
13853
|
const home = opts.home?.trim() || null;
|
|
13329
|
-
const claudeConfigDir = opts.claudeConfigDir?.trim() || (home ?
|
|
13330
|
-
const codexHome = opts.codexHome?.trim() || (home ?
|
|
13854
|
+
const claudeConfigDir = opts.claudeConfigDir?.trim() || (home ? path12.join(home, ".claude") : null);
|
|
13855
|
+
const codexHome = opts.codexHome?.trim() || (home ? path12.join(home, ".codex") : null);
|
|
13331
13856
|
const configuredClients = [];
|
|
13332
13857
|
const verifiedClients = [];
|
|
13333
13858
|
const verifiedClientVersions = {};
|
|
13334
|
-
const claudeConfigured = claudeConfigDir ? readClaudePluginConfigured(
|
|
13859
|
+
const claudeConfigured = claudeConfigDir ? readClaudePluginConfigured(path12.join(claudeConfigDir, "settings.json")) : false;
|
|
13335
13860
|
if (claudeConfigured) {
|
|
13336
13861
|
configuredClients.push("claude-code");
|
|
13337
13862
|
const version = claudeConfigDir ? verifiedClaudeVersion(claudeConfigDir) : null;
|
|
@@ -13340,7 +13865,7 @@ function detectCanonryAgentPlugin(opts = {}) {
|
|
|
13340
13865
|
verifiedClientVersions["claude-code"] = version;
|
|
13341
13866
|
}
|
|
13342
13867
|
}
|
|
13343
|
-
const codexConfigured = codexHome ? readCodexPluginConfigured(
|
|
13868
|
+
const codexConfigured = codexHome ? readCodexPluginConfigured(path12.join(codexHome, "config.toml")) : false;
|
|
13344
13869
|
if (codexConfigured) {
|
|
13345
13870
|
configuredClients.push("codex");
|
|
13346
13871
|
const version = codexHome ? verifiedCodexVersion(codexHome) : null;
|
|
@@ -14603,14 +15128,14 @@ Usage: ${usage}`, {
|
|
|
14603
15128
|
}
|
|
14604
15129
|
}
|
|
14605
15130
|
];
|
|
14606
|
-
function siteHealthAlias(sourcePath,
|
|
15131
|
+
function siteHealthAlias(sourcePath, path13, usage) {
|
|
14607
15132
|
const source = TECHNICAL_AEO_CLI_COMMANDS_BASE.find(
|
|
14608
15133
|
(candidate) => candidate.path.join(" ") === sourcePath.join(" ")
|
|
14609
15134
|
);
|
|
14610
15135
|
if (!source) throw new Error(`Missing Site Health compatibility source: ${sourcePath.join(" ")}`);
|
|
14611
15136
|
return {
|
|
14612
15137
|
...source,
|
|
14613
|
-
path:
|
|
15138
|
+
path: path13,
|
|
14614
15139
|
usage,
|
|
14615
15140
|
run: async (input) => {
|
|
14616
15141
|
try {
|
|
@@ -14621,7 +15146,7 @@ function siteHealthAlias(sourcePath, path12, usage) {
|
|
|
14621
15146
|
throw usageError(`${firstLine}
|
|
14622
15147
|
Usage: ${usage}`, {
|
|
14623
15148
|
message: error.message,
|
|
14624
|
-
details: { ...error.details, command:
|
|
15149
|
+
details: { ...error.details, command: path13.join("."), usage }
|
|
14625
15150
|
});
|
|
14626
15151
|
}
|
|
14627
15152
|
}
|
|
@@ -14920,7 +15445,7 @@ var VISIBILITY_STATS_CLI_COMMANDS = [
|
|
|
14920
15445
|
];
|
|
14921
15446
|
|
|
14922
15447
|
// src/cli-commands/wordpress.ts
|
|
14923
|
-
import
|
|
15448
|
+
import fs16 from "fs";
|
|
14924
15449
|
|
|
14925
15450
|
// src/commands/wordpress.ts
|
|
14926
15451
|
function getClient28() {
|
|
@@ -15159,12 +15684,12 @@ async function wordpressSetMeta(project, body) {
|
|
|
15159
15684
|
printPageDetail(result);
|
|
15160
15685
|
}
|
|
15161
15686
|
async function wordpressBulkSetMeta(project, opts) {
|
|
15162
|
-
const
|
|
15163
|
-
const
|
|
15164
|
-
const filePath =
|
|
15687
|
+
const fs18 = await import("fs/promises");
|
|
15688
|
+
const path13 = await import("path");
|
|
15689
|
+
const filePath = path13.resolve(opts.from);
|
|
15165
15690
|
let raw;
|
|
15166
15691
|
try {
|
|
15167
|
-
raw = await
|
|
15692
|
+
raw = await fs18.readFile(filePath, "utf8");
|
|
15168
15693
|
} catch {
|
|
15169
15694
|
throw new CliError({
|
|
15170
15695
|
code: "FILE_READ_ERROR",
|
|
@@ -15261,13 +15786,13 @@ async function wordpressSetSchema(project, body) {
|
|
|
15261
15786
|
printManualAssist(`Schema update for "${body.slug}"`, result);
|
|
15262
15787
|
}
|
|
15263
15788
|
async function wordpressSchemaDeploy(project, opts) {
|
|
15264
|
-
const
|
|
15265
|
-
const
|
|
15789
|
+
const fs18 = await import("fs/promises");
|
|
15790
|
+
const path13 = await import("path");
|
|
15266
15791
|
const yaml = await loadYamlModule();
|
|
15267
|
-
const filePath =
|
|
15792
|
+
const filePath = path13.resolve(opts.profile);
|
|
15268
15793
|
let raw;
|
|
15269
15794
|
try {
|
|
15270
|
-
raw = await
|
|
15795
|
+
raw = await fs18.readFile(filePath, "utf8");
|
|
15271
15796
|
} catch {
|
|
15272
15797
|
throw new CliError({
|
|
15273
15798
|
code: "FILE_READ_ERROR",
|
|
@@ -15372,13 +15897,13 @@ async function wordpressOnboard(project, opts) {
|
|
|
15372
15897
|
}
|
|
15373
15898
|
let profileData;
|
|
15374
15899
|
if (opts.profile) {
|
|
15375
|
-
const
|
|
15376
|
-
const
|
|
15900
|
+
const fs18 = await import("fs/promises");
|
|
15901
|
+
const path13 = await import("path");
|
|
15377
15902
|
const yaml = await loadYamlModule();
|
|
15378
|
-
const filePath =
|
|
15903
|
+
const filePath = path13.resolve(opts.profile);
|
|
15379
15904
|
let raw;
|
|
15380
15905
|
try {
|
|
15381
|
-
raw = await
|
|
15906
|
+
raw = await fs18.readFile(filePath, "utf8");
|
|
15382
15907
|
} catch {
|
|
15383
15908
|
throw new CliError({
|
|
15384
15909
|
code: "FILE_READ_ERROR",
|
|
@@ -15527,7 +16052,7 @@ function resolveContent(input, command, usage, options) {
|
|
|
15527
16052
|
}
|
|
15528
16053
|
if (contentFile) {
|
|
15529
16054
|
try {
|
|
15530
|
-
return
|
|
16055
|
+
return fs16.readFileSync(contentFile, "utf-8");
|
|
15531
16056
|
} catch (error) {
|
|
15532
16057
|
const message = error instanceof Error ? error.message : String(error);
|
|
15533
16058
|
throw usageError(`Error: could not read --content-file "${contentFile}": ${message}`, {
|
|
@@ -16494,16 +17019,16 @@ Usage: ${usage}`, {
|
|
|
16494
17019
|
|
|
16495
17020
|
// src/commands/measurement-plan.ts
|
|
16496
17021
|
import crypto3 from "crypto";
|
|
16497
|
-
import
|
|
17022
|
+
import fs17 from "fs";
|
|
16498
17023
|
import { parse } from "yaml";
|
|
16499
17024
|
import { z as z2 } from "zod";
|
|
16500
17025
|
function readPlan(source) {
|
|
16501
|
-
const content = source === "-" ?
|
|
17026
|
+
const content = source === "-" ? fs17.readFileSync(0, "utf8") : fs17.readFileSync(source, "utf8");
|
|
16502
17027
|
const parsed = source.endsWith(".json") ? JSON.parse(content) : parse(content);
|
|
16503
17028
|
return measurementPlanInputSchema.parse(parsed);
|
|
16504
17029
|
}
|
|
16505
17030
|
function readDiscoveryRule(source) {
|
|
16506
|
-
const content = source === "-" ?
|
|
17031
|
+
const content = source === "-" ? fs17.readFileSync(0, "utf8") : fs17.readFileSync(source, "utf8");
|
|
16507
17032
|
const parsed = source.endsWith(".json") ? JSON.parse(content) : parse(content);
|
|
16508
17033
|
return measurementDiscoveryRuleSchema.parse(parsed);
|
|
16509
17034
|
}
|
|
@@ -16552,7 +17077,7 @@ var advancedDeactivateInputSchema = z2.object({
|
|
|
16552
17077
|
}).strict();
|
|
16553
17078
|
function readAdvancedMeasurementInput(source) {
|
|
16554
17079
|
if (source === void 0) return {};
|
|
16555
|
-
const content = source === "-" ?
|
|
17080
|
+
const content = source === "-" ? fs17.readFileSync(0, "utf8") : fs17.readFileSync(source, "utf8");
|
|
16556
17081
|
return JSON.parse(content);
|
|
16557
17082
|
}
|
|
16558
17083
|
var advancedMeasurementCollectionKeys = {
|
|
@@ -16734,7 +17259,7 @@ async function replaceMeasurementPlanAssignments(project, opts) {
|
|
|
16734
17259
|
console.log(JSON.stringify({ preview, result }, null, 2));
|
|
16735
17260
|
}
|
|
16736
17261
|
function readGroupMembershipCsv(source) {
|
|
16737
|
-
return source === "-" ?
|
|
17262
|
+
return source === "-" ? fs17.readFileSync(0, "utf8") : fs17.readFileSync(source, "utf8");
|
|
16738
17263
|
}
|
|
16739
17264
|
async function previewMeasurementPlanGroups(project, source) {
|
|
16740
17265
|
const preview = await createApiClient().previewMeasurementDraftGroupMembership(project, {
|