@canonry/canonry 4.117.2 → 4.123.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -0
- package/assets/agent-workspace/skills/canonry/SKILL.md +3 -0
- package/assets/agent-workspace/skills/canonry/references/canonry-cli.md +161 -2
- package/assets/assets/AuditHistoryPanel-BVQMDIhQ.js +1 -0
- package/assets/assets/{BacklinksPage-CschVQxC.js → BacklinksPage-nrHT9VMG.js} +1 -1
- package/assets/assets/ChartPrimitives-_8qLsQzI.js +1 -0
- package/assets/assets/HistoryPage-DqsKKdyy.js +1 -0
- package/assets/assets/ProjectPage-GyKoQUv_.js +7 -0
- package/assets/assets/{RunRow-ggNGlwdN.js → RunRow-BRZZcSjQ.js} +1 -1
- package/assets/assets/RunsPage-24aI8DKF.js +1 -0
- package/assets/assets/{SettingsPage-Cy9SlBJX.js → SettingsPage-BaNeTG1i.js} +1 -1
- package/assets/assets/{TrafficPage-1S2PzSHB.js → TrafficPage-ByvcLSjv.js} +1 -1
- package/assets/assets/{TrafficSourceDetailPage-CqXCoaod.js → TrafficSourceDetailPage-B-siTXVg.js} +1 -1
- package/assets/assets/{arrow-left-kCK5M8Bp.js → arrow-left-CBh1cZNH.js} +1 -1
- package/assets/assets/{extract-error-message-C82LAW6q.js → extract-error-message-DJZCPudR.js} +1 -1
- package/assets/assets/index-jymsMBpZ.css +1 -0
- package/assets/assets/index-mzLganDP.js +210 -0
- package/assets/assets/{trash-2-8Pe4v1T0.js → trash-2-Bh-Yvkgc.js} +1 -1
- package/assets/assets/vendor-recharts-DSxTZhaH.js +36 -0
- package/assets/index.html +3 -3
- package/dist/{chunk-BMPIV35C.js → chunk-4AZ7VES6.js} +5726 -697
- package/dist/{chunk-4PMMEFME.js → chunk-HJIIJI7K.js} +816 -18
- package/dist/{chunk-F2SRZJMY.js → chunk-JYBNBK25.js} +627 -22
- package/dist/{chunk-EZME2J2G.js → chunk-MMFSI3OG.js} +3315 -2191
- package/dist/cli.js +861 -109
- package/dist/index.js +4 -4
- package/dist/{intelligence-service-GNOE4J35.js → intelligence-service-K35EN43I.js} +2 -2
- package/dist/mcp.js +4 -4
- package/package.json +12 -12
- package/assets/assets/ChartPrimitives-D4782Ifx.js +0 -1
- package/assets/assets/ProjectPage-BSGTpBUJ.js +0 -7
- package/assets/assets/RunsPage-Bfp0K6Ry.js +0 -1
- package/assets/assets/index-DrDNyUhv.css +0 -1
- package/assets/assets/index-i-t64RXw.js +0 -210
- package/assets/assets/vendor-recharts-C9EZkgbP.js +0 -36
package/dist/cli.js
CHANGED
|
@@ -29,7 +29,7 @@ import {
|
|
|
29
29
|
setTelemetrySource,
|
|
30
30
|
showFirstRunNotice,
|
|
31
31
|
trackEvent
|
|
32
|
-
} from "./chunk-
|
|
32
|
+
} from "./chunk-JYBNBK25.js";
|
|
33
33
|
import {
|
|
34
34
|
CliError,
|
|
35
35
|
EXIT_SYSTEM_ERROR,
|
|
@@ -46,7 +46,7 @@ import {
|
|
|
46
46
|
saveConfig,
|
|
47
47
|
saveConfigPatch,
|
|
48
48
|
usageError
|
|
49
|
-
} from "./chunk-
|
|
49
|
+
} from "./chunk-HJIIJI7K.js";
|
|
50
50
|
import {
|
|
51
51
|
apiKeys,
|
|
52
52
|
createClient,
|
|
@@ -54,8 +54,11 @@ import {
|
|
|
54
54
|
projects,
|
|
55
55
|
queries,
|
|
56
56
|
renderReportHtml
|
|
57
|
-
} from "./chunk-
|
|
57
|
+
} from "./chunk-4AZ7VES6.js";
|
|
58
58
|
import {
|
|
59
|
+
AdsOperationKinds,
|
|
60
|
+
AdsOperationStates,
|
|
61
|
+
AdsOperationStepStates,
|
|
59
62
|
BacklinkSources,
|
|
60
63
|
CcReleaseSyncStatuses,
|
|
61
64
|
CheckScopes,
|
|
@@ -65,7 +68,19 @@ import {
|
|
|
65
68
|
REPORT_PERIOD_OPTIONS,
|
|
66
69
|
RunStatuses,
|
|
67
70
|
TrafficEventKinds,
|
|
71
|
+
adsActivateTreeRequestSchema,
|
|
72
|
+
adsActivationGrantCreateRequestSchema,
|
|
73
|
+
adsAdCreateRequestSchema,
|
|
74
|
+
adsAdGroupCreateRequestSchema,
|
|
75
|
+
adsAdGroupUpdateRequestSchema,
|
|
76
|
+
adsAdUpdateRequestSchema,
|
|
77
|
+
adsCampaignCreateRequestSchema,
|
|
78
|
+
adsCampaignUpdateRequestSchema,
|
|
79
|
+
adsGeoSearchQuerySchema,
|
|
80
|
+
adsImageUploadRequestSchema,
|
|
81
|
+
adsPauseRequestSchema,
|
|
68
82
|
backlinkSourceSchema,
|
|
83
|
+
buildModelChangeNotice,
|
|
69
84
|
discoveryBucketSchema,
|
|
70
85
|
discoveryCompetitorTypeSchema,
|
|
71
86
|
effectiveDomains,
|
|
@@ -80,7 +95,7 @@ import {
|
|
|
80
95
|
providerQuotaPolicySchema,
|
|
81
96
|
resolveProviderInput,
|
|
82
97
|
winnabilityClassSchema
|
|
83
|
-
} from "./chunk-
|
|
98
|
+
} from "./chunk-MMFSI3OG.js";
|
|
84
99
|
|
|
85
100
|
// src/cli.ts
|
|
86
101
|
import { pathToFileURL } from "url";
|
|
@@ -2713,9 +2728,80 @@ var DOCTOR_CLI_COMMANDS = [
|
|
|
2713
2728
|
];
|
|
2714
2729
|
|
|
2715
2730
|
// src/commands/ads.ts
|
|
2731
|
+
import fs3 from "fs";
|
|
2716
2732
|
function getClient5() {
|
|
2717
2733
|
return createApiClient();
|
|
2718
2734
|
}
|
|
2735
|
+
function readRequest(inputPath, schema) {
|
|
2736
|
+
if (!inputPath) {
|
|
2737
|
+
throw new CliError({
|
|
2738
|
+
code: "ADS_INPUT_REQUIRED",
|
|
2739
|
+
message: "A JSON input file is required",
|
|
2740
|
+
displayMessage: "Error: --input <json-file> is required (use --input - for stdin)"
|
|
2741
|
+
});
|
|
2742
|
+
}
|
|
2743
|
+
try {
|
|
2744
|
+
const raw = fs3.readFileSync(inputPath === "-" ? 0 : inputPath, "utf8");
|
|
2745
|
+
return schema.parse(JSON.parse(raw));
|
|
2746
|
+
} catch (err) {
|
|
2747
|
+
if (err instanceof CliError) throw err;
|
|
2748
|
+
throw new CliError({
|
|
2749
|
+
code: "ADS_INPUT_INVALID",
|
|
2750
|
+
message: err instanceof Error ? err.message : String(err),
|
|
2751
|
+
displayMessage: `Error: invalid ads JSON input (${err instanceof Error ? err.message : String(err)})`,
|
|
2752
|
+
details: { inputPath }
|
|
2753
|
+
});
|
|
2754
|
+
}
|
|
2755
|
+
}
|
|
2756
|
+
function printOperationDetails(operation) {
|
|
2757
|
+
console.log(`Operation: ${operation.operationKey}`);
|
|
2758
|
+
if (operation.entityId) console.log(`Entity: ${operation.entityType ?? "unknown"} ${operation.entityId}`);
|
|
2759
|
+
if (operation.upstreamUpdatedAt != null) console.log(`Updated: ${operation.upstreamUpdatedAt}`);
|
|
2760
|
+
if (operation.errorCode) console.log(`Error: ${operation.errorCode}: ${operation.errorMessage ?? ""}`);
|
|
2761
|
+
if (operation.state === AdsOperationStates.pending || operation.state === AdsOperationStates.unknown || operation.state === AdsOperationStates.reconciling) {
|
|
2762
|
+
if (operation.kind === AdsOperationKinds.campaign_tree_activate) {
|
|
2763
|
+
console.log("Do not retry with a new operation key. Resume activation recovery for the original receipt instead.");
|
|
2764
|
+
} else {
|
|
2765
|
+
console.log("Do not retry with a new operation key. Reconcile the original receipt instead.");
|
|
2766
|
+
}
|
|
2767
|
+
}
|
|
2768
|
+
}
|
|
2769
|
+
function printOperation(result, format) {
|
|
2770
|
+
if (isMachineFormat(format)) {
|
|
2771
|
+
console.log(JSON.stringify(result, null, 2));
|
|
2772
|
+
return;
|
|
2773
|
+
}
|
|
2774
|
+
console.log(`${result.replayed ? "Replayed" : "Recorded"} ${result.operation.kind}: ${result.operation.state}`);
|
|
2775
|
+
printOperationDetails(result.operation);
|
|
2776
|
+
}
|
|
2777
|
+
function printReconciliation(result, format) {
|
|
2778
|
+
if (isMachineFormat(format)) {
|
|
2779
|
+
console.log(JSON.stringify(result, null, 2));
|
|
2780
|
+
return;
|
|
2781
|
+
}
|
|
2782
|
+
console.log(`${result.resolved ? "Resolved" : "Still unresolved"} ${result.operation.kind}: ${result.operation.state}`);
|
|
2783
|
+
printOperationDetails(result.operation);
|
|
2784
|
+
}
|
|
2785
|
+
function printActivationGrant(result, verb, format) {
|
|
2786
|
+
if (isMachineFormat(format)) {
|
|
2787
|
+
console.log(JSON.stringify(result, null, 2));
|
|
2788
|
+
return;
|
|
2789
|
+
}
|
|
2790
|
+
const cancellationRequested = verb === "Revoked" && result.grant.revocationRequestedAt !== null;
|
|
2791
|
+
console.log(cancellationRequested ? `Cancellation requested for activation grant ${result.grant.id}: ${result.grant.state}` : `${verb} activation grant ${result.grant.id}: ${result.grant.state}`);
|
|
2792
|
+
if (cancellationRequested) console.log(`Requested: ${result.grant.revocationRequestedAt}`);
|
|
2793
|
+
console.log(`Manifest: ${result.grant.manifestHash}`);
|
|
2794
|
+
console.log(`Expires: ${result.grant.expiresAt}`);
|
|
2795
|
+
}
|
|
2796
|
+
function printActivation(result, format) {
|
|
2797
|
+
if (isMachineFormat(format)) {
|
|
2798
|
+
console.log(JSON.stringify(result, null, 2));
|
|
2799
|
+
return;
|
|
2800
|
+
}
|
|
2801
|
+
const activeSteps = result.steps.filter((step) => step.state === AdsOperationStepStates.active).length;
|
|
2802
|
+
console.log(`Activation ${result.operation.operationKey}: ${result.operation.state}`);
|
|
2803
|
+
console.log(`Steps: ${activeSteps}/${result.steps.length} active`);
|
|
2804
|
+
}
|
|
2719
2805
|
function describeConnection(status) {
|
|
2720
2806
|
const lines = [];
|
|
2721
2807
|
lines.push(`Connected: ${status.connected ? "yes" : "no"}`);
|
|
@@ -2768,6 +2854,199 @@ async function adsStatus(project, opts) {
|
|
|
2768
2854
|
console.log("Connect with: canonry ads connect " + project + " --api-key <sdk-key>");
|
|
2769
2855
|
}
|
|
2770
2856
|
}
|
|
2857
|
+
async function adsAccount(project, opts) {
|
|
2858
|
+
const result = await getClient5().getAdsAccount(project);
|
|
2859
|
+
if (isMachineFormat(opts?.format)) {
|
|
2860
|
+
console.log(JSON.stringify(result, null, 2));
|
|
2861
|
+
return;
|
|
2862
|
+
}
|
|
2863
|
+
console.log(`Account: ${result.name}`);
|
|
2864
|
+
console.log(`ID: ${result.id}`);
|
|
2865
|
+
console.log(`Status: ${result.status}`);
|
|
2866
|
+
console.log(`Currency: ${result.currencyCode ?? "unknown"}`);
|
|
2867
|
+
console.log(`Timezone: ${result.timezone ?? "unknown"}`);
|
|
2868
|
+
console.log(`Review: ${result.reviewStatus ?? "unknown"}`);
|
|
2869
|
+
console.log(`Integrity: ${result.integrityReviewStatus ?? "unknown"}`);
|
|
2870
|
+
if (result.integrityDecision) console.log(`Decision: ${result.integrityDecision}`);
|
|
2871
|
+
if (result.url) console.log(`Ads Manager: ${result.url}`);
|
|
2872
|
+
}
|
|
2873
|
+
async function adsGeoSearch(project, opts) {
|
|
2874
|
+
const parsed = adsGeoSearchQuerySchema.safeParse({ q: opts.q, limit: opts.limit });
|
|
2875
|
+
if (!parsed.success) {
|
|
2876
|
+
throw new CliError({
|
|
2877
|
+
code: "ADS_GEO_QUERY_INVALID",
|
|
2878
|
+
message: "A valid geo search query is required",
|
|
2879
|
+
displayMessage: "Error: --query is required and --limit must be an integer from 1 to 100",
|
|
2880
|
+
details: {
|
|
2881
|
+
project,
|
|
2882
|
+
issues: parsed.error.issues.map((issue) => ({ path: issue.path.join("."), message: issue.message }))
|
|
2883
|
+
}
|
|
2884
|
+
});
|
|
2885
|
+
}
|
|
2886
|
+
const query = parsed.data;
|
|
2887
|
+
const result = await getClient5().searchAdsGeo(project, query);
|
|
2888
|
+
if (opts.format === "jsonl") {
|
|
2889
|
+
emitJsonl(result.results.map((location) => ({ project, query: result.query, ...location })));
|
|
2890
|
+
return;
|
|
2891
|
+
}
|
|
2892
|
+
if (isMachineFormat(opts.format)) {
|
|
2893
|
+
console.log(JSON.stringify(result, null, 2));
|
|
2894
|
+
return;
|
|
2895
|
+
}
|
|
2896
|
+
if (result.results.length === 0) {
|
|
2897
|
+
console.log(`No OpenAI Ads locations matched "${result.query}".`);
|
|
2898
|
+
return;
|
|
2899
|
+
}
|
|
2900
|
+
for (const location of result.results) {
|
|
2901
|
+
const region = location.regionCode ? `, ${location.regionCode}` : "";
|
|
2902
|
+
console.log(`${location.canonicalName} (${location.type}, ${location.countryCode}${region}) [${location.id}]`);
|
|
2903
|
+
}
|
|
2904
|
+
}
|
|
2905
|
+
async function adsConversionPixels(project, opts) {
|
|
2906
|
+
const result = await getClient5().getAdsConversionPixels(project);
|
|
2907
|
+
if (opts?.format === "jsonl") {
|
|
2908
|
+
emitJsonl(result.pixels.map((pixel) => ({ project, ...pixel })));
|
|
2909
|
+
return;
|
|
2910
|
+
}
|
|
2911
|
+
if (isMachineFormat(opts?.format)) {
|
|
2912
|
+
console.log(JSON.stringify(result, null, 2));
|
|
2913
|
+
return;
|
|
2914
|
+
}
|
|
2915
|
+
if (result.pixels.length === 0) {
|
|
2916
|
+
console.log("No OpenAI Ads conversion pixels are configured.");
|
|
2917
|
+
return;
|
|
2918
|
+
}
|
|
2919
|
+
for (const pixel of result.pixels) {
|
|
2920
|
+
const name = pixel.name ?? "Unnamed conversion pixel";
|
|
2921
|
+
const clientType = pixel.clientType ?? "unknown client";
|
|
2922
|
+
const pixelId = pixel.pixelId ? ` pixel ${pixel.pixelId}` : "";
|
|
2923
|
+
console.log(`${name} (${clientType}) [${pixel.id}]${pixelId}`);
|
|
2924
|
+
}
|
|
2925
|
+
}
|
|
2926
|
+
async function adsConversionEventSettings(project, opts) {
|
|
2927
|
+
const result = await getClient5().getAdsConversionEventSettings(project);
|
|
2928
|
+
if (opts?.format === "jsonl") {
|
|
2929
|
+
emitJsonl(result.eventSettings.map((eventSetting) => ({ project, ...eventSetting })));
|
|
2930
|
+
return;
|
|
2931
|
+
}
|
|
2932
|
+
if (isMachineFormat(opts?.format)) {
|
|
2933
|
+
console.log(JSON.stringify(result, null, 2));
|
|
2934
|
+
return;
|
|
2935
|
+
}
|
|
2936
|
+
if (result.eventSettings.length === 0) {
|
|
2937
|
+
console.log("No OpenAI Ads conversion event settings are configured.");
|
|
2938
|
+
return;
|
|
2939
|
+
}
|
|
2940
|
+
for (const eventSetting of result.eventSettings) {
|
|
2941
|
+
const name = eventSetting.name ?? "Unnamed conversion event";
|
|
2942
|
+
const eventType = eventSetting.eventType ?? "unknown event";
|
|
2943
|
+
const attribution = eventSetting.attributionWindowDays === void 0 ? "unknown attribution window" : `${eventSetting.attributionWindowDays}d attribution`;
|
|
2944
|
+
const sources = eventSetting.sources?.map((source) => source.name ?? source.id).join(", ") || "no source details";
|
|
2945
|
+
const archived = eventSetting.archived === true ? " [archived]" : "";
|
|
2946
|
+
console.log(
|
|
2947
|
+
`${name}${archived}: ${eventType}, ${attribution}, ${sources} [${eventSetting.id}]`
|
|
2948
|
+
);
|
|
2949
|
+
}
|
|
2950
|
+
}
|
|
2951
|
+
async function adsOperationGet(project, opts) {
|
|
2952
|
+
printOperation(await getClient5().getAdsOperation(project, opts.operationKey), opts.format);
|
|
2953
|
+
}
|
|
2954
|
+
async function adsOperationsUnresolved(project, opts) {
|
|
2955
|
+
const query = {
|
|
2956
|
+
state: opts?.state,
|
|
2957
|
+
limit: opts?.limit,
|
|
2958
|
+
cursor: opts?.cursor
|
|
2959
|
+
};
|
|
2960
|
+
const client = getClient5();
|
|
2961
|
+
const result = Object.values(query).some((value) => value !== void 0) ? await client.getUnresolvedAdsOperations(project, query) : await client.getUnresolvedAdsOperations(project);
|
|
2962
|
+
if (opts?.format === "jsonl") {
|
|
2963
|
+
emitJsonl(result.operations.map((operation) => ({ project, ...operation })));
|
|
2964
|
+
return;
|
|
2965
|
+
}
|
|
2966
|
+
if (isMachineFormat(opts?.format)) {
|
|
2967
|
+
console.log(JSON.stringify(result, null, 2));
|
|
2968
|
+
return;
|
|
2969
|
+
}
|
|
2970
|
+
if (result.operations.length === 0) {
|
|
2971
|
+
console.log("No unresolved OpenAI Ads mutation receipts.");
|
|
2972
|
+
return;
|
|
2973
|
+
}
|
|
2974
|
+
console.log("STATE KIND OPERATION KEY");
|
|
2975
|
+
for (const operation of result.operations) {
|
|
2976
|
+
console.log(`${operation.state.padEnd(13)} ${operation.kind.padEnd(21)} ${operation.operationKey}`);
|
|
2977
|
+
}
|
|
2978
|
+
if (result.nextCursor) console.log(`Next cursor: ${result.nextCursor}`);
|
|
2979
|
+
}
|
|
2980
|
+
async function adsOperationReconcile(project, opts) {
|
|
2981
|
+
printReconciliation(
|
|
2982
|
+
await getClient5().reconcileAdsOperation(project, opts.operationKey),
|
|
2983
|
+
opts.format
|
|
2984
|
+
);
|
|
2985
|
+
}
|
|
2986
|
+
async function adsOperationResumeActivation(project, opts) {
|
|
2987
|
+
printActivation(
|
|
2988
|
+
await getClient5().resumeAdsActivation(project, opts.operationKey),
|
|
2989
|
+
opts.format
|
|
2990
|
+
);
|
|
2991
|
+
}
|
|
2992
|
+
async function adsActivationGrantCreate(project, opts) {
|
|
2993
|
+
const request = readRequest(
|
|
2994
|
+
opts.input,
|
|
2995
|
+
adsActivationGrantCreateRequestSchema
|
|
2996
|
+
);
|
|
2997
|
+
printActivationGrant(await getClient5().createAdsActivationGrant(project, request), "Approved", opts.format);
|
|
2998
|
+
}
|
|
2999
|
+
async function adsActivationGrantRevoke(project, grantId, opts) {
|
|
3000
|
+
printActivationGrant(
|
|
3001
|
+
await getClient5().revokeAdsActivationGrant(project, grantId),
|
|
3002
|
+
"Revoked",
|
|
3003
|
+
opts?.format
|
|
3004
|
+
);
|
|
3005
|
+
}
|
|
3006
|
+
async function adsImageUpload(project, opts) {
|
|
3007
|
+
const request = readRequest(opts.input, adsImageUploadRequestSchema);
|
|
3008
|
+
printOperation(await getClient5().uploadAdsImage(project, request), opts.format);
|
|
3009
|
+
}
|
|
3010
|
+
async function adsCampaignCreate(project, opts) {
|
|
3011
|
+
const request = readRequest(opts.input, adsCampaignCreateRequestSchema);
|
|
3012
|
+
printOperation(await getClient5().createAdsCampaign(project, request), opts.format);
|
|
3013
|
+
}
|
|
3014
|
+
async function adsCampaignUpdate(project, campaignId, opts) {
|
|
3015
|
+
const request = readRequest(opts.input, adsCampaignUpdateRequestSchema);
|
|
3016
|
+
printOperation(await getClient5().updateAdsCampaign(project, campaignId, request), opts.format);
|
|
3017
|
+
}
|
|
3018
|
+
async function adsCampaignPause(project, campaignId, opts) {
|
|
3019
|
+
const request = readRequest(opts.input, adsPauseRequestSchema);
|
|
3020
|
+
printOperation(await getClient5().pauseAdsCampaign(project, campaignId, request), opts.format);
|
|
3021
|
+
}
|
|
3022
|
+
async function adsCampaignActivateTree(project, campaignId, opts) {
|
|
3023
|
+
const request = readRequest(opts.input, adsActivateTreeRequestSchema);
|
|
3024
|
+
printActivation(await getClient5().activateAdsCampaignTree(project, campaignId, request), opts.format);
|
|
3025
|
+
}
|
|
3026
|
+
async function adsAdGroupCreate(project, opts) {
|
|
3027
|
+
const request = readRequest(opts.input, adsAdGroupCreateRequestSchema);
|
|
3028
|
+
printOperation(await getClient5().createAdsAdGroup(project, request), opts.format);
|
|
3029
|
+
}
|
|
3030
|
+
async function adsAdGroupUpdate(project, adGroupId, opts) {
|
|
3031
|
+
const request = readRequest(opts.input, adsAdGroupUpdateRequestSchema);
|
|
3032
|
+
printOperation(await getClient5().updateAdsAdGroup(project, adGroupId, request), opts.format);
|
|
3033
|
+
}
|
|
3034
|
+
async function adsAdGroupPause(project, adGroupId, opts) {
|
|
3035
|
+
const request = readRequest(opts.input, adsPauseRequestSchema);
|
|
3036
|
+
printOperation(await getClient5().pauseAdsAdGroup(project, adGroupId, request), opts.format);
|
|
3037
|
+
}
|
|
3038
|
+
async function adsAdCreate(project, opts) {
|
|
3039
|
+
const request = readRequest(opts.input, adsAdCreateRequestSchema);
|
|
3040
|
+
printOperation(await getClient5().createAdsAd(project, request), opts.format);
|
|
3041
|
+
}
|
|
3042
|
+
async function adsAdUpdate(project, adId, opts) {
|
|
3043
|
+
const request = readRequest(opts.input, adsAdUpdateRequestSchema);
|
|
3044
|
+
printOperation(await getClient5().updateAdsAd(project, adId, request), opts.format);
|
|
3045
|
+
}
|
|
3046
|
+
async function adsAdPause(project, adId, opts) {
|
|
3047
|
+
const request = readRequest(opts.input, adsPauseRequestSchema);
|
|
3048
|
+
printOperation(await getClient5().pauseAdsAd(project, adId, request), opts.format);
|
|
3049
|
+
}
|
|
2771
3050
|
async function adsSync(project, opts) {
|
|
2772
3051
|
const client = getClient5();
|
|
2773
3052
|
const result = await client.triggerAdsSync(project);
|
|
@@ -2885,6 +3164,285 @@ var ADS_CLI_COMMANDS = [
|
|
|
2885
3164
|
await adsStatus(project, { format: input.format });
|
|
2886
3165
|
}
|
|
2887
3166
|
},
|
|
3167
|
+
{
|
|
3168
|
+
path: ["ads", "account"],
|
|
3169
|
+
usage: "canonry ads account <project> [--format json]",
|
|
3170
|
+
run: async (input) => {
|
|
3171
|
+
const project = requireProject(input, "ads.account", "canonry ads account <project> [--format json]");
|
|
3172
|
+
await adsAccount(project, { format: input.format });
|
|
3173
|
+
}
|
|
3174
|
+
},
|
|
3175
|
+
{
|
|
3176
|
+
path: ["ads", "geo", "search"],
|
|
3177
|
+
usage: "canonry ads geo search <project> --query <text> [--limit <n>] [--format json|jsonl]",
|
|
3178
|
+
options: {
|
|
3179
|
+
query: stringOption(),
|
|
3180
|
+
limit: stringOption()
|
|
3181
|
+
},
|
|
3182
|
+
run: async (input) => {
|
|
3183
|
+
const usage = "canonry ads geo search <project> --query <text> [--limit <n>] [--format json|jsonl]";
|
|
3184
|
+
const project = requireProject(input, "ads.geo.search", usage);
|
|
3185
|
+
await adsGeoSearch(project, {
|
|
3186
|
+
q: getString(input.values, "query"),
|
|
3187
|
+
limit: parseIntegerOption(input, "limit", {
|
|
3188
|
+
command: "ads.geo.search",
|
|
3189
|
+
message: "--limit must be an integer from 1 to 100",
|
|
3190
|
+
usage
|
|
3191
|
+
}),
|
|
3192
|
+
format: input.format
|
|
3193
|
+
});
|
|
3194
|
+
}
|
|
3195
|
+
},
|
|
3196
|
+
{
|
|
3197
|
+
path: ["ads", "conversions", "pixels"],
|
|
3198
|
+
usage: "canonry ads conversions pixels <project> [--format json|jsonl]",
|
|
3199
|
+
run: async (input) => {
|
|
3200
|
+
const project = requireProject(input, "ads.conversions.pixels", "canonry ads conversions pixels <project> [--format json|jsonl]");
|
|
3201
|
+
await adsConversionPixels(project, { format: input.format });
|
|
3202
|
+
}
|
|
3203
|
+
},
|
|
3204
|
+
{
|
|
3205
|
+
path: ["ads", "conversions", "event-settings"],
|
|
3206
|
+
usage: "canonry ads conversions event-settings <project> [--format json|jsonl]",
|
|
3207
|
+
run: async (input) => {
|
|
3208
|
+
const project = requireProject(input, "ads.conversions.event-settings", "canonry ads conversions event-settings <project> [--format json|jsonl]");
|
|
3209
|
+
await adsConversionEventSettings(project, { format: input.format });
|
|
3210
|
+
}
|
|
3211
|
+
},
|
|
3212
|
+
{
|
|
3213
|
+
path: ["ads", "operations", "unresolved"],
|
|
3214
|
+
usage: "canonry ads operations unresolved <project> [--state <csv>] [--limit <n>] [--cursor <opaque>] [--format json|jsonl]",
|
|
3215
|
+
options: {
|
|
3216
|
+
state: stringOption(),
|
|
3217
|
+
limit: stringOption(),
|
|
3218
|
+
cursor: stringOption()
|
|
3219
|
+
},
|
|
3220
|
+
run: async (input) => {
|
|
3221
|
+
const usage = "canonry ads operations unresolved <project> [--state <csv>] [--limit <n>] [--cursor <opaque>] [--format json|jsonl]";
|
|
3222
|
+
const project = requireProject(
|
|
3223
|
+
input,
|
|
3224
|
+
"ads.operations.unresolved",
|
|
3225
|
+
usage
|
|
3226
|
+
);
|
|
3227
|
+
const rawState = getString(input.values, "state");
|
|
3228
|
+
const state = rawState === void 0 ? void 0 : rawState.split(",").filter((value) => value === "pending" || value === "unknown" || value === "reconciling");
|
|
3229
|
+
if (rawState !== void 0 && state?.length !== rawState.split(",").length) {
|
|
3230
|
+
throw usageError(`Error: --state must contain only pending, unknown, or reconciling
|
|
3231
|
+
Usage: ${usage}`, {
|
|
3232
|
+
message: "--state must contain only pending, unknown, or reconciling",
|
|
3233
|
+
details: { command: "ads.operations.unresolved", usage }
|
|
3234
|
+
});
|
|
3235
|
+
}
|
|
3236
|
+
await adsOperationsUnresolved(project, {
|
|
3237
|
+
state,
|
|
3238
|
+
limit: parseIntegerOption(input, "limit", {
|
|
3239
|
+
command: "ads.operations.unresolved",
|
|
3240
|
+
message: "--limit must be an integer from 1 to 200",
|
|
3241
|
+
usage
|
|
3242
|
+
}),
|
|
3243
|
+
cursor: getString(input.values, "cursor"),
|
|
3244
|
+
format: input.format
|
|
3245
|
+
});
|
|
3246
|
+
}
|
|
3247
|
+
},
|
|
3248
|
+
{
|
|
3249
|
+
path: ["ads", "operation"],
|
|
3250
|
+
usage: "canonry ads operation <project> <operation-key> [--format json]",
|
|
3251
|
+
run: async (input) => {
|
|
3252
|
+
const usage = "canonry ads operation <project> <operation-key> [--format json]";
|
|
3253
|
+
const project = requireProject(input, "ads.operation", usage);
|
|
3254
|
+
const operationKey = requirePositional(input, 1, {
|
|
3255
|
+
command: "ads.operation",
|
|
3256
|
+
usage,
|
|
3257
|
+
message: "operation key is required"
|
|
3258
|
+
});
|
|
3259
|
+
await adsOperationGet(project, { operationKey, format: input.format });
|
|
3260
|
+
}
|
|
3261
|
+
},
|
|
3262
|
+
{
|
|
3263
|
+
path: ["ads", "operation", "reconcile"],
|
|
3264
|
+
usage: "canonry ads operation reconcile <project> --operation-key <key> [--format json]",
|
|
3265
|
+
options: {
|
|
3266
|
+
"operation-key": stringOption()
|
|
3267
|
+
},
|
|
3268
|
+
run: async (input) => {
|
|
3269
|
+
const usage = "canonry ads operation reconcile <project> --operation-key <key> [--format json]";
|
|
3270
|
+
const project = requireProject(input, "ads.operation.reconcile", usage);
|
|
3271
|
+
const operationKey = requireStringOption(input, "operation-key", {
|
|
3272
|
+
command: "ads.operation.reconcile",
|
|
3273
|
+
usage,
|
|
3274
|
+
message: "--operation-key is required"
|
|
3275
|
+
});
|
|
3276
|
+
await adsOperationReconcile(project, {
|
|
3277
|
+
operationKey,
|
|
3278
|
+
format: input.format
|
|
3279
|
+
});
|
|
3280
|
+
}
|
|
3281
|
+
},
|
|
3282
|
+
{
|
|
3283
|
+
path: ["ads", "operation", "resume-activation"],
|
|
3284
|
+
usage: "canonry ads operation resume-activation <project> --operation-key <key> [--format json]",
|
|
3285
|
+
options: { "operation-key": stringOption() },
|
|
3286
|
+
run: async (input) => {
|
|
3287
|
+
const usage = "canonry ads operation resume-activation <project> --operation-key <key> [--format json]";
|
|
3288
|
+
const project = requireProject(input, "ads.operation.resume-activation", usage);
|
|
3289
|
+
await adsOperationResumeActivation(project, {
|
|
3290
|
+
operationKey: requireStringOption(input, "operation-key", {
|
|
3291
|
+
command: "ads.operation.resume-activation",
|
|
3292
|
+
message: "--operation-key is required",
|
|
3293
|
+
usage
|
|
3294
|
+
}),
|
|
3295
|
+
format: input.format
|
|
3296
|
+
});
|
|
3297
|
+
}
|
|
3298
|
+
},
|
|
3299
|
+
{
|
|
3300
|
+
path: ["ads", "activation-grant", "create"],
|
|
3301
|
+
usage: "canonry ads activation-grant create <project> --input <json-file|-> [--format json]",
|
|
3302
|
+
options: { input: stringOption() },
|
|
3303
|
+
run: async (input) => {
|
|
3304
|
+
const usage = "canonry ads activation-grant create <project> --input <json-file|-> [--format json]";
|
|
3305
|
+
const project = requireProject(input, "ads.activation-grant.create", usage);
|
|
3306
|
+
await adsActivationGrantCreate(project, {
|
|
3307
|
+
input: getString(input.values, "input"),
|
|
3308
|
+
format: input.format
|
|
3309
|
+
});
|
|
3310
|
+
}
|
|
3311
|
+
},
|
|
3312
|
+
{
|
|
3313
|
+
path: ["ads", "activation-grant", "revoke"],
|
|
3314
|
+
usage: "canonry ads activation-grant revoke <project> <grant-id> [--format json]",
|
|
3315
|
+
run: async (input) => {
|
|
3316
|
+
const usage = "canonry ads activation-grant revoke <project> <grant-id> [--format json]";
|
|
3317
|
+
const project = requireProject(input, "ads.activation-grant.revoke", usage);
|
|
3318
|
+
const grantId = requirePositional(input, 1, {
|
|
3319
|
+
command: "ads.activation-grant.revoke",
|
|
3320
|
+
usage,
|
|
3321
|
+
message: "activation grant id is required"
|
|
3322
|
+
});
|
|
3323
|
+
await adsActivationGrantRevoke(project, grantId, { format: input.format });
|
|
3324
|
+
}
|
|
3325
|
+
},
|
|
3326
|
+
{
|
|
3327
|
+
path: ["ads", "image", "upload"],
|
|
3328
|
+
usage: "canonry ads image upload <project> --input <json-file|-> [--format json]",
|
|
3329
|
+
options: { input: stringOption() },
|
|
3330
|
+
run: async (input) => {
|
|
3331
|
+
const project = requireProject(input, "ads.image.upload", "canonry ads image upload <project> --input <json-file|-> [--format json]");
|
|
3332
|
+
await adsImageUpload(project, { input: getString(input.values, "input"), format: input.format });
|
|
3333
|
+
}
|
|
3334
|
+
},
|
|
3335
|
+
{
|
|
3336
|
+
path: ["ads", "campaign", "create"],
|
|
3337
|
+
usage: "canonry ads campaign create <project> --input <json-file|-> [--format json]",
|
|
3338
|
+
options: { input: stringOption() },
|
|
3339
|
+
run: async (input) => {
|
|
3340
|
+
const project = requireProject(input, "ads.campaign.create", "canonry ads campaign create <project> --input <json-file|-> [--format json]");
|
|
3341
|
+
await adsCampaignCreate(project, { input: getString(input.values, "input"), format: input.format });
|
|
3342
|
+
}
|
|
3343
|
+
},
|
|
3344
|
+
{
|
|
3345
|
+
path: ["ads", "campaign", "update"],
|
|
3346
|
+
usage: "canonry ads campaign update <project> <campaign-id> --input <json-file|-> [--format json]",
|
|
3347
|
+
options: { input: stringOption() },
|
|
3348
|
+
run: async (input) => {
|
|
3349
|
+
const usage = "canonry ads campaign update <project> <campaign-id> --input <json-file|-> [--format json]";
|
|
3350
|
+
const project = requireProject(input, "ads.campaign.update", usage);
|
|
3351
|
+
const id = requirePositional(input, 1, { command: "ads.campaign.update", usage, message: "campaign id is required" });
|
|
3352
|
+
await adsCampaignUpdate(project, id, { input: getString(input.values, "input"), format: input.format });
|
|
3353
|
+
}
|
|
3354
|
+
},
|
|
3355
|
+
{
|
|
3356
|
+
path: ["ads", "campaign", "activate-tree"],
|
|
3357
|
+
usage: "canonry ads campaign activate-tree <project> <campaign-id> --input <json-file|-> [--format json]",
|
|
3358
|
+
options: { input: stringOption() },
|
|
3359
|
+
run: async (input) => {
|
|
3360
|
+
const usage = "canonry ads campaign activate-tree <project> <campaign-id> --input <json-file|-> [--format json]";
|
|
3361
|
+
const project = requireProject(input, "ads.campaign.activate-tree", usage);
|
|
3362
|
+
const id = requirePositional(input, 1, {
|
|
3363
|
+
command: "ads.campaign.activate-tree",
|
|
3364
|
+
usage,
|
|
3365
|
+
message: "campaign id is required"
|
|
3366
|
+
});
|
|
3367
|
+
await adsCampaignActivateTree(project, id, {
|
|
3368
|
+
input: getString(input.values, "input"),
|
|
3369
|
+
format: input.format
|
|
3370
|
+
});
|
|
3371
|
+
}
|
|
3372
|
+
},
|
|
3373
|
+
{
|
|
3374
|
+
path: ["ads", "campaign", "pause"],
|
|
3375
|
+
usage: "canonry ads campaign pause <project> <campaign-id> --input <json-file|-> [--format json]",
|
|
3376
|
+
options: { input: stringOption() },
|
|
3377
|
+
run: async (input) => {
|
|
3378
|
+
const usage = "canonry ads campaign pause <project> <campaign-id> --input <json-file|-> [--format json]";
|
|
3379
|
+
const project = requireProject(input, "ads.campaign.pause", usage);
|
|
3380
|
+
const id = requirePositional(input, 1, { command: "ads.campaign.pause", usage, message: "campaign id is required" });
|
|
3381
|
+
await adsCampaignPause(project, id, { input: getString(input.values, "input"), format: input.format });
|
|
3382
|
+
}
|
|
3383
|
+
},
|
|
3384
|
+
{
|
|
3385
|
+
path: ["ads", "ad-group", "create"],
|
|
3386
|
+
usage: "canonry ads ad-group create <project> --input <json-file|-> [--format json]",
|
|
3387
|
+
options: { input: stringOption() },
|
|
3388
|
+
run: async (input) => {
|
|
3389
|
+
const project = requireProject(input, "ads.ad-group.create", "canonry ads ad-group create <project> --input <json-file|-> [--format json]");
|
|
3390
|
+
await adsAdGroupCreate(project, { input: getString(input.values, "input"), format: input.format });
|
|
3391
|
+
}
|
|
3392
|
+
},
|
|
3393
|
+
{
|
|
3394
|
+
path: ["ads", "ad-group", "update"],
|
|
3395
|
+
usage: "canonry ads ad-group update <project> <ad-group-id> --input <json-file|-> [--format json]",
|
|
3396
|
+
options: { input: stringOption() },
|
|
3397
|
+
run: async (input) => {
|
|
3398
|
+
const usage = "canonry ads ad-group update <project> <ad-group-id> --input <json-file|-> [--format json]";
|
|
3399
|
+
const project = requireProject(input, "ads.ad-group.update", usage);
|
|
3400
|
+
const id = requirePositional(input, 1, { command: "ads.ad-group.update", usage, message: "ad group id is required" });
|
|
3401
|
+
await adsAdGroupUpdate(project, id, { input: getString(input.values, "input"), format: input.format });
|
|
3402
|
+
}
|
|
3403
|
+
},
|
|
3404
|
+
{
|
|
3405
|
+
path: ["ads", "ad-group", "pause"],
|
|
3406
|
+
usage: "canonry ads ad-group pause <project> <ad-group-id> --input <json-file|-> [--format json]",
|
|
3407
|
+
options: { input: stringOption() },
|
|
3408
|
+
run: async (input) => {
|
|
3409
|
+
const usage = "canonry ads ad-group pause <project> <ad-group-id> --input <json-file|-> [--format json]";
|
|
3410
|
+
const project = requireProject(input, "ads.ad-group.pause", usage);
|
|
3411
|
+
const id = requirePositional(input, 1, { command: "ads.ad-group.pause", usage, message: "ad group id is required" });
|
|
3412
|
+
await adsAdGroupPause(project, id, { input: getString(input.values, "input"), format: input.format });
|
|
3413
|
+
}
|
|
3414
|
+
},
|
|
3415
|
+
{
|
|
3416
|
+
path: ["ads", "ad", "create"],
|
|
3417
|
+
usage: "canonry ads ad create <project> --input <json-file|-> [--format json]",
|
|
3418
|
+
options: { input: stringOption() },
|
|
3419
|
+
run: async (input) => {
|
|
3420
|
+
const project = requireProject(input, "ads.ad.create", "canonry ads ad create <project> --input <json-file|-> [--format json]");
|
|
3421
|
+
await adsAdCreate(project, { input: getString(input.values, "input"), format: input.format });
|
|
3422
|
+
}
|
|
3423
|
+
},
|
|
3424
|
+
{
|
|
3425
|
+
path: ["ads", "ad", "update"],
|
|
3426
|
+
usage: "canonry ads ad update <project> <ad-id> --input <json-file|-> [--format json]",
|
|
3427
|
+
options: { input: stringOption() },
|
|
3428
|
+
run: async (input) => {
|
|
3429
|
+
const usage = "canonry ads ad update <project> <ad-id> --input <json-file|-> [--format json]";
|
|
3430
|
+
const project = requireProject(input, "ads.ad.update", usage);
|
|
3431
|
+
const id = requirePositional(input, 1, { command: "ads.ad.update", usage, message: "ad id is required" });
|
|
3432
|
+
await adsAdUpdate(project, id, { input: getString(input.values, "input"), format: input.format });
|
|
3433
|
+
}
|
|
3434
|
+
},
|
|
3435
|
+
{
|
|
3436
|
+
path: ["ads", "ad", "pause"],
|
|
3437
|
+
usage: "canonry ads ad pause <project> <ad-id> --input <json-file|-> [--format json]",
|
|
3438
|
+
options: { input: stringOption() },
|
|
3439
|
+
run: async (input) => {
|
|
3440
|
+
const usage = "canonry ads ad pause <project> <ad-id> --input <json-file|-> [--format json]";
|
|
3441
|
+
const project = requireProject(input, "ads.ad.pause", usage);
|
|
3442
|
+
const id = requirePositional(input, 1, { command: "ads.ad.pause", usage, message: "ad id is required" });
|
|
3443
|
+
await adsAdPause(project, id, { input: getString(input.values, "input"), format: input.format });
|
|
3444
|
+
}
|
|
3445
|
+
},
|
|
2888
3446
|
{
|
|
2889
3447
|
path: ["ads", "sync"],
|
|
2890
3448
|
usage: "canonry ads sync <project> [--format json]",
|
|
@@ -2948,9 +3506,9 @@ async function gaConnect(project, opts) {
|
|
|
2948
3506
|
propertyId: opts.propertyId
|
|
2949
3507
|
};
|
|
2950
3508
|
if (opts.keyFile) {
|
|
2951
|
-
const
|
|
3509
|
+
const fs15 = await import("fs");
|
|
2952
3510
|
try {
|
|
2953
|
-
const content =
|
|
3511
|
+
const content = fs15.readFileSync(opts.keyFile, "utf-8");
|
|
2954
3512
|
JSON.parse(content);
|
|
2955
3513
|
body.keyJson = content;
|
|
2956
3514
|
} catch (e) {
|
|
@@ -4297,9 +4855,9 @@ async function trafficConnectWordpress(project, opts) {
|
|
|
4297
4855
|
}
|
|
4298
4856
|
let applicationPassword = opts.appPassword?.trim() ?? "";
|
|
4299
4857
|
if (!applicationPassword && opts.appPasswordFile) {
|
|
4300
|
-
const
|
|
4858
|
+
const fs15 = await import("fs");
|
|
4301
4859
|
try {
|
|
4302
|
-
applicationPassword =
|
|
4860
|
+
applicationPassword = fs15.readFileSync(opts.appPasswordFile, "utf-8").trim();
|
|
4303
4861
|
} catch (e) {
|
|
4304
4862
|
const msg = e instanceof Error ? e.message : String(e);
|
|
4305
4863
|
throw new CliError({
|
|
@@ -4355,10 +4913,10 @@ async function trafficConnectCloudRun(project, opts) {
|
|
|
4355
4913
|
details: { project }
|
|
4356
4914
|
});
|
|
4357
4915
|
}
|
|
4358
|
-
const
|
|
4916
|
+
const fs15 = await import("fs");
|
|
4359
4917
|
let keyJson;
|
|
4360
4918
|
try {
|
|
4361
|
-
keyJson =
|
|
4919
|
+
keyJson = fs15.readFileSync(opts.serviceAccountKey, "utf-8");
|
|
4362
4920
|
JSON.parse(keyJson);
|
|
4363
4921
|
} catch (e) {
|
|
4364
4922
|
const msg = e instanceof Error ? e.message : String(e);
|
|
@@ -4418,9 +4976,9 @@ async function trafficConnectVercel(project, opts) {
|
|
|
4418
4976
|
}
|
|
4419
4977
|
let token = opts.token?.trim() ?? "";
|
|
4420
4978
|
if (!token && opts.tokenFile) {
|
|
4421
|
-
const
|
|
4979
|
+
const fs15 = await import("fs");
|
|
4422
4980
|
try {
|
|
4423
|
-
token =
|
|
4981
|
+
token = fs15.readFileSync(opts.tokenFile, "utf-8").trim();
|
|
4424
4982
|
} catch (e) {
|
|
4425
4983
|
const msg = e instanceof Error ? e.message : String(e);
|
|
4426
4984
|
throw new CliError({
|
|
@@ -6238,7 +6796,7 @@ var KEYS_CLI_COMMANDS = [
|
|
|
6238
6796
|
];
|
|
6239
6797
|
|
|
6240
6798
|
// src/commands/keyword.ts
|
|
6241
|
-
import
|
|
6799
|
+
import fs4 from "fs";
|
|
6242
6800
|
function getClient12() {
|
|
6243
6801
|
return createApiClient();
|
|
6244
6802
|
}
|
|
@@ -6306,7 +6864,7 @@ async function listKeywords(project, format) {
|
|
|
6306
6864
|
}
|
|
6307
6865
|
}
|
|
6308
6866
|
async function importKeywords(project, filePath, format) {
|
|
6309
|
-
if (!
|
|
6867
|
+
if (!fs4.existsSync(filePath)) {
|
|
6310
6868
|
throw new CliError({
|
|
6311
6869
|
code: "KEYWORD_IMPORT_FILE_NOT_FOUND",
|
|
6312
6870
|
message: `File not found: ${filePath}`,
|
|
@@ -6317,7 +6875,7 @@ async function importKeywords(project, filePath, format) {
|
|
|
6317
6875
|
}
|
|
6318
6876
|
});
|
|
6319
6877
|
}
|
|
6320
|
-
const content =
|
|
6878
|
+
const content = fs4.readFileSync(filePath, "utf-8");
|
|
6321
6879
|
const keywords = content.split("\n").map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#"));
|
|
6322
6880
|
if (keywords.length === 0) {
|
|
6323
6881
|
if (isMachineFormat(format)) {
|
|
@@ -6518,7 +7076,7 @@ var KEYWORD_CLI_COMMANDS = [
|
|
|
6518
7076
|
];
|
|
6519
7077
|
|
|
6520
7078
|
// src/commands/query.ts
|
|
6521
|
-
import
|
|
7079
|
+
import fs5 from "fs";
|
|
6522
7080
|
function getClient13() {
|
|
6523
7081
|
return createApiClient();
|
|
6524
7082
|
}
|
|
@@ -6609,7 +7167,7 @@ async function listQueries(project, format) {
|
|
|
6609
7167
|
}
|
|
6610
7168
|
}
|
|
6611
7169
|
async function importQueries(project, filePath, format) {
|
|
6612
|
-
if (!
|
|
7170
|
+
if (!fs5.existsSync(filePath)) {
|
|
6613
7171
|
throw new CliError({
|
|
6614
7172
|
code: "QUERY_IMPORT_FILE_NOT_FOUND",
|
|
6615
7173
|
message: `File not found: ${filePath}`,
|
|
@@ -6620,7 +7178,7 @@ async function importQueries(project, filePath, format) {
|
|
|
6620
7178
|
}
|
|
6621
7179
|
});
|
|
6622
7180
|
}
|
|
6623
|
-
const content =
|
|
7181
|
+
const content = fs5.readFileSync(filePath, "utf-8");
|
|
6624
7182
|
const queries2 = content.split("\n").map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#"));
|
|
6625
7183
|
if (queries2.length === 0) {
|
|
6626
7184
|
if (isMachineFormat(format)) {
|
|
@@ -6822,7 +7380,7 @@ var QUERY_CLI_COMMANDS = [
|
|
|
6822
7380
|
];
|
|
6823
7381
|
|
|
6824
7382
|
// src/commands/mcp.ts
|
|
6825
|
-
import
|
|
7383
|
+
import fs6 from "fs";
|
|
6826
7384
|
import path3 from "path";
|
|
6827
7385
|
import { createRequire } from "module";
|
|
6828
7386
|
|
|
@@ -6938,8 +7496,8 @@ function renderClientSnippet(client, serverName, entry) {
|
|
|
6938
7496
|
return renderJsonSnippet(serverName, entry, client.format);
|
|
6939
7497
|
}
|
|
6940
7498
|
function readJsonConfig(configPath) {
|
|
6941
|
-
if (!
|
|
6942
|
-
const raw =
|
|
7499
|
+
if (!fs6.existsSync(configPath)) return {};
|
|
7500
|
+
const raw = fs6.readFileSync(configPath, "utf-8").trim();
|
|
6943
7501
|
if (!raw) return {};
|
|
6944
7502
|
try {
|
|
6945
7503
|
const parsed = JSON.parse(raw);
|
|
@@ -6957,14 +7515,14 @@ function readJsonConfig(configPath) {
|
|
|
6957
7515
|
}
|
|
6958
7516
|
}
|
|
6959
7517
|
function writeJsonConfig(configPath, value) {
|
|
6960
|
-
|
|
6961
|
-
|
|
7518
|
+
fs6.mkdirSync(path3.dirname(configPath), { recursive: true });
|
|
7519
|
+
fs6.writeFileSync(configPath, `${JSON.stringify(value, null, 2)}
|
|
6962
7520
|
`, "utf-8");
|
|
6963
7521
|
}
|
|
6964
7522
|
function backupConfigIfPresent(configPath) {
|
|
6965
|
-
if (!
|
|
7523
|
+
if (!fs6.existsSync(configPath)) return void 0;
|
|
6966
7524
|
const backupPath = `${configPath}.canonry.bak`;
|
|
6967
|
-
|
|
7525
|
+
fs6.copyFileSync(configPath, backupPath);
|
|
6968
7526
|
return backupPath;
|
|
6969
7527
|
}
|
|
6970
7528
|
function findClientOrThrow(id) {
|
|
@@ -7318,13 +7876,13 @@ var NOTIFY_CLI_COMMANDS = [
|
|
|
7318
7876
|
];
|
|
7319
7877
|
|
|
7320
7878
|
// src/commands/apply.ts
|
|
7321
|
-
import
|
|
7879
|
+
import fs7 from "fs";
|
|
7322
7880
|
import { parseAllDocuments } from "yaml";
|
|
7323
7881
|
async function applyConfigFile(filePath) {
|
|
7324
|
-
if (!
|
|
7882
|
+
if (!fs7.existsSync(filePath)) {
|
|
7325
7883
|
throw new Error(`File not found: ${filePath}`);
|
|
7326
7884
|
}
|
|
7327
|
-
const content =
|
|
7885
|
+
const content = fs7.readFileSync(filePath, "utf-8");
|
|
7328
7886
|
const docs = parseAllDocuments(content);
|
|
7329
7887
|
const client = createApiClient();
|
|
7330
7888
|
const errors = [];
|
|
@@ -7430,11 +7988,29 @@ async function showAnalytics(project, options) {
|
|
|
7430
7988
|
console.log(JSON.stringify(results, null, 2));
|
|
7431
7989
|
}
|
|
7432
7990
|
}
|
|
7991
|
+
var BUCKET_DATE_WIDTH = 35;
|
|
7992
|
+
function bucketDates(bucket) {
|
|
7993
|
+
const legacyCompatible = bucket;
|
|
7994
|
+
const start = legacyCompatible.dataStartDate?.slice(0, 10);
|
|
7995
|
+
const end = legacyCompatible.dataEndDate?.slice(0, 10);
|
|
7996
|
+
if (!start || !end) return "date unavailable";
|
|
7997
|
+
const sweeps = legacyCompatible.sweepCount ?? 1;
|
|
7998
|
+
const pooled = sweeps > 1 ? ` (${sweeps} sweeps)` : "";
|
|
7999
|
+
if (start === end) return `${start}${pooled}`;
|
|
8000
|
+
return `${start} \u2192 ${end}${pooled}`;
|
|
8001
|
+
}
|
|
7433
8002
|
function printMetrics(data) {
|
|
7434
8003
|
console.log(`
|
|
7435
8004
|
Citation Rate Trends (${data.window})`);
|
|
7436
8005
|
console.log("\u2500".repeat(50));
|
|
7437
8006
|
const pct4 = (n) => `${(n * 100).toFixed(1)}%`;
|
|
8007
|
+
const modelChangeNotice = buildModelChangeNotice(readModelPointerChanges(data));
|
|
8008
|
+
if (modelChangeNotice?.kind === "change") {
|
|
8009
|
+
console.log(`
|
|
8010
|
+
Model Updates Behind These Numbers:`);
|
|
8011
|
+
console.log(` ${modelChangeNotice.text}`);
|
|
8012
|
+
console.log("");
|
|
8013
|
+
}
|
|
7438
8014
|
console.log(` Overall: ${pct4(data.overall.citationRate)} (${data.overall.cited}/${data.overall.total})`);
|
|
7439
8015
|
console.log(` Trend: ${data.trend}`);
|
|
7440
8016
|
if (Object.keys(data.byProvider).length > 0) {
|
|
@@ -7446,28 +8022,95 @@ Citation Rate Trends (${data.window})`);
|
|
|
7446
8022
|
}
|
|
7447
8023
|
if (data.buckets.length > 0) {
|
|
7448
8024
|
console.log(`
|
|
7449
|
-
Timeline:`);
|
|
8025
|
+
Timeline (dates in UTC):`);
|
|
7450
8026
|
for (const bucket of data.buckets) {
|
|
7451
|
-
const start = bucket.startDate.slice(0, 10);
|
|
7452
8027
|
const bar = bucket.total > 0 ? "\u2588".repeat(Math.round(bucket.citationRate * 20)) : "";
|
|
7453
|
-
console.log(` ${
|
|
8028
|
+
console.log(` ${bucketDates(bucket).padEnd(BUCKET_DATE_WIDTH)} ${pct4(bucket.citationRate).padStart(6)} ${bar}`);
|
|
7454
8029
|
}
|
|
7455
8030
|
}
|
|
7456
8031
|
const providersInBuckets = [...new Set(data.buckets.flatMap((b) => Object.keys(b.byProvider ?? {})))].sort();
|
|
7457
8032
|
if (data.buckets.length > 0 && providersInBuckets.length > 0) {
|
|
7458
8033
|
console.log(`
|
|
7459
|
-
By Provider Timeline:`);
|
|
8034
|
+
By Provider Timeline (dates in UTC):`);
|
|
7460
8035
|
for (const provider of providersInBuckets) {
|
|
7461
8036
|
console.log(` ${provider}:`);
|
|
7462
8037
|
for (const bucket of data.buckets) {
|
|
7463
8038
|
const metric = bucket.byProvider?.[provider];
|
|
7464
8039
|
if (!metric) continue;
|
|
7465
|
-
const start = bucket.startDate.slice(0, 10);
|
|
7466
8040
|
const bar = metric.total > 0 ? "\u2588".repeat(Math.round(metric.citationRate * 20)) : "";
|
|
7467
|
-
console.log(` ${
|
|
8041
|
+
console.log(` ${bucketDates(bucket).padEnd(BUCKET_DATE_WIDTH)} ${pct4(metric.citationRate).padStart(6)} ${bar}`);
|
|
8042
|
+
}
|
|
8043
|
+
}
|
|
8044
|
+
}
|
|
8045
|
+
if (modelChangeNotice?.kind === "no-known-change") {
|
|
8046
|
+
console.log(`
|
|
8047
|
+
Model Updates Behind These Numbers:`);
|
|
8048
|
+
console.log(` ${modelChangeNotice.text}`);
|
|
8049
|
+
console.log(` ${modelChangeNotice.detail}`);
|
|
8050
|
+
}
|
|
8051
|
+
const attributionEntries = Object.entries(readModelAttribution(data)).sort(([a], [b]) => a.localeCompare(b));
|
|
8052
|
+
if (attributionEntries.length > 0) {
|
|
8053
|
+
console.log(`
|
|
8054
|
+
Model Evidence:`);
|
|
8055
|
+
for (const [provider, attribution] of attributionEntries) {
|
|
8056
|
+
const latest = attribution.latestObservation;
|
|
8057
|
+
console.log(` ${provider}: latest ${formatModelEvidence(latest.state)} at ${latest.observedAt}`);
|
|
8058
|
+
for (const event of attribution.events) {
|
|
8059
|
+
const dating = event.fromPreWindowAnchor ? " (on or before)" : "";
|
|
8060
|
+
const priorSweep = event.fromPreWindowAnchor && event.anchorObservedAt ? ` [last seen ${formatModelEvidence(event.from)} on ${event.anchorObservedAt}]` : "";
|
|
8061
|
+
console.log(` ${event.observedAt}${dating} ${formatModelEvidence(event.from)} \u2192 ${formatModelEvidence(event.to)}${priorSweep}`);
|
|
8062
|
+
}
|
|
8063
|
+
const eventTotal = attribution.eventTotal ?? attribution.events.length;
|
|
8064
|
+
if (eventTotal > attribution.events.length) {
|
|
8065
|
+
console.log(` Showing the latest ${attribution.events.length} of ${eventTotal} model changes.`);
|
|
8066
|
+
}
|
|
8067
|
+
if (attribution.anchorUnavailable) {
|
|
8068
|
+
console.log(` We did not look far enough back to be sure this is every change.`);
|
|
7468
8069
|
}
|
|
7469
8070
|
}
|
|
7470
8071
|
}
|
|
8072
|
+
const servedEntries = Object.entries(readServedModelAttribution(data)).sort(([a], [b]) => a.localeCompare(b));
|
|
8073
|
+
if (servedEntries.length > 0) {
|
|
8074
|
+
const mismatch = readModelServiceMismatch(data);
|
|
8075
|
+
console.log(`
|
|
8076
|
+
What the Engines Answered With:`);
|
|
8077
|
+
for (const [provider, served] of servedEntries) {
|
|
8078
|
+
const rawIds = served.latestServedModelIds.length > 0 ? served.latestServedModelIds.join(", ") : formatModelEvidence(served.latestObservation.state);
|
|
8079
|
+
const substituted = mismatch[provider];
|
|
8080
|
+
const note = substituted ? ` \u2014 not the ${formatModelEvidence(substituted.configured)} you selected` : "";
|
|
8081
|
+
console.log(` ${provider}: ${rawIds} at ${served.latestObservation.observedAt}${note}`);
|
|
8082
|
+
for (const event of served.events) {
|
|
8083
|
+
const dating = event.fromPreWindowAnchor ? " (on or before)" : "";
|
|
8084
|
+
console.log(` ${event.observedAt}${dating} ${formatModelEvidence(event.from)} \u2192 ${formatModelEvidence(event.to)}`);
|
|
8085
|
+
}
|
|
8086
|
+
}
|
|
8087
|
+
}
|
|
8088
|
+
}
|
|
8089
|
+
function readServedModelAttribution(data) {
|
|
8090
|
+
const legacyCompatible = data;
|
|
8091
|
+
return legacyCompatible.servedModelAttribution ?? {};
|
|
8092
|
+
}
|
|
8093
|
+
function readModelServiceMismatch(data) {
|
|
8094
|
+
const legacyCompatible = data;
|
|
8095
|
+
return legacyCompatible.modelServiceMismatch ?? {};
|
|
8096
|
+
}
|
|
8097
|
+
function readModelPointerChanges(data) {
|
|
8098
|
+
const legacyCompatible = data;
|
|
8099
|
+
return legacyCompatible.modelPointerChanges ?? {};
|
|
8100
|
+
}
|
|
8101
|
+
function readModelAttribution(data) {
|
|
8102
|
+
const legacyCompatible = data;
|
|
8103
|
+
return legacyCompatible.modelAttribution ?? {};
|
|
8104
|
+
}
|
|
8105
|
+
function formatModelEvidence(state) {
|
|
8106
|
+
switch (state.status) {
|
|
8107
|
+
case "known":
|
|
8108
|
+
return `known ${state.model}`;
|
|
8109
|
+
case "unknown":
|
|
8110
|
+
return "unknown";
|
|
8111
|
+
case "mixed":
|
|
8112
|
+
return `mixed ${state.models.join(", ")}${state.includesUnknown ? " + unknown" : ""}`;
|
|
8113
|
+
}
|
|
7471
8114
|
}
|
|
7472
8115
|
function printGaps(data) {
|
|
7473
8116
|
console.log(`
|
|
@@ -7642,7 +8285,7 @@ async function loadLatestRunForExport(client, project) {
|
|
|
7642
8285
|
}
|
|
7643
8286
|
|
|
7644
8287
|
// src/commands/results-export.ts
|
|
7645
|
-
import
|
|
8288
|
+
import fs8 from "fs";
|
|
7646
8289
|
import path4 from "path";
|
|
7647
8290
|
async function exportResults(project, opts) {
|
|
7648
8291
|
const { output, ...request } = opts;
|
|
@@ -7652,8 +8295,8 @@ async function exportResults(project, opts) {
|
|
|
7652
8295
|
return;
|
|
7653
8296
|
}
|
|
7654
8297
|
const target = output ? path4.resolve(output) : path4.resolve(process.cwd(), path4.basename(artifact.filename));
|
|
7655
|
-
|
|
7656
|
-
|
|
8298
|
+
fs8.mkdirSync(path4.dirname(target), { recursive: true });
|
|
8299
|
+
fs8.writeFileSync(target, artifact.content, "utf8");
|
|
7657
8300
|
console.log(`Results export written to ${target}`);
|
|
7658
8301
|
}
|
|
7659
8302
|
|
|
@@ -7661,39 +8304,48 @@ async function exportResults(project, opts) {
|
|
|
7661
8304
|
function getClient18() {
|
|
7662
8305
|
return createApiClient();
|
|
7663
8306
|
}
|
|
7664
|
-
|
|
8307
|
+
function originLabel(entry) {
|
|
8308
|
+
const userAgent = entry.userAgent?.toLowerCase() ?? "";
|
|
8309
|
+
if (userAgent.includes("canonry-mcp")) return "mcp";
|
|
8310
|
+
if (userAgent.includes("canonry-cli")) return "cli";
|
|
8311
|
+
if (userAgent.includes("mozilla/")) return "dashboard";
|
|
8312
|
+
return entry.actor;
|
|
8313
|
+
}
|
|
8314
|
+
async function showHistory(project, format, opts = {}) {
|
|
7665
8315
|
const client = getClient18();
|
|
7666
8316
|
try {
|
|
7667
|
-
const entries = await client.getHistory(project);
|
|
8317
|
+
const entries = project ? await client.getHistory(project, opts) : await client.getGlobalHistory(opts);
|
|
7668
8318
|
if (format === "json") {
|
|
7669
8319
|
console.log(JSON.stringify(entries, null, 2));
|
|
7670
8320
|
return;
|
|
7671
8321
|
}
|
|
7672
8322
|
if (format === "jsonl") {
|
|
7673
|
-
emitJsonl(entries.map((entry) => ({ project, ...entry })));
|
|
8323
|
+
emitJsonl(entries.map((entry) => ({ project: project ?? null, ...entry })));
|
|
7674
8324
|
return;
|
|
7675
8325
|
}
|
|
7676
8326
|
if (entries.length === 0) {
|
|
7677
|
-
console.log(`No audit history for "${project}".`);
|
|
8327
|
+
console.log(project ? `No audit history for "${project}".` : "No instance audit history.");
|
|
7678
8328
|
return;
|
|
7679
8329
|
}
|
|
7680
|
-
console.log(`Audit history for "${project}" (${entries.length}):
|
|
8330
|
+
console.log(`${project ? `Audit history for "${project}"` : "Instance audit history"} (${entries.length}):
|
|
7681
8331
|
`);
|
|
7682
|
-
console.log(" TIMESTAMP ACTION ENTITY TYPE
|
|
7683
|
-
console.log(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 \u2500\u2500\u2500\u2500\u2500");
|
|
8332
|
+
console.log(" TIMESTAMP ACTION ENTITY TYPE ORIGIN");
|
|
8333
|
+
console.log(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
|
|
7684
8334
|
for (const entry of entries) {
|
|
7685
8335
|
console.log(
|
|
7686
|
-
` ${entry.createdAt.padEnd(23)} ${entry.action.padEnd(18)} ${entry.entityType.padEnd(11)} ${entry
|
|
8336
|
+
` ${entry.createdAt.padEnd(23)} ${entry.action.padEnd(18)} ${entry.entityType.padEnd(11)} ${originLabel(entry)}`
|
|
7687
8337
|
);
|
|
8338
|
+
if (entry.actorSession) console.log(` session: ${entry.actorSession}`);
|
|
8339
|
+
if (entry.diff != null) console.log(` diff: ${JSON.stringify(entry.diff)}`);
|
|
7688
8340
|
}
|
|
7689
8341
|
} catch (err) {
|
|
7690
8342
|
const message = err instanceof Error ? err.message : String(err);
|
|
7691
8343
|
throw new CliError({
|
|
7692
8344
|
code: "HISTORY_FETCH_FAILED",
|
|
7693
|
-
message: `Failed to fetch history for project "${project}"
|
|
8345
|
+
message: project ? `Failed to fetch history for project "${project}"` : "Failed to fetch instance history",
|
|
7694
8346
|
displayMessage: `Failed to fetch history: ${message}`,
|
|
7695
8347
|
details: {
|
|
7696
|
-
project,
|
|
8348
|
+
project: project ?? null,
|
|
7697
8349
|
cause: message
|
|
7698
8350
|
}
|
|
7699
8351
|
});
|
|
@@ -7809,10 +8461,25 @@ var OPERATOR_CLI_COMMANDS = [
|
|
|
7809
8461
|
},
|
|
7810
8462
|
{
|
|
7811
8463
|
path: ["history"],
|
|
7812
|
-
usage: "canonry history <project> [--format json]",
|
|
8464
|
+
usage: "canonry history <project> [--limit <n>] [--since <ISO>] [--action <action>] [--actor <actor>] [--entity-type <type>] [--format json|jsonl]\n canonry history --all [same filters]",
|
|
8465
|
+
options: {
|
|
8466
|
+
all: { type: "boolean", default: false },
|
|
8467
|
+
limit: stringOption(),
|
|
8468
|
+
since: stringOption(),
|
|
8469
|
+
action: stringOption(),
|
|
8470
|
+
actor: stringOption(),
|
|
8471
|
+
"entity-type": stringOption()
|
|
8472
|
+
},
|
|
7813
8473
|
run: async (input) => {
|
|
7814
|
-
const
|
|
7815
|
-
|
|
8474
|
+
const usage = "canonry history <project> [--limit <n>] [--since <ISO>] [--action <action>] [--actor <actor>] [--entity-type <type>] [--format json|jsonl]\n canonry history --all [same filters]";
|
|
8475
|
+
const project = getBoolean(input.values, "all") ? void 0 : requireProject(input, "history", usage);
|
|
8476
|
+
await showHistory(project, input.format, {
|
|
8477
|
+
limit: parseIntegerOption(input, "limit", { command: "history", usage, message: "--limit must be an integer" }),
|
|
8478
|
+
since: getString(input.values, "since"),
|
|
8479
|
+
action: getString(input.values, "action"),
|
|
8480
|
+
actor: getString(input.values, "actor"),
|
|
8481
|
+
entityType: getString(input.values, "entity-type")
|
|
8482
|
+
});
|
|
7816
8483
|
}
|
|
7817
8484
|
},
|
|
7818
8485
|
{
|
|
@@ -7900,7 +8567,9 @@ async function createProject(name, opts) {
|
|
|
7900
8567
|
ownedDomains: opts.ownedDomains ?? [],
|
|
7901
8568
|
aliases: normalizeProjectAliases(opts.displayName, opts.aliases ?? []),
|
|
7902
8569
|
country: opts.country,
|
|
7903
|
-
language: opts.language
|
|
8570
|
+
language: opts.language,
|
|
8571
|
+
providers: opts.providers ?? [],
|
|
8572
|
+
providerModels: opts.providerModels ?? {}
|
|
7904
8573
|
});
|
|
7905
8574
|
if (isMachineFormat(opts.format)) {
|
|
7906
8575
|
console.log(JSON.stringify(result, null, 2));
|
|
@@ -7963,6 +8632,9 @@ async function showProject(name, format) {
|
|
|
7963
8632
|
console.log(` Language: ${project.language}`);
|
|
7964
8633
|
console.log(` Config source: ${project.configSource}`);
|
|
7965
8634
|
console.log(` Config revision: ${project.configRevision}`);
|
|
8635
|
+
console.log(` Providers: ${(project.providers ?? []).length > 0 ? project.providers.join(", ") : "all configured"}`);
|
|
8636
|
+
const providerModels = Object.entries(project.providerModels ?? {});
|
|
8637
|
+
console.log(` Model overrides: ${providerModels.length > 0 ? providerModels.map(([provider, model]) => `${provider}=${model}`).join(", ") : "(none; instance settings inherited)"}`);
|
|
7966
8638
|
console.log(` Tags: ${project.tags.length > 0 ? project.tags.join(", ") : "(none)"}`);
|
|
7967
8639
|
const labelEntries = Object.entries(project.labels);
|
|
7968
8640
|
console.log(` Labels: ${labelEntries.length > 0 ? labelEntries.map(([k, v]) => `${k}=${v}`).join(", ") : "(none)"}`);
|
|
@@ -7992,19 +8664,45 @@ async function updateProjectSettings(name, opts) {
|
|
|
7992
8664
|
const toRemove = new Set(opts.removeAlias.map((a) => a.toLowerCase()));
|
|
7993
8665
|
aliases = aliases.filter((a) => !toRemove.has(a.toLowerCase()));
|
|
7994
8666
|
}
|
|
8667
|
+
const providerModels = { ...project.providerModels ?? {}, ...opts.providerModels ?? {} };
|
|
8668
|
+
for (const provider of opts.clearProviderModels ?? []) delete providerModels[provider];
|
|
8669
|
+
const nextProviders = opts.providers ?? project.providers ?? [];
|
|
8670
|
+
if (nextProviders.length > 0) {
|
|
8671
|
+
const requested = Object.keys(opts.providerModels ?? {}).filter((p) => !nextProviders.includes(p));
|
|
8672
|
+
if (requested.length > 0) {
|
|
8673
|
+
throw usageError(
|
|
8674
|
+
`Error: --provider-model set for ${requested.join(", ")}, which ${requested.length > 1 ? "are" : "is"} not a selected engine for "${name}"`,
|
|
8675
|
+
{
|
|
8676
|
+
message: "provider-model set for a provider the project does not run",
|
|
8677
|
+
details: { command: "project.update", project: name, providers: nextProviders, unselected: requested }
|
|
8678
|
+
}
|
|
8679
|
+
);
|
|
8680
|
+
}
|
|
8681
|
+
}
|
|
7995
8682
|
const result = await client.putProject(name, {
|
|
7996
8683
|
displayName: nextDisplayName,
|
|
7997
8684
|
canonicalDomain: opts.domain ?? project.canonicalDomain,
|
|
7998
8685
|
ownedDomains,
|
|
7999
8686
|
aliases: normalizeProjectAliases(nextDisplayName, aliases),
|
|
8000
8687
|
country: opts.country ?? project.country,
|
|
8001
|
-
language: opts.language ?? project.language
|
|
8688
|
+
language: opts.language ?? project.language,
|
|
8689
|
+
tags: project.tags,
|
|
8690
|
+
labels: project.labels,
|
|
8691
|
+
providers: opts.providers ?? project.providers,
|
|
8692
|
+
providerModels,
|
|
8693
|
+
locations: project.locations,
|
|
8694
|
+
defaultLocation: project.defaultLocation,
|
|
8695
|
+
autoExtractBacklinks: project.autoExtractBacklinks
|
|
8002
8696
|
});
|
|
8003
8697
|
if (isMachineFormat(opts.format)) {
|
|
8004
8698
|
console.log(JSON.stringify(result, null, 2));
|
|
8005
8699
|
return;
|
|
8006
8700
|
}
|
|
8007
8701
|
console.log(`Project updated: ${result.name}`);
|
|
8702
|
+
const dropped = Object.keys(providerModels).filter((provider) => !(provider in result.providerModels));
|
|
8703
|
+
if (dropped.length > 0) {
|
|
8704
|
+
console.log(` Dropped model override for deselected engine(s): ${dropped.join(", ")}`);
|
|
8705
|
+
}
|
|
8008
8706
|
}
|
|
8009
8707
|
async function deleteProject(name, opts) {
|
|
8010
8708
|
const client = getClient20();
|
|
@@ -8108,20 +8806,22 @@ async function setDefaultLocation(project, label, format) {
|
|
|
8108
8806
|
var PROJECT_CLI_COMMANDS = [
|
|
8109
8807
|
{
|
|
8110
8808
|
path: ["project", "create"],
|
|
8111
|
-
usage: "canonry project create <name> [--domain <domain>] [--owned-domain <domain>...] [--alias <name>...] [--country <code>] [--language <lang>] [--display-name <name>] [--format json]",
|
|
8809
|
+
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]",
|
|
8112
8810
|
options: {
|
|
8113
8811
|
domain: { type: "string", short: "d" },
|
|
8114
8812
|
"owned-domain": multiStringOption(),
|
|
8115
8813
|
alias: multiStringOption(),
|
|
8116
8814
|
country: stringOption(),
|
|
8117
8815
|
language: stringOption(),
|
|
8118
|
-
"display-name": stringOption()
|
|
8816
|
+
"display-name": stringOption(),
|
|
8817
|
+
provider: multiStringOption(),
|
|
8818
|
+
"provider-model": multiStringOption()
|
|
8119
8819
|
},
|
|
8120
8820
|
run: async (input) => {
|
|
8121
8821
|
const name = requireProject(
|
|
8122
8822
|
input,
|
|
8123
8823
|
"project.create",
|
|
8124
|
-
"canonry project create <name> [--domain <domain>] [--owned-domain <domain>...] [--alias <name>...] [--country <code>] [--language <lang>] [--display-name <name>] [--format json]"
|
|
8824
|
+
"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]"
|
|
8125
8825
|
);
|
|
8126
8826
|
await createProject(name, {
|
|
8127
8827
|
domain: getString(input.values, "domain") ?? name,
|
|
@@ -8130,13 +8830,15 @@ var PROJECT_CLI_COMMANDS = [
|
|
|
8130
8830
|
country: getString(input.values, "country") ?? "US",
|
|
8131
8831
|
language: getString(input.values, "language") ?? "en",
|
|
8132
8832
|
displayName: getString(input.values, "display-name") ?? name,
|
|
8833
|
+
providers: getStringArray(input.values, "provider") ?? [],
|
|
8834
|
+
providerModels: parseProviderModelAssignments(getStringArray(input.values, "provider-model")),
|
|
8133
8835
|
format: input.format
|
|
8134
8836
|
});
|
|
8135
8837
|
}
|
|
8136
8838
|
},
|
|
8137
8839
|
{
|
|
8138
8840
|
path: ["project", "update"],
|
|
8139
|
-
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>] [--format json]",
|
|
8841
|
+
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]",
|
|
8140
8842
|
options: {
|
|
8141
8843
|
domain: { type: "string", short: "d" },
|
|
8142
8844
|
"owned-domain": multiStringOption(),
|
|
@@ -8147,14 +8849,26 @@ var PROJECT_CLI_COMMANDS = [
|
|
|
8147
8849
|
"remove-alias": multiStringOption(),
|
|
8148
8850
|
country: stringOption(),
|
|
8149
8851
|
language: stringOption(),
|
|
8150
|
-
"display-name": stringOption()
|
|
8852
|
+
"display-name": stringOption(),
|
|
8853
|
+
provider: multiStringOption(),
|
|
8854
|
+
"all-providers": { type: "boolean" },
|
|
8855
|
+
"provider-model": multiStringOption(),
|
|
8856
|
+
"clear-provider-model": multiStringOption()
|
|
8151
8857
|
},
|
|
8152
8858
|
run: async (input) => {
|
|
8153
8859
|
const name = requireProject(
|
|
8154
8860
|
input,
|
|
8155
8861
|
"project.update",
|
|
8156
|
-
"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>] [--format json]"
|
|
8862
|
+
"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]"
|
|
8157
8863
|
);
|
|
8864
|
+
const providers = getStringArray(input.values, "provider");
|
|
8865
|
+
const allProviders = getBoolean(input.values, "all-providers");
|
|
8866
|
+
if (allProviders && providers?.length) throw usageError("Error: --all-providers conflicts with --provider");
|
|
8867
|
+
const providerModels = parseProviderModelAssignments(getStringArray(input.values, "provider-model"));
|
|
8868
|
+
const clearProviderModels = getStringArray(input.values, "clear-provider-model") ?? [];
|
|
8869
|
+
for (const provider of clearProviderModels) {
|
|
8870
|
+
if (provider in providerModels) throw usageError(`Error: --provider-model and --clear-provider-model conflict for ${provider}`);
|
|
8871
|
+
}
|
|
8158
8872
|
await updateProjectSettings(name, {
|
|
8159
8873
|
displayName: getString(input.values, "display-name"),
|
|
8160
8874
|
domain: getString(input.values, "domain"),
|
|
@@ -8166,6 +8880,9 @@ var PROJECT_CLI_COMMANDS = [
|
|
|
8166
8880
|
removeAlias: getStringArray(input.values, "remove-alias"),
|
|
8167
8881
|
country: getString(input.values, "country"),
|
|
8168
8882
|
language: getString(input.values, "language"),
|
|
8883
|
+
providers: allProviders ? [] : providers,
|
|
8884
|
+
providerModels,
|
|
8885
|
+
clearProviderModels,
|
|
8169
8886
|
format: input.format
|
|
8170
8887
|
});
|
|
8171
8888
|
}
|
|
@@ -8281,9 +8998,21 @@ var PROJECT_CLI_COMMANDS = [
|
|
|
8281
8998
|
}
|
|
8282
8999
|
}
|
|
8283
9000
|
];
|
|
9001
|
+
function parseProviderModelAssignments(assignments) {
|
|
9002
|
+
const result = {};
|
|
9003
|
+
for (const assignment of assignments ?? []) {
|
|
9004
|
+
const separator = assignment.indexOf("=");
|
|
9005
|
+
const provider = separator === -1 ? "" : assignment.slice(0, separator).trim();
|
|
9006
|
+
const model = separator === -1 ? "" : assignment.slice(separator + 1).trim();
|
|
9007
|
+
if (!provider || !model) throw usageError(`Error: --provider-model must use provider=model (received ${assignment})`);
|
|
9008
|
+
if (provider in result) throw usageError(`Error: duplicate --provider-model assignment for ${provider}`);
|
|
9009
|
+
result[provider] = model;
|
|
9010
|
+
}
|
|
9011
|
+
return result;
|
|
9012
|
+
}
|
|
8284
9013
|
|
|
8285
9014
|
// src/commands/report.ts
|
|
8286
|
-
import
|
|
9015
|
+
import fs9 from "fs";
|
|
8287
9016
|
import path5 from "path";
|
|
8288
9017
|
function defaultOutputPath(project, audience) {
|
|
8289
9018
|
const date = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
@@ -8300,10 +9029,10 @@ async function runReportCommand(project, opts = {}) {
|
|
|
8300
9029
|
const html = renderReportHtml(report, { audience });
|
|
8301
9030
|
const targetPath = opts.output ? path5.resolve(opts.output) : defaultOutputPath(project, audience);
|
|
8302
9031
|
const dir = path5.dirname(targetPath);
|
|
8303
|
-
if (!
|
|
8304
|
-
|
|
9032
|
+
if (!fs9.existsSync(dir)) {
|
|
9033
|
+
fs9.mkdirSync(dir, { recursive: true });
|
|
8305
9034
|
}
|
|
8306
|
-
|
|
9035
|
+
fs9.writeFileSync(targetPath, html, "utf-8");
|
|
8307
9036
|
console.log(`Report written to ${targetPath}`);
|
|
8308
9037
|
}
|
|
8309
9038
|
|
|
@@ -9168,11 +9897,11 @@ var SKILLS_CLI_COMMANDS = [
|
|
|
9168
9897
|
];
|
|
9169
9898
|
|
|
9170
9899
|
// src/commands/snapshot.ts
|
|
9171
|
-
import
|
|
9900
|
+
import fs11 from "fs";
|
|
9172
9901
|
import path7 from "path";
|
|
9173
9902
|
|
|
9174
9903
|
// src/snapshot-pdf.ts
|
|
9175
|
-
import
|
|
9904
|
+
import fs10 from "fs";
|
|
9176
9905
|
import path6 from "path";
|
|
9177
9906
|
import { PDFDocument, StandardFonts, rgb } from "pdf-lib";
|
|
9178
9907
|
var PAGE_WIDTH = 612;
|
|
@@ -9386,8 +10115,8 @@ async function writeSnapshotPdf(report, outputPath) {
|
|
|
9386
10115
|
renderQueries(pdf, report);
|
|
9387
10116
|
const bytes = await doc.save();
|
|
9388
10117
|
const resolvedPath = path6.resolve(outputPath);
|
|
9389
|
-
|
|
9390
|
-
|
|
10118
|
+
fs10.mkdirSync(path6.dirname(resolvedPath), { recursive: true });
|
|
10119
|
+
fs10.writeFileSync(resolvedPath, bytes);
|
|
9391
10120
|
return resolvedPath;
|
|
9392
10121
|
}
|
|
9393
10122
|
function renderCover(pdf, report) {
|
|
@@ -9546,8 +10275,8 @@ PDF saved: ${savedPdfPath}`);
|
|
|
9546
10275
|
}
|
|
9547
10276
|
function writeSnapshotMarkdown(report, outputPath) {
|
|
9548
10277
|
const resolvedPath = path7.resolve(outputPath);
|
|
9549
|
-
|
|
9550
|
-
|
|
10278
|
+
fs11.mkdirSync(path7.dirname(resolvedPath), { recursive: true });
|
|
10279
|
+
fs11.writeFileSync(resolvedPath, formatSnapshotMarkdown(report), "utf-8");
|
|
9551
10280
|
return resolvedPath;
|
|
9552
10281
|
}
|
|
9553
10282
|
function formatSnapshotMarkdown(report) {
|
|
@@ -10716,18 +11445,36 @@ async function bootstrapCommand(_opts) {
|
|
|
10716
11445
|
} : existingConfig?.google;
|
|
10717
11446
|
const keyHash = crypto.createHash("sha256").update(rawApiKey).digest("hex");
|
|
10718
11447
|
const keyPrefix = rawApiKey.slice(0, 9);
|
|
11448
|
+
const existingConfigKeyHash = existingConfig ? crypto.createHash("sha256").update(existingConfig.apiKey).digest("hex") : void 0;
|
|
10719
11449
|
const db = createClient(databasePath);
|
|
10720
11450
|
migrate(db);
|
|
10721
11451
|
db.transaction((tx) => {
|
|
10722
|
-
|
|
10723
|
-
tx.
|
|
10724
|
-
|
|
10725
|
-
|
|
10726
|
-
|
|
10727
|
-
|
|
10728
|
-
|
|
10729
|
-
|
|
10730
|
-
|
|
11452
|
+
const rotatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
11453
|
+
const existingDefaults = tx.select({ id: apiKeys.id, keyHash: apiKeys.keyHash }).from(apiKeys).where(eq(apiKeys.name, "default")).all();
|
|
11454
|
+
const existingDefault = existingDefaults.find((key) => key.keyHash === existingConfigKeyHash) ?? existingDefaults.at(0);
|
|
11455
|
+
if (existingDefault) {
|
|
11456
|
+
tx.update(apiKeys).set({
|
|
11457
|
+
keyHash,
|
|
11458
|
+
keyPrefix,
|
|
11459
|
+
scopes: ["*"],
|
|
11460
|
+
projectId: null,
|
|
11461
|
+
lastUsedAt: null,
|
|
11462
|
+
revokedAt: null
|
|
11463
|
+
}).where(eq(apiKeys.id, existingDefault.id)).run();
|
|
11464
|
+
for (const duplicate of existingDefaults) {
|
|
11465
|
+
if (duplicate.id === existingDefault.id) continue;
|
|
11466
|
+
tx.update(apiKeys).set({ revokedAt: rotatedAt }).where(eq(apiKeys.id, duplicate.id)).run();
|
|
11467
|
+
}
|
|
11468
|
+
} else {
|
|
11469
|
+
tx.insert(apiKeys).values({
|
|
11470
|
+
id: crypto.randomUUID(),
|
|
11471
|
+
name: "default",
|
|
11472
|
+
keyHash,
|
|
11473
|
+
keyPrefix,
|
|
11474
|
+
scopes: ["*"],
|
|
11475
|
+
createdAt: rotatedAt
|
|
11476
|
+
}).run();
|
|
11477
|
+
}
|
|
10731
11478
|
});
|
|
10732
11479
|
saveConfig({
|
|
10733
11480
|
apiUrl: env.apiUrl || existingConfig?.apiUrl || `http://localhost:${process.env.CANONRY_PORT || "4100"}`,
|
|
@@ -10757,7 +11504,7 @@ async function bootstrapCommand(_opts) {
|
|
|
10757
11504
|
|
|
10758
11505
|
// src/commands/daemon.ts
|
|
10759
11506
|
import { spawn } from "child_process";
|
|
10760
|
-
import
|
|
11507
|
+
import fs12 from "fs";
|
|
10761
11508
|
import path9 from "path";
|
|
10762
11509
|
function getPidPath() {
|
|
10763
11510
|
return path9.join(getConfigDir(), "canonry.pid");
|
|
@@ -10800,8 +11547,8 @@ function buildServeForwardArgs(opts) {
|
|
|
10800
11547
|
async function startDaemon(opts) {
|
|
10801
11548
|
const pidPath = getPidPath();
|
|
10802
11549
|
const format = opts.format ?? "text";
|
|
10803
|
-
if (
|
|
10804
|
-
const existingPid = parseInt(
|
|
11550
|
+
if (fs12.existsSync(pidPath)) {
|
|
11551
|
+
const existingPid = parseInt(fs12.readFileSync(pidPath, "utf-8").trim(), 10);
|
|
10805
11552
|
if (!isNaN(existingPid) && isProcessAlive(existingPid)) {
|
|
10806
11553
|
throw new CliError({
|
|
10807
11554
|
code: "DAEMON_ALREADY_RUNNING",
|
|
@@ -10812,7 +11559,7 @@ async function startDaemon(opts) {
|
|
|
10812
11559
|
}
|
|
10813
11560
|
});
|
|
10814
11561
|
}
|
|
10815
|
-
|
|
11562
|
+
fs12.unlinkSync(pidPath);
|
|
10816
11563
|
}
|
|
10817
11564
|
const cliPath = path9.resolve(new URL(import.meta.url).pathname);
|
|
10818
11565
|
const inSourceMode = new URL(import.meta.url).pathname.endsWith(".ts");
|
|
@@ -10831,10 +11578,10 @@ async function startDaemon(opts) {
|
|
|
10831
11578
|
});
|
|
10832
11579
|
}
|
|
10833
11580
|
const configDir = getConfigDir();
|
|
10834
|
-
if (!
|
|
10835
|
-
|
|
11581
|
+
if (!fs12.existsSync(configDir)) {
|
|
11582
|
+
fs12.mkdirSync(configDir, { recursive: true });
|
|
10836
11583
|
}
|
|
10837
|
-
|
|
11584
|
+
fs12.writeFileSync(pidPath, String(child.pid), "utf-8");
|
|
10838
11585
|
const port = opts.port ?? "4100";
|
|
10839
11586
|
const host = opts.host ?? "127.0.0.1";
|
|
10840
11587
|
if (!isMachineFormat(format)) {
|
|
@@ -10843,7 +11590,7 @@ async function startDaemon(opts) {
|
|
|
10843
11590
|
const ready = await waitForReady(host, port);
|
|
10844
11591
|
if (!ready) {
|
|
10845
11592
|
try {
|
|
10846
|
-
|
|
11593
|
+
fs12.unlinkSync(pidPath);
|
|
10847
11594
|
} catch {
|
|
10848
11595
|
}
|
|
10849
11596
|
throw new CliError({
|
|
@@ -10875,7 +11622,7 @@ async function startDaemon(opts) {
|
|
|
10875
11622
|
}
|
|
10876
11623
|
function stopDaemon(format = "text") {
|
|
10877
11624
|
const pidPath = getPidPath();
|
|
10878
|
-
if (!
|
|
11625
|
+
if (!fs12.existsSync(pidPath)) {
|
|
10879
11626
|
if (isMachineFormat(format)) {
|
|
10880
11627
|
console.log(JSON.stringify({
|
|
10881
11628
|
stopped: false,
|
|
@@ -10886,7 +11633,7 @@ function stopDaemon(format = "text") {
|
|
|
10886
11633
|
console.log("Canonry is not running (no PID file found)");
|
|
10887
11634
|
return;
|
|
10888
11635
|
}
|
|
10889
|
-
const pid = parseInt(
|
|
11636
|
+
const pid = parseInt(fs12.readFileSync(pidPath, "utf-8").trim(), 10);
|
|
10890
11637
|
if (isNaN(pid)) {
|
|
10891
11638
|
if (isMachineFormat(format)) {
|
|
10892
11639
|
console.log(JSON.stringify({
|
|
@@ -10897,7 +11644,7 @@ function stopDaemon(format = "text") {
|
|
|
10897
11644
|
} else {
|
|
10898
11645
|
console.error("Invalid PID file. Removing it.");
|
|
10899
11646
|
}
|
|
10900
|
-
|
|
11647
|
+
fs12.unlinkSync(pidPath);
|
|
10901
11648
|
return;
|
|
10902
11649
|
}
|
|
10903
11650
|
if (!isProcessAlive(pid)) {
|
|
@@ -10911,12 +11658,12 @@ function stopDaemon(format = "text") {
|
|
|
10911
11658
|
} else {
|
|
10912
11659
|
console.log(`Canonry is not running (stale PID: ${pid}). Cleaning up.`);
|
|
10913
11660
|
}
|
|
10914
|
-
|
|
11661
|
+
fs12.unlinkSync(pidPath);
|
|
10915
11662
|
return;
|
|
10916
11663
|
}
|
|
10917
11664
|
try {
|
|
10918
11665
|
process.kill(pid, "SIGTERM");
|
|
10919
|
-
|
|
11666
|
+
fs12.unlinkSync(pidPath);
|
|
10920
11667
|
if (isMachineFormat(format)) {
|
|
10921
11668
|
console.log(JSON.stringify({
|
|
10922
11669
|
stopped: true,
|
|
@@ -10940,7 +11687,7 @@ function stopDaemon(format = "text") {
|
|
|
10940
11687
|
|
|
10941
11688
|
// src/commands/init.ts
|
|
10942
11689
|
import crypto2 from "crypto";
|
|
10943
|
-
import
|
|
11690
|
+
import fs13 from "fs";
|
|
10944
11691
|
import readline from "readline";
|
|
10945
11692
|
import path10 from "path";
|
|
10946
11693
|
function prompt(question) {
|
|
@@ -10964,7 +11711,7 @@ var PROJECT_MARKERS = [".git", "canonry.yaml", "canonry.yml", "package.json"];
|
|
|
10964
11711
|
function cwdLooksLikeProject(dir) {
|
|
10965
11712
|
const home = process.env.HOME ?? "";
|
|
10966
11713
|
if (home && path10.resolve(dir) === path10.resolve(home)) return false;
|
|
10967
|
-
return PROJECT_MARKERS.some((marker) =>
|
|
11714
|
+
return PROJECT_MARKERS.some((marker) => fs13.existsSync(path10.join(dir, marker)));
|
|
10968
11715
|
}
|
|
10969
11716
|
var DEFAULT_AGENT_MODELS = {
|
|
10970
11717
|
anthropic: "anthropic/claude-sonnet-4-6",
|
|
@@ -10995,8 +11742,8 @@ async function initCommand(opts) {
|
|
|
10995
11742
|
return void 0;
|
|
10996
11743
|
}
|
|
10997
11744
|
const configDir = getConfigDir();
|
|
10998
|
-
if (!
|
|
10999
|
-
|
|
11745
|
+
if (!fs13.existsSync(configDir)) {
|
|
11746
|
+
fs13.mkdirSync(configDir, { recursive: true });
|
|
11000
11747
|
}
|
|
11001
11748
|
const bootstrapEnv = getBootstrapEnv(process.env, {
|
|
11002
11749
|
GEMINI_API_KEY: opts?.geminiKey,
|
|
@@ -11663,7 +12410,7 @@ function getClient25() {
|
|
|
11663
12410
|
}
|
|
11664
12411
|
async function technicalAeoScore(project, opts) {
|
|
11665
12412
|
const client = getClient25();
|
|
11666
|
-
const score = await client.getTechnicalAeoScore(project);
|
|
12413
|
+
const score = await client.getTechnicalAeoScore(project, { runId: opts.runId });
|
|
11667
12414
|
if (isMachineFormat(opts.format)) {
|
|
11668
12415
|
console.log(JSON.stringify(score, null, 2));
|
|
11669
12416
|
return;
|
|
@@ -11700,7 +12447,7 @@ async function technicalAeoScore(project, opts) {
|
|
|
11700
12447
|
async function technicalAeoPages(project, opts) {
|
|
11701
12448
|
const client = getClient25();
|
|
11702
12449
|
const status = opts.status === "success" || opts.status === "error" ? opts.status : void 0;
|
|
11703
|
-
const res = await client.getTechnicalAeoPages(project, { status, sort: opts.sort, limit: opts.limit });
|
|
12450
|
+
const res = await client.getTechnicalAeoPages(project, { runId: opts.runId, status, sort: opts.sort, limit: opts.limit });
|
|
11704
12451
|
if (opts.format === "jsonl") {
|
|
11705
12452
|
emitJsonl(res.pages.map((p) => ({ project, runId: res.runId, ...p })));
|
|
11706
12453
|
return;
|
|
@@ -11784,28 +12531,33 @@ async function technicalAeoRun(project, opts) {
|
|
|
11784
12531
|
var TECHNICAL_AEO_CLI_COMMANDS = [
|
|
11785
12532
|
{
|
|
11786
12533
|
path: ["technical-aeo", "score"],
|
|
11787
|
-
usage: "canonry technical-aeo score <project> [--format json]",
|
|
12534
|
+
usage: "canonry technical-aeo score <project> [--run-id <id>] [--format json]",
|
|
12535
|
+
options: {
|
|
12536
|
+
"run-id": stringOption()
|
|
12537
|
+
},
|
|
11788
12538
|
run: async (input) => {
|
|
11789
12539
|
const project = requireProject(
|
|
11790
12540
|
input,
|
|
11791
12541
|
"technical-aeo.score",
|
|
11792
|
-
"canonry technical-aeo score <project> [--format json]"
|
|
12542
|
+
"canonry technical-aeo score <project> [--run-id <id>] [--format json]"
|
|
11793
12543
|
);
|
|
11794
|
-
await technicalAeoScore(project, { format: input.format });
|
|
12544
|
+
await technicalAeoScore(project, { runId: getString(input.values, "run-id"), format: input.format });
|
|
11795
12545
|
}
|
|
11796
12546
|
},
|
|
11797
12547
|
{
|
|
11798
12548
|
path: ["technical-aeo", "pages"],
|
|
11799
|
-
usage: "canonry technical-aeo pages <project> [--status success|error] [--sort score-asc|score-desc|url] [--limit <n>] [--format json|jsonl]",
|
|
12549
|
+
usage: "canonry technical-aeo pages <project> [--run-id <id>] [--status success|error] [--sort score-asc|score-desc|url] [--limit <n>] [--format json|jsonl]",
|
|
11800
12550
|
options: {
|
|
12551
|
+
"run-id": stringOption(),
|
|
11801
12552
|
status: stringOption(),
|
|
11802
12553
|
sort: stringOption(),
|
|
11803
12554
|
limit: stringOption()
|
|
11804
12555
|
},
|
|
11805
12556
|
run: async (input) => {
|
|
11806
|
-
const usage = "canonry technical-aeo pages <project> [--status success|error] [--sort score-asc|score-desc|url] [--limit <n>] [--format json|jsonl]";
|
|
12557
|
+
const usage = "canonry technical-aeo pages <project> [--run-id <id>] [--status success|error] [--sort score-asc|score-desc|url] [--limit <n>] [--format json|jsonl]";
|
|
11807
12558
|
const project = requireProject(input, "technical-aeo.pages", usage);
|
|
11808
12559
|
await technicalAeoPages(project, {
|
|
12560
|
+
runId: getString(input.values, "run-id"),
|
|
11809
12561
|
status: getString(input.values, "status"),
|
|
11810
12562
|
sort: getString(input.values, "sort"),
|
|
11811
12563
|
limit: parseIntegerOption(input, "limit", {
|
|
@@ -12097,7 +12849,7 @@ var VISIBILITY_STATS_CLI_COMMANDS = [
|
|
|
12097
12849
|
];
|
|
12098
12850
|
|
|
12099
12851
|
// src/cli-commands/wordpress.ts
|
|
12100
|
-
import
|
|
12852
|
+
import fs14 from "fs";
|
|
12101
12853
|
|
|
12102
12854
|
// src/commands/wordpress.ts
|
|
12103
12855
|
function getClient26() {
|
|
@@ -12336,12 +13088,12 @@ async function wordpressSetMeta(project, body) {
|
|
|
12336
13088
|
printPageDetail(result);
|
|
12337
13089
|
}
|
|
12338
13090
|
async function wordpressBulkSetMeta(project, opts) {
|
|
12339
|
-
const
|
|
13091
|
+
const fs15 = await import("fs/promises");
|
|
12340
13092
|
const path11 = await import("path");
|
|
12341
13093
|
const filePath = path11.resolve(opts.from);
|
|
12342
13094
|
let raw;
|
|
12343
13095
|
try {
|
|
12344
|
-
raw = await
|
|
13096
|
+
raw = await fs15.readFile(filePath, "utf8");
|
|
12345
13097
|
} catch {
|
|
12346
13098
|
throw new CliError({
|
|
12347
13099
|
code: "FILE_READ_ERROR",
|
|
@@ -12438,13 +13190,13 @@ async function wordpressSetSchema(project, body) {
|
|
|
12438
13190
|
printManualAssist(`Schema update for "${body.slug}"`, result);
|
|
12439
13191
|
}
|
|
12440
13192
|
async function wordpressSchemaDeploy(project, opts) {
|
|
12441
|
-
const
|
|
13193
|
+
const fs15 = await import("fs/promises");
|
|
12442
13194
|
const path11 = await import("path");
|
|
12443
13195
|
const yaml = await loadYamlModule();
|
|
12444
13196
|
const filePath = path11.resolve(opts.profile);
|
|
12445
13197
|
let raw;
|
|
12446
13198
|
try {
|
|
12447
|
-
raw = await
|
|
13199
|
+
raw = await fs15.readFile(filePath, "utf8");
|
|
12448
13200
|
} catch {
|
|
12449
13201
|
throw new CliError({
|
|
12450
13202
|
code: "FILE_READ_ERROR",
|
|
@@ -12549,13 +13301,13 @@ async function wordpressOnboard(project, opts) {
|
|
|
12549
13301
|
}
|
|
12550
13302
|
let profileData;
|
|
12551
13303
|
if (opts.profile) {
|
|
12552
|
-
const
|
|
13304
|
+
const fs15 = await import("fs/promises");
|
|
12553
13305
|
const path11 = await import("path");
|
|
12554
13306
|
const yaml = await loadYamlModule();
|
|
12555
13307
|
const filePath = path11.resolve(opts.profile);
|
|
12556
13308
|
let raw;
|
|
12557
13309
|
try {
|
|
12558
|
-
raw = await
|
|
13310
|
+
raw = await fs15.readFile(filePath, "utf8");
|
|
12559
13311
|
} catch {
|
|
12560
13312
|
throw new CliError({
|
|
12561
13313
|
code: "FILE_READ_ERROR",
|
|
@@ -12704,7 +13456,7 @@ function resolveContent(input, command, usage, options) {
|
|
|
12704
13456
|
}
|
|
12705
13457
|
if (contentFile) {
|
|
12706
13458
|
try {
|
|
12707
|
-
return
|
|
13459
|
+
return fs14.readFileSync(contentFile, "utf-8");
|
|
12708
13460
|
} catch (error) {
|
|
12709
13461
|
const message = error instanceof Error ? error.message : String(error);
|
|
12710
13462
|
throw usageError(`Error: could not read --content-file "${contentFile}": ${message}`, {
|