@ainyc/canonry 3.6.4 → 4.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -10
- package/assets/agent-workspace/AGENTS.md +4 -4
- package/assets/agent-workspace/USER.md +1 -1
- package/assets/agent-workspace/skills/aero/SKILL.md +4 -4
- package/assets/agent-workspace/skills/aero/references/memory-patterns.md +3 -3
- package/assets/agent-workspace/skills/aero/references/orchestration.md +6 -6
- package/assets/agent-workspace/skills/aero/references/regression-playbook.md +7 -7
- package/assets/agent-workspace/skills/aero/references/reporting.md +8 -8
- package/assets/agent-workspace/skills/aero/soul.md +1 -1
- package/assets/agent-workspace/skills/canonry-setup/SKILL.md +5 -5
- package/assets/agent-workspace/skills/canonry-setup/references/aeo-analysis.md +15 -15
- package/assets/agent-workspace/skills/canonry-setup/references/canonry-cli.md +8 -8
- package/assets/assets/index-D7T5wSBj.css +1 -0
- package/assets/assets/index-Dtgn4FDp.js +302 -0
- package/assets/index.html +2 -2
- package/dist/{chunk-JMVBV3AT.js → chunk-BQN6BBHI.js} +707 -456
- package/dist/{chunk-RQMOJEJT.js → chunk-KCETXLDF.js} +106 -16
- package/dist/{chunk-GP2P2WPS.js → chunk-NCWCPBOT.js} +111 -49
- package/dist/{chunk-O7EVT3AF.js → chunk-O5JZQUPX.js} +71 -33
- package/dist/cli.js +472 -193
- package/dist/index.js +4 -4
- package/dist/{intelligence-service-NL4BG3SM.js → intelligence-service-EITZP4KG.js} +2 -2
- package/dist/mcp.js +4 -4
- package/package.json +7 -7
- package/assets/assets/index-C9XiA1Ol.js +0 -302
- package/assets/assets/index-D3wFrrZA.css +0 -1
package/dist/cli.js
CHANGED
|
@@ -18,7 +18,7 @@ import {
|
|
|
18
18
|
setGoogleAuthConfig,
|
|
19
19
|
showFirstRunNotice,
|
|
20
20
|
trackEvent
|
|
21
|
-
} from "./chunk-
|
|
21
|
+
} from "./chunk-BQN6BBHI.js";
|
|
22
22
|
import {
|
|
23
23
|
CliError,
|
|
24
24
|
EXIT_SYSTEM_ERROR,
|
|
@@ -33,7 +33,7 @@ import {
|
|
|
33
33
|
saveConfig,
|
|
34
34
|
saveConfigPatch,
|
|
35
35
|
usageError
|
|
36
|
-
} from "./chunk-
|
|
36
|
+
} from "./chunk-KCETXLDF.js";
|
|
37
37
|
import {
|
|
38
38
|
apiKeys,
|
|
39
39
|
competitors,
|
|
@@ -45,7 +45,7 @@ import {
|
|
|
45
45
|
projects,
|
|
46
46
|
querySnapshots,
|
|
47
47
|
runs
|
|
48
|
-
} from "./chunk-
|
|
48
|
+
} from "./chunk-NCWCPBOT.js";
|
|
49
49
|
import {
|
|
50
50
|
CcReleaseSyncStatuses,
|
|
51
51
|
CheckScopes,
|
|
@@ -63,7 +63,7 @@ import {
|
|
|
63
63
|
providerQuotaPolicySchema,
|
|
64
64
|
resolveProviderInput,
|
|
65
65
|
skillsClientSchema
|
|
66
|
-
} from "./chunk-
|
|
66
|
+
} from "./chunk-O5JZQUPX.js";
|
|
67
67
|
|
|
68
68
|
// src/cli.ts
|
|
69
69
|
import { pathToFileURL } from "url";
|
|
@@ -579,7 +579,7 @@ function readStoredGroundingSources(rawResponse) {
|
|
|
579
579
|
return result;
|
|
580
580
|
}
|
|
581
581
|
async function backfillInsightsCommand(project, opts) {
|
|
582
|
-
const { IntelligenceService } = await import("./intelligence-service-
|
|
582
|
+
const { IntelligenceService } = await import("./intelligence-service-EITZP4KG.js");
|
|
583
583
|
const config = loadConfig();
|
|
584
584
|
const db = createClient(config.database);
|
|
585
585
|
migrate(db);
|
|
@@ -2017,9 +2017,9 @@ async function gaConnect(project, opts) {
|
|
|
2017
2017
|
propertyId: opts.propertyId
|
|
2018
2018
|
};
|
|
2019
2019
|
if (opts.keyFile) {
|
|
2020
|
-
const
|
|
2020
|
+
const fs12 = await import("fs");
|
|
2021
2021
|
try {
|
|
2022
|
-
const content =
|
|
2022
|
+
const content = fs12.readFileSync(opts.keyFile, "utf-8");
|
|
2023
2023
|
JSON.parse(content);
|
|
2024
2024
|
body.keyJson = content;
|
|
2025
2025
|
} catch (e) {
|
|
@@ -3947,8 +3947,285 @@ var KEYWORD_CLI_COMMANDS = [
|
|
|
3947
3947
|
}
|
|
3948
3948
|
];
|
|
3949
3949
|
|
|
3950
|
-
// src/commands/
|
|
3950
|
+
// src/commands/query.ts
|
|
3951
3951
|
import fs2 from "fs";
|
|
3952
|
+
function getClient8() {
|
|
3953
|
+
return createApiClient();
|
|
3954
|
+
}
|
|
3955
|
+
async function addQueries(project, queries, format) {
|
|
3956
|
+
const client = getClient8();
|
|
3957
|
+
await client.appendQueries(project, queries);
|
|
3958
|
+
if (format === "json") {
|
|
3959
|
+
console.log(JSON.stringify({
|
|
3960
|
+
project,
|
|
3961
|
+
queries,
|
|
3962
|
+
addedCount: queries.length
|
|
3963
|
+
}, null, 2));
|
|
3964
|
+
return;
|
|
3965
|
+
}
|
|
3966
|
+
console.log(`Added ${queries.length} ${queries.length === 1 ? "query" : "queries"} to "${project}".`);
|
|
3967
|
+
}
|
|
3968
|
+
async function replaceQueries(project, queries, format) {
|
|
3969
|
+
const client = getClient8();
|
|
3970
|
+
await client.putQueries(project, queries);
|
|
3971
|
+
if (format === "json") {
|
|
3972
|
+
console.log(JSON.stringify({
|
|
3973
|
+
project,
|
|
3974
|
+
queries,
|
|
3975
|
+
replacedCount: queries.length
|
|
3976
|
+
}, null, 2));
|
|
3977
|
+
return;
|
|
3978
|
+
}
|
|
3979
|
+
console.log(`Set ${queries.length} ${queries.length === 1 ? "query" : "queries"} for "${project}".`);
|
|
3980
|
+
}
|
|
3981
|
+
async function removeQueries(project, queries, format) {
|
|
3982
|
+
const client = getClient8();
|
|
3983
|
+
const existing = await client.listQueries(project);
|
|
3984
|
+
const existingSet = new Set(existing.map((q) => q.query));
|
|
3985
|
+
const removedQueries = queries.filter((q) => existingSet.has(q));
|
|
3986
|
+
await client.deleteQueries(project, queries);
|
|
3987
|
+
if (format === "json") {
|
|
3988
|
+
console.log(JSON.stringify({
|
|
3989
|
+
project,
|
|
3990
|
+
queries,
|
|
3991
|
+
removedQueries,
|
|
3992
|
+
removedCount: removedQueries.length
|
|
3993
|
+
}, null, 2));
|
|
3994
|
+
return;
|
|
3995
|
+
}
|
|
3996
|
+
console.log(`Removed ${removedQueries.length} ${removedQueries.length === 1 ? "query" : "queries"} from "${project}".`);
|
|
3997
|
+
}
|
|
3998
|
+
async function listQueries(project, format) {
|
|
3999
|
+
const client = getClient8();
|
|
4000
|
+
const qs = await client.listQueries(project);
|
|
4001
|
+
if (format === "json") {
|
|
4002
|
+
console.log(JSON.stringify(qs, null, 2));
|
|
4003
|
+
return;
|
|
4004
|
+
}
|
|
4005
|
+
if (qs.length === 0) {
|
|
4006
|
+
console.log(`No queries found for "${project}".`);
|
|
4007
|
+
return;
|
|
4008
|
+
}
|
|
4009
|
+
console.log(`Queries for "${project}" (${qs.length}):
|
|
4010
|
+
`);
|
|
4011
|
+
for (const q of qs) {
|
|
4012
|
+
console.log(` ${q.query}`);
|
|
4013
|
+
}
|
|
4014
|
+
}
|
|
4015
|
+
async function importQueries(project, filePath, format) {
|
|
4016
|
+
if (!fs2.existsSync(filePath)) {
|
|
4017
|
+
throw new CliError({
|
|
4018
|
+
code: "QUERY_IMPORT_FILE_NOT_FOUND",
|
|
4019
|
+
message: `File not found: ${filePath}`,
|
|
4020
|
+
displayMessage: `Error: file not found: ${filePath}`,
|
|
4021
|
+
details: {
|
|
4022
|
+
project,
|
|
4023
|
+
filePath
|
|
4024
|
+
}
|
|
4025
|
+
});
|
|
4026
|
+
}
|
|
4027
|
+
const content = fs2.readFileSync(filePath, "utf-8");
|
|
4028
|
+
const queries = content.split("\n").map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#"));
|
|
4029
|
+
if (queries.length === 0) {
|
|
4030
|
+
if (format === "json") {
|
|
4031
|
+
console.log(JSON.stringify({
|
|
4032
|
+
project,
|
|
4033
|
+
filePath,
|
|
4034
|
+
queries: [],
|
|
4035
|
+
importedCount: 0
|
|
4036
|
+
}, null, 2));
|
|
4037
|
+
return;
|
|
4038
|
+
}
|
|
4039
|
+
console.log("No queries found in file.");
|
|
4040
|
+
return;
|
|
4041
|
+
}
|
|
4042
|
+
const client = getClient8();
|
|
4043
|
+
await client.appendQueries(project, queries);
|
|
4044
|
+
if (format === "json") {
|
|
4045
|
+
console.log(JSON.stringify({
|
|
4046
|
+
project,
|
|
4047
|
+
filePath,
|
|
4048
|
+
queries,
|
|
4049
|
+
importedCount: queries.length
|
|
4050
|
+
}, null, 2));
|
|
4051
|
+
return;
|
|
4052
|
+
}
|
|
4053
|
+
console.log(`Imported ${queries.length} ${queries.length === 1 ? "query" : "queries"} to "${project}".`);
|
|
4054
|
+
}
|
|
4055
|
+
async function generateQueries(project, provider, opts) {
|
|
4056
|
+
const client = getClient8();
|
|
4057
|
+
const result = await client.generateQueries(project, provider, opts.count);
|
|
4058
|
+
const saved = Boolean(opts.save && result.queries.length > 0);
|
|
4059
|
+
if (opts.format !== "json") {
|
|
4060
|
+
console.log(`Generated ${result.queries.length} ${result.queries.length === 1 ? "query" : "queries"} using ${result.provider}:
|
|
4061
|
+
`);
|
|
4062
|
+
for (const q of result.queries) {
|
|
4063
|
+
console.log(` ${q}`);
|
|
4064
|
+
}
|
|
4065
|
+
if (result.queries.length > 0 && !saved) {
|
|
4066
|
+
console.log(`
|
|
4067
|
+
To add these, run: canonry query add ${project} <query>...`);
|
|
4068
|
+
}
|
|
4069
|
+
}
|
|
4070
|
+
if (saved) {
|
|
4071
|
+
await client.appendQueries(project, result.queries);
|
|
4072
|
+
if (opts.format !== "json") {
|
|
4073
|
+
console.log(`
|
|
4074
|
+
Saved ${result.queries.length} ${result.queries.length === 1 ? "query" : "queries"} to "${project}".`);
|
|
4075
|
+
}
|
|
4076
|
+
}
|
|
4077
|
+
if (opts.format === "json") {
|
|
4078
|
+
console.log(JSON.stringify({
|
|
4079
|
+
project,
|
|
4080
|
+
provider: result.provider,
|
|
4081
|
+
queries: result.queries,
|
|
4082
|
+
generatedCount: result.queries.length,
|
|
4083
|
+
saved,
|
|
4084
|
+
savedCount: saved ? result.queries.length : 0
|
|
4085
|
+
}, null, 2));
|
|
4086
|
+
}
|
|
4087
|
+
}
|
|
4088
|
+
|
|
4089
|
+
// src/cli-commands/query.ts
|
|
4090
|
+
var QUERY_CLI_COMMANDS = [
|
|
4091
|
+
{
|
|
4092
|
+
path: ["query", "add"],
|
|
4093
|
+
usage: "canonry query add <project> <query...> [--format json]",
|
|
4094
|
+
run: async (input) => {
|
|
4095
|
+
const project = requireProject(input, "query.add", "canonry query add <project> <query...> [--format json]");
|
|
4096
|
+
const queries = input.positionals.slice(1);
|
|
4097
|
+
if (queries.length === 0) {
|
|
4098
|
+
throw usageError("Error: project name and at least one query required\nUsage: canonry query add <project> <query...> [--format json]", {
|
|
4099
|
+
message: "project name and at least one query required",
|
|
4100
|
+
details: {
|
|
4101
|
+
command: "query.add",
|
|
4102
|
+
usage: "canonry query add <project> <query...> [--format json]"
|
|
4103
|
+
}
|
|
4104
|
+
});
|
|
4105
|
+
}
|
|
4106
|
+
await addQueries(project, queries, input.format);
|
|
4107
|
+
}
|
|
4108
|
+
},
|
|
4109
|
+
{
|
|
4110
|
+
path: ["query", "replace"],
|
|
4111
|
+
usage: "canonry query replace <project> <query...> [--format json]",
|
|
4112
|
+
run: async (input) => {
|
|
4113
|
+
const project = requireProject(input, "query.replace", "canonry query replace <project> <query...> [--format json]");
|
|
4114
|
+
const queries = input.positionals.slice(1);
|
|
4115
|
+
if (queries.length === 0) {
|
|
4116
|
+
throw usageError("Error: project name and at least one query required\nUsage: canonry query replace <project> <query...> [--format json]", {
|
|
4117
|
+
message: "project name and at least one query required",
|
|
4118
|
+
details: {
|
|
4119
|
+
command: "query.replace",
|
|
4120
|
+
usage: "canonry query replace <project> <query...> [--format json]"
|
|
4121
|
+
}
|
|
4122
|
+
});
|
|
4123
|
+
}
|
|
4124
|
+
await replaceQueries(project, queries, input.format);
|
|
4125
|
+
}
|
|
4126
|
+
},
|
|
4127
|
+
{
|
|
4128
|
+
path: ["query", "remove"],
|
|
4129
|
+
usage: "canonry query remove <project> <query...> [--format json]",
|
|
4130
|
+
run: async (input) => {
|
|
4131
|
+
const project = requireProject(input, "query.remove", "canonry query remove <project> <query...> [--format json]");
|
|
4132
|
+
const queries = input.positionals.slice(1);
|
|
4133
|
+
if (queries.length === 0) {
|
|
4134
|
+
throw usageError("Error: project name and at least one query required\nUsage: canonry query remove <project> <query...> [--format json]", {
|
|
4135
|
+
message: "project name and at least one query required",
|
|
4136
|
+
details: {
|
|
4137
|
+
command: "query.remove",
|
|
4138
|
+
usage: "canonry query remove <project> <query...> [--format json]"
|
|
4139
|
+
}
|
|
4140
|
+
});
|
|
4141
|
+
}
|
|
4142
|
+
await removeQueries(project, queries, input.format);
|
|
4143
|
+
}
|
|
4144
|
+
},
|
|
4145
|
+
{
|
|
4146
|
+
path: ["query", "delete"],
|
|
4147
|
+
usage: "canonry query delete <project> <query...> [--format json]",
|
|
4148
|
+
run: async (input) => {
|
|
4149
|
+
const project = requireProject(input, "query.delete", "canonry query delete <project> <query...> [--format json]");
|
|
4150
|
+
const queries = input.positionals.slice(1);
|
|
4151
|
+
if (queries.length === 0) {
|
|
4152
|
+
throw usageError("Error: project name and at least one query required\nUsage: canonry query delete <project> <query...> [--format json]", {
|
|
4153
|
+
message: "project name and at least one query required",
|
|
4154
|
+
details: {
|
|
4155
|
+
command: "query.delete",
|
|
4156
|
+
usage: "canonry query delete <project> <query...> [--format json]"
|
|
4157
|
+
}
|
|
4158
|
+
});
|
|
4159
|
+
}
|
|
4160
|
+
await removeQueries(project, queries, input.format);
|
|
4161
|
+
}
|
|
4162
|
+
},
|
|
4163
|
+
{
|
|
4164
|
+
path: ["query", "list"],
|
|
4165
|
+
usage: "canonry query list <project> [--format json]",
|
|
4166
|
+
run: async (input) => {
|
|
4167
|
+
const project = requireProject(input, "query.list", "canonry query list <project> [--format json]");
|
|
4168
|
+
await listQueries(project, input.format);
|
|
4169
|
+
}
|
|
4170
|
+
},
|
|
4171
|
+
{
|
|
4172
|
+
path: ["query", "import"],
|
|
4173
|
+
usage: "canonry query import <project> <file> [--format json]",
|
|
4174
|
+
run: async (input) => {
|
|
4175
|
+
const project = requireProject(input, "query.import", "canonry query import <project> <file> [--format json]");
|
|
4176
|
+
const filePath = requirePositional(input, 1, {
|
|
4177
|
+
command: "query.import",
|
|
4178
|
+
usage: "canonry query import <project> <file> [--format json]",
|
|
4179
|
+
message: "project name and file path required"
|
|
4180
|
+
});
|
|
4181
|
+
await importQueries(project, filePath, input.format);
|
|
4182
|
+
}
|
|
4183
|
+
},
|
|
4184
|
+
{
|
|
4185
|
+
path: ["query", "generate"],
|
|
4186
|
+
usage: "canonry query generate <project> --provider <name> [--count <n>] [--save] [--format json]",
|
|
4187
|
+
options: {
|
|
4188
|
+
provider: stringOption(),
|
|
4189
|
+
count: stringOption(),
|
|
4190
|
+
save: { type: "boolean", default: false }
|
|
4191
|
+
},
|
|
4192
|
+
run: async (input) => {
|
|
4193
|
+
const project = requireProject(
|
|
4194
|
+
input,
|
|
4195
|
+
"query.generate",
|
|
4196
|
+
"canonry query generate <project> --provider <name> [--count <n>] [--save] [--format json]"
|
|
4197
|
+
);
|
|
4198
|
+
const provider = requireStringOption(input, "provider", {
|
|
4199
|
+
command: "query.generate",
|
|
4200
|
+
usage: "canonry query generate <project> --provider <name> [--count <n>] [--save] [--format json]",
|
|
4201
|
+
message: "--provider is required (e.g. gemini, openai, claude, perplexity, local)"
|
|
4202
|
+
});
|
|
4203
|
+
await generateQueries(project, provider, {
|
|
4204
|
+
count: parseIntegerOption(input, "count", {
|
|
4205
|
+
command: "query.generate",
|
|
4206
|
+
usage: "canonry query generate <project> --provider <name> [--count <n>] [--save] [--format json]",
|
|
4207
|
+
message: "--count must be an integer"
|
|
4208
|
+
}),
|
|
4209
|
+
save: getBoolean(input.values, "save"),
|
|
4210
|
+
format: input.format
|
|
4211
|
+
});
|
|
4212
|
+
}
|
|
4213
|
+
},
|
|
4214
|
+
{
|
|
4215
|
+
path: ["query"],
|
|
4216
|
+
usage: "canonry query <add|replace|remove|delete|list|import|generate> <project> [args]",
|
|
4217
|
+
run: async (input) => {
|
|
4218
|
+
unknownSubcommand(input.positionals[0], {
|
|
4219
|
+
command: "query",
|
|
4220
|
+
usage: "canonry query <add|replace|remove|delete|list|import|generate> <project> [args]",
|
|
4221
|
+
available: ["add", "replace", "remove", "delete", "list", "import", "generate"]
|
|
4222
|
+
});
|
|
4223
|
+
}
|
|
4224
|
+
}
|
|
4225
|
+
];
|
|
4226
|
+
|
|
4227
|
+
// src/commands/mcp.ts
|
|
4228
|
+
import fs3 from "fs";
|
|
3952
4229
|
import path2 from "path";
|
|
3953
4230
|
import { createRequire } from "module";
|
|
3954
4231
|
|
|
@@ -4054,8 +4331,8 @@ function renderClientSnippet(client, serverName, entry) {
|
|
|
4054
4331
|
return renderJsonSnippet(serverName, entry, client.format);
|
|
4055
4332
|
}
|
|
4056
4333
|
function readJsonConfig(configPath) {
|
|
4057
|
-
if (!
|
|
4058
|
-
const raw =
|
|
4334
|
+
if (!fs3.existsSync(configPath)) return {};
|
|
4335
|
+
const raw = fs3.readFileSync(configPath, "utf-8").trim();
|
|
4059
4336
|
if (!raw) return {};
|
|
4060
4337
|
try {
|
|
4061
4338
|
const parsed = JSON.parse(raw);
|
|
@@ -4073,14 +4350,14 @@ function readJsonConfig(configPath) {
|
|
|
4073
4350
|
}
|
|
4074
4351
|
}
|
|
4075
4352
|
function writeJsonConfig(configPath, value) {
|
|
4076
|
-
|
|
4077
|
-
|
|
4353
|
+
fs3.mkdirSync(path2.dirname(configPath), { recursive: true });
|
|
4354
|
+
fs3.writeFileSync(configPath, `${JSON.stringify(value, null, 2)}
|
|
4078
4355
|
`, "utf-8");
|
|
4079
4356
|
}
|
|
4080
4357
|
function backupConfigIfPresent(configPath) {
|
|
4081
|
-
if (!
|
|
4358
|
+
if (!fs3.existsSync(configPath)) return void 0;
|
|
4082
4359
|
const backupPath = `${configPath}.canonry.bak`;
|
|
4083
|
-
|
|
4360
|
+
fs3.copyFileSync(configPath, backupPath);
|
|
4084
4361
|
return backupPath;
|
|
4085
4362
|
}
|
|
4086
4363
|
function findClientOrThrow(id) {
|
|
@@ -4260,11 +4537,11 @@ var MCP_CLI_COMMANDS = [
|
|
|
4260
4537
|
];
|
|
4261
4538
|
|
|
4262
4539
|
// src/commands/notify.ts
|
|
4263
|
-
function
|
|
4540
|
+
function getClient9() {
|
|
4264
4541
|
return createApiClient();
|
|
4265
4542
|
}
|
|
4266
4543
|
async function addNotification(project, opts) {
|
|
4267
|
-
const client =
|
|
4544
|
+
const client = getClient9();
|
|
4268
4545
|
const result = await client.createNotification(project, {
|
|
4269
4546
|
channel: "webhook",
|
|
4270
4547
|
url: opts.webhook,
|
|
@@ -4278,7 +4555,7 @@ async function addNotification(project, opts) {
|
|
|
4278
4555
|
printNotification(result);
|
|
4279
4556
|
}
|
|
4280
4557
|
async function listNotifications(project, format) {
|
|
4281
|
-
const client =
|
|
4558
|
+
const client = getClient9();
|
|
4282
4559
|
const results = await client.listNotifications(project);
|
|
4283
4560
|
if (format === "json") {
|
|
4284
4561
|
console.log(JSON.stringify(results, null, 2));
|
|
@@ -4296,7 +4573,7 @@ async function listNotifications(project, format) {
|
|
|
4296
4573
|
}
|
|
4297
4574
|
}
|
|
4298
4575
|
async function removeNotification(project, id, format) {
|
|
4299
|
-
const client =
|
|
4576
|
+
const client = getClient9();
|
|
4300
4577
|
await client.deleteNotification(project, id);
|
|
4301
4578
|
if (format === "json") {
|
|
4302
4579
|
console.log(JSON.stringify({ project, id, removed: true }, null, 2));
|
|
@@ -4305,7 +4582,7 @@ async function removeNotification(project, id, format) {
|
|
|
4305
4582
|
console.log(`Notification ${id} removed from "${project}"`);
|
|
4306
4583
|
}
|
|
4307
4584
|
async function testNotification(project, id, format) {
|
|
4308
|
-
const client =
|
|
4585
|
+
const client = getClient9();
|
|
4309
4586
|
const result = await client.testNotification(project, id);
|
|
4310
4587
|
if (format === "json") {
|
|
4311
4588
|
console.log(JSON.stringify({ project, id, ...result }, null, 2));
|
|
@@ -4318,8 +4595,8 @@ async function testNotification(project, id, format) {
|
|
|
4318
4595
|
}
|
|
4319
4596
|
}
|
|
4320
4597
|
var EVENT_DESCRIPTIONS = {
|
|
4321
|
-
"citation.lost": "A
|
|
4322
|
-
"citation.gained": "A
|
|
4598
|
+
"citation.lost": "A query lost its citation status",
|
|
4599
|
+
"citation.gained": "A query gained citation status",
|
|
4323
4600
|
"run.completed": "A visibility run completed successfully",
|
|
4324
4601
|
"run.failed": "A visibility run failed",
|
|
4325
4602
|
"insight.critical": "A critical-severity insight was generated",
|
|
@@ -4427,13 +4704,13 @@ var NOTIFY_CLI_COMMANDS = [
|
|
|
4427
4704
|
];
|
|
4428
4705
|
|
|
4429
4706
|
// src/commands/apply.ts
|
|
4430
|
-
import
|
|
4707
|
+
import fs4 from "fs";
|
|
4431
4708
|
import { parseAllDocuments } from "yaml";
|
|
4432
4709
|
async function applyConfigFile(filePath) {
|
|
4433
|
-
if (!
|
|
4710
|
+
if (!fs4.existsSync(filePath)) {
|
|
4434
4711
|
throw new Error(`File not found: ${filePath}`);
|
|
4435
4712
|
}
|
|
4436
|
-
const content =
|
|
4713
|
+
const content = fs4.readFileSync(filePath, "utf-8");
|
|
4437
4714
|
const docs = parseAllDocuments(content);
|
|
4438
4715
|
const client = createApiClient();
|
|
4439
4716
|
const errors = [];
|
|
@@ -4496,11 +4773,11 @@ async function applyConfigs(filePaths, format) {
|
|
|
4496
4773
|
}
|
|
4497
4774
|
|
|
4498
4775
|
// src/commands/analytics.ts
|
|
4499
|
-
function
|
|
4776
|
+
function getClient10() {
|
|
4500
4777
|
return createApiClient();
|
|
4501
4778
|
}
|
|
4502
4779
|
async function showAnalytics(project, options) {
|
|
4503
|
-
const client =
|
|
4780
|
+
const client = getClient10();
|
|
4504
4781
|
const features = options.feature ? [options.feature] : ["metrics", "gaps", "sources"];
|
|
4505
4782
|
const results = {};
|
|
4506
4783
|
for (const feature of features) {
|
|
@@ -4571,19 +4848,19 @@ Brand Gap Analysis`);
|
|
|
4571
4848
|
if (data.gap.length > 0) {
|
|
4572
4849
|
console.log(`
|
|
4573
4850
|
Opportunity Gaps (competitors cited, you're not):`);
|
|
4574
|
-
for (const
|
|
4575
|
-
const competitors2 =
|
|
4576
|
-
const cons =
|
|
4577
|
-
console.log(` \u2022 ${
|
|
4851
|
+
for (const q of data.gap) {
|
|
4852
|
+
const competitors2 = q.competitorsCiting.join(", ");
|
|
4853
|
+
const cons = q.consistency.totalRuns > 0 ? ` [cited ${q.consistency.citedRuns}/${q.consistency.totalRuns} runs]` : "";
|
|
4854
|
+
console.log(` \u2022 ${q.query}${cons}`);
|
|
4578
4855
|
console.log(` Competitors: ${competitors2}`);
|
|
4579
4856
|
}
|
|
4580
4857
|
}
|
|
4581
4858
|
if (data.cited.length > 0) {
|
|
4582
4859
|
console.log(`
|
|
4583
|
-
Cited
|
|
4584
|
-
for (const
|
|
4585
|
-
const cons =
|
|
4586
|
-
console.log(` \u2713 ${
|
|
4860
|
+
Cited Queries:`);
|
|
4861
|
+
for (const q of data.cited) {
|
|
4862
|
+
const cons = q.consistency.totalRuns > 0 ? ` [${q.consistency.citedRuns}/${q.consistency.totalRuns} runs]` : "";
|
|
4863
|
+
console.log(` \u2713 ${q.query} (${q.providers.join(", ")})${cons}`);
|
|
4587
4864
|
}
|
|
4588
4865
|
}
|
|
4589
4866
|
}
|
|
@@ -4603,11 +4880,11 @@ Source Origin Breakdown`);
|
|
|
4603
4880
|
}
|
|
4604
4881
|
|
|
4605
4882
|
// src/commands/evidence.ts
|
|
4606
|
-
function
|
|
4883
|
+
function getClient11() {
|
|
4607
4884
|
return createApiClient();
|
|
4608
4885
|
}
|
|
4609
4886
|
async function showEvidence(project, format) {
|
|
4610
|
-
const client =
|
|
4887
|
+
const client = getClient11();
|
|
4611
4888
|
const timeline = await client.getTimeline(project);
|
|
4612
4889
|
if (format === "json") {
|
|
4613
4890
|
const enriched = timeline.map((entry) => ({
|
|
@@ -4618,7 +4895,7 @@ async function showEvidence(project, format) {
|
|
|
4618
4895
|
return;
|
|
4619
4896
|
}
|
|
4620
4897
|
if (timeline.length === 0) {
|
|
4621
|
-
console.log('No
|
|
4898
|
+
console.log('No query evidence yet. Trigger a run first with "canonry run".');
|
|
4622
4899
|
return;
|
|
4623
4900
|
}
|
|
4624
4901
|
console.log(`Evidence: ${project}
|
|
@@ -4628,10 +4905,10 @@ async function showEvidence(project, format) {
|
|
|
4628
4905
|
if (!latest) continue;
|
|
4629
4906
|
const state = latest.citationState === "cited" ? "\u2713 cited" : "\u2717 not-cited";
|
|
4630
4907
|
const transition = latest.transition !== latest.citationState ? ` (${latest.transition})` : "";
|
|
4631
|
-
console.log(` ${state}${transition} ${entry.
|
|
4908
|
+
console.log(` ${state}${transition} ${entry.query}`);
|
|
4632
4909
|
}
|
|
4633
4910
|
console.log(`
|
|
4634
|
-
|
|
4911
|
+
Queries: ${timeline.length}`);
|
|
4635
4912
|
const cited = timeline.filter((e) => e.runs[e.runs.length - 1]?.citationState === "cited").length;
|
|
4636
4913
|
console.log(` Cited: ${cited} / ${timeline.length}`);
|
|
4637
4914
|
}
|
|
@@ -4667,11 +4944,11 @@ async function loadLatestRunForExport(client, project) {
|
|
|
4667
4944
|
}
|
|
4668
4945
|
|
|
4669
4946
|
// src/commands/history.ts
|
|
4670
|
-
function
|
|
4947
|
+
function getClient12() {
|
|
4671
4948
|
return createApiClient();
|
|
4672
4949
|
}
|
|
4673
4950
|
async function showHistory(project, format) {
|
|
4674
|
-
const client =
|
|
4951
|
+
const client = getClient12();
|
|
4675
4952
|
try {
|
|
4676
4953
|
const entries = await client.getHistory(project);
|
|
4677
4954
|
if (format === "json") {
|
|
@@ -4706,11 +4983,11 @@ async function showHistory(project, format) {
|
|
|
4706
4983
|
}
|
|
4707
4984
|
|
|
4708
4985
|
// src/commands/status.ts
|
|
4709
|
-
function
|
|
4986
|
+
function getClient13() {
|
|
4710
4987
|
return createApiClient();
|
|
4711
4988
|
}
|
|
4712
4989
|
async function showStatus(project, format) {
|
|
4713
|
-
const client =
|
|
4990
|
+
const client = getClient13();
|
|
4714
4991
|
const projectData = await client.getProject(project);
|
|
4715
4992
|
const latest = await getLatestRunSummary(client, project);
|
|
4716
4993
|
if (format === "json") {
|
|
@@ -4841,11 +5118,11 @@ var OPERATOR_CLI_COMMANDS = [
|
|
|
4841
5118
|
];
|
|
4842
5119
|
|
|
4843
5120
|
// src/commands/project.ts
|
|
4844
|
-
function
|
|
5121
|
+
function getClient14() {
|
|
4845
5122
|
return createApiClient();
|
|
4846
5123
|
}
|
|
4847
5124
|
async function createProject(name, opts) {
|
|
4848
|
-
const client =
|
|
5125
|
+
const client = getClient14();
|
|
4849
5126
|
const result = await client.putProject(name, {
|
|
4850
5127
|
displayName: opts.displayName,
|
|
4851
5128
|
canonicalDomain: opts.domain,
|
|
@@ -4860,7 +5137,7 @@ async function createProject(name, opts) {
|
|
|
4860
5137
|
console.log(`Project created: ${result.name} (${result.id})`);
|
|
4861
5138
|
}
|
|
4862
5139
|
async function listProjects(format) {
|
|
4863
|
-
const client =
|
|
5140
|
+
const client = getClient14();
|
|
4864
5141
|
const projects2 = await client.listProjects();
|
|
4865
5142
|
if (format === "json") {
|
|
4866
5143
|
console.log(JSON.stringify(projects2, null, 2));
|
|
@@ -4888,7 +5165,7 @@ async function listProjects(format) {
|
|
|
4888
5165
|
}
|
|
4889
5166
|
}
|
|
4890
5167
|
async function showProject(name, format) {
|
|
4891
|
-
const client =
|
|
5168
|
+
const client = getClient14();
|
|
4892
5169
|
const project = await client.getProject(name);
|
|
4893
5170
|
if (format === "json") {
|
|
4894
5171
|
console.log(JSON.stringify(project, null, 2));
|
|
@@ -4914,7 +5191,7 @@ async function showProject(name, format) {
|
|
|
4914
5191
|
if (project.updatedAt) console.log(` Updated: ${project.updatedAt}`);
|
|
4915
5192
|
}
|
|
4916
5193
|
async function updateProjectSettings(name, opts) {
|
|
4917
|
-
const client =
|
|
5194
|
+
const client = getClient14();
|
|
4918
5195
|
const project = await client.getProject(name);
|
|
4919
5196
|
let ownedDomains = opts.ownedDomains ?? project.ownedDomains ?? [];
|
|
4920
5197
|
if (opts.addOwnedDomain) {
|
|
@@ -4939,7 +5216,7 @@ async function updateProjectSettings(name, opts) {
|
|
|
4939
5216
|
console.log(`Project updated: ${result.name}`);
|
|
4940
5217
|
}
|
|
4941
5218
|
async function deleteProject(name, format) {
|
|
4942
|
-
const client =
|
|
5219
|
+
const client = getClient14();
|
|
4943
5220
|
await client.deleteProject(name);
|
|
4944
5221
|
if (format === "json") {
|
|
4945
5222
|
console.log(JSON.stringify({ name, deleted: true }, null, 2));
|
|
@@ -4948,7 +5225,7 @@ async function deleteProject(name, format) {
|
|
|
4948
5225
|
console.log(`Project deleted: ${name}`);
|
|
4949
5226
|
}
|
|
4950
5227
|
async function addLocation(project, opts) {
|
|
4951
|
-
const client =
|
|
5228
|
+
const client = getClient14();
|
|
4952
5229
|
const location = await client.addLocation(project, {
|
|
4953
5230
|
label: opts.label,
|
|
4954
5231
|
city: opts.city,
|
|
@@ -4963,7 +5240,7 @@ async function addLocation(project, opts) {
|
|
|
4963
5240
|
console.log(`Location added: ${opts.label} (${opts.city}, ${opts.region}, ${opts.country})`);
|
|
4964
5241
|
}
|
|
4965
5242
|
async function listLocations(project, format) {
|
|
4966
|
-
const client =
|
|
5243
|
+
const client = getClient14();
|
|
4967
5244
|
const result = await client.listLocations(project);
|
|
4968
5245
|
if (format === "json") {
|
|
4969
5246
|
console.log(JSON.stringify(result, null, 2));
|
|
@@ -4989,7 +5266,7 @@ async function listLocations(project, format) {
|
|
|
4989
5266
|
}
|
|
4990
5267
|
}
|
|
4991
5268
|
async function removeLocation(project, label, format) {
|
|
4992
|
-
const client =
|
|
5269
|
+
const client = getClient14();
|
|
4993
5270
|
await client.removeLocation(project, label);
|
|
4994
5271
|
if (format === "json") {
|
|
4995
5272
|
console.log(JSON.stringify({ project, label, removed: true }, null, 2));
|
|
@@ -4998,7 +5275,7 @@ async function removeLocation(project, label, format) {
|
|
|
4998
5275
|
console.log(`Location removed: ${label}`);
|
|
4999
5276
|
}
|
|
5000
5277
|
async function setDefaultLocation(project, label, format) {
|
|
5001
|
-
const client =
|
|
5278
|
+
const client = getClient14();
|
|
5002
5279
|
const result = await client.setDefaultLocation(project, label);
|
|
5003
5280
|
if (format === "json") {
|
|
5004
5281
|
console.log(JSON.stringify({ project, ...result }, null, 2));
|
|
@@ -5177,7 +5454,7 @@ var PROJECT_CLI_COMMANDS = [
|
|
|
5177
5454
|
];
|
|
5178
5455
|
|
|
5179
5456
|
// src/commands/report.ts
|
|
5180
|
-
import
|
|
5457
|
+
import fs5 from "fs";
|
|
5181
5458
|
import path3 from "path";
|
|
5182
5459
|
function defaultOutputPath(project) {
|
|
5183
5460
|
const date = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
@@ -5193,10 +5470,10 @@ async function runReportCommand(project, opts = {}) {
|
|
|
5193
5470
|
const html = renderReportHtml(report);
|
|
5194
5471
|
const targetPath = opts.output ? path3.resolve(opts.output) : defaultOutputPath(project);
|
|
5195
5472
|
const dir = path3.dirname(targetPath);
|
|
5196
|
-
if (!
|
|
5197
|
-
|
|
5473
|
+
if (!fs5.existsSync(dir)) {
|
|
5474
|
+
fs5.mkdirSync(dir, { recursive: true });
|
|
5198
5475
|
}
|
|
5199
|
-
|
|
5476
|
+
fs5.writeFileSync(targetPath, html, "utf-8");
|
|
5200
5477
|
console.log(`Report written to ${targetPath}`);
|
|
5201
5478
|
}
|
|
5202
5479
|
|
|
@@ -5220,12 +5497,12 @@ var REPORT_CLI_COMMANDS = [
|
|
|
5220
5497
|
];
|
|
5221
5498
|
|
|
5222
5499
|
// src/commands/run.ts
|
|
5223
|
-
function
|
|
5500
|
+
function getClient15() {
|
|
5224
5501
|
return createApiClient();
|
|
5225
5502
|
}
|
|
5226
5503
|
var TERMINAL_STATUSES = /* @__PURE__ */ new Set(["completed", "partial", "failed", "cancelled"]);
|
|
5227
5504
|
async function triggerRun(project, opts) {
|
|
5228
|
-
const client =
|
|
5505
|
+
const client = getClient15();
|
|
5229
5506
|
const body = {};
|
|
5230
5507
|
if (opts?.provider) {
|
|
5231
5508
|
const providerInputs = opts.provider.split(",").map((s) => s.trim()).filter(Boolean);
|
|
@@ -5321,7 +5598,7 @@ async function triggerRun(project, opts) {
|
|
|
5321
5598
|
}
|
|
5322
5599
|
}
|
|
5323
5600
|
async function triggerRunAll(opts) {
|
|
5324
|
-
const client =
|
|
5601
|
+
const client = getClient15();
|
|
5325
5602
|
const projects2 = await client.listProjects();
|
|
5326
5603
|
if (projects2.length === 0) {
|
|
5327
5604
|
if (opts?.format === "json") {
|
|
@@ -5379,7 +5656,7 @@ async function triggerRunAll(opts) {
|
|
|
5379
5656
|
}
|
|
5380
5657
|
}
|
|
5381
5658
|
async function cancelRun(project, runId, format) {
|
|
5382
|
-
const client =
|
|
5659
|
+
const client = getClient15();
|
|
5383
5660
|
let targetId = runId;
|
|
5384
5661
|
if (!targetId) {
|
|
5385
5662
|
const runs2 = await client.listRuns(project);
|
|
@@ -5411,7 +5688,7 @@ To cancel by ID : canonry run cancel ${project} <run-id>`,
|
|
|
5411
5688
|
console.log(`Run ${result.id} cancelled.`);
|
|
5412
5689
|
}
|
|
5413
5690
|
async function showRun(id, format) {
|
|
5414
|
-
const client =
|
|
5691
|
+
const client = getClient15();
|
|
5415
5692
|
const run = await client.getRun(id);
|
|
5416
5693
|
if (format === "json") {
|
|
5417
5694
|
console.log(JSON.stringify(run, null, 2));
|
|
@@ -5420,7 +5697,7 @@ async function showRun(id, format) {
|
|
|
5420
5697
|
printRunDetail(run);
|
|
5421
5698
|
}
|
|
5422
5699
|
async function listRuns(project, opts) {
|
|
5423
|
-
const client =
|
|
5700
|
+
const client = getClient15();
|
|
5424
5701
|
const runs2 = await client.listRuns(project, opts?.limit);
|
|
5425
5702
|
if (opts?.format === "json") {
|
|
5426
5703
|
console.log(JSON.stringify(runs2, null, 2));
|
|
@@ -5477,7 +5754,7 @@ function printRunDetail(run) {
|
|
|
5477
5754
|
for (const s of run.snapshots) {
|
|
5478
5755
|
const state = typeof s.answerMentioned === "boolean" ? s.answerMentioned ? " visible " : " not-vis " : s.citationState === "cited" ? " cited " : " not-cited";
|
|
5479
5756
|
const modelLabel = s.model ? ` (${s.model})` : "";
|
|
5480
|
-
console.log(` ${state} ${s.provider}${modelLabel} ${s.
|
|
5757
|
+
console.log(` ${state} ${s.provider}${modelLabel} ${s.query}`);
|
|
5481
5758
|
}
|
|
5482
5759
|
}
|
|
5483
5760
|
}
|
|
@@ -5572,11 +5849,11 @@ var RUN_CLI_COMMANDS = [
|
|
|
5572
5849
|
];
|
|
5573
5850
|
|
|
5574
5851
|
// src/commands/schedule.ts
|
|
5575
|
-
function
|
|
5852
|
+
function getClient16() {
|
|
5576
5853
|
return createApiClient();
|
|
5577
5854
|
}
|
|
5578
5855
|
async function setSchedule(project, opts) {
|
|
5579
|
-
const client =
|
|
5856
|
+
const client = getClient16();
|
|
5580
5857
|
const body = {};
|
|
5581
5858
|
if (opts.preset) body.preset = opts.preset;
|
|
5582
5859
|
if (opts.cron) body.cron = opts.cron;
|
|
@@ -5591,7 +5868,7 @@ async function setSchedule(project, opts) {
|
|
|
5591
5868
|
printSchedule(result);
|
|
5592
5869
|
}
|
|
5593
5870
|
async function showSchedule(project, format) {
|
|
5594
|
-
const client =
|
|
5871
|
+
const client = getClient16();
|
|
5595
5872
|
const result = await client.getSchedule(project);
|
|
5596
5873
|
if (format === "json") {
|
|
5597
5874
|
console.log(JSON.stringify(result, null, 2));
|
|
@@ -5600,7 +5877,7 @@ async function showSchedule(project, format) {
|
|
|
5600
5877
|
printSchedule(result);
|
|
5601
5878
|
}
|
|
5602
5879
|
async function enableSchedule(project, format) {
|
|
5603
|
-
const client =
|
|
5880
|
+
const client = getClient16();
|
|
5604
5881
|
const current = await client.getSchedule(project);
|
|
5605
5882
|
const body = { timezone: current.timezone, enabled: true };
|
|
5606
5883
|
if (current.preset) body.preset = current.preset;
|
|
@@ -5614,7 +5891,7 @@ async function enableSchedule(project, format) {
|
|
|
5614
5891
|
console.log(`Schedule enabled for "${project}"`);
|
|
5615
5892
|
}
|
|
5616
5893
|
async function disableSchedule(project, format) {
|
|
5617
|
-
const client =
|
|
5894
|
+
const client = getClient16();
|
|
5618
5895
|
const current = await client.getSchedule(project);
|
|
5619
5896
|
const body = { timezone: current.timezone, enabled: false };
|
|
5620
5897
|
if (current.preset) body.preset = current.preset;
|
|
@@ -5628,7 +5905,7 @@ async function disableSchedule(project, format) {
|
|
|
5628
5905
|
console.log(`Schedule disabled for "${project}"`);
|
|
5629
5906
|
}
|
|
5630
5907
|
async function removeSchedule(project, format) {
|
|
5631
|
-
const client =
|
|
5908
|
+
const client = getClient16();
|
|
5632
5909
|
await client.deleteSchedule(project);
|
|
5633
5910
|
if (format === "json") {
|
|
5634
5911
|
console.log(JSON.stringify({ project, removed: true }, null, 2));
|
|
@@ -5735,11 +6012,11 @@ var SCHEDULE_CLI_COMMANDS = [
|
|
|
5735
6012
|
];
|
|
5736
6013
|
|
|
5737
6014
|
// src/commands/settings.ts
|
|
5738
|
-
function
|
|
6015
|
+
function getClient17() {
|
|
5739
6016
|
return createApiClient();
|
|
5740
6017
|
}
|
|
5741
6018
|
async function setProvider(name, opts) {
|
|
5742
|
-
const client =
|
|
6019
|
+
const client = getClient17();
|
|
5743
6020
|
const { format, ...payload } = opts;
|
|
5744
6021
|
const result = await client.updateProvider(name, payload);
|
|
5745
6022
|
if (format === "json") {
|
|
@@ -5755,7 +6032,7 @@ async function setProvider(name, opts) {
|
|
|
5755
6032
|
}
|
|
5756
6033
|
}
|
|
5757
6034
|
async function showSettings(format) {
|
|
5758
|
-
const client =
|
|
6035
|
+
const client = getClient17();
|
|
5759
6036
|
const config = loadConfig();
|
|
5760
6037
|
const settings = await client.getSettings();
|
|
5761
6038
|
if (format === "json") {
|
|
@@ -5927,7 +6204,7 @@ Usage: canonry settings provider ${name} --api-key <key> [--model <model>] [--ma
|
|
|
5927
6204
|
];
|
|
5928
6205
|
|
|
5929
6206
|
// src/commands/skills.ts
|
|
5930
|
-
import
|
|
6207
|
+
import fs6 from "fs";
|
|
5931
6208
|
import path4 from "path";
|
|
5932
6209
|
import { fileURLToPath } from "url";
|
|
5933
6210
|
var BUNDLED_SKILL_NAMES = ["canonry-setup", "aero"];
|
|
@@ -5939,7 +6216,7 @@ function resolveBundledSkillsRoot(pkgDir) {
|
|
|
5939
6216
|
path4.join(here, "../../../../skills")
|
|
5940
6217
|
];
|
|
5941
6218
|
for (const candidate of candidates) {
|
|
5942
|
-
if (BUNDLED_SKILL_NAMES.every((name) =>
|
|
6219
|
+
if (BUNDLED_SKILL_NAMES.every((name) => fs6.existsSync(path4.join(candidate, name, "SKILL.md")))) {
|
|
5943
6220
|
return candidate;
|
|
5944
6221
|
}
|
|
5945
6222
|
}
|
|
@@ -5962,13 +6239,13 @@ function getBundledSkills(pkgDir) {
|
|
|
5962
6239
|
return BUNDLED_SKILL_NAMES.map((name) => {
|
|
5963
6240
|
const skillDir = path4.join(root, name);
|
|
5964
6241
|
const skillFile = path4.join(skillDir, "SKILL.md");
|
|
5965
|
-
const content =
|
|
6242
|
+
const content = fs6.readFileSync(skillFile, "utf-8");
|
|
5966
6243
|
return { name, description: parseDescription(content), bundledPath: skillDir };
|
|
5967
6244
|
});
|
|
5968
6245
|
}
|
|
5969
6246
|
function walkRelative(dir, prefix = "") {
|
|
5970
6247
|
const out = [];
|
|
5971
|
-
for (const entry of
|
|
6248
|
+
for (const entry of fs6.readdirSync(dir, { withFileTypes: true })) {
|
|
5972
6249
|
const rel = prefix ? path4.join(prefix, entry.name) : entry.name;
|
|
5973
6250
|
const full = path4.join(dir, entry.name);
|
|
5974
6251
|
if (entry.isDirectory()) {
|
|
@@ -5980,28 +6257,28 @@ function walkRelative(dir, prefix = "") {
|
|
|
5980
6257
|
return out.sort();
|
|
5981
6258
|
}
|
|
5982
6259
|
function compareDirContent(srcDir, destDir) {
|
|
5983
|
-
if (!
|
|
5984
|
-
if (!
|
|
6260
|
+
if (!fs6.existsSync(destDir)) return "missing";
|
|
6261
|
+
if (!fs6.statSync(destDir).isDirectory()) return "different";
|
|
5985
6262
|
const srcFiles = walkRelative(srcDir);
|
|
5986
6263
|
const destFiles = walkRelative(destDir);
|
|
5987
6264
|
if (srcFiles.length !== destFiles.length) return "different";
|
|
5988
6265
|
for (let i = 0; i < srcFiles.length; i++) {
|
|
5989
6266
|
if (srcFiles[i] !== destFiles[i]) return "different";
|
|
5990
|
-
const srcBytes =
|
|
5991
|
-
const destBytes =
|
|
6267
|
+
const srcBytes = fs6.readFileSync(path4.join(srcDir, srcFiles[i]));
|
|
6268
|
+
const destBytes = fs6.readFileSync(path4.join(destDir, destFiles[i]));
|
|
5992
6269
|
if (!srcBytes.equals(destBytes)) return "different";
|
|
5993
6270
|
}
|
|
5994
6271
|
return "match";
|
|
5995
6272
|
}
|
|
5996
6273
|
function copyDirRecursive(src, dest) {
|
|
5997
|
-
|
|
5998
|
-
for (const entry of
|
|
6274
|
+
fs6.mkdirSync(dest, { recursive: true });
|
|
6275
|
+
for (const entry of fs6.readdirSync(src, { withFileTypes: true })) {
|
|
5999
6276
|
const srcPath = path4.join(src, entry.name);
|
|
6000
6277
|
const destPath = path4.join(dest, entry.name);
|
|
6001
6278
|
if (entry.isDirectory()) {
|
|
6002
6279
|
copyDirRecursive(srcPath, destPath);
|
|
6003
6280
|
} else if (entry.isFile()) {
|
|
6004
|
-
|
|
6281
|
+
fs6.copyFileSync(srcPath, destPath);
|
|
6005
6282
|
}
|
|
6006
6283
|
}
|
|
6007
6284
|
}
|
|
@@ -6026,7 +6303,7 @@ function installClaudeSkill(skill, targetDir, force) {
|
|
|
6026
6303
|
});
|
|
6027
6304
|
}
|
|
6028
6305
|
if (compare === "different") {
|
|
6029
|
-
|
|
6306
|
+
fs6.rmSync(targetPath, { recursive: true, force: true });
|
|
6030
6307
|
}
|
|
6031
6308
|
copyDirRecursive(skill.bundledPath, targetPath);
|
|
6032
6309
|
return {
|
|
@@ -6041,15 +6318,15 @@ function installCodexSymlink(skill, targetDir, force) {
|
|
|
6041
6318
|
const codexPath = path4.join(targetDir, ".codex", "skills", skill.name);
|
|
6042
6319
|
const claudePath = path4.join(targetDir, ".claude", "skills", skill.name);
|
|
6043
6320
|
const linkTarget = path4.relative(path4.dirname(codexPath), claudePath);
|
|
6044
|
-
|
|
6321
|
+
fs6.mkdirSync(path4.dirname(codexPath), { recursive: true });
|
|
6045
6322
|
let stat;
|
|
6046
6323
|
try {
|
|
6047
|
-
stat =
|
|
6324
|
+
stat = fs6.lstatSync(codexPath);
|
|
6048
6325
|
} catch {
|
|
6049
6326
|
stat = void 0;
|
|
6050
6327
|
}
|
|
6051
6328
|
if (stat?.isSymbolicLink()) {
|
|
6052
|
-
const existing =
|
|
6329
|
+
const existing = fs6.readlinkSync(codexPath);
|
|
6053
6330
|
if (existing === linkTarget) {
|
|
6054
6331
|
return {
|
|
6055
6332
|
skill: skill.name,
|
|
@@ -6067,8 +6344,8 @@ function installCodexSymlink(skill, targetDir, force) {
|
|
|
6067
6344
|
exitCode: 1
|
|
6068
6345
|
});
|
|
6069
6346
|
}
|
|
6070
|
-
|
|
6071
|
-
|
|
6347
|
+
fs6.unlinkSync(codexPath);
|
|
6348
|
+
fs6.symlinkSync(linkTarget, codexPath);
|
|
6072
6349
|
return {
|
|
6073
6350
|
skill: skill.name,
|
|
6074
6351
|
client: CodingAgents.codex,
|
|
@@ -6086,9 +6363,9 @@ function installCodexSymlink(skill, targetDir, force) {
|
|
|
6086
6363
|
exitCode: 1
|
|
6087
6364
|
});
|
|
6088
6365
|
}
|
|
6089
|
-
|
|
6366
|
+
fs6.rmSync(codexPath, { recursive: true, force: true });
|
|
6090
6367
|
}
|
|
6091
|
-
|
|
6368
|
+
fs6.symlinkSync(linkTarget, codexPath);
|
|
6092
6369
|
return {
|
|
6093
6370
|
skill: skill.name,
|
|
6094
6371
|
client: CodingAgents.codex,
|
|
@@ -6120,7 +6397,7 @@ async function installSkills(opts = {}) {
|
|
|
6120
6397
|
});
|
|
6121
6398
|
}
|
|
6122
6399
|
const skillsToInstall = allSkills.filter((s) => requestedNames.includes(s.name));
|
|
6123
|
-
|
|
6400
|
+
fs6.mkdirSync(targetDir, { recursive: true });
|
|
6124
6401
|
const results = [];
|
|
6125
6402
|
for (const skill of skillsToInstall) {
|
|
6126
6403
|
results.push(installClaudeSkill(skill, targetDir, force));
|
|
@@ -6221,11 +6498,11 @@ var SKILLS_CLI_COMMANDS = [
|
|
|
6221
6498
|
];
|
|
6222
6499
|
|
|
6223
6500
|
// src/commands/snapshot.ts
|
|
6224
|
-
import
|
|
6501
|
+
import fs8 from "fs";
|
|
6225
6502
|
import path6 from "path";
|
|
6226
6503
|
|
|
6227
6504
|
// src/snapshot-pdf.ts
|
|
6228
|
-
import
|
|
6505
|
+
import fs7 from "fs";
|
|
6229
6506
|
import path5 from "path";
|
|
6230
6507
|
import { PDFDocument, StandardFonts, rgb } from "pdf-lib";
|
|
6231
6508
|
var PAGE_WIDTH = 612;
|
|
@@ -6436,8 +6713,8 @@ async function writeSnapshotPdf(report, outputPath) {
|
|
|
6436
6713
|
renderQueries(pdf, report);
|
|
6437
6714
|
const bytes = await doc.save();
|
|
6438
6715
|
const resolvedPath = path5.resolve(outputPath);
|
|
6439
|
-
|
|
6440
|
-
|
|
6716
|
+
fs7.mkdirSync(path5.dirname(resolvedPath), { recursive: true });
|
|
6717
|
+
fs7.writeFileSync(resolvedPath, bytes);
|
|
6441
6718
|
return resolvedPath;
|
|
6442
6719
|
}
|
|
6443
6720
|
function renderCover(pdf, report) {
|
|
@@ -6500,9 +6777,9 @@ function renderCompetitors(pdf, report) {
|
|
|
6500
6777
|
}
|
|
6501
6778
|
function renderQueries(pdf, report) {
|
|
6502
6779
|
pdf.heading("Provider Comparison");
|
|
6503
|
-
for (const
|
|
6504
|
-
pdf.subheading(query
|
|
6505
|
-
for (const result of
|
|
6780
|
+
for (const queryResult of report.queryResults) {
|
|
6781
|
+
pdf.subheading(queryResult.query, 11);
|
|
6782
|
+
for (const result of queryResult.providerResults) {
|
|
6506
6783
|
const status = result.error ? "error" : result.mentioned ? result.cited ? "mentioned and cited" : "mentioned" : "not mentioned";
|
|
6507
6784
|
const accuracy = result.describedAccurately === "not-mentioned" ? "" : `; accuracy: ${result.describedAccurately}`;
|
|
6508
6785
|
const competitors2 = result.recommendedCompetitors.length > 0 ? `; recommended instead: ${result.recommendedCompetitors.join(", ")}` : "";
|
|
@@ -6552,7 +6829,7 @@ function wrapText(font, text, size, maxWidth) {
|
|
|
6552
6829
|
}
|
|
6553
6830
|
|
|
6554
6831
|
// src/commands/snapshot.ts
|
|
6555
|
-
function
|
|
6832
|
+
function getClient18() {
|
|
6556
6833
|
return createApiClient();
|
|
6557
6834
|
}
|
|
6558
6835
|
function slugify(value) {
|
|
@@ -6563,11 +6840,11 @@ function autoOutputPath(companyName, ext) {
|
|
|
6563
6840
|
return `${slugify(companyName)}-snapshot-${date}.${ext}`;
|
|
6564
6841
|
}
|
|
6565
6842
|
async function createSnapshotReport(companyName, opts) {
|
|
6566
|
-
const client =
|
|
6843
|
+
const client = getClient18();
|
|
6567
6844
|
const report = await client.createSnapshot({
|
|
6568
6845
|
companyName,
|
|
6569
6846
|
domain: opts.domain,
|
|
6570
|
-
...opts.
|
|
6847
|
+
...opts.queries && opts.queries.length > 0 ? { queries: opts.queries } : {},
|
|
6571
6848
|
...opts.competitors && opts.competitors.length > 0 ? { competitors: opts.competitors } : {}
|
|
6572
6849
|
});
|
|
6573
6850
|
let savedMdPath;
|
|
@@ -6596,8 +6873,8 @@ PDF saved: ${savedPdfPath}`);
|
|
|
6596
6873
|
}
|
|
6597
6874
|
function writeSnapshotMarkdown(report, outputPath) {
|
|
6598
6875
|
const resolvedPath = path6.resolve(outputPath);
|
|
6599
|
-
|
|
6600
|
-
|
|
6876
|
+
fs8.mkdirSync(path6.dirname(resolvedPath), { recursive: true });
|
|
6877
|
+
fs8.writeFileSync(resolvedPath, formatSnapshotMarkdown(report), "utf-8");
|
|
6601
6878
|
return resolvedPath;
|
|
6602
6879
|
}
|
|
6603
6880
|
function formatSnapshotMarkdown(report) {
|
|
@@ -6640,12 +6917,12 @@ function formatSnapshotMarkdown(report) {
|
|
|
6640
6917
|
}
|
|
6641
6918
|
lines.push("## Provider Comparison");
|
|
6642
6919
|
lines.push("");
|
|
6643
|
-
for (const
|
|
6644
|
-
lines.push(`### "${query
|
|
6920
|
+
for (const queryResult of report.queryResults) {
|
|
6921
|
+
lines.push(`### "${queryResult.query}"`);
|
|
6645
6922
|
lines.push("");
|
|
6646
6923
|
lines.push("| Provider | Mentioned | Cited | Accuracy | Competitors Recommended |");
|
|
6647
6924
|
lines.push("|----------|-----------|-------|----------|------------------------|");
|
|
6648
|
-
for (const result of
|
|
6925
|
+
for (const result of queryResult.providerResults) {
|
|
6649
6926
|
if (result.error) {
|
|
6650
6927
|
lines.push(`| ${result.displayName} | ERROR | - | - | ${result.error} |`);
|
|
6651
6928
|
continue;
|
|
@@ -6696,11 +6973,11 @@ function formatSnapshotText(report) {
|
|
|
6696
6973
|
}
|
|
6697
6974
|
const providerWidth = Math.max(
|
|
6698
6975
|
8,
|
|
6699
|
-
...report.queryResults.flatMap((
|
|
6976
|
+
...report.queryResults.flatMap((queryResult) => queryResult.providerResults.map((result) => result.displayName.length))
|
|
6700
6977
|
);
|
|
6701
|
-
for (const
|
|
6702
|
-
lines.push(`"${query
|
|
6703
|
-
for (const result of
|
|
6978
|
+
for (const queryResult of report.queryResults) {
|
|
6979
|
+
lines.push(`"${queryResult.query}"`);
|
|
6980
|
+
for (const result of queryResult.providerResults) {
|
|
6704
6981
|
lines.push(` ${result.displayName.padEnd(providerWidth)} ${formatProviderLine(result)}`);
|
|
6705
6982
|
}
|
|
6706
6983
|
lines.push("");
|
|
@@ -6741,9 +7018,10 @@ function parseCsvOption(value) {
|
|
|
6741
7018
|
var SNAPSHOT_CLI_COMMANDS = [
|
|
6742
7019
|
{
|
|
6743
7020
|
path: ["snapshot"],
|
|
6744
|
-
usage: 'canonry snapshot <company-name> --domain <domain> [--phrases "a,b"] [--competitors "x,y"] [--md] [--output <path>] [--pdf] [--format table|json]',
|
|
7021
|
+
usage: 'canonry snapshot <company-name> --domain <domain> [--queries "a,b"] [--phrases "a,b" (legacy alias)] [--competitors "x,y"] [--md] [--output <path>] [--pdf] [--format table|json]',
|
|
6745
7022
|
options: {
|
|
6746
7023
|
domain: stringOption(),
|
|
7024
|
+
queries: stringOption(),
|
|
6747
7025
|
phrases: stringOption(),
|
|
6748
7026
|
competitors: stringOption(),
|
|
6749
7027
|
md: { type: "boolean" },
|
|
@@ -6751,7 +7029,7 @@ var SNAPSHOT_CLI_COMMANDS = [
|
|
|
6751
7029
|
output: stringOption()
|
|
6752
7030
|
},
|
|
6753
7031
|
run: async (input) => {
|
|
6754
|
-
const usage = 'canonry snapshot <company-name> --domain <domain> [--phrases "a,b"] [--competitors "x,y"] [--md] [--output <path>] [--pdf] [--format table|json]';
|
|
7032
|
+
const usage = 'canonry snapshot <company-name> --domain <domain> [--queries "a,b"] [--phrases "a,b" (legacy alias)] [--competitors "x,y"] [--md] [--output <path>] [--pdf] [--format table|json]';
|
|
6755
7033
|
const companyName = requirePositional(input, 0, {
|
|
6756
7034
|
command: "snapshot",
|
|
6757
7035
|
usage,
|
|
@@ -6768,7 +7046,7 @@ var SNAPSHOT_CLI_COMMANDS = [
|
|
|
6768
7046
|
const wantsMd = explicitMd || !!outputPath && !wantsPdf;
|
|
6769
7047
|
await createSnapshotReport(companyName, {
|
|
6770
7048
|
domain,
|
|
6771
|
-
|
|
7049
|
+
queries: parseCsvOption(getString(input.values, "queries") ?? getString(input.values, "phrases")),
|
|
6772
7050
|
competitors: parseCsvOption(getString(input.values, "competitors")),
|
|
6773
7051
|
md: wantsMd,
|
|
6774
7052
|
pdf: wantsPdf,
|
|
@@ -6867,7 +7145,7 @@ async function showOverview(project, opts) {
|
|
|
6867
7145
|
console.log(JSON.stringify(overview, null, 2));
|
|
6868
7146
|
return;
|
|
6869
7147
|
}
|
|
6870
|
-
const { project: meta, latestRun, health, topInsights,
|
|
7148
|
+
const { project: meta, latestRun, health, topInsights, queryCounts, providers, transitions } = overview;
|
|
6871
7149
|
console.log(`Overview: ${meta.displayName ?? meta.name} (${meta.name})
|
|
6872
7150
|
`);
|
|
6873
7151
|
console.log(` Domain: ${meta.canonicalDomain}`);
|
|
@@ -6882,7 +7160,7 @@ async function showOverview(project, opts) {
|
|
|
6882
7160
|
console.log("\n No runs yet.");
|
|
6883
7161
|
}
|
|
6884
7162
|
console.log(`
|
|
6885
|
-
|
|
7163
|
+
Queries cited: ${queryCounts.citedQueries}/${queryCounts.totalQueries} (${pct(queryCounts.citedRate)})`);
|
|
6886
7164
|
if (providers.length > 0) {
|
|
6887
7165
|
console.log(" Providers:");
|
|
6888
7166
|
for (const p of providers) {
|
|
@@ -6924,14 +7202,14 @@ async function searchProject(project, opts) {
|
|
|
6924
7202
|
}
|
|
6925
7203
|
for (const hit of result.hits) {
|
|
6926
7204
|
if (hit.kind === "snapshot") {
|
|
6927
|
-
console.log(` [snapshot] ${hit.
|
|
7205
|
+
console.log(` [snapshot] ${hit.query} (${hit.provider}, ${hit.citationState}) \u2014 ${hit.matchedField}`);
|
|
6928
7206
|
console.log(` ${hit.snippet}`);
|
|
6929
7207
|
console.log(` run=${hit.runId} at ${hit.createdAt}`);
|
|
6930
7208
|
} else {
|
|
6931
7209
|
const dismissed = hit.dismissed ? " [dismissed]" : "";
|
|
6932
7210
|
console.log(` [insight ${hit.severity.toUpperCase()}] ${hit.type} \u2014 ${hit.title}${dismissed}`);
|
|
6933
7211
|
console.log(` ${hit.snippet}`);
|
|
6934
|
-
console.log(`
|
|
7212
|
+
console.log(` query=${hit.query} at ${hit.createdAt}`);
|
|
6935
7213
|
}
|
|
6936
7214
|
console.log("");
|
|
6937
7215
|
}
|
|
@@ -6946,8 +7224,8 @@ async function showCitationVisibility(project, opts) {
|
|
|
6946
7224
|
return;
|
|
6947
7225
|
}
|
|
6948
7226
|
if (data.status === "no-data") {
|
|
6949
|
-
if (data.reason === "no-
|
|
6950
|
-
console.log("No
|
|
7227
|
+
if (data.reason === "no-queries") {
|
|
7228
|
+
console.log("No queries configured. Add some with `canonry query add`.");
|
|
6951
7229
|
} else {
|
|
6952
7230
|
console.log("No citation data yet \u2014 run a sweep first (canonry run <project>).");
|
|
6953
7231
|
}
|
|
@@ -6966,11 +7244,11 @@ function printSummary(data) {
|
|
|
6966
7244
|
providersCiting,
|
|
6967
7245
|
providersMentioning,
|
|
6968
7246
|
providersConfigured,
|
|
6969
|
-
|
|
6970
|
-
|
|
6971
|
-
|
|
6972
|
-
|
|
6973
|
-
|
|
7247
|
+
totalQueries,
|
|
7248
|
+
queriesCitedAndMentioned,
|
|
7249
|
+
queriesCitedOnly,
|
|
7250
|
+
queriesMentionedOnly,
|
|
7251
|
+
queriesInvisible
|
|
6974
7252
|
} = data.summary;
|
|
6975
7253
|
console.log("Citation visibility");
|
|
6976
7254
|
if (data.summary.latestRunAt) {
|
|
@@ -6979,36 +7257,36 @@ function printSummary(data) {
|
|
|
6979
7257
|
console.log(`Cited in sources: ${providersCiting}/${providersConfigured} engines`);
|
|
6980
7258
|
console.log(`Mentioned in answers: ${providersMentioning}/${providersConfigured} engines`);
|
|
6981
7259
|
console.log("");
|
|
6982
|
-
console.log(`
|
|
6983
|
-
console.log(` cited + mentioned: ${
|
|
6984
|
-
console.log(` cited only: ${
|
|
6985
|
-
console.log(` mentioned only: ${
|
|
6986
|
-
console.log(` invisible: ${
|
|
7260
|
+
console.log(`Queries (${totalQueries} total):`);
|
|
7261
|
+
console.log(` cited + mentioned: ${queriesCitedAndMentioned}`);
|
|
7262
|
+
console.log(` cited only: ${queriesCitedOnly}`);
|
|
7263
|
+
console.log(` mentioned only: ${queriesMentionedOnly}`);
|
|
7264
|
+
console.log(` invisible: ${queriesInvisible}`);
|
|
6987
7265
|
}
|
|
6988
7266
|
function printCoverage(data) {
|
|
6989
|
-
if (data.
|
|
6990
|
-
console.log("No
|
|
7267
|
+
if (data.byQuery.length === 0) {
|
|
7268
|
+
console.log("No query coverage rows.");
|
|
6991
7269
|
return;
|
|
6992
7270
|
}
|
|
6993
7271
|
const providerSet = /* @__PURE__ */ new Set();
|
|
6994
|
-
for (const row of data.
|
|
7272
|
+
for (const row of data.byQuery) {
|
|
6995
7273
|
for (const p of row.providers) providerSet.add(p.provider);
|
|
6996
7274
|
}
|
|
6997
7275
|
const providerColumns = Array.from(providerSet).sort();
|
|
6998
7276
|
if (providerColumns.length === 0) {
|
|
6999
|
-
console.log("Per-
|
|
7000
|
-
for (const row of data.
|
|
7001
|
-
console.log(` ${row.
|
|
7277
|
+
console.log("Per-query coverage:");
|
|
7278
|
+
for (const row of data.byQuery) {
|
|
7279
|
+
console.log(` ${row.query.padEnd(35)} no snapshots`);
|
|
7002
7280
|
}
|
|
7003
7281
|
return;
|
|
7004
7282
|
}
|
|
7005
7283
|
const cellWidth = Math.max(6, ...providerColumns.map((p) => p.length));
|
|
7006
|
-
const
|
|
7007
|
-
const header = ["
|
|
7008
|
-
console.log("Per-
|
|
7284
|
+
const queryWidth = Math.max(7, ...data.byQuery.map((r) => r.query.length));
|
|
7285
|
+
const header = ["Query".padEnd(queryWidth), ...providerColumns.map((p) => p.padEnd(cellWidth)), "Cite", "Ment"].join(" ");
|
|
7286
|
+
console.log("Per-query coverage: (cell = [citation][mention]; C=cited c=not, M=mentioned m=not, \u2013=no data)");
|
|
7009
7287
|
console.log(header);
|
|
7010
7288
|
console.log("\u2500".repeat(header.length));
|
|
7011
|
-
for (const row of data.
|
|
7289
|
+
for (const row of data.byQuery) {
|
|
7012
7290
|
const cells = providerColumns.map((p) => {
|
|
7013
7291
|
const provider = row.providers.find((x) => x.provider === p);
|
|
7014
7292
|
if (!provider) return "\u2013".padEnd(cellWidth);
|
|
@@ -7018,16 +7296,16 @@ function printCoverage(data) {
|
|
|
7018
7296
|
});
|
|
7019
7297
|
const citeCol = `${row.citedCount}/${row.totalProviders}`;
|
|
7020
7298
|
const mentCol = `${row.mentionedCount}/${row.totalProviders}`;
|
|
7021
|
-
console.log([row.
|
|
7299
|
+
console.log([row.query.padEnd(queryWidth), ...cells, citeCol, mentCol].join(" "));
|
|
7022
7300
|
}
|
|
7023
7301
|
}
|
|
7024
7302
|
function printGaps2(data) {
|
|
7025
7303
|
console.log("Competitor gaps (not cited but a competitor is):");
|
|
7026
|
-
const
|
|
7304
|
+
const queryWidth = Math.max(7, ...data.competitorGaps.map((g) => g.query.length));
|
|
7027
7305
|
const providerWidth = Math.max(8, ...data.competitorGaps.map((g) => g.provider.length));
|
|
7028
7306
|
for (const gap of data.competitorGaps) {
|
|
7029
7307
|
console.log(
|
|
7030
|
-
` ${gap.
|
|
7308
|
+
` ${gap.query.padEnd(queryWidth)} ${gap.provider.padEnd(providerWidth)} ${gap.citingCompetitors.join(", ")}`
|
|
7031
7309
|
);
|
|
7032
7310
|
}
|
|
7033
7311
|
}
|
|
@@ -7469,7 +7747,7 @@ async function bootstrapCommand(_opts) {
|
|
|
7469
7747
|
|
|
7470
7748
|
// src/commands/daemon.ts
|
|
7471
7749
|
import { spawn } from "child_process";
|
|
7472
|
-
import
|
|
7750
|
+
import fs9 from "fs";
|
|
7473
7751
|
import path8 from "path";
|
|
7474
7752
|
function getPidPath() {
|
|
7475
7753
|
return path8.join(getConfigDir(), "canonry.pid");
|
|
@@ -7499,8 +7777,8 @@ async function waitForReady(host, port, maxMs = 1e4) {
|
|
|
7499
7777
|
async function startDaemon(opts) {
|
|
7500
7778
|
const pidPath = getPidPath();
|
|
7501
7779
|
const format = opts.format ?? "text";
|
|
7502
|
-
if (
|
|
7503
|
-
const existingPid = parseInt(
|
|
7780
|
+
if (fs9.existsSync(pidPath)) {
|
|
7781
|
+
const existingPid = parseInt(fs9.readFileSync(pidPath, "utf-8").trim(), 10);
|
|
7504
7782
|
if (!isNaN(existingPid) && isProcessAlive(existingPid)) {
|
|
7505
7783
|
throw new CliError({
|
|
7506
7784
|
code: "DAEMON_ALREADY_RUNNING",
|
|
@@ -7511,7 +7789,7 @@ async function startDaemon(opts) {
|
|
|
7511
7789
|
}
|
|
7512
7790
|
});
|
|
7513
7791
|
}
|
|
7514
|
-
|
|
7792
|
+
fs9.unlinkSync(pidPath);
|
|
7515
7793
|
}
|
|
7516
7794
|
const cliPath = path8.resolve(new URL(import.meta.url).pathname);
|
|
7517
7795
|
const inSourceMode = new URL(import.meta.url).pathname.endsWith(".ts");
|
|
@@ -7532,10 +7810,10 @@ async function startDaemon(opts) {
|
|
|
7532
7810
|
});
|
|
7533
7811
|
}
|
|
7534
7812
|
const configDir = getConfigDir();
|
|
7535
|
-
if (!
|
|
7536
|
-
|
|
7813
|
+
if (!fs9.existsSync(configDir)) {
|
|
7814
|
+
fs9.mkdirSync(configDir, { recursive: true });
|
|
7537
7815
|
}
|
|
7538
|
-
|
|
7816
|
+
fs9.writeFileSync(pidPath, String(child.pid), "utf-8");
|
|
7539
7817
|
const port = opts.port ?? "4100";
|
|
7540
7818
|
const host = opts.host ?? "127.0.0.1";
|
|
7541
7819
|
if (format !== "json") {
|
|
@@ -7544,7 +7822,7 @@ async function startDaemon(opts) {
|
|
|
7544
7822
|
const ready = await waitForReady(host, port);
|
|
7545
7823
|
if (!ready) {
|
|
7546
7824
|
try {
|
|
7547
|
-
|
|
7825
|
+
fs9.unlinkSync(pidPath);
|
|
7548
7826
|
} catch {
|
|
7549
7827
|
}
|
|
7550
7828
|
throw new CliError({
|
|
@@ -7576,7 +7854,7 @@ async function startDaemon(opts) {
|
|
|
7576
7854
|
}
|
|
7577
7855
|
function stopDaemon(format = "text") {
|
|
7578
7856
|
const pidPath = getPidPath();
|
|
7579
|
-
if (!
|
|
7857
|
+
if (!fs9.existsSync(pidPath)) {
|
|
7580
7858
|
if (format === "json") {
|
|
7581
7859
|
console.log(JSON.stringify({
|
|
7582
7860
|
stopped: false,
|
|
@@ -7587,7 +7865,7 @@ function stopDaemon(format = "text") {
|
|
|
7587
7865
|
console.log("Canonry is not running (no PID file found)");
|
|
7588
7866
|
return;
|
|
7589
7867
|
}
|
|
7590
|
-
const pid = parseInt(
|
|
7868
|
+
const pid = parseInt(fs9.readFileSync(pidPath, "utf-8").trim(), 10);
|
|
7591
7869
|
if (isNaN(pid)) {
|
|
7592
7870
|
if (format === "json") {
|
|
7593
7871
|
console.log(JSON.stringify({
|
|
@@ -7598,7 +7876,7 @@ function stopDaemon(format = "text") {
|
|
|
7598
7876
|
} else {
|
|
7599
7877
|
console.error("Invalid PID file. Removing it.");
|
|
7600
7878
|
}
|
|
7601
|
-
|
|
7879
|
+
fs9.unlinkSync(pidPath);
|
|
7602
7880
|
return;
|
|
7603
7881
|
}
|
|
7604
7882
|
if (!isProcessAlive(pid)) {
|
|
@@ -7612,12 +7890,12 @@ function stopDaemon(format = "text") {
|
|
|
7612
7890
|
} else {
|
|
7613
7891
|
console.log(`Canonry is not running (stale PID: ${pid}). Cleaning up.`);
|
|
7614
7892
|
}
|
|
7615
|
-
|
|
7893
|
+
fs9.unlinkSync(pidPath);
|
|
7616
7894
|
return;
|
|
7617
7895
|
}
|
|
7618
7896
|
try {
|
|
7619
7897
|
process.kill(pid, "SIGTERM");
|
|
7620
|
-
|
|
7898
|
+
fs9.unlinkSync(pidPath);
|
|
7621
7899
|
if (format === "json") {
|
|
7622
7900
|
console.log(JSON.stringify({
|
|
7623
7901
|
stopped: true,
|
|
@@ -7641,7 +7919,7 @@ function stopDaemon(format = "text") {
|
|
|
7641
7919
|
|
|
7642
7920
|
// src/commands/init.ts
|
|
7643
7921
|
import crypto2 from "crypto";
|
|
7644
|
-
import
|
|
7922
|
+
import fs10 from "fs";
|
|
7645
7923
|
import readline from "readline";
|
|
7646
7924
|
import path9 from "path";
|
|
7647
7925
|
function prompt(question) {
|
|
@@ -7665,7 +7943,7 @@ var PROJECT_MARKERS = [".git", "canonry.yaml", "canonry.yml", "package.json"];
|
|
|
7665
7943
|
function cwdLooksLikeProject(dir) {
|
|
7666
7944
|
const home = process.env.HOME ?? "";
|
|
7667
7945
|
if (home && path9.resolve(dir) === path9.resolve(home)) return false;
|
|
7668
|
-
return PROJECT_MARKERS.some((marker) =>
|
|
7946
|
+
return PROJECT_MARKERS.some((marker) => fs10.existsSync(path9.join(dir, marker)));
|
|
7669
7947
|
}
|
|
7670
7948
|
var DEFAULT_AGENT_MODELS = {
|
|
7671
7949
|
anthropic: "anthropic/claude-sonnet-4-6",
|
|
@@ -7695,8 +7973,8 @@ async function initCommand(opts) {
|
|
|
7695
7973
|
return void 0;
|
|
7696
7974
|
}
|
|
7697
7975
|
const configDir = getConfigDir();
|
|
7698
|
-
if (!
|
|
7699
|
-
|
|
7976
|
+
if (!fs10.existsSync(configDir)) {
|
|
7977
|
+
fs10.mkdirSync(configDir, { recursive: true });
|
|
7700
7978
|
}
|
|
7701
7979
|
const bootstrapEnv = getBootstrapEnv(process.env, {
|
|
7702
7980
|
GEMINI_API_KEY: opts?.geminiKey,
|
|
@@ -8259,10 +8537,10 @@ var SYSTEM_CLI_COMMANDS = [
|
|
|
8259
8537
|
];
|
|
8260
8538
|
|
|
8261
8539
|
// src/cli-commands/wordpress.ts
|
|
8262
|
-
import
|
|
8540
|
+
import fs11 from "fs";
|
|
8263
8541
|
|
|
8264
8542
|
// src/commands/wordpress.ts
|
|
8265
|
-
function
|
|
8543
|
+
function getClient19() {
|
|
8266
8544
|
return createApiClient();
|
|
8267
8545
|
}
|
|
8268
8546
|
function printJson2(value) {
|
|
@@ -8409,7 +8687,7 @@ async function wordpressConnect(project, opts) {
|
|
|
8409
8687
|
details: { project }
|
|
8410
8688
|
});
|
|
8411
8689
|
}
|
|
8412
|
-
const client =
|
|
8690
|
+
const client = getClient19();
|
|
8413
8691
|
const result = await client.wordpressConnect(project, {
|
|
8414
8692
|
url: opts.url,
|
|
8415
8693
|
stagingUrl: opts.stagingUrl,
|
|
@@ -8426,7 +8704,7 @@ async function wordpressConnect(project, opts) {
|
|
|
8426
8704
|
printWordpressStatus(project, result);
|
|
8427
8705
|
}
|
|
8428
8706
|
async function wordpressDisconnect(project, format) {
|
|
8429
|
-
const client =
|
|
8707
|
+
const client = getClient19();
|
|
8430
8708
|
await client.wordpressDisconnect(project);
|
|
8431
8709
|
if (format === "json") {
|
|
8432
8710
|
printJson2({ project, disconnected: true });
|
|
@@ -8435,7 +8713,7 @@ async function wordpressDisconnect(project, format) {
|
|
|
8435
8713
|
console.log(`WordPress disconnected from project "${project}".`);
|
|
8436
8714
|
}
|
|
8437
8715
|
async function wordpressStatus(project, format) {
|
|
8438
|
-
const client =
|
|
8716
|
+
const client = getClient19();
|
|
8439
8717
|
const result = await client.wordpressStatus(project);
|
|
8440
8718
|
if (format === "json") {
|
|
8441
8719
|
printJson2(result);
|
|
@@ -8444,7 +8722,7 @@ async function wordpressStatus(project, format) {
|
|
|
8444
8722
|
printWordpressStatus(project, result);
|
|
8445
8723
|
}
|
|
8446
8724
|
async function wordpressPages(project, opts) {
|
|
8447
|
-
const client =
|
|
8725
|
+
const client = getClient19();
|
|
8448
8726
|
const result = await client.wordpressPages(project, opts.env);
|
|
8449
8727
|
if (opts.format === "json") {
|
|
8450
8728
|
printJson2(result);
|
|
@@ -8453,7 +8731,7 @@ async function wordpressPages(project, opts) {
|
|
|
8453
8731
|
printPages(project, result.env, result.pages);
|
|
8454
8732
|
}
|
|
8455
8733
|
async function wordpressPage(project, slug, opts) {
|
|
8456
|
-
const client =
|
|
8734
|
+
const client = getClient19();
|
|
8457
8735
|
const result = await client.wordpressPage(project, slug, opts.env);
|
|
8458
8736
|
if (opts.format === "json") {
|
|
8459
8737
|
printJson2(result);
|
|
@@ -8462,7 +8740,7 @@ async function wordpressPage(project, slug, opts) {
|
|
|
8462
8740
|
printPageDetail(result);
|
|
8463
8741
|
}
|
|
8464
8742
|
async function wordpressCreatePage(project, body) {
|
|
8465
|
-
const client =
|
|
8743
|
+
const client = getClient19();
|
|
8466
8744
|
const result = await client.wordpressCreatePage(project, body);
|
|
8467
8745
|
if (body.format === "json") {
|
|
8468
8746
|
printJson2(result);
|
|
@@ -8473,7 +8751,7 @@ async function wordpressCreatePage(project, body) {
|
|
|
8473
8751
|
printPageDetail(result);
|
|
8474
8752
|
}
|
|
8475
8753
|
async function wordpressUpdatePage(project, body) {
|
|
8476
|
-
const client =
|
|
8754
|
+
const client = getClient19();
|
|
8477
8755
|
const result = await client.wordpressUpdatePage(project, body);
|
|
8478
8756
|
if (body.format === "json") {
|
|
8479
8757
|
printJson2(result);
|
|
@@ -8484,7 +8762,7 @@ async function wordpressUpdatePage(project, body) {
|
|
|
8484
8762
|
printPageDetail(result);
|
|
8485
8763
|
}
|
|
8486
8764
|
async function wordpressSetMeta(project, body) {
|
|
8487
|
-
const client =
|
|
8765
|
+
const client = getClient19();
|
|
8488
8766
|
const result = await client.wordpressSetMeta(project, body);
|
|
8489
8767
|
if (body.format === "json") {
|
|
8490
8768
|
printJson2(result);
|
|
@@ -8495,12 +8773,12 @@ async function wordpressSetMeta(project, body) {
|
|
|
8495
8773
|
printPageDetail(result);
|
|
8496
8774
|
}
|
|
8497
8775
|
async function wordpressBulkSetMeta(project, opts) {
|
|
8498
|
-
const
|
|
8776
|
+
const fs12 = await import("fs/promises");
|
|
8499
8777
|
const path10 = await import("path");
|
|
8500
8778
|
const filePath = path10.resolve(opts.from);
|
|
8501
8779
|
let raw;
|
|
8502
8780
|
try {
|
|
8503
|
-
raw = await
|
|
8781
|
+
raw = await fs12.readFile(filePath, "utf8");
|
|
8504
8782
|
} catch {
|
|
8505
8783
|
throw new CliError({
|
|
8506
8784
|
code: "FILE_READ_ERROR",
|
|
@@ -8534,7 +8812,7 @@ async function wordpressBulkSetMeta(project, opts) {
|
|
|
8534
8812
|
details: { path: filePath }
|
|
8535
8813
|
});
|
|
8536
8814
|
}
|
|
8537
|
-
const client =
|
|
8815
|
+
const client = getClient19();
|
|
8538
8816
|
const result = await client.wordpressBulkSetMeta(project, { entries, env: opts.env });
|
|
8539
8817
|
if (opts.format === "json") {
|
|
8540
8818
|
printJson2(result);
|
|
@@ -8577,7 +8855,7 @@ async function wordpressBulkSetMeta(project, opts) {
|
|
|
8577
8855
|
Total: ${applied.length} applied, ${skipped.length} skipped, ${manual.length} manual`);
|
|
8578
8856
|
}
|
|
8579
8857
|
async function wordpressSchema(project, slug, opts) {
|
|
8580
|
-
const client =
|
|
8858
|
+
const client = getClient19();
|
|
8581
8859
|
const result = await client.wordpressSchema(project, slug, opts.env);
|
|
8582
8860
|
if (opts.format === "json") {
|
|
8583
8861
|
printJson2(result);
|
|
@@ -8588,7 +8866,7 @@ async function wordpressSchema(project, slug, opts) {
|
|
|
8588
8866
|
printSchemaBlocks(result.blocks);
|
|
8589
8867
|
}
|
|
8590
8868
|
async function wordpressSetSchema(project, body) {
|
|
8591
|
-
const client =
|
|
8869
|
+
const client = getClient19();
|
|
8592
8870
|
const result = await client.wordpressSetSchema(project, body);
|
|
8593
8871
|
if (body.format === "json") {
|
|
8594
8872
|
printJson2(result);
|
|
@@ -8597,13 +8875,13 @@ async function wordpressSetSchema(project, body) {
|
|
|
8597
8875
|
printManualAssist(`Schema update for "${body.slug}"`, result);
|
|
8598
8876
|
}
|
|
8599
8877
|
async function wordpressSchemaDeploy(project, opts) {
|
|
8600
|
-
const
|
|
8878
|
+
const fs12 = await import("fs/promises");
|
|
8601
8879
|
const path10 = await import("path");
|
|
8602
8880
|
const yaml = await import("yaml").catch(() => null);
|
|
8603
8881
|
const filePath = path10.resolve(opts.profile);
|
|
8604
8882
|
let raw;
|
|
8605
8883
|
try {
|
|
8606
|
-
raw = await
|
|
8884
|
+
raw = await fs12.readFile(filePath, "utf8");
|
|
8607
8885
|
} catch {
|
|
8608
8886
|
throw new CliError({
|
|
8609
8887
|
code: "FILE_READ_ERROR",
|
|
@@ -8636,7 +8914,7 @@ async function wordpressSchemaDeploy(project, opts) {
|
|
|
8636
8914
|
details: { path: filePath }
|
|
8637
8915
|
});
|
|
8638
8916
|
}
|
|
8639
|
-
const client =
|
|
8917
|
+
const client = getClient19();
|
|
8640
8918
|
const result = await client.wordpressSchemaDeploy(project, { profile: parsed, env: opts.env });
|
|
8641
8919
|
if (opts.format === "json") {
|
|
8642
8920
|
printJson2(result);
|
|
@@ -8675,7 +8953,7 @@ async function wordpressSchemaDeploy(project, opts) {
|
|
|
8675
8953
|
Total: ${deployed} deployed, ${stripped} stripped, ${skipped} skipped, ${failed} failed`);
|
|
8676
8954
|
}
|
|
8677
8955
|
async function wordpressSchemaStatus(project, opts) {
|
|
8678
|
-
const client =
|
|
8956
|
+
const client = getClient19();
|
|
8679
8957
|
const result = await client.wordpressSchemaStatus(project, opts.env);
|
|
8680
8958
|
if (opts.format === "json") {
|
|
8681
8959
|
printJson2(result);
|
|
@@ -8708,13 +8986,13 @@ async function wordpressOnboard(project, opts) {
|
|
|
8708
8986
|
}
|
|
8709
8987
|
let profileData;
|
|
8710
8988
|
if (opts.profile) {
|
|
8711
|
-
const
|
|
8989
|
+
const fs12 = await import("fs/promises");
|
|
8712
8990
|
const path10 = await import("path");
|
|
8713
8991
|
const yaml = await import("yaml").catch(() => null);
|
|
8714
8992
|
const filePath = path10.resolve(opts.profile);
|
|
8715
8993
|
let raw;
|
|
8716
8994
|
try {
|
|
8717
|
-
raw = await
|
|
8995
|
+
raw = await fs12.readFile(filePath, "utf8");
|
|
8718
8996
|
} catch {
|
|
8719
8997
|
throw new CliError({
|
|
8720
8998
|
code: "FILE_READ_ERROR",
|
|
@@ -8734,7 +9012,7 @@ async function wordpressOnboard(project, opts) {
|
|
|
8734
9012
|
});
|
|
8735
9013
|
}
|
|
8736
9014
|
}
|
|
8737
|
-
const client =
|
|
9015
|
+
const client = getClient19();
|
|
8738
9016
|
const result = await client.wordpressOnboard(project, {
|
|
8739
9017
|
url: opts.url,
|
|
8740
9018
|
username: opts.user,
|
|
@@ -8759,7 +9037,7 @@ async function wordpressOnboard(project, opts) {
|
|
|
8759
9037
|
}
|
|
8760
9038
|
}
|
|
8761
9039
|
async function wordpressLlmsTxt(project, opts) {
|
|
8762
|
-
const client =
|
|
9040
|
+
const client = getClient19();
|
|
8763
9041
|
const result = await client.wordpressLlmsTxt(project, opts.env);
|
|
8764
9042
|
if (opts.format === "json") {
|
|
8765
9043
|
printJson2(result);
|
|
@@ -8770,7 +9048,7 @@ async function wordpressLlmsTxt(project, opts) {
|
|
|
8770
9048
|
console.log(result.content ?? "(not found)");
|
|
8771
9049
|
}
|
|
8772
9050
|
async function wordpressSetLlmsTxt(project, body) {
|
|
8773
|
-
const client =
|
|
9051
|
+
const client = getClient19();
|
|
8774
9052
|
const result = await client.wordpressSetLlmsTxt(project, body);
|
|
8775
9053
|
if (body.format === "json") {
|
|
8776
9054
|
printJson2(result);
|
|
@@ -8779,7 +9057,7 @@ async function wordpressSetLlmsTxt(project, body) {
|
|
|
8779
9057
|
printManualAssist(`llms.txt update for "${project}"`, result);
|
|
8780
9058
|
}
|
|
8781
9059
|
async function wordpressAudit(project, opts) {
|
|
8782
|
-
const client =
|
|
9060
|
+
const client = getClient19();
|
|
8783
9061
|
const result = await client.wordpressAudit(project, opts.env);
|
|
8784
9062
|
if (opts.format === "json") {
|
|
8785
9063
|
printJson2(result);
|
|
@@ -8793,7 +9071,7 @@ async function wordpressAudit(project, opts) {
|
|
|
8793
9071
|
printAuditIssues(result.issues);
|
|
8794
9072
|
}
|
|
8795
9073
|
async function wordpressDiff(project, slug, format) {
|
|
8796
|
-
const client =
|
|
9074
|
+
const client = getClient19();
|
|
8797
9075
|
const result = await client.wordpressDiff(project, slug);
|
|
8798
9076
|
if (format === "json") {
|
|
8799
9077
|
printJson2(result);
|
|
@@ -8802,7 +9080,7 @@ async function wordpressDiff(project, slug, format) {
|
|
|
8802
9080
|
printDiff(result);
|
|
8803
9081
|
}
|
|
8804
9082
|
async function wordpressStagingStatus(project, format) {
|
|
8805
|
-
const client =
|
|
9083
|
+
const client = getClient19();
|
|
8806
9084
|
const result = await client.wordpressStagingStatus(project);
|
|
8807
9085
|
if (format === "json") {
|
|
8808
9086
|
printJson2(result);
|
|
@@ -8816,7 +9094,7 @@ async function wordpressStagingStatus(project, format) {
|
|
|
8816
9094
|
console.log(` Admin URL: ${result.adminUrl}`);
|
|
8817
9095
|
}
|
|
8818
9096
|
async function wordpressStagingPush(project, format) {
|
|
8819
|
-
const client =
|
|
9097
|
+
const client = getClient19();
|
|
8820
9098
|
const result = await client.wordpressStagingPush(project);
|
|
8821
9099
|
if (format === "json") {
|
|
8822
9100
|
printJson2(result);
|
|
@@ -8863,7 +9141,7 @@ function resolveContent(input, command, usage, options) {
|
|
|
8863
9141
|
}
|
|
8864
9142
|
if (contentFile) {
|
|
8865
9143
|
try {
|
|
8866
|
-
return
|
|
9144
|
+
return fs11.readFileSync(contentFile, "utf-8");
|
|
8867
9145
|
} catch (error) {
|
|
8868
9146
|
const message = error instanceof Error ? error.message : String(error);
|
|
8869
9147
|
throw usageError(`Error: could not read --content-file "${contentFile}": ${message}`, {
|
|
@@ -9795,6 +10073,7 @@ var REGISTERED_CLI_COMMANDS = [
|
|
|
9795
10073
|
...SYSTEM_CLI_COMMANDS,
|
|
9796
10074
|
...PROJECT_CLI_COMMANDS,
|
|
9797
10075
|
...REPORT_CLI_COMMANDS,
|
|
10076
|
+
...QUERY_CLI_COMMANDS,
|
|
9798
10077
|
...KEYWORD_CLI_COMMANDS,
|
|
9799
10078
|
...COMPETITOR_CLI_COMMANDS,
|
|
9800
10079
|
...SETTINGS_CLI_COMMANDS,
|
|
@@ -9832,14 +10111,14 @@ Setup:
|
|
|
9832
10111
|
|
|
9833
10112
|
Projects:
|
|
9834
10113
|
project Create, update, list, show, delete projects
|
|
9835
|
-
|
|
10114
|
+
query Add, replace, remove, list, import, generate queries
|
|
9836
10115
|
competitor Add, remove, list competitors
|
|
9837
10116
|
|
|
9838
10117
|
Monitoring:
|
|
9839
10118
|
run Trigger visibility sweeps
|
|
9840
10119
|
snapshot One-shot AI perception report
|
|
9841
10120
|
status <project> Show project summary
|
|
9842
|
-
evidence <project> Show per-
|
|
10121
|
+
evidence <project> Show per-query results
|
|
9843
10122
|
analytics <project> Show analytics (metrics, gaps, sources)
|
|
9844
10123
|
insights <project> Show intelligence insights
|
|
9845
10124
|
health <project> Show citation health
|
|
@@ -9893,7 +10172,7 @@ async function runCli(args = process.argv.slice(2)) {
|
|
|
9893
10172
|
showFirstRunNotice();
|
|
9894
10173
|
getOrCreateAnonymousId();
|
|
9895
10174
|
}
|
|
9896
|
-
const SUBCOMMAND_COMMANDS = /* @__PURE__ */ new Set(["backfill", "project", "keyword", "competitor", "schedule", "notify", "settings", "telemetry", "google", "bing", "wordpress", "cdp"]);
|
|
10175
|
+
const SUBCOMMAND_COMMANDS = /* @__PURE__ */ new Set(["backfill", "project", "query", "keyword", "competitor", "schedule", "notify", "settings", "telemetry", "google", "bing", "wordpress", "cdp"]);
|
|
9897
10176
|
const MIXED_SUBCOMMANDS = {
|
|
9898
10177
|
insights: /* @__PURE__ */ new Set(["dismiss"]),
|
|
9899
10178
|
run: /* @__PURE__ */ new Set(["show", "cancel"])
|