@canonry/canonry 5.21.0 → 5.28.1
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 -1
- package/assets/agent-workspace/skills/aero/SKILL.md +37 -3
- package/assets/agent-workspace/skills/aero/references/portfolio-analysis.md +26 -12
- package/assets/agent-workspace/skills/aero/references/reporting.md +1 -0
- package/assets/agent-workspace/skills/canonry/references/canonry-cli.md +101 -6
- package/assets/agent-workspace/skills/canonry/references/google-business-profile.md +30 -6
- package/assets/agent-workspace/skills/canonry/references/indexing.md +1 -0
- package/assets/agent-workspace/skills/canonry/references/server-side-traffic.md +30 -0
- package/assets/assets/{AuditHistoryPanel-CjxWA3lx.js → AuditHistoryPanel-CWd9UN6L.js} +1 -1
- package/assets/assets/{BacklinksPage-Bdd164Up.js → BacklinksPage-B2gPTzyE.js} +1 -1
- package/assets/assets/ChartPrimitives-B5okB5g-.js +1 -0
- package/assets/assets/{HistoryPage-BvJmE-kP.js → HistoryPage-xtkG8SZN.js} +1 -1
- package/assets/assets/{MeasurementPropertyPage-HuP6RMZ-.js → MeasurementPropertyPage-DxIMU3_T.js} +1 -1
- package/assets/assets/{ProjectPage-DOvXVbyy.js → ProjectPage-CMAFZq_s.js} +5 -5
- package/assets/assets/{RunRow-Cnb3JOBw.js → RunRow-DnSo7Nle.js} +1 -1
- package/assets/assets/{RunsPage-Dvz1JYIm.js → RunsPage-FAEuo4Ra.js} +1 -1
- package/assets/assets/{SettingsPage-DYGvphkg.js → SettingsPage-C4OZ1HB9.js} +1 -1
- package/assets/assets/{SiteHealthSection-CUNiKaod.js → SiteHealthSection-BzMxlz8-.js} +3 -3
- package/assets/assets/{TrafficPage-Bf4Tq95q.js → TrafficPage-D03S6xF6.js} +1 -1
- package/assets/assets/{TrafficSourceDetailPage-CIuLWZgT.js → TrafficSourceDetailPage-DBu6oFFK.js} +1 -1
- package/assets/assets/{extract-error-message-DZex1e4H.js → extract-error-message-BuH2CCaJ.js} +1 -1
- package/assets/assets/index-Ba-Zs45G.js +86 -0
- package/assets/assets/index-DfhmHULl.css +1 -0
- package/assets/assets/{react-sigma_core.esm.min-CxvtwEkv.js → react-sigma_core.esm.min-CxwZsgxR.js} +1 -1
- package/assets/assets/{v2-overview-adapter-Bh4qIzLp.js → v2-overview-adapter-DTCKxzZT.js} +1 -1
- package/assets/index.html +2 -2
- package/dist/{chunk-FAVYLZQV.js → chunk-7RKCDOWO.js} +7005 -5086
- package/dist/{chunk-MEAKE5W7.js → chunk-DX2JUC5H.js} +3 -3
- package/dist/{chunk-NFN5GSEJ.js → chunk-EP4STEPI.js} +3558 -1170
- package/dist/{chunk-FRWZWJB3.js → chunk-FNGITGDF.js} +176 -8
- package/dist/{chunk-2LO7FLVD.js → chunk-KVQHG3WB.js} +6721 -5889
- package/dist/{chunk-WUT3G2GM.js → chunk-MPICT6V7.js} +131 -36
- package/dist/{chunk-HDJHQPW3.js → chunk-ONTEFAA7.js} +2 -2
- package/dist/cli.js +443 -48
- package/dist/{demo-server-7BVVVXPM.js → demo-server-HWWIMGD6.js} +4 -4
- package/dist/index.d.ts +9 -1
- package/dist/index.js +6 -6
- package/dist/{intelligence-service-3UUKRAZ5.js → intelligence-service-6LDMVI66.js} +2 -2
- package/dist/mcp.js +4 -4
- package/package.json +12 -11
- package/assets/assets/ChartPrimitives-BQC2VH0O.js +0 -1
- package/assets/assets/index-CeXW-lzj.js +0 -86
- package/assets/assets/index-EJC8PSbh.css +0 -1
package/dist/cli.js
CHANGED
|
@@ -35,11 +35,11 @@ import {
|
|
|
35
35
|
trackCliCommandFinished,
|
|
36
36
|
trackEvent,
|
|
37
37
|
waitForServerRuntimeStartup
|
|
38
|
-
} from "./chunk-
|
|
38
|
+
} from "./chunk-EP4STEPI.js";
|
|
39
39
|
import {
|
|
40
40
|
autoSyncSkills,
|
|
41
41
|
formatAutoSyncNotice
|
|
42
|
-
} from "./chunk-
|
|
42
|
+
} from "./chunk-DX2JUC5H.js";
|
|
43
43
|
import {
|
|
44
44
|
configExists,
|
|
45
45
|
createApiClient,
|
|
@@ -56,13 +56,13 @@ import {
|
|
|
56
56
|
saveConfig,
|
|
57
57
|
saveConfigPatch,
|
|
58
58
|
shouldPrintServeSkillsNudge
|
|
59
|
-
} from "./chunk-
|
|
59
|
+
} from "./chunk-FNGITGDF.js";
|
|
60
60
|
import {
|
|
61
61
|
DEFAULT_DEMO_TRUSTED_PROXIES,
|
|
62
62
|
trustedProxiesProblem,
|
|
63
63
|
trustedProxyProblem
|
|
64
64
|
} from "./chunk-RB2F4D3Z.js";
|
|
65
|
-
import "./chunk-
|
|
65
|
+
import "./chunk-ONTEFAA7.js";
|
|
66
66
|
import {
|
|
67
67
|
CliError,
|
|
68
68
|
EXIT_SYSTEM_ERROR,
|
|
@@ -74,7 +74,7 @@ import {
|
|
|
74
74
|
runMeasurementDraftAction,
|
|
75
75
|
systemError,
|
|
76
76
|
usageError
|
|
77
|
-
} from "./chunk-
|
|
77
|
+
} from "./chunk-MPICT6V7.js";
|
|
78
78
|
import {
|
|
79
79
|
CLOUDFLARE_WORKER_BINDINGS,
|
|
80
80
|
CLOUDFLARE_WORKER_GENERATED_MARKER,
|
|
@@ -89,7 +89,7 @@ import {
|
|
|
89
89
|
queries,
|
|
90
90
|
renderReportHtml,
|
|
91
91
|
runs
|
|
92
|
-
} from "./chunk-
|
|
92
|
+
} from "./chunk-7RKCDOWO.js";
|
|
93
93
|
import {
|
|
94
94
|
AdsDeliverySnapshotStatuses,
|
|
95
95
|
AdsHistoricalCampaignRollupStatuses,
|
|
@@ -103,6 +103,8 @@ import {
|
|
|
103
103
|
CheckStatuses,
|
|
104
104
|
CitationStates,
|
|
105
105
|
MeasurementEvidenceShapes,
|
|
106
|
+
OUTSTANDING_PROVIDER_BATCH_STATUSES,
|
|
107
|
+
ProviderBatchStatuses,
|
|
106
108
|
READ_ONLY_SCOPE,
|
|
107
109
|
REPORT_PERIOD_OPTIONS,
|
|
108
110
|
ResearchRunStatuses,
|
|
@@ -145,6 +147,7 @@ import {
|
|
|
145
147
|
formatRatio,
|
|
146
148
|
formatRunErrorOneLine,
|
|
147
149
|
formatWholePercent,
|
|
150
|
+
gbpNegativeReviewMaxStarsSchema,
|
|
148
151
|
googleAdsMetricsWindowSchema,
|
|
149
152
|
isVisibilityCompareClassMetric,
|
|
150
153
|
logQuerySchema,
|
|
@@ -166,11 +169,14 @@ import {
|
|
|
166
169
|
normalizeProjectAliases,
|
|
167
170
|
normalizeTelemetryStatus,
|
|
168
171
|
notificationEventSchema,
|
|
172
|
+
providerDispatchModeSchema,
|
|
169
173
|
queryClassFilterSchema,
|
|
170
174
|
queryTrackingCommitRequestSchema,
|
|
171
175
|
queryTrackingPreviewRequestSchema,
|
|
176
|
+
referralAssessmentQuerySchema,
|
|
172
177
|
reportUnattributedAnswers,
|
|
173
178
|
researchBatchCreateSchema,
|
|
179
|
+
resolveNegativeReviewMaxStars,
|
|
174
180
|
resolveProviderInput,
|
|
175
181
|
resultsClearRequestSchema,
|
|
176
182
|
shareOfVoiceLabel,
|
|
@@ -179,7 +185,7 @@ import {
|
|
|
179
185
|
visibilityCompareSelectionSchema,
|
|
180
186
|
visibilityReportRequestSchema,
|
|
181
187
|
winnabilityClassSchema
|
|
182
|
-
} from "./chunk-
|
|
188
|
+
} from "./chunk-KVQHG3WB.js";
|
|
183
189
|
|
|
184
190
|
// src/cli.ts
|
|
185
191
|
import { pathToFileURL } from "url";
|
|
@@ -444,7 +450,7 @@ function parseDemoTrustedProxies(values) {
|
|
|
444
450
|
async function demoCommand(options) {
|
|
445
451
|
const { host, port } = parseDemoListenOptions(options);
|
|
446
452
|
const trustProxy = parseDemoTrustedProxies(options.trustProxy);
|
|
447
|
-
const { createDemoServer } = await import("./demo-server-
|
|
453
|
+
const { createDemoServer } = await import("./demo-server-HWWIMGD6.js");
|
|
448
454
|
const app = await createDemoServer({ trustProxy });
|
|
449
455
|
let stopping = false;
|
|
450
456
|
const stop = () => {
|
|
@@ -3051,7 +3057,8 @@ async function doctorCommand(opts) {
|
|
|
3051
3057
|
const client = createApiClient();
|
|
3052
3058
|
const report = await client.runDoctor({
|
|
3053
3059
|
project: opts.project,
|
|
3054
|
-
checkIds: opts.checks
|
|
3060
|
+
checkIds: opts.checks,
|
|
3061
|
+
...opts.reportMonth ? { reportMonth: opts.reportMonth } : {}
|
|
3055
3062
|
});
|
|
3056
3063
|
if (opts.format === "json") {
|
|
3057
3064
|
console.log(JSON.stringify(report, null, 2));
|
|
@@ -3077,8 +3084,8 @@ async function runDoctorAll(opts) {
|
|
|
3077
3084
|
const client = createApiClient();
|
|
3078
3085
|
const projects2 = await client.listProjects();
|
|
3079
3086
|
const [globalReport, ...projectReports] = await Promise.all([
|
|
3080
|
-
client.runDoctor({ checkIds: opts.checks }),
|
|
3081
|
-
...projects2.map((p) => client.runDoctor({ project: p.name, checkIds: opts.checks }))
|
|
3087
|
+
client.runDoctor({ checkIds: opts.checks, ...opts.reportMonth ? { reportMonth: opts.reportMonth } : {} }),
|
|
3088
|
+
...projects2.map((p) => client.runDoctor({ project: p.name, checkIds: opts.checks, ...opts.reportMonth ? { reportMonth: opts.reportMonth } : {} }))
|
|
3082
3089
|
]);
|
|
3083
3090
|
const byKey = { __global__: globalReport };
|
|
3084
3091
|
projects2.forEach((p, i) => {
|
|
@@ -3174,13 +3181,14 @@ ${header}`);
|
|
|
3174
3181
|
}
|
|
3175
3182
|
|
|
3176
3183
|
// src/cli-commands/doctor.ts
|
|
3177
|
-
var USAGE = "canonry doctor [--project <name>|--all] [--check <id>...] [--format json|jsonl]";
|
|
3184
|
+
var USAGE = "canonry doctor [--project <name>|--all] [--check <id>...] [--report-month YYYY-MM] [--format json|jsonl]";
|
|
3178
3185
|
var DOCTOR_CLI_COMMANDS = [
|
|
3179
3186
|
{
|
|
3180
3187
|
path: ["doctor"],
|
|
3181
3188
|
usage: USAGE,
|
|
3182
3189
|
options: {
|
|
3183
3190
|
project: stringOption(),
|
|
3191
|
+
"report-month": stringOption(),
|
|
3184
3192
|
check: multiStringOption(),
|
|
3185
3193
|
all: { type: "boolean" }
|
|
3186
3194
|
},
|
|
@@ -3188,6 +3196,7 @@ var DOCTOR_CLI_COMMANDS = [
|
|
|
3188
3196
|
run: async (input) => {
|
|
3189
3197
|
await doctorCommand({
|
|
3190
3198
|
project: getString(input.values, "project"),
|
|
3199
|
+
reportMonth: getString(input.values, "report-month"),
|
|
3191
3200
|
all: getBoolean(input.values, "all"),
|
|
3192
3201
|
checks: getStringArray(input.values, "check"),
|
|
3193
3202
|
format: input.format
|
|
@@ -5223,6 +5232,45 @@ async function gbpPlaces(project, opts) {
|
|
|
5223
5232
|
console.log(` ${p.locationName} [${p.tier}] ${amenities}`);
|
|
5224
5233
|
}
|
|
5225
5234
|
}
|
|
5235
|
+
async function gbpReviews(project, opts) {
|
|
5236
|
+
const client = getClient8();
|
|
5237
|
+
const response = await client.listGbpReviews(project, {
|
|
5238
|
+
locationName: opts.location,
|
|
5239
|
+
...opts.negative ? { negative: true } : {},
|
|
5240
|
+
...opts.limit !== void 0 ? { limit: opts.limit } : {}
|
|
5241
|
+
});
|
|
5242
|
+
if (isMachineFormat(opts.format)) {
|
|
5243
|
+
console.log(JSON.stringify(response, null, 2));
|
|
5244
|
+
return;
|
|
5245
|
+
}
|
|
5246
|
+
if (response.locations.length === 0 && response.reviews.length === 0) {
|
|
5247
|
+
console.log('No reviews yet. Run "canonry gbp sync" first.');
|
|
5248
|
+
return;
|
|
5249
|
+
}
|
|
5250
|
+
const names = new Map(response.locations.map((loc) => [loc.locationName, loc.displayName]));
|
|
5251
|
+
for (const loc of response.locations) {
|
|
5252
|
+
const rating = loc.rating !== null ? `${Number(loc.rating.toFixed(2))} from ${loc.reviewCount ?? "?"} reviews (${loc.ratingOrigin === "gbp" ? "Business Profile" : "public listing"})` : "no rating yet";
|
|
5253
|
+
const access = loc.reviewsAccess === "ok" ? "every review" : loc.reviewsAccess === null ? "review access not checked yet" : `Business Profile reviews ${loc.reviewsAccess}${loc.reviewsAccessReason ? ` (${loc.reviewsAccessReason})` : ""}`;
|
|
5254
|
+
console.log(`${loc.displayName} ${loc.locationName}`);
|
|
5255
|
+
console.log(` ${rating}; ${access}`);
|
|
5256
|
+
}
|
|
5257
|
+
const label = opts.negative ? "negative review(s)" : "review(s)";
|
|
5258
|
+
if (response.reviews.length === 0) {
|
|
5259
|
+
console.log(`
|
|
5260
|
+
No ${label} stored.`);
|
|
5261
|
+
return;
|
|
5262
|
+
}
|
|
5263
|
+
const shown = response.reviews.length < response.total ? `, newest ${response.reviews.length} shown` : "";
|
|
5264
|
+
console.log(`
|
|
5265
|
+
${response.total} ${label}${shown}:`);
|
|
5266
|
+
const manyLocations = response.locations.length > 1;
|
|
5267
|
+
for (const r of response.reviews) {
|
|
5268
|
+
const stars = r.starRating !== null ? `${r.starRating}\u2605` : "--";
|
|
5269
|
+
const where = manyLocations ? ` ${names.get(r.locationName) ?? r.locationName}` : "";
|
|
5270
|
+
const text = r.comment ? `"${r.comment.length > 80 ? `${r.comment.slice(0, 79)}\u2026` : r.comment}"` : "(rating only)";
|
|
5271
|
+
console.log(` ${r.updateTime.slice(0, 10)} ${stars}${where} ${r.reviewerName ?? "Anonymous"} ${text} [${r.origin}, ${r.alertState}]`);
|
|
5272
|
+
}
|
|
5273
|
+
}
|
|
5226
5274
|
function fmtDelta(pct4) {
|
|
5227
5275
|
if (pct4 === null) return "n/a";
|
|
5228
5276
|
return `${pct4 >= 0 ? "+" : ""}${pct4}%`;
|
|
@@ -5443,6 +5491,25 @@ var GBP_CLI_COMMANDS = [
|
|
|
5443
5491
|
await gbpPlaces(project, { location: getString(input.values, "location"), format: input.format });
|
|
5444
5492
|
}
|
|
5445
5493
|
},
|
|
5494
|
+
{
|
|
5495
|
+
path: ["gbp", "reviews"],
|
|
5496
|
+
usage: "canonry gbp reviews <project> [--location <name>] [--negative] [--limit N] [--format json]",
|
|
5497
|
+
options: {
|
|
5498
|
+
location: stringOption(),
|
|
5499
|
+
negative: { type: "boolean" },
|
|
5500
|
+
limit: stringOption()
|
|
5501
|
+
},
|
|
5502
|
+
run: async (input) => {
|
|
5503
|
+
const usage = "canonry gbp reviews <project> [--location <name>] [--negative] [--limit N] [--format json]";
|
|
5504
|
+
const project = requireProject(input, "gbp.reviews", usage);
|
|
5505
|
+
await gbpReviews(project, {
|
|
5506
|
+
location: getString(input.values, "location"),
|
|
5507
|
+
negative: getBoolean(input.values, "negative") ?? false,
|
|
5508
|
+
limit: parseIntegerOption(input, "limit", { message: "--limit must be an integer", usage, command: "gbp.reviews" }),
|
|
5509
|
+
format: input.format
|
|
5510
|
+
});
|
|
5511
|
+
}
|
|
5512
|
+
},
|
|
5446
5513
|
{
|
|
5447
5514
|
path: ["gbp", "summary"],
|
|
5448
5515
|
usage: "canonry gbp summary <project> [--location <name>] [--format json]",
|
|
@@ -7362,9 +7429,47 @@ async function trafficEvents(project, opts) {
|
|
|
7362
7429
|
console.log(` ${formatEventLine(event)}`);
|
|
7363
7430
|
}
|
|
7364
7431
|
}
|
|
7432
|
+
async function trafficReferralAssessment(project, options) {
|
|
7433
|
+
const { format, ...query } = options;
|
|
7434
|
+
const parsed = referralAssessmentQuerySchema.safeParse(query);
|
|
7435
|
+
if (!parsed.success) throw new CliError({ code: "TRAFFIC_INVALID_ASSESSMENT", message: parsed.error.message });
|
|
7436
|
+
const result = await createApiClient().trafficReferralAssessment(project, parsed.data);
|
|
7437
|
+
if (isMachineFormat(format)) {
|
|
7438
|
+
console.log(JSON.stringify(result, null, 2));
|
|
7439
|
+
return;
|
|
7440
|
+
}
|
|
7441
|
+
console.log(`AI referral assessment for "${project}" \xB7 ${result.window.startDate} to ${result.window.endDate} UTC`);
|
|
7442
|
+
console.log(` Raw stored hits: ${result.totals.raw.total}`);
|
|
7443
|
+
console.log(` Countable stored hits: ${result.totals.countable.total}`);
|
|
7444
|
+
console.log(` Suspected burst hits: ${result.totals.suspected.total} (not confirmed automation)`);
|
|
7445
|
+
console.log(` Adjusted estimate: ${result.totals.adjustedEstimate.total} (countable minus candidate bursts)`);
|
|
7446
|
+
console.log(` Rule: ${result.rule.version}; threshold ${result.rule.burstThreshold}; ${result.rule.calibration}`);
|
|
7447
|
+
console.log(` Observed server/GA quotient: ${result.comparison.observedRatio ?? "unavailable"}; comparable coverage: unavailable`);
|
|
7448
|
+
console.log(` Evidence: ${result.evidence.returned} of ${result.evidence.total} candidate groups (source, product, normalized path, UTC hour)`);
|
|
7449
|
+
for (const burst of result.bursts) console.log(` ${burst.tsHour} ${burst.sourceId} ${burst.product} ${burst.landingPathNormalized} ${burst.counts.total}`);
|
|
7450
|
+
for (const caveat of result.caveats) console.log(` ${caveat}`);
|
|
7451
|
+
}
|
|
7365
7452
|
|
|
7366
7453
|
// src/cli-commands/traffic.ts
|
|
7367
7454
|
var TRAFFIC_CLI_COMMANDS = [
|
|
7455
|
+
{
|
|
7456
|
+
path: ["traffic", "referral-assessment"],
|
|
7457
|
+
usage: "canonry traffic referral-assessment <project> --start-date YYYY-MM-DD --end-date YYYY-MM-DD [--source <id>] [--burst-threshold 100] [--ratio-threshold 3] [--limit 100] [--format json]",
|
|
7458
|
+
help: "DB-only diagnostic. Raw headlines stay unchanged. Candidate bursts are not confirmed automation; adjusted counts are estimates. Property, Target and market attribution are unavailable. The observed GA quotient has unknown comparable coverage.",
|
|
7459
|
+
options: { "start-date": stringOption(), "end-date": stringOption(), source: stringOption(), "burst-threshold": stringOption(), "ratio-threshold": stringOption(), limit: stringOption() },
|
|
7460
|
+
run: async (input) => {
|
|
7461
|
+
const project = requireProject(input, "traffic.referral-assessment", "canonry traffic referral-assessment <project> --start-date YYYY-MM-DD --end-date YYYY-MM-DD");
|
|
7462
|
+
await trafficReferralAssessment(project, {
|
|
7463
|
+
startDate: getString(input.values, "start-date"),
|
|
7464
|
+
endDate: getString(input.values, "end-date"),
|
|
7465
|
+
sourceId: getString(input.values, "source"),
|
|
7466
|
+
burstThreshold: getString(input.values, "burst-threshold") === void 0 ? void 0 : Number(getString(input.values, "burst-threshold")),
|
|
7467
|
+
ratioThreshold: getString(input.values, "ratio-threshold") === void 0 ? void 0 : Number(getString(input.values, "ratio-threshold")),
|
|
7468
|
+
limit: getString(input.values, "limit") === void 0 ? void 0 : Number(getString(input.values, "limit")),
|
|
7469
|
+
format: input.format
|
|
7470
|
+
});
|
|
7471
|
+
}
|
|
7472
|
+
},
|
|
7368
7473
|
{
|
|
7369
7474
|
path: ["traffic", "connect", "cloudflare"],
|
|
7370
7475
|
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]",
|
|
@@ -8307,6 +8412,65 @@ async function googleTopPages(project, opts) {
|
|
|
8307
8412
|
console.log(' Adding up the page rows would not give it. Run "canonry google sync" to fetch it.');
|
|
8308
8413
|
}
|
|
8309
8414
|
}
|
|
8415
|
+
async function googleQueryTotals(project, opts) {
|
|
8416
|
+
const client = getClient11();
|
|
8417
|
+
const params = {};
|
|
8418
|
+
if (opts.startDate) params.startDate = opts.startDate;
|
|
8419
|
+
if (opts.endDate) params.endDate = opts.endDate;
|
|
8420
|
+
if (opts.window) params.window = opts.window;
|
|
8421
|
+
if (opts.limit !== void 0) params.limit = String(opts.limit);
|
|
8422
|
+
if (opts.offset !== void 0) params.offset = String(opts.offset);
|
|
8423
|
+
const data = await client.gscQueryTotals(project, Object.keys(params).length > 0 ? params : void 0);
|
|
8424
|
+
const { rows, totalMatching, truncated, window } = data;
|
|
8425
|
+
if (opts.format === "json") {
|
|
8426
|
+
console.log(JSON.stringify(data, null, 2));
|
|
8427
|
+
return;
|
|
8428
|
+
} else if (opts.format === "jsonl") {
|
|
8429
|
+
emitJsonl(rows.map((row) => ({ project, window, ...row })));
|
|
8430
|
+
return;
|
|
8431
|
+
}
|
|
8432
|
+
if (rows.length === 0) {
|
|
8433
|
+
if (totalMatching > 0) {
|
|
8434
|
+
console.log(
|
|
8435
|
+
`Offset ${opts.offset ?? 0} is past the end of the result set (${totalMatching.toLocaleString()} queries). Lower --offset to page through them.`
|
|
8436
|
+
);
|
|
8437
|
+
return;
|
|
8438
|
+
}
|
|
8439
|
+
console.log('No GSC query data found in this window. Run "canonry google sync" first.');
|
|
8440
|
+
return;
|
|
8441
|
+
}
|
|
8442
|
+
const range = `${window.startDate ?? "earliest"} to ${window.endDate ?? "latest"}`;
|
|
8443
|
+
console.log(`GSC query totals (${rows.length.toLocaleString()} of ${totalMatching.toLocaleString()} queries, ${range}):
|
|
8444
|
+
`);
|
|
8445
|
+
const showSource = rows.some((row) => row.source !== "google");
|
|
8446
|
+
console.log(
|
|
8447
|
+
` ${"QUERY".padEnd(40)}${"CLICKS".padStart(8)}${"IMPR".padStart(10)}${"CTR".padStart(8)}${"POS".padStart(7)}${"DAYS".padStart(6)}${showSource ? " SOURCE" : ""}`
|
|
8448
|
+
);
|
|
8449
|
+
console.log(` ${"\u2500".repeat(40)}${"\u2500".repeat(8)}${"\u2500".repeat(10)}${"\u2500".repeat(8)}${"\u2500".repeat(7)}${"\u2500".repeat(6)}${showSource ? ` ${"\u2500".repeat(11)}` : ""}`);
|
|
8450
|
+
for (const row of rows.slice(0, 50)) {
|
|
8451
|
+
const query = row.query.length > 38 ? row.query.slice(0, 35) + "..." : row.query;
|
|
8452
|
+
console.log(
|
|
8453
|
+
` ${query.padEnd(40)}${row.clicks.toLocaleString().padStart(8)}${row.impressions.toLocaleString().padStart(10)}${`${(row.ctr * 100).toFixed(1)}%`.padStart(8)}${row.position.toFixed(1).padStart(7)}${String(row.days).padStart(6)}${showSource ? ` ${row.source}` : ""}`
|
|
8454
|
+
);
|
|
8455
|
+
}
|
|
8456
|
+
if (rows.length > 50) {
|
|
8457
|
+
console.log(`
|
|
8458
|
+
... and ${rows.length - 50} more rows on this page (use --format json for full output)`);
|
|
8459
|
+
}
|
|
8460
|
+
if (truncated) {
|
|
8461
|
+
const nextOffset = (opts.offset ?? 0) + rows.length;
|
|
8462
|
+
console.log(`
|
|
8463
|
+
This is one page. Use --offset ${nextOffset} (with the same --limit) to read the next.`);
|
|
8464
|
+
}
|
|
8465
|
+
console.log();
|
|
8466
|
+
if (showSource) {
|
|
8467
|
+
console.log(" Rows marked page-summed or mixed use the legacy page table for some days. Their impressions");
|
|
8468
|
+
console.log(" over-count, because one search showing several of your pages was stored once per page.");
|
|
8469
|
+
console.log(` Run "canonry google sync" over the window to replace them with Google's per-query totals.`);
|
|
8470
|
+
}
|
|
8471
|
+
console.log(" These are the queries Google names. Google leaves rare queries out, so the rows do not add up");
|
|
8472
|
+
console.log(' to the property total. Use "canonry google performance-daily" for that.');
|
|
8473
|
+
}
|
|
8310
8474
|
async function googlePerformance(project, opts) {
|
|
8311
8475
|
const client = getClient11();
|
|
8312
8476
|
const params = {};
|
|
@@ -8889,6 +9053,7 @@ async function googleDeindexed(project, format) {
|
|
|
8889
9053
|
|
|
8890
9054
|
// src/cli-commands/google.ts
|
|
8891
9055
|
var GOOGLE_PERFORMANCE_USAGE = "canonry google performance <project> [--days <n> | --start <YYYY-MM-DD> --end <YYYY-MM-DD>] [--keyword <kw>] [--page <url>] [--limit <n>] [--offset <n>] [--order-by clicks|impressions|date] [--format json]";
|
|
9056
|
+
var GOOGLE_QUERY_TOTALS_USAGE = "canonry google query-totals <project> [--start <YYYY-MM-DD>] [--end <YYYY-MM-DD>] [--window 7d|30d|90d|all] [--limit <n>] [--offset <n>] [--format json]";
|
|
8892
9057
|
var GOOGLE_CLI_COMMANDS = [
|
|
8893
9058
|
{
|
|
8894
9059
|
path: ["google", "connect"],
|
|
@@ -9122,6 +9287,36 @@ Usage: ${GOOGLE_PERFORMANCE_USAGE}`,
|
|
|
9122
9287
|
});
|
|
9123
9288
|
}
|
|
9124
9289
|
},
|
|
9290
|
+
{
|
|
9291
|
+
path: ["google", "query-totals"],
|
|
9292
|
+
usage: GOOGLE_QUERY_TOTALS_USAGE,
|
|
9293
|
+
options: {
|
|
9294
|
+
window: stringOption(),
|
|
9295
|
+
start: stringOption(),
|
|
9296
|
+
end: stringOption(),
|
|
9297
|
+
limit: stringOption(),
|
|
9298
|
+
offset: stringOption()
|
|
9299
|
+
},
|
|
9300
|
+
run: async (input) => {
|
|
9301
|
+
const project = requireProject(input, "google.query-totals", GOOGLE_QUERY_TOTALS_USAGE);
|
|
9302
|
+
await googleQueryTotals(project, {
|
|
9303
|
+
window: getString(input.values, "window"),
|
|
9304
|
+
startDate: getString(input.values, "start"),
|
|
9305
|
+
endDate: getString(input.values, "end"),
|
|
9306
|
+
limit: parseIntegerOption(input, "limit", {
|
|
9307
|
+
command: "google.query-totals",
|
|
9308
|
+
usage: GOOGLE_QUERY_TOTALS_USAGE,
|
|
9309
|
+
message: "--limit must be an integer"
|
|
9310
|
+
}),
|
|
9311
|
+
offset: parseIntegerOption(input, "offset", {
|
|
9312
|
+
command: "google.query-totals",
|
|
9313
|
+
usage: GOOGLE_QUERY_TOTALS_USAGE,
|
|
9314
|
+
message: "--offset must be an integer"
|
|
9315
|
+
}),
|
|
9316
|
+
format: input.format
|
|
9317
|
+
});
|
|
9318
|
+
}
|
|
9319
|
+
},
|
|
9125
9320
|
{
|
|
9126
9321
|
path: ["google", "inspect"],
|
|
9127
9322
|
usage: "canonry google inspect <project> <url> [--format json]",
|
|
@@ -9251,12 +9446,12 @@ Usage: ${GOOGLE_PERFORMANCE_USAGE}`,
|
|
|
9251
9446
|
},
|
|
9252
9447
|
{
|
|
9253
9448
|
path: ["google"],
|
|
9254
|
-
usage: "canonry google <connect|disconnect|status|properties|set-property|set-sitemap|list-sitemaps|submit-sitemap|discover-sitemaps|sync|performance|performance-daily|inspect|inspect-sitemap|coverage|coverage-history|inspections|deindexed|request-indexing|refresh> <project> [args]",
|
|
9449
|
+
usage: "canonry google <connect|disconnect|status|properties|set-property|set-sitemap|list-sitemaps|submit-sitemap|discover-sitemaps|sync|performance|performance-daily|top-pages|query-totals|inspect|inspect-sitemap|coverage|coverage-history|inspections|deindexed|request-indexing|refresh> <project> [args]",
|
|
9255
9450
|
run: async (input) => {
|
|
9256
9451
|
unknownSubcommand(input.positionals[0], {
|
|
9257
9452
|
command: "google",
|
|
9258
|
-
usage: "canonry google <connect|disconnect|status|properties|set-property|set-sitemap|list-sitemaps|submit-sitemap|discover-sitemaps|sync|performance|performance-daily|inspect|inspect-sitemap|coverage|coverage-history|inspections|deindexed|request-indexing|refresh> <project> [args]",
|
|
9259
|
-
available: ["connect", "disconnect", "status", "properties", "set-property", "set-sitemap", "list-sitemaps", "submit-sitemap", "discover-sitemaps", "sync", "performance", "performance-daily", "inspect", "inspect-sitemap", "coverage", "coverage-history", "inspections", "deindexed", "request-indexing", "refresh"]
|
|
9453
|
+
usage: "canonry google <connect|disconnect|status|properties|set-property|set-sitemap|list-sitemaps|submit-sitemap|discover-sitemaps|sync|performance|performance-daily|top-pages|query-totals|inspect|inspect-sitemap|coverage|coverage-history|inspections|deindexed|request-indexing|refresh> <project> [args]",
|
|
9454
|
+
available: ["connect", "disconnect", "status", "properties", "set-property", "set-sitemap", "list-sitemaps", "submit-sitemap", "discover-sitemaps", "sync", "performance", "performance-daily", "top-pages", "query-totals", "inspect", "inspect-sitemap", "coverage", "coverage-history", "inspections", "deindexed", "request-indexing", "refresh"]
|
|
9260
9455
|
});
|
|
9261
9456
|
}
|
|
9262
9457
|
}
|
|
@@ -9886,7 +10081,7 @@ var KEYWORD_CLI_COMMANDS = [
|
|
|
9886
10081
|
const provider = requireStringOption(input, "provider", {
|
|
9887
10082
|
command: "keyword.generate",
|
|
9888
10083
|
usage: "canonry keyword generate <project> --provider <name> [--count <n>] [--save] [--format json]",
|
|
9889
|
-
message: "--provider is required (e.g. gemini, openai, claude, perplexity, local)"
|
|
10084
|
+
message: "--provider is required (e.g. gemini, openai, claude, perplexity, muse, local)"
|
|
9890
10085
|
});
|
|
9891
10086
|
await generateKeywords(project, provider, {
|
|
9892
10087
|
count: parseIntegerOption(input, "count", {
|
|
@@ -10630,7 +10825,7 @@ var QUERY_CLI_COMMANDS = [
|
|
|
10630
10825
|
const provider = requireStringOption(input, "provider", {
|
|
10631
10826
|
command: "query.generate",
|
|
10632
10827
|
usage: "canonry query generate <project> --provider <name> [--count <n>] [--save] [--format json]",
|
|
10633
|
-
message: "--provider is required (e.g. gemini, openai, claude, perplexity, local)"
|
|
10828
|
+
message: "--provider is required (e.g. gemini, openai, claude, perplexity, muse, local)"
|
|
10634
10829
|
});
|
|
10635
10830
|
await generateQueries(project, provider, {
|
|
10636
10831
|
count: parseIntegerOption(input, "count", {
|
|
@@ -11047,7 +11242,9 @@ var EVENT_DESCRIPTIONS = {
|
|
|
11047
11242
|
"insight.critical": "A critical-severity insight was generated",
|
|
11048
11243
|
"insight.high": "A high-severity insight was generated",
|
|
11049
11244
|
"health.degraded": "A scheduled health check found the measurement degraded, daily data stopped arriving, or the website stopped answering (sent on change, not every pass)",
|
|
11050
|
-
"health.recovered": "A degraded health check is passing again, or a website that was paged as down answers again"
|
|
11245
|
+
"health.recovered": "A degraded health check is passing again, or a website that was paged as down answers again",
|
|
11246
|
+
"review.negative": "A new or edited Google review at or below the project's negative-review threshold, 3 stars by default (Business Profile reviews, or the public listing when Google has not enabled review access)",
|
|
11247
|
+
"review.rating-dropped": "A location's public Google rating fell (sent for locations without Business Profile review access)"
|
|
11051
11248
|
};
|
|
11052
11249
|
function listEvents(format, target = "local") {
|
|
11053
11250
|
if (target === "server") return getClient16().listNotificationEvents().then((events) => printEvents(events, format));
|
|
@@ -11953,14 +12150,23 @@ async function createProject(name, opts) {
|
|
|
11953
12150
|
country: opts.country,
|
|
11954
12151
|
language: opts.language,
|
|
11955
12152
|
providers: opts.providers ?? [],
|
|
11956
|
-
providerModels: opts.providerModels ?? {}
|
|
12153
|
+
providerModels: opts.providerModels ?? {},
|
|
12154
|
+
...opts.providerDispatchModes && Object.keys(opts.providerDispatchModes).length > 0 ? { providerDispatchModes: opts.providerDispatchModes } : {}
|
|
11957
12155
|
});
|
|
12156
|
+
warnDroppedDispatchModes(opts.providerDispatchModes ?? {}, result);
|
|
11958
12157
|
if (isMachineFormat(opts.format)) {
|
|
11959
12158
|
console.log(JSON.stringify(result, null, 2));
|
|
11960
12159
|
return;
|
|
11961
12160
|
}
|
|
11962
12161
|
console.log(`Project created: ${result.name} (${result.id})`);
|
|
11963
12162
|
}
|
|
12163
|
+
function warnDroppedDispatchModes(sent, result) {
|
|
12164
|
+
const kept = result.providerDispatchModes ?? {};
|
|
12165
|
+
const dropped = Object.keys(sent).filter((provider) => !(provider in kept));
|
|
12166
|
+
if (dropped.length > 0) {
|
|
12167
|
+
console.error(`Warning: Dropped dispatch mode for engine(s) the project does not measure: ${dropped.join(", ")}`);
|
|
12168
|
+
}
|
|
12169
|
+
}
|
|
11964
12170
|
async function listProjects(format) {
|
|
11965
12171
|
const client = getClient22();
|
|
11966
12172
|
const projects2 = await client.listProjects();
|
|
@@ -12019,6 +12225,9 @@ async function showProject(name, format) {
|
|
|
12019
12225
|
console.log(` Providers: ${(project.providers ?? []).length > 0 ? project.providers.join(", ") : "all configured"}`);
|
|
12020
12226
|
const providerModels = Object.entries(project.providerModels ?? {});
|
|
12021
12227
|
console.log(` Model overrides: ${providerModels.length > 0 ? providerModels.map(([provider, model]) => `${provider}=${model}`).join(", ") : "(none; instance settings inherited)"}`);
|
|
12228
|
+
const dispatchModes = Object.entries(project.providerDispatchModes ?? {}).sort(([left], [right]) => left.localeCompare(right));
|
|
12229
|
+
console.log(` Dispatch modes: ${dispatchModes.length > 0 ? `${dispatchModes.map(([provider, mode]) => `${provider}=${mode}`).join(", ")} (scheduled sweeps; others sync)` : "(none; every provider runs sync)"}`);
|
|
12230
|
+
console.log(` Negative reviews: ${resolveNegativeReviewMaxStars(project.negativeReviewMaxStars)} stars or fewer${project.negativeReviewMaxStars == null ? " (default)" : ""}`);
|
|
12022
12231
|
console.log(` Tags: ${project.tags.length > 0 ? project.tags.join(", ") : "(none)"}`);
|
|
12023
12232
|
const labelEntries = Object.entries(project.labels);
|
|
12024
12233
|
console.log(` Labels: ${labelEntries.length > 0 ? labelEntries.map(([k, v]) => `${k}=${v}`).join(", ") : "(none)"}`);
|
|
@@ -12063,6 +12272,9 @@ async function updateProjectSettings(name, opts) {
|
|
|
12063
12272
|
);
|
|
12064
12273
|
}
|
|
12065
12274
|
}
|
|
12275
|
+
const touchesDispatch = Object.keys(opts.dispatchModes ?? {}).length > 0 || (opts.clearDispatchModes ?? []).length > 0;
|
|
12276
|
+
const providerDispatchModes = { ...project.providerDispatchModes ?? {}, ...opts.dispatchModes ?? {} };
|
|
12277
|
+
for (const provider of opts.clearDispatchModes ?? []) delete providerDispatchModes[provider];
|
|
12066
12278
|
const result = await client.putProject(name, {
|
|
12067
12279
|
displayName: nextDisplayName,
|
|
12068
12280
|
canonicalDomain: opts.domain ?? project.canonicalDomain,
|
|
@@ -12074,10 +12286,15 @@ async function updateProjectSettings(name, opts) {
|
|
|
12074
12286
|
labels: project.labels,
|
|
12075
12287
|
providers: opts.providers ?? project.providers,
|
|
12076
12288
|
providerModels,
|
|
12289
|
+
...touchesDispatch ? { providerDispatchModes } : {},
|
|
12077
12290
|
locations: project.locations,
|
|
12078
12291
|
defaultLocation: project.defaultLocation,
|
|
12079
|
-
autoExtractBacklinks: project.autoExtractBacklinks
|
|
12292
|
+
autoExtractBacklinks: project.autoExtractBacklinks,
|
|
12293
|
+
// Sent only when asked for: the server keeps the stored threshold when the
|
|
12294
|
+
// field is absent, so a stale read cannot overwrite a newer setting.
|
|
12295
|
+
...opts.negativeReviewMaxStars !== void 0 ? { negativeReviewMaxStars: opts.negativeReviewMaxStars } : {}
|
|
12080
12296
|
});
|
|
12297
|
+
warnDroppedDispatchModes(touchesDispatch ? providerDispatchModes : project.providerDispatchModes ?? {}, result);
|
|
12081
12298
|
if (isMachineFormat(opts.format)) {
|
|
12082
12299
|
console.log(JSON.stringify(result, null, 2));
|
|
12083
12300
|
return;
|
|
@@ -12190,7 +12407,7 @@ async function setDefaultLocation(project, label, format) {
|
|
|
12190
12407
|
var PROJECT_CLI_COMMANDS = [
|
|
12191
12408
|
{
|
|
12192
12409
|
path: ["project", "create"],
|
|
12193
|
-
usage: "canonry project create <name> [--domain <domain>] [--owned-domain <domain>...] [--alias <name>...] [--country <code>] [--language <lang>] [--display-name <name>] [--provider <name>...] [--provider-model provider=model...] [--format json]",
|
|
12410
|
+
usage: "canonry project create <name> [--domain <domain>] [--owned-domain <domain>...] [--alias <name>...] [--country <code>] [--language <lang>] [--display-name <name>] [--provider <name>...] [--provider-model provider=model...] [--dispatch-mode provider=sync|batch...] [--format json]",
|
|
12194
12411
|
help: "Create a project. Pass --domain for the public site to scan. Provider credentials are not required for Page Health.",
|
|
12195
12412
|
options: {
|
|
12196
12413
|
domain: { type: "string", short: "d" },
|
|
@@ -12200,13 +12417,14 @@ var PROJECT_CLI_COMMANDS = [
|
|
|
12200
12417
|
language: stringOption(),
|
|
12201
12418
|
"display-name": stringOption(),
|
|
12202
12419
|
provider: multiStringOption(),
|
|
12203
|
-
"provider-model": multiStringOption()
|
|
12420
|
+
"provider-model": multiStringOption(),
|
|
12421
|
+
"dispatch-mode": multiStringOption()
|
|
12204
12422
|
},
|
|
12205
12423
|
run: async (input) => {
|
|
12206
12424
|
const name = requireProject(
|
|
12207
12425
|
input,
|
|
12208
12426
|
"project.create",
|
|
12209
|
-
"canonry project create <name> [--domain <domain>] [--owned-domain <domain>...] [--alias <name>...] [--country <code>] [--language <lang>] [--display-name <name>] [--provider <name>...] [--provider-model provider=model...] [--format json]"
|
|
12427
|
+
"canonry project create <name> [--domain <domain>] [--owned-domain <domain>...] [--alias <name>...] [--country <code>] [--language <lang>] [--display-name <name>] [--provider <name>...] [--provider-model provider=model...] [--dispatch-mode provider=sync|batch...] [--format json]"
|
|
12210
12428
|
);
|
|
12211
12429
|
await createProject(name, {
|
|
12212
12430
|
domain: getString(input.values, "domain") ?? name,
|
|
@@ -12217,13 +12435,14 @@ var PROJECT_CLI_COMMANDS = [
|
|
|
12217
12435
|
displayName: getString(input.values, "display-name") ?? name,
|
|
12218
12436
|
providers: getStringArray(input.values, "provider") ?? [],
|
|
12219
12437
|
providerModels: parseProviderModelAssignments(getStringArray(input.values, "provider-model")),
|
|
12438
|
+
providerDispatchModes: parseDispatchModeAssignments(getStringArray(input.values, "dispatch-mode")),
|
|
12220
12439
|
format: input.format
|
|
12221
12440
|
});
|
|
12222
12441
|
}
|
|
12223
12442
|
},
|
|
12224
12443
|
{
|
|
12225
12444
|
path: ["project", "update"],
|
|
12226
|
-
usage: "canonry project update <name> [--domain <domain>] [--owned-domain <domain>...] [--add-domain <domain>...] [--remove-domain <domain>...] [--alias <name>...] [--add-alias <name>...] [--remove-alias <name>...] [--country <code>] [--language <lang>] [--display-name <name>] [--provider <name>...] [--all-providers] [--provider-model provider=model...] [--clear-provider-model <provider>...] [--format json]",
|
|
12445
|
+
usage: "canonry project update <name> [--domain <domain>] [--owned-domain <domain>...] [--add-domain <domain>...] [--remove-domain <domain>...] [--alias <name>...] [--add-alias <name>...] [--remove-alias <name>...] [--country <code>] [--language <lang>] [--display-name <name>] [--provider <name>...] [--all-providers] [--provider-model provider=model...] [--clear-provider-model <provider>...] [--dispatch-mode provider=sync|batch...] [--clear-dispatch-mode <provider>...] [--negative-review-max-stars <1-4|default>] [--format json]",
|
|
12227
12446
|
options: {
|
|
12228
12447
|
domain: { type: "string", short: "d" },
|
|
12229
12448
|
"owned-domain": multiStringOption(),
|
|
@@ -12238,13 +12457,16 @@ var PROJECT_CLI_COMMANDS = [
|
|
|
12238
12457
|
provider: multiStringOption(),
|
|
12239
12458
|
"all-providers": { type: "boolean" },
|
|
12240
12459
|
"provider-model": multiStringOption(),
|
|
12241
|
-
"clear-provider-model": multiStringOption()
|
|
12460
|
+
"clear-provider-model": multiStringOption(),
|
|
12461
|
+
"dispatch-mode": multiStringOption(),
|
|
12462
|
+
"clear-dispatch-mode": multiStringOption(),
|
|
12463
|
+
"negative-review-max-stars": stringOption()
|
|
12242
12464
|
},
|
|
12243
12465
|
run: async (input) => {
|
|
12244
12466
|
const name = requireProject(
|
|
12245
12467
|
input,
|
|
12246
12468
|
"project.update",
|
|
12247
|
-
"canonry project update <name> [--domain <domain>] [--owned-domain <domain>...] [--add-domain <domain>...] [--remove-domain <domain>...] [--alias <name>...] [--add-alias <name>...] [--remove-alias <name>...] [--country <code>] [--language <lang>] [--display-name <name>] [--provider <name>...] [--all-providers] [--provider-model provider=model...] [--clear-provider-model <provider>...] [--format json]"
|
|
12469
|
+
"canonry project update <name> [--domain <domain>] [--owned-domain <domain>...] [--add-domain <domain>...] [--remove-domain <domain>...] [--alias <name>...] [--add-alias <name>...] [--remove-alias <name>...] [--country <code>] [--language <lang>] [--display-name <name>] [--provider <name>...] [--all-providers] [--provider-model provider=model...] [--clear-provider-model <provider>...] [--dispatch-mode provider=sync|batch...] [--clear-dispatch-mode <provider>...] [--negative-review-max-stars <1-4|default>] [--format json]"
|
|
12248
12470
|
);
|
|
12249
12471
|
const providers = getStringArray(input.values, "provider");
|
|
12250
12472
|
const allProviders = getBoolean(input.values, "all-providers");
|
|
@@ -12254,6 +12476,11 @@ var PROJECT_CLI_COMMANDS = [
|
|
|
12254
12476
|
for (const provider of clearProviderModels) {
|
|
12255
12477
|
if (provider in providerModels) throw usageError(`Error: --provider-model and --clear-provider-model conflict for ${provider}`);
|
|
12256
12478
|
}
|
|
12479
|
+
const dispatchModes = parseDispatchModeAssignments(getStringArray(input.values, "dispatch-mode"));
|
|
12480
|
+
const clearDispatchModes = getStringArray(input.values, "clear-dispatch-mode") ?? [];
|
|
12481
|
+
for (const provider of clearDispatchModes) {
|
|
12482
|
+
if (provider in dispatchModes) throw usageError(`Error: --dispatch-mode and --clear-dispatch-mode conflict for ${provider}`);
|
|
12483
|
+
}
|
|
12257
12484
|
await updateProjectSettings(name, {
|
|
12258
12485
|
displayName: getString(input.values, "display-name"),
|
|
12259
12486
|
domain: getString(input.values, "domain"),
|
|
@@ -12268,6 +12495,9 @@ var PROJECT_CLI_COMMANDS = [
|
|
|
12268
12495
|
providers: allProviders ? [] : providers,
|
|
12269
12496
|
providerModels,
|
|
12270
12497
|
clearProviderModels,
|
|
12498
|
+
dispatchModes,
|
|
12499
|
+
clearDispatchModes,
|
|
12500
|
+
negativeReviewMaxStars: parseNegativeReviewMaxStars(getString(input.values, "negative-review-max-stars")),
|
|
12271
12501
|
format: input.format
|
|
12272
12502
|
});
|
|
12273
12503
|
}
|
|
@@ -12383,6 +12613,18 @@ var PROJECT_CLI_COMMANDS = [
|
|
|
12383
12613
|
}
|
|
12384
12614
|
}
|
|
12385
12615
|
];
|
|
12616
|
+
function parseDispatchModeAssignments(assignments) {
|
|
12617
|
+
const result = {};
|
|
12618
|
+
for (const assignment of assignments ?? []) {
|
|
12619
|
+
const separator = assignment.indexOf("=");
|
|
12620
|
+
const provider = separator === -1 ? "" : assignment.slice(0, separator).trim();
|
|
12621
|
+
const mode = providerDispatchModeSchema.safeParse(separator === -1 ? "" : assignment.slice(separator + 1).trim().toLowerCase());
|
|
12622
|
+
if (!provider || !mode.success) throw usageError(`Error: --dispatch-mode must use provider=sync or provider=batch (received ${assignment})`);
|
|
12623
|
+
if (provider in result) throw usageError(`Error: duplicate --dispatch-mode assignment for ${provider}`);
|
|
12624
|
+
result[provider] = mode.data;
|
|
12625
|
+
}
|
|
12626
|
+
return result;
|
|
12627
|
+
}
|
|
12386
12628
|
function parseProviderModelAssignments(assignments) {
|
|
12387
12629
|
const result = {};
|
|
12388
12630
|
for (const assignment of assignments ?? []) {
|
|
@@ -12395,6 +12637,18 @@ function parseProviderModelAssignments(assignments) {
|
|
|
12395
12637
|
}
|
|
12396
12638
|
return result;
|
|
12397
12639
|
}
|
|
12640
|
+
function parseNegativeReviewMaxStars(raw) {
|
|
12641
|
+
if (raw === void 0) return void 0;
|
|
12642
|
+
if (raw === "default") return null;
|
|
12643
|
+
const parsed = gbpNegativeReviewMaxStarsSchema.safeParse(Number(raw));
|
|
12644
|
+
if (!parsed.success) {
|
|
12645
|
+
throw usageError('Error: --negative-review-max-stars must be 1, 2, 3, 4, or "default" (3)', {
|
|
12646
|
+
message: '--negative-review-max-stars must be 1-4 or "default"',
|
|
12647
|
+
details: { command: "project.update", value: raw }
|
|
12648
|
+
});
|
|
12649
|
+
}
|
|
12650
|
+
return parsed.data;
|
|
12651
|
+
}
|
|
12398
12652
|
|
|
12399
12653
|
// src/commands/report.ts
|
|
12400
12654
|
import fs13 from "fs";
|
|
@@ -12530,6 +12784,27 @@ function getClient23() {
|
|
|
12530
12784
|
return createApiClient();
|
|
12531
12785
|
}
|
|
12532
12786
|
var TERMINAL_STATUSES = /* @__PURE__ */ new Set(["completed", "partial", "failed", "cancelled"]);
|
|
12787
|
+
function throwIfWaitedRunFailed(waited) {
|
|
12788
|
+
const failed = waited.filter((r) => r.status === RunStatuses.failed);
|
|
12789
|
+
if (failed.length === 0) return;
|
|
12790
|
+
const reason = (r) => r.error ? formatRunErrorOneLine(r.error) : null;
|
|
12791
|
+
const label = (r) => [r.project, r.location ? `(${r.location})` : null, `run ${r.runId}`].filter(Boolean).join(" ");
|
|
12792
|
+
const message = waited.length === 1 ? `Run ${failed[0].runId} failed${reason(failed[0]) ? `: ${reason(failed[0])}` : ""}` : `${failed.length} of ${waited.length} runs failed: ${failed.map((r) => `${label(r)}${reason(r) ? `: ${reason(r)}` : ""}`).join("; ")}`;
|
|
12793
|
+
throw new CliError({
|
|
12794
|
+
code: "RUN_FAILED",
|
|
12795
|
+
message,
|
|
12796
|
+
exitCode: EXIT_SYSTEM_ERROR,
|
|
12797
|
+
details: {
|
|
12798
|
+
waitedRunCount: waited.length,
|
|
12799
|
+
failedRuns: failed.map((r) => ({
|
|
12800
|
+
runId: r.runId,
|
|
12801
|
+
...r.project ? { project: r.project } : {},
|
|
12802
|
+
...r.location ? { location: r.location } : {},
|
|
12803
|
+
error: reason(r)
|
|
12804
|
+
}))
|
|
12805
|
+
}
|
|
12806
|
+
});
|
|
12807
|
+
}
|
|
12533
12808
|
async function triggerRun(project, opts) {
|
|
12534
12809
|
const client = getClient23();
|
|
12535
12810
|
const body = {};
|
|
@@ -12559,19 +12834,22 @@ async function triggerRun(project, opts) {
|
|
|
12559
12834
|
if (opts?.probe) {
|
|
12560
12835
|
body.trigger = "probe";
|
|
12561
12836
|
}
|
|
12837
|
+
if (opts?.dispatchMode) {
|
|
12838
|
+
body.dispatchMode = opts.dispatchMode;
|
|
12839
|
+
}
|
|
12562
12840
|
const response = await client.triggerRun(project, body);
|
|
12563
12841
|
if (Array.isArray(response)) {
|
|
12564
12842
|
const locationRuns = response;
|
|
12565
12843
|
if (isMachineFormat(opts?.format)) {
|
|
12566
12844
|
if (opts?.wait) {
|
|
12567
|
-
const
|
|
12568
|
-
locationRuns.map(async (r) =>
|
|
12569
|
-
if (!r.id || r.status === "conflict") return r;
|
|
12570
|
-
const final = await pollRun2(client, r.id);
|
|
12571
|
-
return { ...r, ...final };
|
|
12572
|
-
})
|
|
12845
|
+
const finals = await Promise.all(
|
|
12846
|
+
locationRuns.map(async (r) => !r.id || r.status === "conflict" ? null : pollRun2(client, r.id, false))
|
|
12573
12847
|
);
|
|
12574
|
-
console.log(JSON.stringify(
|
|
12848
|
+
console.log(JSON.stringify(locationRuns.map((r, i) => finals[i] ? { ...r, ...finals[i] } : r), null, 2));
|
|
12849
|
+
throwIfWaitedRunFailed(locationRuns.flatMap((r, i) => {
|
|
12850
|
+
const final = finals[i];
|
|
12851
|
+
return final ? [{ runId: final.id, status: final.status, project, location: r.location ?? null, error: final.error }] : [];
|
|
12852
|
+
}));
|
|
12575
12853
|
} else {
|
|
12576
12854
|
console.log(JSON.stringify(locationRuns, null, 2));
|
|
12577
12855
|
}
|
|
@@ -12588,12 +12866,17 @@ async function triggerRun(project, opts) {
|
|
|
12588
12866
|
}
|
|
12589
12867
|
if (opts?.wait) {
|
|
12590
12868
|
const pending = locationRuns.filter((r) => r.id && r.status !== "conflict" && !TERMINAL_STATUSES.has(r.status));
|
|
12869
|
+
const errors = /* @__PURE__ */ new Map();
|
|
12591
12870
|
if (pending.length > 0) {
|
|
12592
12871
|
process.stderr.write(`Waiting for ${pending.length} run(s)`);
|
|
12872
|
+
const batchWaits = /* @__PURE__ */ new Map();
|
|
12593
12873
|
await Promise.all(
|
|
12594
12874
|
pending.map(async (r) => {
|
|
12595
12875
|
const final = await pollRun2(client, r.id);
|
|
12596
12876
|
r.status = final.status;
|
|
12877
|
+
errors.set(r.id, final.error);
|
|
12878
|
+
const waiting = batchWaitLine(final);
|
|
12879
|
+
if (waiting) batchWaits.set(r.id, waiting);
|
|
12597
12880
|
})
|
|
12598
12881
|
);
|
|
12599
12882
|
process.stderr.write("\n");
|
|
@@ -12602,20 +12885,31 @@ async function triggerRun(project, opts) {
|
|
|
12602
12885
|
const loc = (r.location ?? "(unknown)").padEnd(15);
|
|
12603
12886
|
console.log(` ${loc} ${r.status}`);
|
|
12604
12887
|
}
|
|
12888
|
+
if (batchWaits.size > 0) console.log("");
|
|
12889
|
+
for (const r of locationRuns) {
|
|
12890
|
+
const waiting = batchWaits.get(r.id);
|
|
12891
|
+
if (waiting) console.log(` ${r.location ?? "(unknown)"}: ${waiting}`);
|
|
12892
|
+
}
|
|
12605
12893
|
}
|
|
12894
|
+
throwIfWaitedRunFailed(locationRuns.filter((r) => r.id && r.status !== "conflict").map((r) => ({ runId: r.id, status: r.status, project, location: r.location ?? null, error: errors.get(r.id) })));
|
|
12606
12895
|
}
|
|
12607
12896
|
return;
|
|
12608
12897
|
}
|
|
12609
12898
|
const run = response;
|
|
12610
12899
|
if (opts?.wait && run.id && !TERMINAL_STATUSES.has(run.status)) {
|
|
12611
|
-
|
|
12612
|
-
|
|
12900
|
+
const showProgress = !isMachineFormat(opts?.format);
|
|
12901
|
+
if (showProgress) process.stderr.write(`Run ${run.id} started`);
|
|
12902
|
+
const result = await pollRun2(client, run.id, showProgress);
|
|
12613
12903
|
if (isMachineFormat(opts?.format)) {
|
|
12614
12904
|
console.log(JSON.stringify(result, null, 2));
|
|
12615
12905
|
} else {
|
|
12616
12906
|
process.stderr.write("\n");
|
|
12617
12907
|
printRunDetail(result);
|
|
12908
|
+
const waiting = batchWaitLine(result);
|
|
12909
|
+
if (waiting) console.log(`
|
|
12910
|
+
${waiting}`);
|
|
12618
12911
|
}
|
|
12912
|
+
throwIfWaitedRunFailed([{ runId: result.id, status: result.status, project, error: result.error }]);
|
|
12619
12913
|
return;
|
|
12620
12914
|
}
|
|
12621
12915
|
if (opts?.wait && (TERMINAL_STATUSES.has(run.status) || !run.id)) {
|
|
@@ -12625,6 +12919,7 @@ async function triggerRun(project, opts) {
|
|
|
12625
12919
|
} else {
|
|
12626
12920
|
printRunDetail(result);
|
|
12627
12921
|
}
|
|
12922
|
+
throwIfWaitedRunFailed([{ runId: result.id, status: result.status, project, error: result.error }]);
|
|
12628
12923
|
return;
|
|
12629
12924
|
}
|
|
12630
12925
|
if (isMachineFormat(opts?.format)) {
|
|
@@ -12661,6 +12956,9 @@ async function triggerRunAll(opts) {
|
|
|
12661
12956
|
if (opts?.noLocation) {
|
|
12662
12957
|
baseBody.noLocation = true;
|
|
12663
12958
|
}
|
|
12959
|
+
if (opts?.dispatchMode) {
|
|
12960
|
+
baseBody.dispatchMode = opts.dispatchMode;
|
|
12961
|
+
}
|
|
12664
12962
|
const results = [];
|
|
12665
12963
|
for (const p of projects2) {
|
|
12666
12964
|
const body = { ...baseBody };
|
|
@@ -12683,19 +12981,27 @@ async function triggerRunAll(opts) {
|
|
|
12683
12981
|
results.push({ project: p.name, runId: "", status: "error", location: null, error: msg });
|
|
12684
12982
|
}
|
|
12685
12983
|
}
|
|
12984
|
+
const errors = /* @__PURE__ */ new Map();
|
|
12985
|
+
const batchWaits = /* @__PURE__ */ new Map();
|
|
12686
12986
|
if (opts?.wait) {
|
|
12687
12987
|
const pending = results.filter((r) => r.runId && !TERMINAL_STATUSES.has(r.status));
|
|
12688
12988
|
if (pending.length > 0) {
|
|
12689
|
-
|
|
12989
|
+
const showProgress = !isMachineFormat(opts?.format);
|
|
12990
|
+
if (showProgress) process.stderr.write(`Waiting for ${pending.length} run(s)`);
|
|
12690
12991
|
await Promise.all(pending.map(async (r) => {
|
|
12691
|
-
const final = await pollRun2(client, r.runId);
|
|
12992
|
+
const final = await pollRun2(client, r.runId, showProgress);
|
|
12692
12993
|
r.status = final.status;
|
|
12994
|
+
errors.set(r.runId, final.error);
|
|
12995
|
+
const waiting = batchWaitLine(final);
|
|
12996
|
+
if (waiting) batchWaits.set(r.runId, waiting);
|
|
12693
12997
|
}));
|
|
12694
|
-
process.stderr.write("\n");
|
|
12998
|
+
if (showProgress) process.stderr.write("\n");
|
|
12695
12999
|
}
|
|
12696
13000
|
}
|
|
13001
|
+
const waited = results.filter((r) => r.runId).map((r) => ({ runId: r.runId, status: r.status, project: r.project, location: r.location, error: errors.get(r.runId) }));
|
|
12697
13002
|
if (isMachineFormat(opts?.format)) {
|
|
12698
13003
|
console.log(JSON.stringify(results, null, 2));
|
|
13004
|
+
if (opts?.wait) throwIfWaitedRunFailed(waited);
|
|
12699
13005
|
return;
|
|
12700
13006
|
}
|
|
12701
13007
|
const showLocationColumn = results.some((r) => r.location !== null);
|
|
@@ -12720,6 +13026,12 @@ async function triggerRunAll(opts) {
|
|
|
12720
13026
|
console.log(` ${proj} ${id} ${r.status}`);
|
|
12721
13027
|
}
|
|
12722
13028
|
}
|
|
13029
|
+
if (batchWaits.size > 0) console.log("");
|
|
13030
|
+
for (const r of results) {
|
|
13031
|
+
const waiting = batchWaits.get(r.runId);
|
|
13032
|
+
if (waiting) console.log(` ${r.location ? `${r.project} (${r.location})` : r.project}: ${waiting}`);
|
|
13033
|
+
}
|
|
13034
|
+
if (opts?.wait) throwIfWaitedRunFailed(waited);
|
|
12723
13035
|
}
|
|
12724
13036
|
async function cancelRun(project, runId, format) {
|
|
12725
13037
|
const client = getClient23();
|
|
@@ -12803,7 +13115,7 @@ async function listRuns(project, opts) {
|
|
|
12803
13115
|
}
|
|
12804
13116
|
}
|
|
12805
13117
|
var POLL_TIMEOUT_MS3 = 10 * 60 * 1e3;
|
|
12806
|
-
async function pollRun2(client, runId) {
|
|
13118
|
+
async function pollRun2(client, runId, showProgress = true) {
|
|
12807
13119
|
const deadline = Date.now() + POLL_TIMEOUT_MS3;
|
|
12808
13120
|
for (; ; ) {
|
|
12809
13121
|
await new Promise((r) => setTimeout(r, 2e3));
|
|
@@ -12811,12 +13123,22 @@ async function pollRun2(client, runId) {
|
|
|
12811
13123
|
throw new Error(`Timed out waiting for run ${runId} after ${POLL_TIMEOUT_MS3 / 1e3}s`);
|
|
12812
13124
|
}
|
|
12813
13125
|
const run = await client.getRun(runId);
|
|
12814
|
-
process.stderr.write(".");
|
|
12815
|
-
if (TERMINAL_STATUSES.has(run.status)) {
|
|
13126
|
+
if (showProgress) process.stderr.write(".");
|
|
13127
|
+
if (TERMINAL_STATUSES.has(run.status) || outstandingBatches(run).length > 0) {
|
|
12816
13128
|
return run;
|
|
12817
13129
|
}
|
|
12818
13130
|
}
|
|
12819
13131
|
}
|
|
13132
|
+
function outstandingBatches(run) {
|
|
13133
|
+
if (TERMINAL_STATUSES.has(run.status)) return [];
|
|
13134
|
+
return (run.providerBatches ?? []).filter((batch) => OUTSTANDING_PROVIDER_BATCH_STATUSES.includes(batch.status));
|
|
13135
|
+
}
|
|
13136
|
+
function batchWaitLine(run) {
|
|
13137
|
+
const outstanding = outstandingBatches(run);
|
|
13138
|
+
if (outstanding.length === 0) return null;
|
|
13139
|
+
const batches = outstanding.map((batch) => `${batch.provider} \u2014 ${batch.requestCount} requests, submitted ${batch.submittedAt ?? "unknown"}, deadline ${batch.deadlineAt}`);
|
|
13140
|
+
return `Waiting on provider batch(es): ${batches.join("; ")}; check with canonry run show ${run.id}`;
|
|
13141
|
+
}
|
|
12820
13142
|
function printRunDetail(run) {
|
|
12821
13143
|
console.log(`Run: ${run.id}`);
|
|
12822
13144
|
console.log(` Status: ${run.status}`);
|
|
@@ -12833,6 +13155,13 @@ function printRunDetail(run) {
|
|
|
12833
13155
|
}
|
|
12834
13156
|
}
|
|
12835
13157
|
}
|
|
13158
|
+
const batched = Object.keys(run.dispatchModes ?? {}).sort();
|
|
13159
|
+
if (batched.length > 0) console.log(` Dispatch: ${batched.map((provider) => `${provider}=batch`).join(", ")} (other providers sync)`);
|
|
13160
|
+
if (run.providerBatches && run.providerBatches.length > 0) {
|
|
13161
|
+
console.log("\n Provider batches:");
|
|
13162
|
+
for (const batch of run.providerBatches) console.log(` ${providerBatchLine(batch)}`);
|
|
13163
|
+
}
|
|
13164
|
+
if (run.usage && run.usage.length > 0) printUsageTable(run.usage);
|
|
12836
13165
|
if (run.snapshots && run.snapshots.length > 0) {
|
|
12837
13166
|
console.log(`
|
|
12838
13167
|
Snapshots: ${run.snapshots.length} (cell = [citation][mention]; C=cited c=not, M=mentioned m=not, \u2013=no data)`);
|
|
@@ -12844,6 +13173,46 @@ function printRunDetail(run) {
|
|
|
12844
13173
|
}
|
|
12845
13174
|
}
|
|
12846
13175
|
}
|
|
13176
|
+
function providerBatchLine(batch) {
|
|
13177
|
+
const recorded = `${batch.recordedCount} of ${batch.requestCount} answers recorded`;
|
|
13178
|
+
const withError = (text) => batch.error ? `${text} (${batch.error})` : text;
|
|
13179
|
+
switch (batch.status) {
|
|
13180
|
+
case ProviderBatchStatuses.submitting:
|
|
13181
|
+
return `submitting provider batch: ${batch.provider} \u2014 ${batch.requestCount} requests`;
|
|
13182
|
+
// The two outstanding statuses: the run stays `running` until they clear.
|
|
13183
|
+
case ProviderBatchStatuses.submitted:
|
|
13184
|
+
case ProviderBatchStatuses.ended:
|
|
13185
|
+
return `waiting on provider batch: ${batch.provider} \u2014 ${batch.requestCount} requests, submitted ${batch.submittedAt ?? "unknown"}, deadline ${batch.deadlineAt}`;
|
|
13186
|
+
case ProviderBatchStatuses.ingested:
|
|
13187
|
+
return withError(`provider batch ${batch.provider}: ingested \u2014 ${recorded}`);
|
|
13188
|
+
case ProviderBatchStatuses.cancelled:
|
|
13189
|
+
return withError(`provider batch ${batch.provider}: cancelled \u2014 ${recorded}; the rest are missing and can be filled`);
|
|
13190
|
+
case ProviderBatchStatuses.failed:
|
|
13191
|
+
return withError(`provider batch ${batch.provider}: rejected by the provider \u2014 its ${batch.requestCount} answers ran sync instead`);
|
|
13192
|
+
case ProviderBatchStatuses.unknown:
|
|
13193
|
+
return withError(`provider batch ${batch.provider}: submit outcome unknown \u2014 never resubmitted; its ${batch.requestCount} answers stay missing`);
|
|
13194
|
+
}
|
|
13195
|
+
}
|
|
13196
|
+
function printUsageTable(rows) {
|
|
13197
|
+
const cost = (row) => row.estimatedCostMicros === null ? "unpriced" : `${formatMicros(row.estimatedCostMicros, "USD", { fractionDigits: 4, showTinyAsLessThan: true })}${row.unpricedAnswers > 0 ? ` (+${row.unpricedAnswers} unpriced)` : ""}`;
|
|
13198
|
+
const table = rows.map((row) => [
|
|
13199
|
+
row.provider,
|
|
13200
|
+
row.pricingTier,
|
|
13201
|
+
row.answers.toLocaleString("en-US"),
|
|
13202
|
+
row.inputTokens.toLocaleString("en-US"),
|
|
13203
|
+
row.cachedInputTokens.toLocaleString("en-US"),
|
|
13204
|
+
row.cacheWriteTokens.toLocaleString("en-US"),
|
|
13205
|
+
row.outputTokens.toLocaleString("en-US"),
|
|
13206
|
+
row.searchCount.toLocaleString("en-US"),
|
|
13207
|
+
cost(row)
|
|
13208
|
+
]);
|
|
13209
|
+
const header = ["PROVIDER", "TIER", "ANSWERS", "INPUT", "CACHED", "CACHE WRITE", "OUTPUT", "SEARCHES", "EST. COST"];
|
|
13210
|
+
const widths = header.map((title, index) => Math.max(title.length, ...table.map((cells) => cells[index].length)));
|
|
13211
|
+
const line = (cells) => ` ${cells.map((cell2, index) => cell2.padEnd(widths[index])).join(" ").trimEnd()}`;
|
|
13212
|
+
console.log("\n Usage (answers with recorded usage; cost is an estimate):");
|
|
13213
|
+
console.log(line(header));
|
|
13214
|
+
for (const cells of table) console.log(line(cells));
|
|
13215
|
+
}
|
|
12847
13216
|
var FILL_POLL_INTERVAL_MS = 3e3;
|
|
12848
13217
|
var FILL_POLL_TIMEOUT_MS = 6 * 60 * 60 * 1e3;
|
|
12849
13218
|
function missingSummary(completeness) {
|
|
@@ -12931,8 +13300,8 @@ async function fillRun(runId, opts = {}) {
|
|
|
12931
13300
|
}
|
|
12932
13301
|
|
|
12933
13302
|
// src/cli-commands/run.ts
|
|
12934
|
-
var RUN_TRIGGER_USAGE = "canonry run trigger <project> [--group <key>]... [--target <key>]... [--provider <name>] [--query <q>...] [--location <label>] [--all-locations] [--no-location] [--probe] [--wait] [--format json]";
|
|
12935
|
-
var RUN_USAGE2 = "canonry run <project|--all> [--group <key>]... [--target <key>]... [--provider <name>] [--query <q>...] [--location <label>] [--all-locations] [--no-location] [--probe] [--wait] [--format json]";
|
|
13303
|
+
var RUN_TRIGGER_USAGE = "canonry run trigger <project> [--group <key>]... [--target <key>]... [--provider <name>] [--query <q>...] [--location <label>] [--all-locations] [--no-location] [--probe] [--dispatch-mode sync|batch] [--wait] [--format json]";
|
|
13304
|
+
var RUN_USAGE2 = "canonry run <project|--all> [--group <key>]... [--target <key>]... [--provider <name>] [--query <q>...] [--location <label>] [--all-locations] [--no-location] [--probe] [--dispatch-mode sync|batch] [--wait] [--format json]";
|
|
12936
13305
|
var RUN_FILL_USAGE = "canonry run fill <run-id> [--provider <name>[,<name>]] [--dry-run] [--wait] [--format json]";
|
|
12937
13306
|
var RUNS_USAGE = "canonry runs <project> [--limit <n>] [--kind <kind>] [--status <status>] [--format json]";
|
|
12938
13307
|
var RUN_TRIGGER_OPTIONS = {
|
|
@@ -12951,13 +13320,26 @@ var RUN_TRIGGER_OPTIONS = {
|
|
|
12951
13320
|
// for inspection but are excluded from dashboard / analytics /
|
|
12952
13321
|
// intelligence / notifications. Use when you want to verify a
|
|
12953
13322
|
// provider works without polluting the project's metrics.
|
|
12954
|
-
probe: { type: "boolean", default: false }
|
|
13323
|
+
probe: { type: "boolean", default: false },
|
|
13324
|
+
// `batch` sends every provider that can to its asynchronous batch API (a
|
|
13325
|
+
// full plan sweep, enabled in config.yaml); refused when none can.
|
|
13326
|
+
"dispatch-mode": stringOption()
|
|
12955
13327
|
};
|
|
13328
|
+
function parseDispatchMode(value, command, usage) {
|
|
13329
|
+
if (value === void 0) return void 0;
|
|
13330
|
+
const parsed = providerDispatchModeSchema.safeParse(value.trim().toLowerCase());
|
|
13331
|
+
if (parsed.success) return parsed.data;
|
|
13332
|
+
throw usageError(`Error: --dispatch-mode must be one of: ${providerDispatchModeSchema.options.join(", ")} (received ${value})`, {
|
|
13333
|
+
message: "--dispatch-mode must be sync or batch",
|
|
13334
|
+
details: { command, usage }
|
|
13335
|
+
});
|
|
13336
|
+
}
|
|
12956
13337
|
async function triggerRunCommand(input, command) {
|
|
12957
13338
|
const groups = getStringArray(input.values, "group") ?? [];
|
|
12958
13339
|
const targets = getStringArray(input.values, "target") ?? [];
|
|
12959
13340
|
const queries2 = getStringArray(input.values, "query") ?? [];
|
|
12960
13341
|
const usage = command === "run" ? RUN_USAGE2 : RUN_TRIGGER_USAGE;
|
|
13342
|
+
const dispatchMode = parseDispatchMode(getString(input.values, "dispatch-mode"), command, usage);
|
|
12961
13343
|
if (getBoolean(input.values, "all")) {
|
|
12962
13344
|
if (input.positionals.length > 0) {
|
|
12963
13345
|
throw usageError("Error: --all cannot be combined with a project name", {
|
|
@@ -12988,6 +13370,7 @@ async function triggerRunCommand(input, command) {
|
|
|
12988
13370
|
wait: getBoolean(input.values, "wait"),
|
|
12989
13371
|
allLocations: getBoolean(input.values, "all-locations"),
|
|
12990
13372
|
noLocation: getBoolean(input.values, "no-location"),
|
|
13373
|
+
dispatchMode,
|
|
12991
13374
|
format: input.format
|
|
12992
13375
|
});
|
|
12993
13376
|
return;
|
|
@@ -13014,6 +13397,7 @@ async function triggerRunCommand(input, command) {
|
|
|
13014
13397
|
allLocations: getBoolean(input.values, "all-locations"),
|
|
13015
13398
|
noLocation: getBoolean(input.values, "no-location"),
|
|
13016
13399
|
probe: getBoolean(input.values, "probe"),
|
|
13400
|
+
dispatchMode,
|
|
13017
13401
|
format: input.format
|
|
13018
13402
|
});
|
|
13019
13403
|
}
|
|
@@ -13471,7 +13855,7 @@ var SETTINGS_CLI_COMMANDS = [
|
|
|
13471
13855
|
const name = requirePositional(input, 0, {
|
|
13472
13856
|
command: "settings.provider",
|
|
13473
13857
|
usage: "canonry settings provider <name> [--api-key <key>] [--base-url <url>] [--model <model>] [--max-concurrent <n>] [--max-per-minute <n>] [--max-per-day <n>] [--format json]",
|
|
13474
|
-
message: "provider name is required (e.g. gemini, openai, claude, perplexity, local)"
|
|
13858
|
+
message: "provider name is required (e.g. gemini, openai, claude, perplexity, muse, local)"
|
|
13475
13859
|
});
|
|
13476
13860
|
const apiKey = getString(input.values, "api-key");
|
|
13477
13861
|
const baseUrl = getString(input.values, "base-url");
|
|
@@ -15057,6 +15441,7 @@ async function bootstrapCommand(opts) {
|
|
|
15057
15441
|
if (providers.openai) mergedProviders.openai = providers.openai;
|
|
15058
15442
|
if (providers.claude) mergedProviders.claude = providers.claude;
|
|
15059
15443
|
if (providers.perplexity) mergedProviders.perplexity = providers.perplexity;
|
|
15444
|
+
if (providers.muse) mergedProviders.muse = providers.muse;
|
|
15060
15445
|
if (providers.local) mergedProviders.local = providers.local;
|
|
15061
15446
|
if (env.googleClientId && !env.googleClientSecret || !env.googleClientId && env.googleClientSecret) {
|
|
15062
15447
|
console.warn("Warning: GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET must both be set to configure Google OAuth. Skipping Google auth config.");
|
|
@@ -15450,6 +15835,7 @@ async function initCommand(opts) {
|
|
|
15450
15835
|
OPENAI_API_KEY: opts?.openaiKey,
|
|
15451
15836
|
ANTHROPIC_API_KEY: opts?.claudeKey,
|
|
15452
15837
|
PERPLEXITY_API_KEY: opts?.perplexityKey,
|
|
15838
|
+
MUSE_API_KEY: opts?.museKey,
|
|
15453
15839
|
LOCAL_BASE_URL: opts?.localUrl,
|
|
15454
15840
|
LOCAL_MODEL: opts?.localModel,
|
|
15455
15841
|
LOCAL_API_KEY: opts?.localKey,
|
|
@@ -15468,7 +15854,7 @@ async function initCommand(opts) {
|
|
|
15468
15854
|
}
|
|
15469
15855
|
const envProviders = bootstrapEnv.providers;
|
|
15470
15856
|
const envGoogleConfigured = !!(bootstrapEnv.googleClientId && bootstrapEnv.googleClientSecret);
|
|
15471
|
-
const nonInteractive = !!(envProviders.gemini || envProviders.openai || envProviders.claude || envProviders.perplexity || envProviders.local || envGoogleConfigured);
|
|
15857
|
+
const nonInteractive = !!(envProviders.gemini || envProviders.openai || envProviders.claude || envProviders.perplexity || envProviders.muse || envProviders.local || envGoogleConfigured);
|
|
15472
15858
|
const providers = {};
|
|
15473
15859
|
let google;
|
|
15474
15860
|
if (isMachineFormat(format) && !nonInteractive) {
|
|
@@ -15494,6 +15880,7 @@ async function initCommand(opts) {
|
|
|
15494
15880
|
console.log("Configure AI providers (at least one required):\n");
|
|
15495
15881
|
console.log("Tip: For non-interactive setup, pass provider flags or set");
|
|
15496
15882
|
console.log("GEMINI_API_KEY, OPENAI_API_KEY, ANTHROPIC_API_KEY, PERPLEXITY_API_KEY,");
|
|
15883
|
+
console.log("MUSE_API_KEY,");
|
|
15497
15884
|
console.log("GOOGLE_CLIENT_ID, and GOOGLE_CLIENT_SECRET env vars.");
|
|
15498
15885
|
console.log('Or use "canonry bootstrap".\n');
|
|
15499
15886
|
const geminiApiKey = await promptProviderApiKey("Gemini", "GEMINI_API_KEY");
|
|
@@ -15516,6 +15903,11 @@ async function initCommand(opts) {
|
|
|
15516
15903
|
const perplexityModel = await prompt(" Perplexity preset or model [fast]: ") || "fast";
|
|
15517
15904
|
providers.perplexity = { apiKey: perplexityApiKey, model: perplexityModel, quota: DEFAULT_QUOTA };
|
|
15518
15905
|
}
|
|
15906
|
+
const museApiKey = await promptProviderApiKey("Muse", "MUSE_API_KEY");
|
|
15907
|
+
if (museApiKey) {
|
|
15908
|
+
const museModel = await prompt(" Muse model [muse-spark-1.3]: ") || "muse-spark-1.3";
|
|
15909
|
+
providers.muse = { apiKey: museApiKey, model: museModel, quota: DEFAULT_QUOTA };
|
|
15910
|
+
}
|
|
15519
15911
|
console.log("\nLocal LLM (Ollama, LM Studio, llama.cpp, vLLM \u2014 any OpenAI-compatible API):");
|
|
15520
15912
|
const localBaseUrl = await prompt("Local LLM base URL (press Enter to skip, e.g. http://localhost:11434/v1): ");
|
|
15521
15913
|
if (localBaseUrl) {
|
|
@@ -15544,7 +15936,7 @@ async function initCommand(opts) {
|
|
|
15544
15936
|
};
|
|
15545
15937
|
}
|
|
15546
15938
|
}
|
|
15547
|
-
const hasProvider = providers.gemini || providers.openai || providers.claude || providers.perplexity || providers.local;
|
|
15939
|
+
const hasProvider = providers.gemini || providers.openai || providers.claude || providers.perplexity || providers.muse || providers.local;
|
|
15548
15940
|
if (!hasProvider) {
|
|
15549
15941
|
throw new CliError({
|
|
15550
15942
|
code: "INIT_PROVIDER_REQUIRED",
|
|
@@ -16261,7 +16653,7 @@ var SYSTEM_CLI_COMMANDS = [
|
|
|
16261
16653
|
},
|
|
16262
16654
|
{
|
|
16263
16655
|
path: ["init"],
|
|
16264
|
-
usage: "canonry init [--force] [--gemini-key <key>] [--openai-key <key>] [--claude-key <key>] [--perplexity-key <key>] [--local-url <url>] [--local-model <name>] [--local-key <key>] [--google-client-id <id>] [--google-client-secret <key>] [--skip-skills] [--skip-mcp] [--skills-dir <path>] [--format json]",
|
|
16656
|
+
usage: "canonry init [--force] [--gemini-key <key>] [--openai-key <key>] [--claude-key <key>] [--perplexity-key <key>] [--muse-key <key>] [--local-url <url>] [--local-model <name>] [--local-key <key>] [--google-client-id <id>] [--google-client-secret <key>] [--skip-skills] [--skip-mcp] [--skills-dir <path>] [--format json]",
|
|
16265
16657
|
help: 'Optional interactive provider and OAuth setup. First-run Page Health does not need this: use "canonry bootstrap", then "canonry serve". At least one provider is required here. --format json needs provider flags or environment variables.',
|
|
16266
16658
|
options: {
|
|
16267
16659
|
force: { type: "boolean", short: "f", default: false },
|
|
@@ -16269,6 +16661,7 @@ var SYSTEM_CLI_COMMANDS = [
|
|
|
16269
16661
|
"openai-key": stringOption(),
|
|
16270
16662
|
"claude-key": stringOption(),
|
|
16271
16663
|
"perplexity-key": stringOption(),
|
|
16664
|
+
"muse-key": stringOption(),
|
|
16272
16665
|
"local-url": stringOption(),
|
|
16273
16666
|
"local-model": stringOption(),
|
|
16274
16667
|
"local-key": stringOption(),
|
|
@@ -16286,6 +16679,7 @@ var SYSTEM_CLI_COMMANDS = [
|
|
|
16286
16679
|
openaiKey: getString(input.values, "openai-key"),
|
|
16287
16680
|
claudeKey: getString(input.values, "claude-key"),
|
|
16288
16681
|
perplexityKey: getString(input.values, "perplexity-key"),
|
|
16682
|
+
museKey: getString(input.values, "muse-key"),
|
|
16289
16683
|
localUrl: getString(input.values, "local-url"),
|
|
16290
16684
|
localModel: getString(input.values, "local-model"),
|
|
16291
16685
|
localKey: getString(input.values, "local-key"),
|
|
@@ -19786,6 +20180,7 @@ var TELEMETRY_ERROR_CODES = /* @__PURE__ */ new Set([
|
|
|
19786
20180
|
"RUN_FILL_REFUSED",
|
|
19787
20181
|
"RUN_FILL_IN_PROGRESS",
|
|
19788
20182
|
"RUN_FILL_INCOMPLETE",
|
|
20183
|
+
"RUN_FAILED",
|
|
19789
20184
|
"API_ERROR",
|
|
19790
20185
|
"CLI_ERROR",
|
|
19791
20186
|
"CLI_USAGE_ERROR",
|