@ainyc/canonry 4.14.0 → 4.15.2
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 +57 -223
- package/assets/assets/{index-D0EPNRDs.css → index-C1WW21tz.css} +1 -1
- package/assets/assets/index-Qq_oMI-C.js +302 -0
- package/assets/index.html +2 -2
- package/dist/{chunk-5NYG5EC7.js → chunk-7SRKUAZO.js} +131 -2
- package/dist/{chunk-UQHWSCTE.js → chunk-IVNWS2YU.js} +473 -104
- package/dist/{chunk-7HBZCGRL.js → chunk-MI33SQL6.js} +27 -2
- package/dist/{chunk-6QTH5NS5.js → chunk-ONI3TX2A.js} +62 -0
- package/dist/cli.js +354 -119
- package/dist/index.d.ts +1 -0
- package/dist/index.js +4 -4
- package/dist/{intelligence-service-BCKXIKIL.js → intelligence-service-JYV3CO4H.js} +2 -2
- package/dist/mcp.js +9 -3
- package/package.json +7 -7
- package/assets/assets/index-B6Mi9Fd1.js +0 -302
package/dist/cli.js
CHANGED
|
@@ -3,6 +3,7 @@ import {
|
|
|
3
3
|
coerceAgentProvider,
|
|
4
4
|
computeCompetitorOverlap,
|
|
5
5
|
createServer,
|
|
6
|
+
detectAndTrackUpgrade,
|
|
6
7
|
determineCitationState,
|
|
7
8
|
extractRecommendedCompetitors,
|
|
8
9
|
formatAuditFactorScore,
|
|
@@ -16,9 +17,10 @@ import {
|
|
|
16
17
|
reparseStoredResult3,
|
|
17
18
|
reparseStoredResult4,
|
|
18
19
|
setGoogleAuthConfig,
|
|
20
|
+
setTelemetrySource,
|
|
19
21
|
showFirstRunNotice,
|
|
20
22
|
trackEvent
|
|
21
|
-
} from "./chunk-
|
|
23
|
+
} from "./chunk-IVNWS2YU.js";
|
|
22
24
|
import {
|
|
23
25
|
CliError,
|
|
24
26
|
EXIT_SYSTEM_ERROR,
|
|
@@ -29,11 +31,12 @@ import {
|
|
|
29
31
|
getConfigPath,
|
|
30
32
|
isEndpointMissing,
|
|
31
33
|
loadConfig,
|
|
34
|
+
loadConfigRaw,
|
|
32
35
|
printCliError,
|
|
33
36
|
saveConfig,
|
|
34
37
|
saveConfigPatch,
|
|
35
38
|
usageError
|
|
36
|
-
} from "./chunk-
|
|
39
|
+
} from "./chunk-7SRKUAZO.js";
|
|
37
40
|
import {
|
|
38
41
|
apiKeys,
|
|
39
42
|
competitors,
|
|
@@ -43,9 +46,10 @@ import {
|
|
|
43
46
|
migrate,
|
|
44
47
|
parseJsonColumn,
|
|
45
48
|
projects,
|
|
49
|
+
queries,
|
|
46
50
|
querySnapshots,
|
|
47
51
|
runs
|
|
48
|
-
} from "./chunk-
|
|
52
|
+
} from "./chunk-MI33SQL6.js";
|
|
49
53
|
import {
|
|
50
54
|
CcReleaseSyncStatuses,
|
|
51
55
|
CheckScopes,
|
|
@@ -56,6 +60,7 @@ import {
|
|
|
56
60
|
RunKinds,
|
|
57
61
|
RunStatuses,
|
|
58
62
|
SkillsClients,
|
|
63
|
+
TrafficEventKinds,
|
|
59
64
|
determineAnswerMentioned,
|
|
60
65
|
effectiveDomains,
|
|
61
66
|
formatRunErrorOneLine,
|
|
@@ -64,11 +69,47 @@ import {
|
|
|
64
69
|
providerQuotaPolicySchema,
|
|
65
70
|
resolveProviderInput,
|
|
66
71
|
skillsClientSchema
|
|
67
|
-
} from "./chunk-
|
|
72
|
+
} from "./chunk-ONI3TX2A.js";
|
|
68
73
|
|
|
69
74
|
// src/cli.ts
|
|
70
75
|
import { pathToFileURL } from "url";
|
|
71
76
|
|
|
77
|
+
// src/setup-state.ts
|
|
78
|
+
import fs from "fs";
|
|
79
|
+
function buildSetupState() {
|
|
80
|
+
if (!configExists()) return void 0;
|
|
81
|
+
let provider_count = 0;
|
|
82
|
+
let has_keywords = false;
|
|
83
|
+
let project_count = 0;
|
|
84
|
+
let is_first_run = true;
|
|
85
|
+
let dbPath;
|
|
86
|
+
try {
|
|
87
|
+
const raw = loadConfigRaw();
|
|
88
|
+
if (raw) {
|
|
89
|
+
is_first_run = !raw.anonymousId;
|
|
90
|
+
if (raw.providers) {
|
|
91
|
+
provider_count = Object.values(raw.providers).filter(
|
|
92
|
+
(p) => Boolean(p?.apiKey) || Boolean(p?.baseUrl)
|
|
93
|
+
).length;
|
|
94
|
+
}
|
|
95
|
+
if (typeof raw.database === "string" && raw.database.length > 0) {
|
|
96
|
+
dbPath = raw.database;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
} catch {
|
|
100
|
+
}
|
|
101
|
+
if (dbPath && fs.existsSync(dbPath)) {
|
|
102
|
+
try {
|
|
103
|
+
const db = createClient(dbPath);
|
|
104
|
+
project_count = db.select({ id: projects.id }).from(projects).all().length;
|
|
105
|
+
const firstQuery = db.select({ id: queries.id }).from(queries).limit(1).all();
|
|
106
|
+
has_keywords = firstQuery.length > 0;
|
|
107
|
+
} catch {
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return { provider_count, has_keywords, project_count, is_first_run };
|
|
111
|
+
}
|
|
112
|
+
|
|
72
113
|
// src/cli-dispatch.ts
|
|
73
114
|
import { parseArgs } from "util";
|
|
74
115
|
function commandId(spec) {
|
|
@@ -580,7 +621,7 @@ function readStoredGroundingSources(rawResponse) {
|
|
|
580
621
|
return result;
|
|
581
622
|
}
|
|
582
623
|
async function backfillInsightsCommand(project, opts) {
|
|
583
|
-
const { IntelligenceService } = await import("./intelligence-service-
|
|
624
|
+
const { IntelligenceService } = await import("./intelligence-service-JYV3CO4H.js");
|
|
584
625
|
const config = loadConfig();
|
|
585
626
|
const db = createClient(config.database);
|
|
586
627
|
migrate(db);
|
|
@@ -2018,9 +2059,9 @@ async function gaConnect(project, opts) {
|
|
|
2018
2059
|
propertyId: opts.propertyId
|
|
2019
2060
|
};
|
|
2020
2061
|
if (opts.keyFile) {
|
|
2021
|
-
const
|
|
2062
|
+
const fs13 = await import("fs");
|
|
2022
2063
|
try {
|
|
2023
|
-
const content =
|
|
2064
|
+
const content = fs13.readFileSync(opts.keyFile, "utf-8");
|
|
2024
2065
|
JSON.parse(content);
|
|
2025
2066
|
body.keyJson = content;
|
|
2026
2067
|
} catch (e) {
|
|
@@ -2698,10 +2739,10 @@ async function trafficConnectCloudRun(project, opts) {
|
|
|
2698
2739
|
details: { project }
|
|
2699
2740
|
});
|
|
2700
2741
|
}
|
|
2701
|
-
const
|
|
2742
|
+
const fs13 = await import("fs");
|
|
2702
2743
|
let keyJson;
|
|
2703
2744
|
try {
|
|
2704
|
-
keyJson =
|
|
2745
|
+
keyJson = fs13.readFileSync(opts.serviceAccountKey, "utf-8");
|
|
2705
2746
|
JSON.parse(keyJson);
|
|
2706
2747
|
} catch (e) {
|
|
2707
2748
|
const msg = e instanceof Error ? e.message : String(e);
|
|
@@ -2761,6 +2802,128 @@ async function trafficSync(project, opts) {
|
|
|
2761
2802
|
console.log(` Sample rows: ${result.sampleRows}`);
|
|
2762
2803
|
console.log(` Synced at: ${result.syncedAt}`);
|
|
2763
2804
|
}
|
|
2805
|
+
function formatSourceLine(source) {
|
|
2806
|
+
const parts = [
|
|
2807
|
+
source.id,
|
|
2808
|
+
source.sourceType,
|
|
2809
|
+
source.status,
|
|
2810
|
+
source.lastSyncedAt ?? "never synced",
|
|
2811
|
+
source.displayName
|
|
2812
|
+
];
|
|
2813
|
+
return parts.join(" ");
|
|
2814
|
+
}
|
|
2815
|
+
async function trafficSources(project, opts) {
|
|
2816
|
+
const client = createApiClient();
|
|
2817
|
+
const result = await client.trafficListSources(project);
|
|
2818
|
+
if (opts.format === "json") {
|
|
2819
|
+
console.log(JSON.stringify(result, null, 2));
|
|
2820
|
+
return;
|
|
2821
|
+
}
|
|
2822
|
+
if (result.sources.length === 0) {
|
|
2823
|
+
console.log(`No traffic sources connected for project "${project}".`);
|
|
2824
|
+
console.log("Run: canonry traffic connect cloud-run <project> --gcp-project <id> --service-account-key <path>");
|
|
2825
|
+
return;
|
|
2826
|
+
}
|
|
2827
|
+
console.log(`Traffic sources for "${project}":`);
|
|
2828
|
+
console.log(" ID TYPE STATUS LAST_SYNCED DISPLAY_NAME");
|
|
2829
|
+
for (const source of result.sources) {
|
|
2830
|
+
console.log(` ${formatSourceLine(source)}`);
|
|
2831
|
+
}
|
|
2832
|
+
}
|
|
2833
|
+
async function trafficStatus(project, opts) {
|
|
2834
|
+
const client = createApiClient();
|
|
2835
|
+
const result = await client.trafficStatus(project);
|
|
2836
|
+
const details = result.sources;
|
|
2837
|
+
if (opts.format === "json") {
|
|
2838
|
+
console.log(JSON.stringify(result, null, 2));
|
|
2839
|
+
return;
|
|
2840
|
+
}
|
|
2841
|
+
if (details.length === 0) {
|
|
2842
|
+
console.log(`No traffic sources connected for project "${project}".`);
|
|
2843
|
+
console.log("Run: canonry traffic connect cloud-run <project> --gcp-project <id> --service-account-key <path>");
|
|
2844
|
+
return;
|
|
2845
|
+
}
|
|
2846
|
+
for (const d of details) {
|
|
2847
|
+
console.log(`Source ${d.id} (${d.sourceType})`);
|
|
2848
|
+
console.log(` Display name: ${d.displayName}`);
|
|
2849
|
+
console.log(` Status: ${d.status}`);
|
|
2850
|
+
console.log(` Last synced: ${d.lastSyncedAt ?? "never"}`);
|
|
2851
|
+
if (d.lastError) console.log(` Last error: ${d.lastError}`);
|
|
2852
|
+
console.log(` 24h crawler: ${d.totals24h.crawlerHits} hits`);
|
|
2853
|
+
console.log(` 24h AI referral: ${d.totals24h.aiReferralHits} hits`);
|
|
2854
|
+
console.log(` 24h samples: ${d.totals24h.sampleCount}`);
|
|
2855
|
+
if (d.latestRun) {
|
|
2856
|
+
console.log(` Latest run: ${d.latestRun.runId} (${d.latestRun.status})`);
|
|
2857
|
+
console.log(` Started: ${d.latestRun.startedAt}`);
|
|
2858
|
+
if (d.latestRun.finishedAt) console.log(` Finished: ${d.latestRun.finishedAt}`);
|
|
2859
|
+
if (d.latestRun.error) console.log(` Error: ${d.latestRun.error}`);
|
|
2860
|
+
} else {
|
|
2861
|
+
console.log(` Latest run: (none)`);
|
|
2862
|
+
}
|
|
2863
|
+
console.log("");
|
|
2864
|
+
}
|
|
2865
|
+
}
|
|
2866
|
+
function formatEventLine(event) {
|
|
2867
|
+
if (event.kind === TrafficEventKinds.crawler) {
|
|
2868
|
+
return [
|
|
2869
|
+
event.tsHour,
|
|
2870
|
+
"crawler",
|
|
2871
|
+
event.botId,
|
|
2872
|
+
event.verificationStatus,
|
|
2873
|
+
String(event.status),
|
|
2874
|
+
event.pathNormalized,
|
|
2875
|
+
`${event.hits} hits`
|
|
2876
|
+
].join(" ");
|
|
2877
|
+
}
|
|
2878
|
+
return [
|
|
2879
|
+
event.tsHour,
|
|
2880
|
+
"ai-referral",
|
|
2881
|
+
event.product,
|
|
2882
|
+
event.evidenceType,
|
|
2883
|
+
event.sourceDomain,
|
|
2884
|
+
event.landingPathNormalized,
|
|
2885
|
+
`${event.hits} hits`
|
|
2886
|
+
].join(" ");
|
|
2887
|
+
}
|
|
2888
|
+
async function trafficEvents(project, opts) {
|
|
2889
|
+
if (opts.kind && opts.kind !== "all" && opts.kind !== TrafficEventKinds.crawler && opts.kind !== TrafficEventKinds["ai-referral"]) {
|
|
2890
|
+
throw new CliError({
|
|
2891
|
+
code: "TRAFFIC_INVALID_KIND",
|
|
2892
|
+
message: `--kind must be one of: all, ${TrafficEventKinds.crawler}, ${TrafficEventKinds["ai-referral"]}`,
|
|
2893
|
+
displayMessage: `Error: --kind must be "all", "${TrafficEventKinds.crawler}", or "${TrafficEventKinds["ai-referral"]}"`,
|
|
2894
|
+
details: { project, kind: opts.kind }
|
|
2895
|
+
});
|
|
2896
|
+
}
|
|
2897
|
+
const params = {};
|
|
2898
|
+
if (opts.kind && opts.kind !== "all") params.kind = opts.kind;
|
|
2899
|
+
if (opts.source) params.sourceId = opts.source;
|
|
2900
|
+
if (opts.limit !== void 0) params.limit = opts.limit;
|
|
2901
|
+
if (opts.sinceMinutes !== void 0) {
|
|
2902
|
+
const since = new Date(Date.now() - opts.sinceMinutes * 6e4).toISOString();
|
|
2903
|
+
params.since = since;
|
|
2904
|
+
} else if (opts.since) {
|
|
2905
|
+
params.since = opts.since;
|
|
2906
|
+
}
|
|
2907
|
+
if (opts.until) params.until = opts.until;
|
|
2908
|
+
const client = createApiClient();
|
|
2909
|
+
const result = await client.trafficListEvents(project, params);
|
|
2910
|
+
if (opts.format === "json") {
|
|
2911
|
+
console.log(JSON.stringify(result, null, 2));
|
|
2912
|
+
return;
|
|
2913
|
+
}
|
|
2914
|
+
console.log(`Traffic events for "${project}" ${result.windowStart} \u2192 ${result.windowEnd}`);
|
|
2915
|
+
console.log(` Crawler hits (window): ${result.totals.crawlerHits}`);
|
|
2916
|
+
console.log(` AI referral hits (window): ${result.totals.aiReferralHits}`);
|
|
2917
|
+
console.log("");
|
|
2918
|
+
if (result.events.length === 0) {
|
|
2919
|
+
console.log("No events in this window.");
|
|
2920
|
+
return;
|
|
2921
|
+
}
|
|
2922
|
+
console.log(" TS_HOUR KIND IDENTITY EVIDENCE/STATUS PATH HITS");
|
|
2923
|
+
for (const event of result.events) {
|
|
2924
|
+
console.log(` ${formatEventLine(event)}`);
|
|
2925
|
+
}
|
|
2926
|
+
}
|
|
2764
2927
|
|
|
2765
2928
|
// src/cli-commands/traffic.ts
|
|
2766
2929
|
var TRAFFIC_CLI_COMMANDS = [
|
|
@@ -2807,7 +2970,7 @@ var TRAFFIC_CLI_COMMANDS = [
|
|
|
2807
2970
|
},
|
|
2808
2971
|
{
|
|
2809
2972
|
path: ["traffic", "sync"],
|
|
2810
|
-
usage: "canonry traffic sync <project> --source <id> [--since-minutes
|
|
2973
|
+
usage: "canonry traffic sync <project> --source <id> [--since-minutes 43200] [--format json]",
|
|
2811
2974
|
options: {
|
|
2812
2975
|
source: stringOption(),
|
|
2813
2976
|
"since-minutes": stringOption()
|
|
@@ -2816,12 +2979,15 @@ var TRAFFIC_CLI_COMMANDS = [
|
|
|
2816
2979
|
const project = requireProject(
|
|
2817
2980
|
input,
|
|
2818
2981
|
"traffic.sync",
|
|
2819
|
-
"canonry traffic sync <project> --source <id> [--since-minutes
|
|
2982
|
+
"canonry traffic sync <project> --source <id> [--since-minutes 43200]"
|
|
2820
2983
|
);
|
|
2821
2984
|
const source = getString(input.values, "source");
|
|
2822
2985
|
if (!source) throw new Error("--source <id> is required");
|
|
2823
|
-
const
|
|
2824
|
-
|
|
2986
|
+
const sinceMinutes = parseIntegerOption(input, "since-minutes", {
|
|
2987
|
+
command: "traffic.sync",
|
|
2988
|
+
usage: "canonry traffic sync <project> --source <id> [--since-minutes 43200]",
|
|
2989
|
+
message: "--since-minutes must be an integer"
|
|
2990
|
+
});
|
|
2825
2991
|
await trafficSync(project, {
|
|
2826
2992
|
source,
|
|
2827
2993
|
sinceMinutes,
|
|
@@ -2829,6 +2995,68 @@ var TRAFFIC_CLI_COMMANDS = [
|
|
|
2829
2995
|
});
|
|
2830
2996
|
}
|
|
2831
2997
|
},
|
|
2998
|
+
{
|
|
2999
|
+
path: ["traffic", "sources"],
|
|
3000
|
+
usage: "canonry traffic sources <project> [--format json]",
|
|
3001
|
+
run: async (input) => {
|
|
3002
|
+
const project = requireProject(
|
|
3003
|
+
input,
|
|
3004
|
+
"traffic.sources",
|
|
3005
|
+
"canonry traffic sources <project>"
|
|
3006
|
+
);
|
|
3007
|
+
await trafficSources(project, { format: input.format });
|
|
3008
|
+
}
|
|
3009
|
+
},
|
|
3010
|
+
{
|
|
3011
|
+
path: ["traffic", "status"],
|
|
3012
|
+
usage: "canonry traffic status <project> [--format json]",
|
|
3013
|
+
run: async (input) => {
|
|
3014
|
+
const project = requireProject(
|
|
3015
|
+
input,
|
|
3016
|
+
"traffic.status",
|
|
3017
|
+
"canonry traffic status <project>"
|
|
3018
|
+
);
|
|
3019
|
+
await trafficStatus(project, { format: input.format });
|
|
3020
|
+
}
|
|
3021
|
+
},
|
|
3022
|
+
{
|
|
3023
|
+
path: ["traffic", "events"],
|
|
3024
|
+
usage: "canonry traffic events <project> [--kind crawler|ai-referral|all] [--source <id>] [--since-minutes 1440] [--since <iso>] [--until <iso>] [--limit 500] [--format json]",
|
|
3025
|
+
options: {
|
|
3026
|
+
kind: stringOption(),
|
|
3027
|
+
source: stringOption(),
|
|
3028
|
+
"since-minutes": stringOption(),
|
|
3029
|
+
since: stringOption(),
|
|
3030
|
+
until: stringOption(),
|
|
3031
|
+
limit: stringOption()
|
|
3032
|
+
},
|
|
3033
|
+
run: async (input) => {
|
|
3034
|
+
const project = requireProject(
|
|
3035
|
+
input,
|
|
3036
|
+
"traffic.events",
|
|
3037
|
+
"canonry traffic events <project>"
|
|
3038
|
+
);
|
|
3039
|
+
const sinceMinutes = parseIntegerOption(input, "since-minutes", {
|
|
3040
|
+
command: "traffic.events",
|
|
3041
|
+
usage: "canonry traffic events <project> [--since-minutes 1440]",
|
|
3042
|
+
message: "--since-minutes must be an integer"
|
|
3043
|
+
});
|
|
3044
|
+
const limit = parseIntegerOption(input, "limit", {
|
|
3045
|
+
command: "traffic.events",
|
|
3046
|
+
usage: "canonry traffic events <project> [--limit 500]",
|
|
3047
|
+
message: "--limit must be an integer"
|
|
3048
|
+
});
|
|
3049
|
+
await trafficEvents(project, {
|
|
3050
|
+
kind: getString(input.values, "kind"),
|
|
3051
|
+
source: getString(input.values, "source"),
|
|
3052
|
+
sinceMinutes,
|
|
3053
|
+
since: getString(input.values, "since"),
|
|
3054
|
+
until: getString(input.values, "until"),
|
|
3055
|
+
limit,
|
|
3056
|
+
format: input.format
|
|
3057
|
+
});
|
|
3058
|
+
}
|
|
3059
|
+
},
|
|
2832
3060
|
{
|
|
2833
3061
|
path: ["traffic"],
|
|
2834
3062
|
usage: "canonry traffic <subcommand> <project> [args]",
|
|
@@ -2836,7 +3064,7 @@ var TRAFFIC_CLI_COMMANDS = [
|
|
|
2836
3064
|
unknownSubcommand(input.positionals[0], {
|
|
2837
3065
|
command: "traffic",
|
|
2838
3066
|
usage: "canonry traffic <subcommand> <project> [args]",
|
|
2839
|
-
available: ["connect", "sync"]
|
|
3067
|
+
available: ["connect", "sync", "status", "sources", "events"]
|
|
2840
3068
|
});
|
|
2841
3069
|
}
|
|
2842
3070
|
}
|
|
@@ -3837,7 +4065,7 @@ var GOOGLE_CLI_COMMANDS = [
|
|
|
3837
4065
|
];
|
|
3838
4066
|
|
|
3839
4067
|
// src/commands/keyword.ts
|
|
3840
|
-
import
|
|
4068
|
+
import fs2 from "fs";
|
|
3841
4069
|
function getClient8() {
|
|
3842
4070
|
return createApiClient();
|
|
3843
4071
|
}
|
|
@@ -3902,7 +4130,7 @@ async function listKeywords(project, format) {
|
|
|
3902
4130
|
}
|
|
3903
4131
|
}
|
|
3904
4132
|
async function importKeywords(project, filePath, format) {
|
|
3905
|
-
if (!
|
|
4133
|
+
if (!fs2.existsSync(filePath)) {
|
|
3906
4134
|
throw new CliError({
|
|
3907
4135
|
code: "KEYWORD_IMPORT_FILE_NOT_FOUND",
|
|
3908
4136
|
message: `File not found: ${filePath}`,
|
|
@@ -3913,7 +4141,7 @@ async function importKeywords(project, filePath, format) {
|
|
|
3913
4141
|
}
|
|
3914
4142
|
});
|
|
3915
4143
|
}
|
|
3916
|
-
const content =
|
|
4144
|
+
const content = fs2.readFileSync(filePath, "utf-8");
|
|
3917
4145
|
const keywords = content.split("\n").map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#"));
|
|
3918
4146
|
if (keywords.length === 0) {
|
|
3919
4147
|
if (format === "json") {
|
|
@@ -4114,46 +4342,46 @@ var KEYWORD_CLI_COMMANDS = [
|
|
|
4114
4342
|
];
|
|
4115
4343
|
|
|
4116
4344
|
// src/commands/query.ts
|
|
4117
|
-
import
|
|
4345
|
+
import fs3 from "fs";
|
|
4118
4346
|
function getClient9() {
|
|
4119
4347
|
return createApiClient();
|
|
4120
4348
|
}
|
|
4121
|
-
async function addQueries(project,
|
|
4349
|
+
async function addQueries(project, queries2, format) {
|
|
4122
4350
|
const client = getClient9();
|
|
4123
|
-
await client.appendQueries(project,
|
|
4351
|
+
await client.appendQueries(project, queries2);
|
|
4124
4352
|
if (format === "json") {
|
|
4125
4353
|
console.log(JSON.stringify({
|
|
4126
4354
|
project,
|
|
4127
|
-
queries,
|
|
4128
|
-
addedCount:
|
|
4355
|
+
queries: queries2,
|
|
4356
|
+
addedCount: queries2.length
|
|
4129
4357
|
}, null, 2));
|
|
4130
4358
|
return;
|
|
4131
4359
|
}
|
|
4132
|
-
console.log(`Added ${
|
|
4360
|
+
console.log(`Added ${queries2.length} ${queries2.length === 1 ? "query" : "queries"} to "${project}".`);
|
|
4133
4361
|
}
|
|
4134
|
-
async function replaceQueries(project,
|
|
4362
|
+
async function replaceQueries(project, queries2, format) {
|
|
4135
4363
|
const client = getClient9();
|
|
4136
|
-
await client.putQueries(project,
|
|
4364
|
+
await client.putQueries(project, queries2);
|
|
4137
4365
|
if (format === "json") {
|
|
4138
4366
|
console.log(JSON.stringify({
|
|
4139
4367
|
project,
|
|
4140
|
-
queries,
|
|
4141
|
-
replacedCount:
|
|
4368
|
+
queries: queries2,
|
|
4369
|
+
replacedCount: queries2.length
|
|
4142
4370
|
}, null, 2));
|
|
4143
4371
|
return;
|
|
4144
4372
|
}
|
|
4145
|
-
console.log(`Set ${
|
|
4373
|
+
console.log(`Set ${queries2.length} ${queries2.length === 1 ? "query" : "queries"} for "${project}".`);
|
|
4146
4374
|
}
|
|
4147
|
-
async function removeQueries(project,
|
|
4375
|
+
async function removeQueries(project, queries2, format) {
|
|
4148
4376
|
const client = getClient9();
|
|
4149
4377
|
const existing = await client.listQueries(project);
|
|
4150
4378
|
const existingSet = new Set(existing.map((q) => q.query));
|
|
4151
|
-
const removedQueries =
|
|
4152
|
-
await client.deleteQueries(project,
|
|
4379
|
+
const removedQueries = queries2.filter((q) => existingSet.has(q));
|
|
4380
|
+
await client.deleteQueries(project, queries2);
|
|
4153
4381
|
if (format === "json") {
|
|
4154
4382
|
console.log(JSON.stringify({
|
|
4155
4383
|
project,
|
|
4156
|
-
queries,
|
|
4384
|
+
queries: queries2,
|
|
4157
4385
|
removedQueries,
|
|
4158
4386
|
removedCount: removedQueries.length
|
|
4159
4387
|
}, null, 2));
|
|
@@ -4179,7 +4407,7 @@ async function listQueries(project, format) {
|
|
|
4179
4407
|
}
|
|
4180
4408
|
}
|
|
4181
4409
|
async function importQueries(project, filePath, format) {
|
|
4182
|
-
if (!
|
|
4410
|
+
if (!fs3.existsSync(filePath)) {
|
|
4183
4411
|
throw new CliError({
|
|
4184
4412
|
code: "QUERY_IMPORT_FILE_NOT_FOUND",
|
|
4185
4413
|
message: `File not found: ${filePath}`,
|
|
@@ -4190,9 +4418,9 @@ async function importQueries(project, filePath, format) {
|
|
|
4190
4418
|
}
|
|
4191
4419
|
});
|
|
4192
4420
|
}
|
|
4193
|
-
const content =
|
|
4194
|
-
const
|
|
4195
|
-
if (
|
|
4421
|
+
const content = fs3.readFileSync(filePath, "utf-8");
|
|
4422
|
+
const queries2 = content.split("\n").map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#"));
|
|
4423
|
+
if (queries2.length === 0) {
|
|
4196
4424
|
if (format === "json") {
|
|
4197
4425
|
console.log(JSON.stringify({
|
|
4198
4426
|
project,
|
|
@@ -4206,17 +4434,17 @@ async function importQueries(project, filePath, format) {
|
|
|
4206
4434
|
return;
|
|
4207
4435
|
}
|
|
4208
4436
|
const client = getClient9();
|
|
4209
|
-
await client.appendQueries(project,
|
|
4437
|
+
await client.appendQueries(project, queries2);
|
|
4210
4438
|
if (format === "json") {
|
|
4211
4439
|
console.log(JSON.stringify({
|
|
4212
4440
|
project,
|
|
4213
4441
|
filePath,
|
|
4214
|
-
queries,
|
|
4215
|
-
importedCount:
|
|
4442
|
+
queries: queries2,
|
|
4443
|
+
importedCount: queries2.length
|
|
4216
4444
|
}, null, 2));
|
|
4217
4445
|
return;
|
|
4218
4446
|
}
|
|
4219
|
-
console.log(`Imported ${
|
|
4447
|
+
console.log(`Imported ${queries2.length} ${queries2.length === 1 ? "query" : "queries"} to "${project}".`);
|
|
4220
4448
|
}
|
|
4221
4449
|
async function generateQueries(project, provider, opts) {
|
|
4222
4450
|
const client = getClient9();
|
|
@@ -4259,8 +4487,8 @@ var QUERY_CLI_COMMANDS = [
|
|
|
4259
4487
|
usage: "canonry query add <project> <query...> [--format json]",
|
|
4260
4488
|
run: async (input) => {
|
|
4261
4489
|
const project = requireProject(input, "query.add", "canonry query add <project> <query...> [--format json]");
|
|
4262
|
-
const
|
|
4263
|
-
if (
|
|
4490
|
+
const queries2 = input.positionals.slice(1);
|
|
4491
|
+
if (queries2.length === 0) {
|
|
4264
4492
|
throw usageError("Error: project name and at least one query required\nUsage: canonry query add <project> <query...> [--format json]", {
|
|
4265
4493
|
message: "project name and at least one query required",
|
|
4266
4494
|
details: {
|
|
@@ -4269,7 +4497,7 @@ var QUERY_CLI_COMMANDS = [
|
|
|
4269
4497
|
}
|
|
4270
4498
|
});
|
|
4271
4499
|
}
|
|
4272
|
-
await addQueries(project,
|
|
4500
|
+
await addQueries(project, queries2, input.format);
|
|
4273
4501
|
}
|
|
4274
4502
|
},
|
|
4275
4503
|
{
|
|
@@ -4277,8 +4505,8 @@ var QUERY_CLI_COMMANDS = [
|
|
|
4277
4505
|
usage: "canonry query replace <project> <query...> [--format json]",
|
|
4278
4506
|
run: async (input) => {
|
|
4279
4507
|
const project = requireProject(input, "query.replace", "canonry query replace <project> <query...> [--format json]");
|
|
4280
|
-
const
|
|
4281
|
-
if (
|
|
4508
|
+
const queries2 = input.positionals.slice(1);
|
|
4509
|
+
if (queries2.length === 0) {
|
|
4282
4510
|
throw usageError("Error: project name and at least one query required\nUsage: canonry query replace <project> <query...> [--format json]", {
|
|
4283
4511
|
message: "project name and at least one query required",
|
|
4284
4512
|
details: {
|
|
@@ -4287,7 +4515,7 @@ var QUERY_CLI_COMMANDS = [
|
|
|
4287
4515
|
}
|
|
4288
4516
|
});
|
|
4289
4517
|
}
|
|
4290
|
-
await replaceQueries(project,
|
|
4518
|
+
await replaceQueries(project, queries2, input.format);
|
|
4291
4519
|
}
|
|
4292
4520
|
},
|
|
4293
4521
|
{
|
|
@@ -4295,8 +4523,8 @@ var QUERY_CLI_COMMANDS = [
|
|
|
4295
4523
|
usage: "canonry query remove <project> <query...> [--format json]",
|
|
4296
4524
|
run: async (input) => {
|
|
4297
4525
|
const project = requireProject(input, "query.remove", "canonry query remove <project> <query...> [--format json]");
|
|
4298
|
-
const
|
|
4299
|
-
if (
|
|
4526
|
+
const queries2 = input.positionals.slice(1);
|
|
4527
|
+
if (queries2.length === 0) {
|
|
4300
4528
|
throw usageError("Error: project name and at least one query required\nUsage: canonry query remove <project> <query...> [--format json]", {
|
|
4301
4529
|
message: "project name and at least one query required",
|
|
4302
4530
|
details: {
|
|
@@ -4305,7 +4533,7 @@ var QUERY_CLI_COMMANDS = [
|
|
|
4305
4533
|
}
|
|
4306
4534
|
});
|
|
4307
4535
|
}
|
|
4308
|
-
await removeQueries(project,
|
|
4536
|
+
await removeQueries(project, queries2, input.format);
|
|
4309
4537
|
}
|
|
4310
4538
|
},
|
|
4311
4539
|
{
|
|
@@ -4313,8 +4541,8 @@ var QUERY_CLI_COMMANDS = [
|
|
|
4313
4541
|
usage: "canonry query delete <project> <query...> [--format json]",
|
|
4314
4542
|
run: async (input) => {
|
|
4315
4543
|
const project = requireProject(input, "query.delete", "canonry query delete <project> <query...> [--format json]");
|
|
4316
|
-
const
|
|
4317
|
-
if (
|
|
4544
|
+
const queries2 = input.positionals.slice(1);
|
|
4545
|
+
if (queries2.length === 0) {
|
|
4318
4546
|
throw usageError("Error: project name and at least one query required\nUsage: canonry query delete <project> <query...> [--format json]", {
|
|
4319
4547
|
message: "project name and at least one query required",
|
|
4320
4548
|
details: {
|
|
@@ -4323,7 +4551,7 @@ var QUERY_CLI_COMMANDS = [
|
|
|
4323
4551
|
}
|
|
4324
4552
|
});
|
|
4325
4553
|
}
|
|
4326
|
-
await removeQueries(project,
|
|
4554
|
+
await removeQueries(project, queries2, input.format);
|
|
4327
4555
|
}
|
|
4328
4556
|
},
|
|
4329
4557
|
{
|
|
@@ -4391,7 +4619,7 @@ var QUERY_CLI_COMMANDS = [
|
|
|
4391
4619
|
];
|
|
4392
4620
|
|
|
4393
4621
|
// src/commands/mcp.ts
|
|
4394
|
-
import
|
|
4622
|
+
import fs4 from "fs";
|
|
4395
4623
|
import path2 from "path";
|
|
4396
4624
|
import { createRequire } from "module";
|
|
4397
4625
|
|
|
@@ -4497,8 +4725,8 @@ function renderClientSnippet(client, serverName, entry) {
|
|
|
4497
4725
|
return renderJsonSnippet(serverName, entry, client.format);
|
|
4498
4726
|
}
|
|
4499
4727
|
function readJsonConfig(configPath) {
|
|
4500
|
-
if (!
|
|
4501
|
-
const raw =
|
|
4728
|
+
if (!fs4.existsSync(configPath)) return {};
|
|
4729
|
+
const raw = fs4.readFileSync(configPath, "utf-8").trim();
|
|
4502
4730
|
if (!raw) return {};
|
|
4503
4731
|
try {
|
|
4504
4732
|
const parsed = JSON.parse(raw);
|
|
@@ -4516,14 +4744,14 @@ function readJsonConfig(configPath) {
|
|
|
4516
4744
|
}
|
|
4517
4745
|
}
|
|
4518
4746
|
function writeJsonConfig(configPath, value) {
|
|
4519
|
-
|
|
4520
|
-
|
|
4747
|
+
fs4.mkdirSync(path2.dirname(configPath), { recursive: true });
|
|
4748
|
+
fs4.writeFileSync(configPath, `${JSON.stringify(value, null, 2)}
|
|
4521
4749
|
`, "utf-8");
|
|
4522
4750
|
}
|
|
4523
4751
|
function backupConfigIfPresent(configPath) {
|
|
4524
|
-
if (!
|
|
4752
|
+
if (!fs4.existsSync(configPath)) return void 0;
|
|
4525
4753
|
const backupPath = `${configPath}.canonry.bak`;
|
|
4526
|
-
|
|
4754
|
+
fs4.copyFileSync(configPath, backupPath);
|
|
4527
4755
|
return backupPath;
|
|
4528
4756
|
}
|
|
4529
4757
|
function findClientOrThrow(id) {
|
|
@@ -4870,13 +5098,13 @@ var NOTIFY_CLI_COMMANDS = [
|
|
|
4870
5098
|
];
|
|
4871
5099
|
|
|
4872
5100
|
// src/commands/apply.ts
|
|
4873
|
-
import
|
|
5101
|
+
import fs5 from "fs";
|
|
4874
5102
|
import { parseAllDocuments } from "yaml";
|
|
4875
5103
|
async function applyConfigFile(filePath) {
|
|
4876
|
-
if (!
|
|
5104
|
+
if (!fs5.existsSync(filePath)) {
|
|
4877
5105
|
throw new Error(`File not found: ${filePath}`);
|
|
4878
5106
|
}
|
|
4879
|
-
const content =
|
|
5107
|
+
const content = fs5.readFileSync(filePath, "utf-8");
|
|
4880
5108
|
const docs = parseAllDocuments(content);
|
|
4881
5109
|
const client = createApiClient();
|
|
4882
5110
|
const errors = [];
|
|
@@ -5620,7 +5848,7 @@ var PROJECT_CLI_COMMANDS = [
|
|
|
5620
5848
|
];
|
|
5621
5849
|
|
|
5622
5850
|
// src/commands/report.ts
|
|
5623
|
-
import
|
|
5851
|
+
import fs6 from "fs";
|
|
5624
5852
|
import path3 from "path";
|
|
5625
5853
|
function defaultOutputPath(project, audience) {
|
|
5626
5854
|
const date = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
@@ -5637,10 +5865,10 @@ async function runReportCommand(project, opts = {}) {
|
|
|
5637
5865
|
const html = renderReportHtml(report, { audience });
|
|
5638
5866
|
const targetPath = opts.output ? path3.resolve(opts.output) : defaultOutputPath(project, audience);
|
|
5639
5867
|
const dir = path3.dirname(targetPath);
|
|
5640
|
-
if (!
|
|
5641
|
-
|
|
5868
|
+
if (!fs6.existsSync(dir)) {
|
|
5869
|
+
fs6.mkdirSync(dir, { recursive: true });
|
|
5642
5870
|
}
|
|
5643
|
-
|
|
5871
|
+
fs6.writeFileSync(targetPath, html, "utf-8");
|
|
5644
5872
|
console.log(`Report written to ${targetPath}`);
|
|
5645
5873
|
}
|
|
5646
5874
|
|
|
@@ -6381,7 +6609,7 @@ Usage: canonry settings provider ${name} --api-key <key> [--model <model>] [--ma
|
|
|
6381
6609
|
];
|
|
6382
6610
|
|
|
6383
6611
|
// src/commands/skills.ts
|
|
6384
|
-
import
|
|
6612
|
+
import fs7 from "fs";
|
|
6385
6613
|
import path4 from "path";
|
|
6386
6614
|
import { fileURLToPath } from "url";
|
|
6387
6615
|
var BUNDLED_SKILL_NAMES = ["canonry-setup", "aero"];
|
|
@@ -6393,7 +6621,7 @@ function resolveBundledSkillsRoot(pkgDir) {
|
|
|
6393
6621
|
path4.join(here, "../../../../skills")
|
|
6394
6622
|
];
|
|
6395
6623
|
for (const candidate of candidates) {
|
|
6396
|
-
if (BUNDLED_SKILL_NAMES.every((name) =>
|
|
6624
|
+
if (BUNDLED_SKILL_NAMES.every((name) => fs7.existsSync(path4.join(candidate, name, "SKILL.md")))) {
|
|
6397
6625
|
return candidate;
|
|
6398
6626
|
}
|
|
6399
6627
|
}
|
|
@@ -6416,13 +6644,13 @@ function getBundledSkills(pkgDir) {
|
|
|
6416
6644
|
return BUNDLED_SKILL_NAMES.map((name) => {
|
|
6417
6645
|
const skillDir = path4.join(root, name);
|
|
6418
6646
|
const skillFile = path4.join(skillDir, "SKILL.md");
|
|
6419
|
-
const content =
|
|
6647
|
+
const content = fs7.readFileSync(skillFile, "utf-8");
|
|
6420
6648
|
return { name, description: parseDescription(content), bundledPath: skillDir };
|
|
6421
6649
|
});
|
|
6422
6650
|
}
|
|
6423
6651
|
function walkRelative(dir, prefix = "") {
|
|
6424
6652
|
const out = [];
|
|
6425
|
-
for (const entry of
|
|
6653
|
+
for (const entry of fs7.readdirSync(dir, { withFileTypes: true })) {
|
|
6426
6654
|
const rel = prefix ? path4.join(prefix, entry.name) : entry.name;
|
|
6427
6655
|
const full = path4.join(dir, entry.name);
|
|
6428
6656
|
if (entry.isDirectory()) {
|
|
@@ -6434,28 +6662,28 @@ function walkRelative(dir, prefix = "") {
|
|
|
6434
6662
|
return out.sort();
|
|
6435
6663
|
}
|
|
6436
6664
|
function compareDirContent(srcDir, destDir) {
|
|
6437
|
-
if (!
|
|
6438
|
-
if (!
|
|
6665
|
+
if (!fs7.existsSync(destDir)) return "missing";
|
|
6666
|
+
if (!fs7.statSync(destDir).isDirectory()) return "different";
|
|
6439
6667
|
const srcFiles = walkRelative(srcDir);
|
|
6440
6668
|
const destFiles = walkRelative(destDir);
|
|
6441
6669
|
if (srcFiles.length !== destFiles.length) return "different";
|
|
6442
6670
|
for (let i = 0; i < srcFiles.length; i++) {
|
|
6443
6671
|
if (srcFiles[i] !== destFiles[i]) return "different";
|
|
6444
|
-
const srcBytes =
|
|
6445
|
-
const destBytes =
|
|
6672
|
+
const srcBytes = fs7.readFileSync(path4.join(srcDir, srcFiles[i]));
|
|
6673
|
+
const destBytes = fs7.readFileSync(path4.join(destDir, destFiles[i]));
|
|
6446
6674
|
if (!srcBytes.equals(destBytes)) return "different";
|
|
6447
6675
|
}
|
|
6448
6676
|
return "match";
|
|
6449
6677
|
}
|
|
6450
6678
|
function copyDirRecursive(src, dest) {
|
|
6451
|
-
|
|
6452
|
-
for (const entry of
|
|
6679
|
+
fs7.mkdirSync(dest, { recursive: true });
|
|
6680
|
+
for (const entry of fs7.readdirSync(src, { withFileTypes: true })) {
|
|
6453
6681
|
const srcPath = path4.join(src, entry.name);
|
|
6454
6682
|
const destPath = path4.join(dest, entry.name);
|
|
6455
6683
|
if (entry.isDirectory()) {
|
|
6456
6684
|
copyDirRecursive(srcPath, destPath);
|
|
6457
6685
|
} else if (entry.isFile()) {
|
|
6458
|
-
|
|
6686
|
+
fs7.copyFileSync(srcPath, destPath);
|
|
6459
6687
|
}
|
|
6460
6688
|
}
|
|
6461
6689
|
}
|
|
@@ -6480,7 +6708,7 @@ function installClaudeSkill(skill, targetDir, force) {
|
|
|
6480
6708
|
});
|
|
6481
6709
|
}
|
|
6482
6710
|
if (compare === "different") {
|
|
6483
|
-
|
|
6711
|
+
fs7.rmSync(targetPath, { recursive: true, force: true });
|
|
6484
6712
|
}
|
|
6485
6713
|
copyDirRecursive(skill.bundledPath, targetPath);
|
|
6486
6714
|
return {
|
|
@@ -6495,15 +6723,15 @@ function installCodexSymlink(skill, targetDir, force) {
|
|
|
6495
6723
|
const codexPath = path4.join(targetDir, ".codex", "skills", skill.name);
|
|
6496
6724
|
const claudePath = path4.join(targetDir, ".claude", "skills", skill.name);
|
|
6497
6725
|
const linkTarget = path4.relative(path4.dirname(codexPath), claudePath);
|
|
6498
|
-
|
|
6726
|
+
fs7.mkdirSync(path4.dirname(codexPath), { recursive: true });
|
|
6499
6727
|
let stat;
|
|
6500
6728
|
try {
|
|
6501
|
-
stat =
|
|
6729
|
+
stat = fs7.lstatSync(codexPath);
|
|
6502
6730
|
} catch {
|
|
6503
6731
|
stat = void 0;
|
|
6504
6732
|
}
|
|
6505
6733
|
if (stat?.isSymbolicLink()) {
|
|
6506
|
-
const existing =
|
|
6734
|
+
const existing = fs7.readlinkSync(codexPath);
|
|
6507
6735
|
if (existing === linkTarget) {
|
|
6508
6736
|
return {
|
|
6509
6737
|
skill: skill.name,
|
|
@@ -6521,8 +6749,8 @@ function installCodexSymlink(skill, targetDir, force) {
|
|
|
6521
6749
|
exitCode: 1
|
|
6522
6750
|
});
|
|
6523
6751
|
}
|
|
6524
|
-
|
|
6525
|
-
|
|
6752
|
+
fs7.unlinkSync(codexPath);
|
|
6753
|
+
fs7.symlinkSync(linkTarget, codexPath);
|
|
6526
6754
|
return {
|
|
6527
6755
|
skill: skill.name,
|
|
6528
6756
|
client: CodingAgents.codex,
|
|
@@ -6540,9 +6768,9 @@ function installCodexSymlink(skill, targetDir, force) {
|
|
|
6540
6768
|
exitCode: 1
|
|
6541
6769
|
});
|
|
6542
6770
|
}
|
|
6543
|
-
|
|
6771
|
+
fs7.rmSync(codexPath, { recursive: true, force: true });
|
|
6544
6772
|
}
|
|
6545
|
-
|
|
6773
|
+
fs7.symlinkSync(linkTarget, codexPath);
|
|
6546
6774
|
return {
|
|
6547
6775
|
skill: skill.name,
|
|
6548
6776
|
client: CodingAgents.codex,
|
|
@@ -6574,7 +6802,7 @@ async function installSkills(opts = {}) {
|
|
|
6574
6802
|
});
|
|
6575
6803
|
}
|
|
6576
6804
|
const skillsToInstall = allSkills.filter((s) => requestedNames.includes(s.name));
|
|
6577
|
-
|
|
6805
|
+
fs7.mkdirSync(targetDir, { recursive: true });
|
|
6578
6806
|
const results = [];
|
|
6579
6807
|
for (const skill of skillsToInstall) {
|
|
6580
6808
|
results.push(installClaudeSkill(skill, targetDir, force));
|
|
@@ -6675,11 +6903,11 @@ var SKILLS_CLI_COMMANDS = [
|
|
|
6675
6903
|
];
|
|
6676
6904
|
|
|
6677
6905
|
// src/commands/snapshot.ts
|
|
6678
|
-
import
|
|
6906
|
+
import fs9 from "fs";
|
|
6679
6907
|
import path6 from "path";
|
|
6680
6908
|
|
|
6681
6909
|
// src/snapshot-pdf.ts
|
|
6682
|
-
import
|
|
6910
|
+
import fs8 from "fs";
|
|
6683
6911
|
import path5 from "path";
|
|
6684
6912
|
import { PDFDocument, StandardFonts, rgb } from "pdf-lib";
|
|
6685
6913
|
var PAGE_WIDTH = 612;
|
|
@@ -6890,8 +7118,8 @@ async function writeSnapshotPdf(report, outputPath) {
|
|
|
6890
7118
|
renderQueries(pdf, report);
|
|
6891
7119
|
const bytes = await doc.save();
|
|
6892
7120
|
const resolvedPath = path5.resolve(outputPath);
|
|
6893
|
-
|
|
6894
|
-
|
|
7121
|
+
fs8.mkdirSync(path5.dirname(resolvedPath), { recursive: true });
|
|
7122
|
+
fs8.writeFileSync(resolvedPath, bytes);
|
|
6895
7123
|
return resolvedPath;
|
|
6896
7124
|
}
|
|
6897
7125
|
function renderCover(pdf, report) {
|
|
@@ -7050,8 +7278,8 @@ PDF saved: ${savedPdfPath}`);
|
|
|
7050
7278
|
}
|
|
7051
7279
|
function writeSnapshotMarkdown(report, outputPath) {
|
|
7052
7280
|
const resolvedPath = path6.resolve(outputPath);
|
|
7053
|
-
|
|
7054
|
-
|
|
7281
|
+
fs9.mkdirSync(path6.dirname(resolvedPath), { recursive: true });
|
|
7282
|
+
fs9.writeFileSync(resolvedPath, formatSnapshotMarkdown(report), "utf-8");
|
|
7055
7283
|
return resolvedPath;
|
|
7056
7284
|
}
|
|
7057
7285
|
function formatSnapshotMarkdown(report) {
|
|
@@ -7993,7 +8221,7 @@ async function bootstrapCommand(_opts) {
|
|
|
7993
8221
|
|
|
7994
8222
|
// src/commands/daemon.ts
|
|
7995
8223
|
import { spawn } from "child_process";
|
|
7996
|
-
import
|
|
8224
|
+
import fs10 from "fs";
|
|
7997
8225
|
import path8 from "path";
|
|
7998
8226
|
function getPidPath() {
|
|
7999
8227
|
return path8.join(getConfigDir(), "canonry.pid");
|
|
@@ -8023,8 +8251,8 @@ async function waitForReady(host, port, maxMs = 1e4) {
|
|
|
8023
8251
|
async function startDaemon(opts) {
|
|
8024
8252
|
const pidPath = getPidPath();
|
|
8025
8253
|
const format = opts.format ?? "text";
|
|
8026
|
-
if (
|
|
8027
|
-
const existingPid = parseInt(
|
|
8254
|
+
if (fs10.existsSync(pidPath)) {
|
|
8255
|
+
const existingPid = parseInt(fs10.readFileSync(pidPath, "utf-8").trim(), 10);
|
|
8028
8256
|
if (!isNaN(existingPid) && isProcessAlive(existingPid)) {
|
|
8029
8257
|
throw new CliError({
|
|
8030
8258
|
code: "DAEMON_ALREADY_RUNNING",
|
|
@@ -8035,7 +8263,7 @@ async function startDaemon(opts) {
|
|
|
8035
8263
|
}
|
|
8036
8264
|
});
|
|
8037
8265
|
}
|
|
8038
|
-
|
|
8266
|
+
fs10.unlinkSync(pidPath);
|
|
8039
8267
|
}
|
|
8040
8268
|
const cliPath = path8.resolve(new URL(import.meta.url).pathname);
|
|
8041
8269
|
const inSourceMode = new URL(import.meta.url).pathname.endsWith(".ts");
|
|
@@ -8056,10 +8284,10 @@ async function startDaemon(opts) {
|
|
|
8056
8284
|
});
|
|
8057
8285
|
}
|
|
8058
8286
|
const configDir = getConfigDir();
|
|
8059
|
-
if (!
|
|
8060
|
-
|
|
8287
|
+
if (!fs10.existsSync(configDir)) {
|
|
8288
|
+
fs10.mkdirSync(configDir, { recursive: true });
|
|
8061
8289
|
}
|
|
8062
|
-
|
|
8290
|
+
fs10.writeFileSync(pidPath, String(child.pid), "utf-8");
|
|
8063
8291
|
const port = opts.port ?? "4100";
|
|
8064
8292
|
const host = opts.host ?? "127.0.0.1";
|
|
8065
8293
|
if (format !== "json") {
|
|
@@ -8068,7 +8296,7 @@ async function startDaemon(opts) {
|
|
|
8068
8296
|
const ready = await waitForReady(host, port);
|
|
8069
8297
|
if (!ready) {
|
|
8070
8298
|
try {
|
|
8071
|
-
|
|
8299
|
+
fs10.unlinkSync(pidPath);
|
|
8072
8300
|
} catch {
|
|
8073
8301
|
}
|
|
8074
8302
|
throw new CliError({
|
|
@@ -8100,7 +8328,7 @@ async function startDaemon(opts) {
|
|
|
8100
8328
|
}
|
|
8101
8329
|
function stopDaemon(format = "text") {
|
|
8102
8330
|
const pidPath = getPidPath();
|
|
8103
|
-
if (!
|
|
8331
|
+
if (!fs10.existsSync(pidPath)) {
|
|
8104
8332
|
if (format === "json") {
|
|
8105
8333
|
console.log(JSON.stringify({
|
|
8106
8334
|
stopped: false,
|
|
@@ -8111,7 +8339,7 @@ function stopDaemon(format = "text") {
|
|
|
8111
8339
|
console.log("Canonry is not running (no PID file found)");
|
|
8112
8340
|
return;
|
|
8113
8341
|
}
|
|
8114
|
-
const pid = parseInt(
|
|
8342
|
+
const pid = parseInt(fs10.readFileSync(pidPath, "utf-8").trim(), 10);
|
|
8115
8343
|
if (isNaN(pid)) {
|
|
8116
8344
|
if (format === "json") {
|
|
8117
8345
|
console.log(JSON.stringify({
|
|
@@ -8122,7 +8350,7 @@ function stopDaemon(format = "text") {
|
|
|
8122
8350
|
} else {
|
|
8123
8351
|
console.error("Invalid PID file. Removing it.");
|
|
8124
8352
|
}
|
|
8125
|
-
|
|
8353
|
+
fs10.unlinkSync(pidPath);
|
|
8126
8354
|
return;
|
|
8127
8355
|
}
|
|
8128
8356
|
if (!isProcessAlive(pid)) {
|
|
@@ -8136,12 +8364,12 @@ function stopDaemon(format = "text") {
|
|
|
8136
8364
|
} else {
|
|
8137
8365
|
console.log(`Canonry is not running (stale PID: ${pid}). Cleaning up.`);
|
|
8138
8366
|
}
|
|
8139
|
-
|
|
8367
|
+
fs10.unlinkSync(pidPath);
|
|
8140
8368
|
return;
|
|
8141
8369
|
}
|
|
8142
8370
|
try {
|
|
8143
8371
|
process.kill(pid, "SIGTERM");
|
|
8144
|
-
|
|
8372
|
+
fs10.unlinkSync(pidPath);
|
|
8145
8373
|
if (format === "json") {
|
|
8146
8374
|
console.log(JSON.stringify({
|
|
8147
8375
|
stopped: true,
|
|
@@ -8165,7 +8393,7 @@ function stopDaemon(format = "text") {
|
|
|
8165
8393
|
|
|
8166
8394
|
// src/commands/init.ts
|
|
8167
8395
|
import crypto2 from "crypto";
|
|
8168
|
-
import
|
|
8396
|
+
import fs11 from "fs";
|
|
8169
8397
|
import readline from "readline";
|
|
8170
8398
|
import path9 from "path";
|
|
8171
8399
|
function prompt(question) {
|
|
@@ -8189,7 +8417,7 @@ var PROJECT_MARKERS = [".git", "canonry.yaml", "canonry.yml", "package.json"];
|
|
|
8189
8417
|
function cwdLooksLikeProject(dir) {
|
|
8190
8418
|
const home = process.env.HOME ?? "";
|
|
8191
8419
|
if (home && path9.resolve(dir) === path9.resolve(home)) return false;
|
|
8192
|
-
return PROJECT_MARKERS.some((marker) =>
|
|
8420
|
+
return PROJECT_MARKERS.some((marker) => fs11.existsSync(path9.join(dir, marker)));
|
|
8193
8421
|
}
|
|
8194
8422
|
var DEFAULT_AGENT_MODELS = {
|
|
8195
8423
|
anthropic: "anthropic/claude-sonnet-4-6",
|
|
@@ -8219,8 +8447,8 @@ async function initCommand(opts) {
|
|
|
8219
8447
|
return void 0;
|
|
8220
8448
|
}
|
|
8221
8449
|
const configDir = getConfigDir();
|
|
8222
|
-
if (!
|
|
8223
|
-
|
|
8450
|
+
if (!fs11.existsSync(configDir)) {
|
|
8451
|
+
fs11.mkdirSync(configDir, { recursive: true });
|
|
8224
8452
|
}
|
|
8225
8453
|
const bootstrapEnv = getBootstrapEnv(process.env, {
|
|
8226
8454
|
GEMINI_API_KEY: opts?.geminiKey,
|
|
@@ -8530,6 +8758,7 @@ Received ${signal}, stopping server...`);
|
|
|
8530
8758
|
Canonry server running at ${url}`);
|
|
8531
8759
|
console.log("Press Ctrl+C to stop.\n");
|
|
8532
8760
|
}
|
|
8761
|
+
setTelemetrySource("cli-server");
|
|
8533
8762
|
const providerNames = Object.keys(config.providers ?? {}).filter(
|
|
8534
8763
|
(k) => config.providers?.[k]?.apiKey || config.providers?.[k]?.baseUrl
|
|
8535
8764
|
);
|
|
@@ -8815,7 +9044,7 @@ var SYSTEM_CLI_COMMANDS = [
|
|
|
8815
9044
|
];
|
|
8816
9045
|
|
|
8817
9046
|
// src/cli-commands/wordpress.ts
|
|
8818
|
-
import
|
|
9047
|
+
import fs12 from "fs";
|
|
8819
9048
|
|
|
8820
9049
|
// src/commands/wordpress.ts
|
|
8821
9050
|
function getClient20() {
|
|
@@ -9051,12 +9280,12 @@ async function wordpressSetMeta(project, body) {
|
|
|
9051
9280
|
printPageDetail(result);
|
|
9052
9281
|
}
|
|
9053
9282
|
async function wordpressBulkSetMeta(project, opts) {
|
|
9054
|
-
const
|
|
9283
|
+
const fs13 = await import("fs/promises");
|
|
9055
9284
|
const path10 = await import("path");
|
|
9056
9285
|
const filePath = path10.resolve(opts.from);
|
|
9057
9286
|
let raw;
|
|
9058
9287
|
try {
|
|
9059
|
-
raw = await
|
|
9288
|
+
raw = await fs13.readFile(filePath, "utf8");
|
|
9060
9289
|
} catch {
|
|
9061
9290
|
throw new CliError({
|
|
9062
9291
|
code: "FILE_READ_ERROR",
|
|
@@ -9153,13 +9382,13 @@ async function wordpressSetSchema(project, body) {
|
|
|
9153
9382
|
printManualAssist(`Schema update for "${body.slug}"`, result);
|
|
9154
9383
|
}
|
|
9155
9384
|
async function wordpressSchemaDeploy(project, opts) {
|
|
9156
|
-
const
|
|
9385
|
+
const fs13 = await import("fs/promises");
|
|
9157
9386
|
const path10 = await import("path");
|
|
9158
9387
|
const yaml = await import("yaml").catch(() => null);
|
|
9159
9388
|
const filePath = path10.resolve(opts.profile);
|
|
9160
9389
|
let raw;
|
|
9161
9390
|
try {
|
|
9162
|
-
raw = await
|
|
9391
|
+
raw = await fs13.readFile(filePath, "utf8");
|
|
9163
9392
|
} catch {
|
|
9164
9393
|
throw new CliError({
|
|
9165
9394
|
code: "FILE_READ_ERROR",
|
|
@@ -9264,13 +9493,13 @@ async function wordpressOnboard(project, opts) {
|
|
|
9264
9493
|
}
|
|
9265
9494
|
let profileData;
|
|
9266
9495
|
if (opts.profile) {
|
|
9267
|
-
const
|
|
9496
|
+
const fs13 = await import("fs/promises");
|
|
9268
9497
|
const path10 = await import("path");
|
|
9269
9498
|
const yaml = await import("yaml").catch(() => null);
|
|
9270
9499
|
const filePath = path10.resolve(opts.profile);
|
|
9271
9500
|
let raw;
|
|
9272
9501
|
try {
|
|
9273
|
-
raw = await
|
|
9502
|
+
raw = await fs13.readFile(filePath, "utf8");
|
|
9274
9503
|
} catch {
|
|
9275
9504
|
throw new CliError({
|
|
9276
9505
|
code: "FILE_READ_ERROR",
|
|
@@ -9419,7 +9648,7 @@ function resolveContent(input, command, usage, options) {
|
|
|
9419
9648
|
}
|
|
9420
9649
|
if (contentFile) {
|
|
9421
9650
|
try {
|
|
9422
|
-
return
|
|
9651
|
+
return fs12.readFileSync(contentFile, "utf-8");
|
|
9423
9652
|
} catch (error) {
|
|
9424
9653
|
const message = error instanceof Error ? error.message : String(error);
|
|
9425
9654
|
throw usageError(`Error: could not read --content-file "${contentFile}": ${message}`, {
|
|
@@ -10410,6 +10639,7 @@ Integrations:
|
|
|
10410
10639
|
google Google Search Console / Analytics
|
|
10411
10640
|
bing Bing Webmaster Tools
|
|
10412
10641
|
wordpress WordPress REST API
|
|
10642
|
+
traffic Server-side traffic ingestion (Cloud Run)
|
|
10413
10643
|
|
|
10414
10644
|
Automation:
|
|
10415
10645
|
schedule Manage scheduled runs
|
|
@@ -10451,7 +10681,7 @@ async function runCli(args = process.argv.slice(2)) {
|
|
|
10451
10681
|
showFirstRunNotice();
|
|
10452
10682
|
getOrCreateAnonymousId();
|
|
10453
10683
|
}
|
|
10454
|
-
const SUBCOMMAND_COMMANDS = /* @__PURE__ */ new Set(["backfill", "project", "query", "keyword", "competitor", "schedule", "notify", "settings", "telemetry", "google", "bing", "wordpress", "cdp"]);
|
|
10684
|
+
const SUBCOMMAND_COMMANDS = /* @__PURE__ */ new Set(["backfill", "project", "query", "keyword", "competitor", "schedule", "notify", "settings", "telemetry", "google", "bing", "wordpress", "cdp", "traffic"]);
|
|
10455
10685
|
const MIXED_SUBCOMMANDS = {
|
|
10456
10686
|
insights: /* @__PURE__ */ new Set(["dismiss"]),
|
|
10457
10687
|
run: /* @__PURE__ */ new Set(["show", "cancel"])
|
|
@@ -10465,7 +10695,12 @@ async function runCli(args = process.argv.slice(2)) {
|
|
|
10465
10695
|
resolvedCommand = command;
|
|
10466
10696
|
}
|
|
10467
10697
|
if (!isHelpRequest && command !== "telemetry") {
|
|
10468
|
-
|
|
10698
|
+
detectAndTrackUpgrade();
|
|
10699
|
+
const setupState = buildSetupState();
|
|
10700
|
+
trackEvent("cli.command", {
|
|
10701
|
+
command: resolvedCommand,
|
|
10702
|
+
...setupState ? { setup_state: setupState } : {}
|
|
10703
|
+
});
|
|
10469
10704
|
}
|
|
10470
10705
|
try {
|
|
10471
10706
|
if (await dispatchRegisteredCommand(args, format, REGISTERED_CLI_COMMANDS)) {
|