@canonry/canonry 5.22.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 +100 -5
- 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-teTDC-4-.js → AuditHistoryPanel-CWd9UN6L.js} +1 -1
- package/assets/assets/{BacklinksPage-Bqm7bido.js → BacklinksPage-B2gPTzyE.js} +1 -1
- package/assets/assets/ChartPrimitives-B5okB5g-.js +1 -0
- package/assets/assets/{HistoryPage-IcqwPBmt.js → HistoryPage-xtkG8SZN.js} +1 -1
- package/assets/assets/{MeasurementPropertyPage-BLpptT3_.js → MeasurementPropertyPage-DxIMU3_T.js} +1 -1
- package/assets/assets/{ProjectPage-nn2Y9H-m.js → ProjectPage-CMAFZq_s.js} +5 -5
- package/assets/assets/{RunRow-S_SCHr0o.js → RunRow-DnSo7Nle.js} +1 -1
- package/assets/assets/{RunsPage-DFiif626.js → RunsPage-FAEuo4Ra.js} +1 -1
- package/assets/assets/{SettingsPage-CitZ6FAS.js → SettingsPage-C4OZ1HB9.js} +1 -1
- package/assets/assets/{SiteHealthSection-C2dsyNGY.js → SiteHealthSection-BzMxlz8-.js} +3 -3
- package/assets/assets/{TrafficPage-lY8na5Pg.js → TrafficPage-D03S6xF6.js} +1 -1
- package/assets/assets/{TrafficSourceDetailPage-BTykoVGT.js → TrafficSourceDetailPage-DBu6oFFK.js} +1 -1
- package/assets/assets/{extract-error-message-r0Gpc7mv.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-DDrDxbO1.js → react-sigma_core.esm.min-CxwZsgxR.js} +1 -1
- package/assets/assets/{v2-overview-adapter-OsQiglXU.js → v2-overview-adapter-DTCKxzZT.js} +1 -1
- package/assets/index.html +2 -2
- package/dist/{chunk-P5VXVFQW.js → chunk-7RKCDOWO.js} +6773 -5254
- package/dist/{chunk-YTSXQUJ3.js → chunk-DX2JUC5H.js} +3 -3
- package/dist/{chunk-XFHXKLNT.js → chunk-EP4STEPI.js} +3542 -1165
- package/dist/{chunk-GLLY2GEV.js → chunk-FNGITGDF.js} +172 -7
- package/dist/{chunk-S5OSY3PI.js → chunk-KVQHG3WB.js} +6693 -5891
- package/dist/{chunk-DDL2XSNM.js → chunk-MPICT6V7.js} +128 -35
- package/dist/{chunk-3LCR6WU6.js → chunk-ONTEFAA7.js} +2 -2
- package/dist/cli.js +436 -44
- package/dist/{demo-server-HULNEORN.js → demo-server-HWWIMGD6.js} +4 -4
- package/dist/index.d.ts +9 -1
- package/dist/index.js +6 -6
- package/dist/{intelligence-service-CXNBKRHV.js → intelligence-service-6LDMVI66.js} +2 -2
- package/dist/mcp.js +4 -4
- package/package.json +14 -13
- package/assets/assets/ChartPrimitives-BQC2VH0O.js +0 -1
- package/assets/assets/index-DwlO8KfE.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 = () => {
|
|
@@ -5226,6 +5232,45 @@ async function gbpPlaces(project, opts) {
|
|
|
5226
5232
|
console.log(` ${p.locationName} [${p.tier}] ${amenities}`);
|
|
5227
5233
|
}
|
|
5228
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
|
+
}
|
|
5229
5274
|
function fmtDelta(pct4) {
|
|
5230
5275
|
if (pct4 === null) return "n/a";
|
|
5231
5276
|
return `${pct4 >= 0 ? "+" : ""}${pct4}%`;
|
|
@@ -5446,6 +5491,25 @@ var GBP_CLI_COMMANDS = [
|
|
|
5446
5491
|
await gbpPlaces(project, { location: getString(input.values, "location"), format: input.format });
|
|
5447
5492
|
}
|
|
5448
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
|
+
},
|
|
5449
5513
|
{
|
|
5450
5514
|
path: ["gbp", "summary"],
|
|
5451
5515
|
usage: "canonry gbp summary <project> [--location <name>] [--format json]",
|
|
@@ -7365,9 +7429,47 @@ async function trafficEvents(project, opts) {
|
|
|
7365
7429
|
console.log(` ${formatEventLine(event)}`);
|
|
7366
7430
|
}
|
|
7367
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
|
+
}
|
|
7368
7452
|
|
|
7369
7453
|
// src/cli-commands/traffic.ts
|
|
7370
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
|
+
},
|
|
7371
7473
|
{
|
|
7372
7474
|
path: ["traffic", "connect", "cloudflare"],
|
|
7373
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]",
|
|
@@ -8310,6 +8412,65 @@ async function googleTopPages(project, opts) {
|
|
|
8310
8412
|
console.log(' Adding up the page rows would not give it. Run "canonry google sync" to fetch it.');
|
|
8311
8413
|
}
|
|
8312
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
|
+
}
|
|
8313
8474
|
async function googlePerformance(project, opts) {
|
|
8314
8475
|
const client = getClient11();
|
|
8315
8476
|
const params = {};
|
|
@@ -8892,6 +9053,7 @@ async function googleDeindexed(project, format) {
|
|
|
8892
9053
|
|
|
8893
9054
|
// src/cli-commands/google.ts
|
|
8894
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]";
|
|
8895
9057
|
var GOOGLE_CLI_COMMANDS = [
|
|
8896
9058
|
{
|
|
8897
9059
|
path: ["google", "connect"],
|
|
@@ -9125,6 +9287,36 @@ Usage: ${GOOGLE_PERFORMANCE_USAGE}`,
|
|
|
9125
9287
|
});
|
|
9126
9288
|
}
|
|
9127
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
|
+
},
|
|
9128
9320
|
{
|
|
9129
9321
|
path: ["google", "inspect"],
|
|
9130
9322
|
usage: "canonry google inspect <project> <url> [--format json]",
|
|
@@ -9254,12 +9446,12 @@ Usage: ${GOOGLE_PERFORMANCE_USAGE}`,
|
|
|
9254
9446
|
},
|
|
9255
9447
|
{
|
|
9256
9448
|
path: ["google"],
|
|
9257
|
-
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]",
|
|
9258
9450
|
run: async (input) => {
|
|
9259
9451
|
unknownSubcommand(input.positionals[0], {
|
|
9260
9452
|
command: "google",
|
|
9261
|
-
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]",
|
|
9262
|
-
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"]
|
|
9263
9455
|
});
|
|
9264
9456
|
}
|
|
9265
9457
|
}
|
|
@@ -9889,7 +10081,7 @@ var KEYWORD_CLI_COMMANDS = [
|
|
|
9889
10081
|
const provider = requireStringOption(input, "provider", {
|
|
9890
10082
|
command: "keyword.generate",
|
|
9891
10083
|
usage: "canonry keyword generate <project> --provider <name> [--count <n>] [--save] [--format json]",
|
|
9892
|
-
message: "--provider is required (e.g. gemini, openai, claude, perplexity, local)"
|
|
10084
|
+
message: "--provider is required (e.g. gemini, openai, claude, perplexity, muse, local)"
|
|
9893
10085
|
});
|
|
9894
10086
|
await generateKeywords(project, provider, {
|
|
9895
10087
|
count: parseIntegerOption(input, "count", {
|
|
@@ -10633,7 +10825,7 @@ var QUERY_CLI_COMMANDS = [
|
|
|
10633
10825
|
const provider = requireStringOption(input, "provider", {
|
|
10634
10826
|
command: "query.generate",
|
|
10635
10827
|
usage: "canonry query generate <project> --provider <name> [--count <n>] [--save] [--format json]",
|
|
10636
|
-
message: "--provider is required (e.g. gemini, openai, claude, perplexity, local)"
|
|
10828
|
+
message: "--provider is required (e.g. gemini, openai, claude, perplexity, muse, local)"
|
|
10637
10829
|
});
|
|
10638
10830
|
await generateQueries(project, provider, {
|
|
10639
10831
|
count: parseIntegerOption(input, "count", {
|
|
@@ -11050,7 +11242,9 @@ var EVENT_DESCRIPTIONS = {
|
|
|
11050
11242
|
"insight.critical": "A critical-severity insight was generated",
|
|
11051
11243
|
"insight.high": "A high-severity insight was generated",
|
|
11052
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)",
|
|
11053
|
-
"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)"
|
|
11054
11248
|
};
|
|
11055
11249
|
function listEvents(format, target = "local") {
|
|
11056
11250
|
if (target === "server") return getClient16().listNotificationEvents().then((events) => printEvents(events, format));
|
|
@@ -11956,14 +12150,23 @@ async function createProject(name, opts) {
|
|
|
11956
12150
|
country: opts.country,
|
|
11957
12151
|
language: opts.language,
|
|
11958
12152
|
providers: opts.providers ?? [],
|
|
11959
|
-
providerModels: opts.providerModels ?? {}
|
|
12153
|
+
providerModels: opts.providerModels ?? {},
|
|
12154
|
+
...opts.providerDispatchModes && Object.keys(opts.providerDispatchModes).length > 0 ? { providerDispatchModes: opts.providerDispatchModes } : {}
|
|
11960
12155
|
});
|
|
12156
|
+
warnDroppedDispatchModes(opts.providerDispatchModes ?? {}, result);
|
|
11961
12157
|
if (isMachineFormat(opts.format)) {
|
|
11962
12158
|
console.log(JSON.stringify(result, null, 2));
|
|
11963
12159
|
return;
|
|
11964
12160
|
}
|
|
11965
12161
|
console.log(`Project created: ${result.name} (${result.id})`);
|
|
11966
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
|
+
}
|
|
11967
12170
|
async function listProjects(format) {
|
|
11968
12171
|
const client = getClient22();
|
|
11969
12172
|
const projects2 = await client.listProjects();
|
|
@@ -12022,6 +12225,9 @@ async function showProject(name, format) {
|
|
|
12022
12225
|
console.log(` Providers: ${(project.providers ?? []).length > 0 ? project.providers.join(", ") : "all configured"}`);
|
|
12023
12226
|
const providerModels = Object.entries(project.providerModels ?? {});
|
|
12024
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)" : ""}`);
|
|
12025
12231
|
console.log(` Tags: ${project.tags.length > 0 ? project.tags.join(", ") : "(none)"}`);
|
|
12026
12232
|
const labelEntries = Object.entries(project.labels);
|
|
12027
12233
|
console.log(` Labels: ${labelEntries.length > 0 ? labelEntries.map(([k, v]) => `${k}=${v}`).join(", ") : "(none)"}`);
|
|
@@ -12066,6 +12272,9 @@ async function updateProjectSettings(name, opts) {
|
|
|
12066
12272
|
);
|
|
12067
12273
|
}
|
|
12068
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];
|
|
12069
12278
|
const result = await client.putProject(name, {
|
|
12070
12279
|
displayName: nextDisplayName,
|
|
12071
12280
|
canonicalDomain: opts.domain ?? project.canonicalDomain,
|
|
@@ -12077,10 +12286,15 @@ async function updateProjectSettings(name, opts) {
|
|
|
12077
12286
|
labels: project.labels,
|
|
12078
12287
|
providers: opts.providers ?? project.providers,
|
|
12079
12288
|
providerModels,
|
|
12289
|
+
...touchesDispatch ? { providerDispatchModes } : {},
|
|
12080
12290
|
locations: project.locations,
|
|
12081
12291
|
defaultLocation: project.defaultLocation,
|
|
12082
|
-
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 } : {}
|
|
12083
12296
|
});
|
|
12297
|
+
warnDroppedDispatchModes(touchesDispatch ? providerDispatchModes : project.providerDispatchModes ?? {}, result);
|
|
12084
12298
|
if (isMachineFormat(opts.format)) {
|
|
12085
12299
|
console.log(JSON.stringify(result, null, 2));
|
|
12086
12300
|
return;
|
|
@@ -12193,7 +12407,7 @@ async function setDefaultLocation(project, label, format) {
|
|
|
12193
12407
|
var PROJECT_CLI_COMMANDS = [
|
|
12194
12408
|
{
|
|
12195
12409
|
path: ["project", "create"],
|
|
12196
|
-
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]",
|
|
12197
12411
|
help: "Create a project. Pass --domain for the public site to scan. Provider credentials are not required for Page Health.",
|
|
12198
12412
|
options: {
|
|
12199
12413
|
domain: { type: "string", short: "d" },
|
|
@@ -12203,13 +12417,14 @@ var PROJECT_CLI_COMMANDS = [
|
|
|
12203
12417
|
language: stringOption(),
|
|
12204
12418
|
"display-name": stringOption(),
|
|
12205
12419
|
provider: multiStringOption(),
|
|
12206
|
-
"provider-model": multiStringOption()
|
|
12420
|
+
"provider-model": multiStringOption(),
|
|
12421
|
+
"dispatch-mode": multiStringOption()
|
|
12207
12422
|
},
|
|
12208
12423
|
run: async (input) => {
|
|
12209
12424
|
const name = requireProject(
|
|
12210
12425
|
input,
|
|
12211
12426
|
"project.create",
|
|
12212
|
-
"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]"
|
|
12213
12428
|
);
|
|
12214
12429
|
await createProject(name, {
|
|
12215
12430
|
domain: getString(input.values, "domain") ?? name,
|
|
@@ -12220,13 +12435,14 @@ var PROJECT_CLI_COMMANDS = [
|
|
|
12220
12435
|
displayName: getString(input.values, "display-name") ?? name,
|
|
12221
12436
|
providers: getStringArray(input.values, "provider") ?? [],
|
|
12222
12437
|
providerModels: parseProviderModelAssignments(getStringArray(input.values, "provider-model")),
|
|
12438
|
+
providerDispatchModes: parseDispatchModeAssignments(getStringArray(input.values, "dispatch-mode")),
|
|
12223
12439
|
format: input.format
|
|
12224
12440
|
});
|
|
12225
12441
|
}
|
|
12226
12442
|
},
|
|
12227
12443
|
{
|
|
12228
12444
|
path: ["project", "update"],
|
|
12229
|
-
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]",
|
|
12230
12446
|
options: {
|
|
12231
12447
|
domain: { type: "string", short: "d" },
|
|
12232
12448
|
"owned-domain": multiStringOption(),
|
|
@@ -12241,13 +12457,16 @@ var PROJECT_CLI_COMMANDS = [
|
|
|
12241
12457
|
provider: multiStringOption(),
|
|
12242
12458
|
"all-providers": { type: "boolean" },
|
|
12243
12459
|
"provider-model": multiStringOption(),
|
|
12244
|
-
"clear-provider-model": multiStringOption()
|
|
12460
|
+
"clear-provider-model": multiStringOption(),
|
|
12461
|
+
"dispatch-mode": multiStringOption(),
|
|
12462
|
+
"clear-dispatch-mode": multiStringOption(),
|
|
12463
|
+
"negative-review-max-stars": stringOption()
|
|
12245
12464
|
},
|
|
12246
12465
|
run: async (input) => {
|
|
12247
12466
|
const name = requireProject(
|
|
12248
12467
|
input,
|
|
12249
12468
|
"project.update",
|
|
12250
|
-
"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]"
|
|
12251
12470
|
);
|
|
12252
12471
|
const providers = getStringArray(input.values, "provider");
|
|
12253
12472
|
const allProviders = getBoolean(input.values, "all-providers");
|
|
@@ -12257,6 +12476,11 @@ var PROJECT_CLI_COMMANDS = [
|
|
|
12257
12476
|
for (const provider of clearProviderModels) {
|
|
12258
12477
|
if (provider in providerModels) throw usageError(`Error: --provider-model and --clear-provider-model conflict for ${provider}`);
|
|
12259
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
|
+
}
|
|
12260
12484
|
await updateProjectSettings(name, {
|
|
12261
12485
|
displayName: getString(input.values, "display-name"),
|
|
12262
12486
|
domain: getString(input.values, "domain"),
|
|
@@ -12271,6 +12495,9 @@ var PROJECT_CLI_COMMANDS = [
|
|
|
12271
12495
|
providers: allProviders ? [] : providers,
|
|
12272
12496
|
providerModels,
|
|
12273
12497
|
clearProviderModels,
|
|
12498
|
+
dispatchModes,
|
|
12499
|
+
clearDispatchModes,
|
|
12500
|
+
negativeReviewMaxStars: parseNegativeReviewMaxStars(getString(input.values, "negative-review-max-stars")),
|
|
12274
12501
|
format: input.format
|
|
12275
12502
|
});
|
|
12276
12503
|
}
|
|
@@ -12386,6 +12613,18 @@ var PROJECT_CLI_COMMANDS = [
|
|
|
12386
12613
|
}
|
|
12387
12614
|
}
|
|
12388
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
|
+
}
|
|
12389
12628
|
function parseProviderModelAssignments(assignments) {
|
|
12390
12629
|
const result = {};
|
|
12391
12630
|
for (const assignment of assignments ?? []) {
|
|
@@ -12398,6 +12637,18 @@ function parseProviderModelAssignments(assignments) {
|
|
|
12398
12637
|
}
|
|
12399
12638
|
return result;
|
|
12400
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
|
+
}
|
|
12401
12652
|
|
|
12402
12653
|
// src/commands/report.ts
|
|
12403
12654
|
import fs13 from "fs";
|
|
@@ -12533,6 +12784,27 @@ function getClient23() {
|
|
|
12533
12784
|
return createApiClient();
|
|
12534
12785
|
}
|
|
12535
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
|
+
}
|
|
12536
12808
|
async function triggerRun(project, opts) {
|
|
12537
12809
|
const client = getClient23();
|
|
12538
12810
|
const body = {};
|
|
@@ -12562,19 +12834,22 @@ async function triggerRun(project, opts) {
|
|
|
12562
12834
|
if (opts?.probe) {
|
|
12563
12835
|
body.trigger = "probe";
|
|
12564
12836
|
}
|
|
12837
|
+
if (opts?.dispatchMode) {
|
|
12838
|
+
body.dispatchMode = opts.dispatchMode;
|
|
12839
|
+
}
|
|
12565
12840
|
const response = await client.triggerRun(project, body);
|
|
12566
12841
|
if (Array.isArray(response)) {
|
|
12567
12842
|
const locationRuns = response;
|
|
12568
12843
|
if (isMachineFormat(opts?.format)) {
|
|
12569
12844
|
if (opts?.wait) {
|
|
12570
|
-
const
|
|
12571
|
-
locationRuns.map(async (r) =>
|
|
12572
|
-
if (!r.id || r.status === "conflict") return r;
|
|
12573
|
-
const final = await pollRun2(client, r.id);
|
|
12574
|
-
return { ...r, ...final };
|
|
12575
|
-
})
|
|
12845
|
+
const finals = await Promise.all(
|
|
12846
|
+
locationRuns.map(async (r) => !r.id || r.status === "conflict" ? null : pollRun2(client, r.id, false))
|
|
12576
12847
|
);
|
|
12577
|
-
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
|
+
}));
|
|
12578
12853
|
} else {
|
|
12579
12854
|
console.log(JSON.stringify(locationRuns, null, 2));
|
|
12580
12855
|
}
|
|
@@ -12591,12 +12866,17 @@ async function triggerRun(project, opts) {
|
|
|
12591
12866
|
}
|
|
12592
12867
|
if (opts?.wait) {
|
|
12593
12868
|
const pending = locationRuns.filter((r) => r.id && r.status !== "conflict" && !TERMINAL_STATUSES.has(r.status));
|
|
12869
|
+
const errors = /* @__PURE__ */ new Map();
|
|
12594
12870
|
if (pending.length > 0) {
|
|
12595
12871
|
process.stderr.write(`Waiting for ${pending.length} run(s)`);
|
|
12872
|
+
const batchWaits = /* @__PURE__ */ new Map();
|
|
12596
12873
|
await Promise.all(
|
|
12597
12874
|
pending.map(async (r) => {
|
|
12598
12875
|
const final = await pollRun2(client, r.id);
|
|
12599
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);
|
|
12600
12880
|
})
|
|
12601
12881
|
);
|
|
12602
12882
|
process.stderr.write("\n");
|
|
@@ -12605,20 +12885,31 @@ async function triggerRun(project, opts) {
|
|
|
12605
12885
|
const loc = (r.location ?? "(unknown)").padEnd(15);
|
|
12606
12886
|
console.log(` ${loc} ${r.status}`);
|
|
12607
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
|
+
}
|
|
12608
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) })));
|
|
12609
12895
|
}
|
|
12610
12896
|
return;
|
|
12611
12897
|
}
|
|
12612
12898
|
const run = response;
|
|
12613
12899
|
if (opts?.wait && run.id && !TERMINAL_STATUSES.has(run.status)) {
|
|
12614
|
-
|
|
12615
|
-
|
|
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);
|
|
12616
12903
|
if (isMachineFormat(opts?.format)) {
|
|
12617
12904
|
console.log(JSON.stringify(result, null, 2));
|
|
12618
12905
|
} else {
|
|
12619
12906
|
process.stderr.write("\n");
|
|
12620
12907
|
printRunDetail(result);
|
|
12908
|
+
const waiting = batchWaitLine(result);
|
|
12909
|
+
if (waiting) console.log(`
|
|
12910
|
+
${waiting}`);
|
|
12621
12911
|
}
|
|
12912
|
+
throwIfWaitedRunFailed([{ runId: result.id, status: result.status, project, error: result.error }]);
|
|
12622
12913
|
return;
|
|
12623
12914
|
}
|
|
12624
12915
|
if (opts?.wait && (TERMINAL_STATUSES.has(run.status) || !run.id)) {
|
|
@@ -12628,6 +12919,7 @@ async function triggerRun(project, opts) {
|
|
|
12628
12919
|
} else {
|
|
12629
12920
|
printRunDetail(result);
|
|
12630
12921
|
}
|
|
12922
|
+
throwIfWaitedRunFailed([{ runId: result.id, status: result.status, project, error: result.error }]);
|
|
12631
12923
|
return;
|
|
12632
12924
|
}
|
|
12633
12925
|
if (isMachineFormat(opts?.format)) {
|
|
@@ -12664,6 +12956,9 @@ async function triggerRunAll(opts) {
|
|
|
12664
12956
|
if (opts?.noLocation) {
|
|
12665
12957
|
baseBody.noLocation = true;
|
|
12666
12958
|
}
|
|
12959
|
+
if (opts?.dispatchMode) {
|
|
12960
|
+
baseBody.dispatchMode = opts.dispatchMode;
|
|
12961
|
+
}
|
|
12667
12962
|
const results = [];
|
|
12668
12963
|
for (const p of projects2) {
|
|
12669
12964
|
const body = { ...baseBody };
|
|
@@ -12686,19 +12981,27 @@ async function triggerRunAll(opts) {
|
|
|
12686
12981
|
results.push({ project: p.name, runId: "", status: "error", location: null, error: msg });
|
|
12687
12982
|
}
|
|
12688
12983
|
}
|
|
12984
|
+
const errors = /* @__PURE__ */ new Map();
|
|
12985
|
+
const batchWaits = /* @__PURE__ */ new Map();
|
|
12689
12986
|
if (opts?.wait) {
|
|
12690
12987
|
const pending = results.filter((r) => r.runId && !TERMINAL_STATUSES.has(r.status));
|
|
12691
12988
|
if (pending.length > 0) {
|
|
12692
|
-
|
|
12989
|
+
const showProgress = !isMachineFormat(opts?.format);
|
|
12990
|
+
if (showProgress) process.stderr.write(`Waiting for ${pending.length} run(s)`);
|
|
12693
12991
|
await Promise.all(pending.map(async (r) => {
|
|
12694
|
-
const final = await pollRun2(client, r.runId);
|
|
12992
|
+
const final = await pollRun2(client, r.runId, showProgress);
|
|
12695
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);
|
|
12696
12997
|
}));
|
|
12697
|
-
process.stderr.write("\n");
|
|
12998
|
+
if (showProgress) process.stderr.write("\n");
|
|
12698
12999
|
}
|
|
12699
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) }));
|
|
12700
13002
|
if (isMachineFormat(opts?.format)) {
|
|
12701
13003
|
console.log(JSON.stringify(results, null, 2));
|
|
13004
|
+
if (opts?.wait) throwIfWaitedRunFailed(waited);
|
|
12702
13005
|
return;
|
|
12703
13006
|
}
|
|
12704
13007
|
const showLocationColumn = results.some((r) => r.location !== null);
|
|
@@ -12723,6 +13026,12 @@ async function triggerRunAll(opts) {
|
|
|
12723
13026
|
console.log(` ${proj} ${id} ${r.status}`);
|
|
12724
13027
|
}
|
|
12725
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);
|
|
12726
13035
|
}
|
|
12727
13036
|
async function cancelRun(project, runId, format) {
|
|
12728
13037
|
const client = getClient23();
|
|
@@ -12806,7 +13115,7 @@ async function listRuns(project, opts) {
|
|
|
12806
13115
|
}
|
|
12807
13116
|
}
|
|
12808
13117
|
var POLL_TIMEOUT_MS3 = 10 * 60 * 1e3;
|
|
12809
|
-
async function pollRun2(client, runId) {
|
|
13118
|
+
async function pollRun2(client, runId, showProgress = true) {
|
|
12810
13119
|
const deadline = Date.now() + POLL_TIMEOUT_MS3;
|
|
12811
13120
|
for (; ; ) {
|
|
12812
13121
|
await new Promise((r) => setTimeout(r, 2e3));
|
|
@@ -12814,12 +13123,22 @@ async function pollRun2(client, runId) {
|
|
|
12814
13123
|
throw new Error(`Timed out waiting for run ${runId} after ${POLL_TIMEOUT_MS3 / 1e3}s`);
|
|
12815
13124
|
}
|
|
12816
13125
|
const run = await client.getRun(runId);
|
|
12817
|
-
process.stderr.write(".");
|
|
12818
|
-
if (TERMINAL_STATUSES.has(run.status)) {
|
|
13126
|
+
if (showProgress) process.stderr.write(".");
|
|
13127
|
+
if (TERMINAL_STATUSES.has(run.status) || outstandingBatches(run).length > 0) {
|
|
12819
13128
|
return run;
|
|
12820
13129
|
}
|
|
12821
13130
|
}
|
|
12822
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
|
+
}
|
|
12823
13142
|
function printRunDetail(run) {
|
|
12824
13143
|
console.log(`Run: ${run.id}`);
|
|
12825
13144
|
console.log(` Status: ${run.status}`);
|
|
@@ -12836,6 +13155,13 @@ function printRunDetail(run) {
|
|
|
12836
13155
|
}
|
|
12837
13156
|
}
|
|
12838
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);
|
|
12839
13165
|
if (run.snapshots && run.snapshots.length > 0) {
|
|
12840
13166
|
console.log(`
|
|
12841
13167
|
Snapshots: ${run.snapshots.length} (cell = [citation][mention]; C=cited c=not, M=mentioned m=not, \u2013=no data)`);
|
|
@@ -12847,6 +13173,46 @@ function printRunDetail(run) {
|
|
|
12847
13173
|
}
|
|
12848
13174
|
}
|
|
12849
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
|
+
}
|
|
12850
13216
|
var FILL_POLL_INTERVAL_MS = 3e3;
|
|
12851
13217
|
var FILL_POLL_TIMEOUT_MS = 6 * 60 * 60 * 1e3;
|
|
12852
13218
|
function missingSummary(completeness) {
|
|
@@ -12934,8 +13300,8 @@ async function fillRun(runId, opts = {}) {
|
|
|
12934
13300
|
}
|
|
12935
13301
|
|
|
12936
13302
|
// src/cli-commands/run.ts
|
|
12937
|
-
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]";
|
|
12938
|
-
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]";
|
|
12939
13305
|
var RUN_FILL_USAGE = "canonry run fill <run-id> [--provider <name>[,<name>]] [--dry-run] [--wait] [--format json]";
|
|
12940
13306
|
var RUNS_USAGE = "canonry runs <project> [--limit <n>] [--kind <kind>] [--status <status>] [--format json]";
|
|
12941
13307
|
var RUN_TRIGGER_OPTIONS = {
|
|
@@ -12954,13 +13320,26 @@ var RUN_TRIGGER_OPTIONS = {
|
|
|
12954
13320
|
// for inspection but are excluded from dashboard / analytics /
|
|
12955
13321
|
// intelligence / notifications. Use when you want to verify a
|
|
12956
13322
|
// provider works without polluting the project's metrics.
|
|
12957
|
-
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()
|
|
12958
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
|
+
}
|
|
12959
13337
|
async function triggerRunCommand(input, command) {
|
|
12960
13338
|
const groups = getStringArray(input.values, "group") ?? [];
|
|
12961
13339
|
const targets = getStringArray(input.values, "target") ?? [];
|
|
12962
13340
|
const queries2 = getStringArray(input.values, "query") ?? [];
|
|
12963
13341
|
const usage = command === "run" ? RUN_USAGE2 : RUN_TRIGGER_USAGE;
|
|
13342
|
+
const dispatchMode = parseDispatchMode(getString(input.values, "dispatch-mode"), command, usage);
|
|
12964
13343
|
if (getBoolean(input.values, "all")) {
|
|
12965
13344
|
if (input.positionals.length > 0) {
|
|
12966
13345
|
throw usageError("Error: --all cannot be combined with a project name", {
|
|
@@ -12991,6 +13370,7 @@ async function triggerRunCommand(input, command) {
|
|
|
12991
13370
|
wait: getBoolean(input.values, "wait"),
|
|
12992
13371
|
allLocations: getBoolean(input.values, "all-locations"),
|
|
12993
13372
|
noLocation: getBoolean(input.values, "no-location"),
|
|
13373
|
+
dispatchMode,
|
|
12994
13374
|
format: input.format
|
|
12995
13375
|
});
|
|
12996
13376
|
return;
|
|
@@ -13017,6 +13397,7 @@ async function triggerRunCommand(input, command) {
|
|
|
13017
13397
|
allLocations: getBoolean(input.values, "all-locations"),
|
|
13018
13398
|
noLocation: getBoolean(input.values, "no-location"),
|
|
13019
13399
|
probe: getBoolean(input.values, "probe"),
|
|
13400
|
+
dispatchMode,
|
|
13020
13401
|
format: input.format
|
|
13021
13402
|
});
|
|
13022
13403
|
}
|
|
@@ -13474,7 +13855,7 @@ var SETTINGS_CLI_COMMANDS = [
|
|
|
13474
13855
|
const name = requirePositional(input, 0, {
|
|
13475
13856
|
command: "settings.provider",
|
|
13476
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]",
|
|
13477
|
-
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)"
|
|
13478
13859
|
});
|
|
13479
13860
|
const apiKey = getString(input.values, "api-key");
|
|
13480
13861
|
const baseUrl = getString(input.values, "base-url");
|
|
@@ -15060,6 +15441,7 @@ async function bootstrapCommand(opts) {
|
|
|
15060
15441
|
if (providers.openai) mergedProviders.openai = providers.openai;
|
|
15061
15442
|
if (providers.claude) mergedProviders.claude = providers.claude;
|
|
15062
15443
|
if (providers.perplexity) mergedProviders.perplexity = providers.perplexity;
|
|
15444
|
+
if (providers.muse) mergedProviders.muse = providers.muse;
|
|
15063
15445
|
if (providers.local) mergedProviders.local = providers.local;
|
|
15064
15446
|
if (env.googleClientId && !env.googleClientSecret || !env.googleClientId && env.googleClientSecret) {
|
|
15065
15447
|
console.warn("Warning: GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET must both be set to configure Google OAuth. Skipping Google auth config.");
|
|
@@ -15453,6 +15835,7 @@ async function initCommand(opts) {
|
|
|
15453
15835
|
OPENAI_API_KEY: opts?.openaiKey,
|
|
15454
15836
|
ANTHROPIC_API_KEY: opts?.claudeKey,
|
|
15455
15837
|
PERPLEXITY_API_KEY: opts?.perplexityKey,
|
|
15838
|
+
MUSE_API_KEY: opts?.museKey,
|
|
15456
15839
|
LOCAL_BASE_URL: opts?.localUrl,
|
|
15457
15840
|
LOCAL_MODEL: opts?.localModel,
|
|
15458
15841
|
LOCAL_API_KEY: opts?.localKey,
|
|
@@ -15471,7 +15854,7 @@ async function initCommand(opts) {
|
|
|
15471
15854
|
}
|
|
15472
15855
|
const envProviders = bootstrapEnv.providers;
|
|
15473
15856
|
const envGoogleConfigured = !!(bootstrapEnv.googleClientId && bootstrapEnv.googleClientSecret);
|
|
15474
|
-
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);
|
|
15475
15858
|
const providers = {};
|
|
15476
15859
|
let google;
|
|
15477
15860
|
if (isMachineFormat(format) && !nonInteractive) {
|
|
@@ -15497,6 +15880,7 @@ async function initCommand(opts) {
|
|
|
15497
15880
|
console.log("Configure AI providers (at least one required):\n");
|
|
15498
15881
|
console.log("Tip: For non-interactive setup, pass provider flags or set");
|
|
15499
15882
|
console.log("GEMINI_API_KEY, OPENAI_API_KEY, ANTHROPIC_API_KEY, PERPLEXITY_API_KEY,");
|
|
15883
|
+
console.log("MUSE_API_KEY,");
|
|
15500
15884
|
console.log("GOOGLE_CLIENT_ID, and GOOGLE_CLIENT_SECRET env vars.");
|
|
15501
15885
|
console.log('Or use "canonry bootstrap".\n');
|
|
15502
15886
|
const geminiApiKey = await promptProviderApiKey("Gemini", "GEMINI_API_KEY");
|
|
@@ -15519,6 +15903,11 @@ async function initCommand(opts) {
|
|
|
15519
15903
|
const perplexityModel = await prompt(" Perplexity preset or model [fast]: ") || "fast";
|
|
15520
15904
|
providers.perplexity = { apiKey: perplexityApiKey, model: perplexityModel, quota: DEFAULT_QUOTA };
|
|
15521
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
|
+
}
|
|
15522
15911
|
console.log("\nLocal LLM (Ollama, LM Studio, llama.cpp, vLLM \u2014 any OpenAI-compatible API):");
|
|
15523
15912
|
const localBaseUrl = await prompt("Local LLM base URL (press Enter to skip, e.g. http://localhost:11434/v1): ");
|
|
15524
15913
|
if (localBaseUrl) {
|
|
@@ -15547,7 +15936,7 @@ async function initCommand(opts) {
|
|
|
15547
15936
|
};
|
|
15548
15937
|
}
|
|
15549
15938
|
}
|
|
15550
|
-
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;
|
|
15551
15940
|
if (!hasProvider) {
|
|
15552
15941
|
throw new CliError({
|
|
15553
15942
|
code: "INIT_PROVIDER_REQUIRED",
|
|
@@ -16264,7 +16653,7 @@ var SYSTEM_CLI_COMMANDS = [
|
|
|
16264
16653
|
},
|
|
16265
16654
|
{
|
|
16266
16655
|
path: ["init"],
|
|
16267
|
-
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]",
|
|
16268
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.',
|
|
16269
16658
|
options: {
|
|
16270
16659
|
force: { type: "boolean", short: "f", default: false },
|
|
@@ -16272,6 +16661,7 @@ var SYSTEM_CLI_COMMANDS = [
|
|
|
16272
16661
|
"openai-key": stringOption(),
|
|
16273
16662
|
"claude-key": stringOption(),
|
|
16274
16663
|
"perplexity-key": stringOption(),
|
|
16664
|
+
"muse-key": stringOption(),
|
|
16275
16665
|
"local-url": stringOption(),
|
|
16276
16666
|
"local-model": stringOption(),
|
|
16277
16667
|
"local-key": stringOption(),
|
|
@@ -16289,6 +16679,7 @@ var SYSTEM_CLI_COMMANDS = [
|
|
|
16289
16679
|
openaiKey: getString(input.values, "openai-key"),
|
|
16290
16680
|
claudeKey: getString(input.values, "claude-key"),
|
|
16291
16681
|
perplexityKey: getString(input.values, "perplexity-key"),
|
|
16682
|
+
museKey: getString(input.values, "muse-key"),
|
|
16292
16683
|
localUrl: getString(input.values, "local-url"),
|
|
16293
16684
|
localModel: getString(input.values, "local-model"),
|
|
16294
16685
|
localKey: getString(input.values, "local-key"),
|
|
@@ -19789,6 +20180,7 @@ var TELEMETRY_ERROR_CODES = /* @__PURE__ */ new Set([
|
|
|
19789
20180
|
"RUN_FILL_REFUSED",
|
|
19790
20181
|
"RUN_FILL_IN_PROGRESS",
|
|
19791
20182
|
"RUN_FILL_INCOMPLETE",
|
|
20183
|
+
"RUN_FAILED",
|
|
19792
20184
|
"API_ERROR",
|
|
19793
20185
|
"CLI_ERROR",
|
|
19794
20186
|
"CLI_USAGE_ERROR",
|