@ainyc/canonry 1.37.1 → 1.39.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node --import tsx
|
|
2
2
|
import {
|
|
3
|
+
IntelligenceService,
|
|
3
4
|
apiKeys,
|
|
4
5
|
configExists,
|
|
5
6
|
createClient,
|
|
@@ -26,22 +27,26 @@ import {
|
|
|
26
27
|
setGoogleAuthConfig,
|
|
27
28
|
showFirstRunNotice,
|
|
28
29
|
trackEvent
|
|
29
|
-
} from "./chunk-
|
|
30
|
+
} from "./chunk-4W7EW7SN.js";
|
|
30
31
|
|
|
31
32
|
// src/cli.ts
|
|
32
33
|
import { pathToFileURL } from "url";
|
|
33
34
|
|
|
34
35
|
// src/cli-error.ts
|
|
36
|
+
var EXIT_USER_ERROR = 1;
|
|
37
|
+
var EXIT_SYSTEM_ERROR = 2;
|
|
35
38
|
var CliError = class extends Error {
|
|
36
39
|
code;
|
|
37
40
|
displayMessage;
|
|
38
41
|
details;
|
|
42
|
+
exitCode;
|
|
39
43
|
constructor(options) {
|
|
40
44
|
super(options.message);
|
|
41
45
|
this.name = "CliError";
|
|
42
46
|
this.code = options.code;
|
|
43
47
|
this.displayMessage = options.displayMessage;
|
|
44
48
|
this.details = options.details;
|
|
49
|
+
this.exitCode = options.exitCode ?? EXIT_USER_ERROR;
|
|
45
50
|
}
|
|
46
51
|
};
|
|
47
52
|
function usageError(displayMessage, options) {
|
|
@@ -223,6 +228,41 @@ async function backfillAnswerVisibilityCommand(opts) {
|
|
|
223
228
|
console.log(` Updated: ${updated}`);
|
|
224
229
|
console.log(` Visible: ${visible}`);
|
|
225
230
|
}
|
|
231
|
+
async function backfillInsightsCommand(project, opts) {
|
|
232
|
+
const config = loadConfig();
|
|
233
|
+
const db = createClient(config.database);
|
|
234
|
+
migrate(db);
|
|
235
|
+
const service = new IntelligenceService(db);
|
|
236
|
+
const isJson = opts?.format === "json";
|
|
237
|
+
if (!isJson) {
|
|
238
|
+
process.stderr.write(`Backfilling insights for "${project}"...
|
|
239
|
+
`);
|
|
240
|
+
}
|
|
241
|
+
const result = service.backfill(project, {
|
|
242
|
+
fromRunId: opts?.fromRun,
|
|
243
|
+
toRunId: opts?.toRun
|
|
244
|
+
}, (info) => {
|
|
245
|
+
if (!isJson) {
|
|
246
|
+
process.stderr.write(` [${info.index}/${info.total}] ${info.runId} \u2014 ${info.insights} insights
|
|
247
|
+
`);
|
|
248
|
+
}
|
|
249
|
+
});
|
|
250
|
+
const output = {
|
|
251
|
+
project,
|
|
252
|
+
processed: result.processed,
|
|
253
|
+
skipped: result.skipped,
|
|
254
|
+
totalInsights: result.totalInsights
|
|
255
|
+
};
|
|
256
|
+
if (isJson) {
|
|
257
|
+
console.log(JSON.stringify(output, null, 2));
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
260
|
+
console.log(`
|
|
261
|
+
Backfill complete.`);
|
|
262
|
+
console.log(` Processed: ${result.processed}`);
|
|
263
|
+
console.log(` Skipped: ${result.skipped}`);
|
|
264
|
+
console.log(` Insights: ${result.totalInsights}`);
|
|
265
|
+
}
|
|
226
266
|
|
|
227
267
|
// src/cli-command-helpers.ts
|
|
228
268
|
function getString(values, key) {
|
|
@@ -320,14 +360,31 @@ var BACKFILL_CLI_COMMANDS = [
|
|
|
320
360
|
});
|
|
321
361
|
}
|
|
322
362
|
},
|
|
363
|
+
{
|
|
364
|
+
path: ["backfill", "insights"],
|
|
365
|
+
usage: "canonry backfill insights <project> [--from-run <id>] [--to-run <id>] [--format json]",
|
|
366
|
+
options: {
|
|
367
|
+
"from-run": stringOption(),
|
|
368
|
+
"to-run": stringOption()
|
|
369
|
+
},
|
|
370
|
+
run: async (input) => {
|
|
371
|
+
const usage = "canonry backfill insights <project> [--from-run <id>] [--to-run <id>] [--format json]";
|
|
372
|
+
const project = requireProject(input, "backfill insights", usage);
|
|
373
|
+
await backfillInsightsCommand(project, {
|
|
374
|
+
fromRun: getString(input.values, "from-run"),
|
|
375
|
+
toRun: getString(input.values, "to-run"),
|
|
376
|
+
format: input.format
|
|
377
|
+
});
|
|
378
|
+
}
|
|
379
|
+
},
|
|
323
380
|
{
|
|
324
381
|
path: ["backfill"],
|
|
325
|
-
usage: "canonry backfill <answer-visibility> [
|
|
382
|
+
usage: "canonry backfill <answer-visibility|insights> [options]",
|
|
326
383
|
run: async (input) => {
|
|
327
384
|
unknownSubcommand(input.positionals[0], {
|
|
328
385
|
command: "backfill",
|
|
329
|
-
usage: "canonry backfill <answer-visibility> [
|
|
330
|
-
available: ["answer-visibility"]
|
|
386
|
+
usage: "canonry backfill <answer-visibility|insights> [options]",
|
|
387
|
+
available: ["answer-visibility", "insights"]
|
|
331
388
|
});
|
|
332
389
|
}
|
|
333
390
|
}
|
|
@@ -398,13 +455,16 @@ var ApiClient = class {
|
|
|
398
455
|
body: serializedBody
|
|
399
456
|
});
|
|
400
457
|
} catch (err) {
|
|
458
|
+
if (err instanceof CliError) throw err;
|
|
401
459
|
const msg = err instanceof Error ? err.message : String(err);
|
|
402
460
|
if (msg.includes("fetch failed") || msg.includes("ECONNREFUSED") || msg.includes("connect ECONNREFUSED")) {
|
|
403
|
-
throw new
|
|
404
|
-
|
|
405
|
-
|
|
461
|
+
throw new CliError({
|
|
462
|
+
code: "CONNECTION_ERROR",
|
|
463
|
+
message: `Could not connect to canonry server at ${this.baseUrl.replace("/api/v1", "")}. Start it with "canonry serve" (or "canonry serve &" to run in background).`,
|
|
464
|
+
exitCode: EXIT_SYSTEM_ERROR
|
|
465
|
+
});
|
|
406
466
|
}
|
|
407
|
-
throw
|
|
467
|
+
throw new CliError({ code: "CONNECTION_ERROR", message: msg, exitCode: EXIT_SYSTEM_ERROR });
|
|
408
468
|
}
|
|
409
469
|
if (!res.ok) {
|
|
410
470
|
let errorBody;
|
|
@@ -413,8 +473,11 @@ var ApiClient = class {
|
|
|
413
473
|
} catch {
|
|
414
474
|
errorBody = { error: { code: "UNKNOWN", message: res.statusText } };
|
|
415
475
|
}
|
|
416
|
-
const
|
|
417
|
-
|
|
476
|
+
const errorObj = errorBody && typeof errorBody === "object" && "error" in errorBody && errorBody.error && typeof errorBody.error === "object" ? errorBody.error : null;
|
|
477
|
+
const msg = errorObj?.message ? String(errorObj.message) : `HTTP ${res.status}: ${res.statusText}`;
|
|
478
|
+
const code = errorObj?.code ? String(errorObj.code) : "API_ERROR";
|
|
479
|
+
const exitCode = res.status >= 500 ? EXIT_SYSTEM_ERROR : EXIT_USER_ERROR;
|
|
480
|
+
throw new CliError({ code, message: msg, exitCode });
|
|
418
481
|
}
|
|
419
482
|
if (res.status === 204) {
|
|
420
483
|
return void 0;
|
|
@@ -744,6 +807,24 @@ var ApiClient = class {
|
|
|
744
807
|
async wordpressStagingPush(project) {
|
|
745
808
|
return this.request("POST", `/projects/${encodeURIComponent(project)}/wordpress/staging/push`);
|
|
746
809
|
}
|
|
810
|
+
// ── Intelligence ──────────────────────────────────────────────────────
|
|
811
|
+
async getInsights(project, opts) {
|
|
812
|
+
const params = new URLSearchParams();
|
|
813
|
+
if (opts?.dismissed) params.set("dismissed", "true");
|
|
814
|
+
if (opts?.runId) params.set("runId", opts.runId);
|
|
815
|
+
const qs = params.toString();
|
|
816
|
+
return this.request("GET", `/projects/${encodeURIComponent(project)}/insights${qs ? `?${qs}` : ""}`);
|
|
817
|
+
}
|
|
818
|
+
async dismissInsight(project, id) {
|
|
819
|
+
return this.request("POST", `/projects/${encodeURIComponent(project)}/insights/${encodeURIComponent(id)}/dismiss`);
|
|
820
|
+
}
|
|
821
|
+
async getHealth(project) {
|
|
822
|
+
return this.request("GET", `/projects/${encodeURIComponent(project)}/health/latest`);
|
|
823
|
+
}
|
|
824
|
+
async getHealthHistory(project, limit) {
|
|
825
|
+
const qs = limit ? `?limit=${limit}` : "";
|
|
826
|
+
return this.request("GET", `/projects/${encodeURIComponent(project)}/health/history${qs}`);
|
|
827
|
+
}
|
|
747
828
|
};
|
|
748
829
|
|
|
749
830
|
// src/commands/bing.ts
|
|
@@ -751,22 +832,12 @@ function getClient() {
|
|
|
751
832
|
return createApiClient();
|
|
752
833
|
}
|
|
753
834
|
async function bingConnect(project, opts) {
|
|
754
|
-
|
|
755
|
-
if (!apiKey) {
|
|
756
|
-
const readline2 = await import("readline");
|
|
757
|
-
const rl = readline2.createInterface({ input: process.stdin, output: process.stderr });
|
|
758
|
-
apiKey = await new Promise((resolve) => {
|
|
759
|
-
rl.question("Bing Webmaster Tools API key: ", (answer) => {
|
|
760
|
-
rl.close();
|
|
761
|
-
resolve(answer.trim());
|
|
762
|
-
});
|
|
763
|
-
});
|
|
764
|
-
}
|
|
835
|
+
const apiKey = opts?.apiKey;
|
|
765
836
|
if (!apiKey) {
|
|
766
837
|
throw new CliError({
|
|
767
838
|
code: "BING_API_KEY_REQUIRED",
|
|
768
|
-
message: "API key is required
|
|
769
|
-
displayMessage: "Error: API key is required
|
|
839
|
+
message: "API key is required. Pass --api-key <key>.",
|
|
840
|
+
displayMessage: "Error: API key is required. Pass --api-key <key>.",
|
|
770
841
|
details: {
|
|
771
842
|
project
|
|
772
843
|
}
|
|
@@ -3287,13 +3358,13 @@ async function showStatus(project, format) {
|
|
|
3287
3358
|
console.log(JSON.stringify({ project: projectData, runs: runs2 }, null, 2));
|
|
3288
3359
|
return;
|
|
3289
3360
|
}
|
|
3290
|
-
console.log(`Status: ${projectData.displayName} (${projectData.name})
|
|
3361
|
+
console.log(`Status: ${projectData.displayName ?? projectData.name} (${projectData.name})
|
|
3291
3362
|
`);
|
|
3292
3363
|
console.log(` Domain: ${projectData.canonicalDomain}`);
|
|
3293
3364
|
console.log(` Country: ${projectData.country}`);
|
|
3294
3365
|
console.log(` Language: ${projectData.language}`);
|
|
3295
3366
|
if (runs2.length > 0) {
|
|
3296
|
-
const latest = runs2[
|
|
3367
|
+
const latest = runs2[0];
|
|
3297
3368
|
console.log(`
|
|
3298
3369
|
Latest run:`);
|
|
3299
3370
|
console.log(` ID: ${latest.id}`);
|
|
@@ -3313,25 +3384,25 @@ async function showStatus(project, format) {
|
|
|
3313
3384
|
var OPERATOR_CLI_COMMANDS = [
|
|
3314
3385
|
{
|
|
3315
3386
|
path: ["status"],
|
|
3316
|
-
usage: "canonry status <project>",
|
|
3387
|
+
usage: "canonry status <project> [--format json]",
|
|
3317
3388
|
run: async (input) => {
|
|
3318
|
-
const project = requireProject(input, "status", "canonry status <project>");
|
|
3389
|
+
const project = requireProject(input, "status", "canonry status <project> [--format json]");
|
|
3319
3390
|
await showStatus(project, input.format);
|
|
3320
3391
|
}
|
|
3321
3392
|
},
|
|
3322
3393
|
{
|
|
3323
3394
|
path: ["evidence"],
|
|
3324
|
-
usage: "canonry evidence <project>",
|
|
3395
|
+
usage: "canonry evidence <project> [--format json]",
|
|
3325
3396
|
run: async (input) => {
|
|
3326
|
-
const project = requireProject(input, "evidence", "canonry evidence <project>");
|
|
3397
|
+
const project = requireProject(input, "evidence", "canonry evidence <project> [--format json]");
|
|
3327
3398
|
await showEvidence(project, input.format);
|
|
3328
3399
|
}
|
|
3329
3400
|
},
|
|
3330
3401
|
{
|
|
3331
3402
|
path: ["history"],
|
|
3332
|
-
usage: "canonry history <project>",
|
|
3403
|
+
usage: "canonry history <project> [--format json]",
|
|
3333
3404
|
run: async (input) => {
|
|
3334
|
-
const project = requireProject(input, "history", "canonry history <project>");
|
|
3405
|
+
const project = requireProject(input, "history", "canonry history <project> [--format json]");
|
|
3335
3406
|
await showHistory(project, input.format);
|
|
3336
3407
|
}
|
|
3337
3408
|
},
|
|
@@ -3437,7 +3508,7 @@ async function showProject(name, format) {
|
|
|
3437
3508
|
console.log(JSON.stringify(project, null, 2));
|
|
3438
3509
|
return;
|
|
3439
3510
|
}
|
|
3440
|
-
console.log(`Project: ${project.displayName}
|
|
3511
|
+
console.log(`Project: ${project.displayName ?? project.name}
|
|
3441
3512
|
`);
|
|
3442
3513
|
console.log(` Name: ${project.name}`);
|
|
3443
3514
|
console.log(` ID: ${project.id}`);
|
|
@@ -3453,8 +3524,8 @@ async function showProject(name, format) {
|
|
|
3453
3524
|
console.log(` Tags: ${project.tags.length > 0 ? project.tags.join(", ") : "(none)"}`);
|
|
3454
3525
|
const labelEntries = Object.entries(project.labels);
|
|
3455
3526
|
console.log(` Labels: ${labelEntries.length > 0 ? labelEntries.map(([k, v]) => `${k}=${v}`).join(", ") : "(none)"}`);
|
|
3456
|
-
console.log(` Created: ${project.createdAt}`);
|
|
3457
|
-
console.log(` Updated: ${project.updatedAt}`);
|
|
3527
|
+
if (project.createdAt) console.log(` Created: ${project.createdAt}`);
|
|
3528
|
+
if (project.updatedAt) console.log(` Updated: ${project.updatedAt}`);
|
|
3458
3529
|
}
|
|
3459
3530
|
async function updateProjectSettings(name, opts) {
|
|
3460
3531
|
const client = getClient12();
|
|
@@ -3469,7 +3540,7 @@ async function updateProjectSettings(name, opts) {
|
|
|
3469
3540
|
ownedDomains = ownedDomains.filter((d) => !toRemove.has(d));
|
|
3470
3541
|
}
|
|
3471
3542
|
const result = await client.putProject(name, {
|
|
3472
|
-
displayName: opts.displayName ?? project.displayName,
|
|
3543
|
+
displayName: opts.displayName ?? project.displayName ?? project.name,
|
|
3473
3544
|
canonicalDomain: opts.domain ?? project.canonicalDomain,
|
|
3474
3545
|
ownedDomains,
|
|
3475
3546
|
country: opts.country ?? project.country,
|
|
@@ -4855,6 +4926,126 @@ var SNAPSHOT_CLI_COMMANDS = [
|
|
|
4855
4926
|
}
|
|
4856
4927
|
];
|
|
4857
4928
|
|
|
4929
|
+
// src/commands/insights.ts
|
|
4930
|
+
async function listInsights(project, opts) {
|
|
4931
|
+
const client = createApiClient();
|
|
4932
|
+
const insights = await client.getInsights(project, { dismissed: opts.dismissed });
|
|
4933
|
+
if (opts.format === "json") {
|
|
4934
|
+
console.log(JSON.stringify(insights, null, 2));
|
|
4935
|
+
return;
|
|
4936
|
+
}
|
|
4937
|
+
if (insights.length === 0) {
|
|
4938
|
+
console.log("No insights found.");
|
|
4939
|
+
return;
|
|
4940
|
+
}
|
|
4941
|
+
for (const insight of insights) {
|
|
4942
|
+
const severity = insight.severity.toUpperCase().padEnd(8);
|
|
4943
|
+
const dismissed = insight.dismissed ? " [dismissed]" : "";
|
|
4944
|
+
console.log(`${severity} ${insight.type.padEnd(12)} ${insight.title}${dismissed}`);
|
|
4945
|
+
if (insight.recommendation) {
|
|
4946
|
+
console.log(` Action: ${insight.recommendation.action}${insight.recommendation.target ? ` \u2192 ${insight.recommendation.target}` : ""}`);
|
|
4947
|
+
console.log(` Reason: ${insight.recommendation.reason}`);
|
|
4948
|
+
}
|
|
4949
|
+
if (insight.cause) {
|
|
4950
|
+
console.log(` Cause: ${insight.cause.cause}${insight.cause.competitorDomain ? ` (${insight.cause.competitorDomain})` : ""}`);
|
|
4951
|
+
}
|
|
4952
|
+
console.log("");
|
|
4953
|
+
}
|
|
4954
|
+
}
|
|
4955
|
+
async function dismissInsight(project, id, opts) {
|
|
4956
|
+
const client = createApiClient();
|
|
4957
|
+
const result = await client.dismissInsight(project, id);
|
|
4958
|
+
if (opts.format === "json") {
|
|
4959
|
+
console.log(JSON.stringify(result, null, 2));
|
|
4960
|
+
return;
|
|
4961
|
+
}
|
|
4962
|
+
console.log(`Insight ${id} dismissed.`);
|
|
4963
|
+
}
|
|
4964
|
+
|
|
4965
|
+
// src/commands/health-cmd.ts
|
|
4966
|
+
async function showHealth(project, opts) {
|
|
4967
|
+
const client = createApiClient();
|
|
4968
|
+
if (opts.history) {
|
|
4969
|
+
const snapshots = await client.getHealthHistory(project, opts.limit);
|
|
4970
|
+
if (opts.format === "json") {
|
|
4971
|
+
console.log(JSON.stringify(snapshots, null, 2));
|
|
4972
|
+
return;
|
|
4973
|
+
}
|
|
4974
|
+
if (snapshots.length === 0) {
|
|
4975
|
+
console.log("No health history available.");
|
|
4976
|
+
return;
|
|
4977
|
+
}
|
|
4978
|
+
console.log("Date Cited Rate Cited/Total");
|
|
4979
|
+
console.log("\u2500".repeat(55));
|
|
4980
|
+
for (const snap of snapshots) {
|
|
4981
|
+
const rate2 = (snap.overallCitedRate * 100).toFixed(1).padStart(5) + "%";
|
|
4982
|
+
const ratio = `${snap.citedPairs}/${snap.totalPairs}`;
|
|
4983
|
+
const date = snap.createdAt.slice(0, 19).padEnd(25);
|
|
4984
|
+
console.log(`${date} ${rate2} ${ratio}`);
|
|
4985
|
+
}
|
|
4986
|
+
return;
|
|
4987
|
+
}
|
|
4988
|
+
const health = await client.getHealth(project);
|
|
4989
|
+
if (opts.format === "json") {
|
|
4990
|
+
console.log(JSON.stringify(health, null, 2));
|
|
4991
|
+
return;
|
|
4992
|
+
}
|
|
4993
|
+
const rate = (health.overallCitedRate * 100).toFixed(1);
|
|
4994
|
+
console.log(`Health: ${rate}% cited (${health.citedPairs}/${health.totalPairs} pairs)`);
|
|
4995
|
+
console.log("");
|
|
4996
|
+
if (health.providerBreakdown && Object.keys(health.providerBreakdown).length > 0) {
|
|
4997
|
+
console.log("Provider Breakdown:");
|
|
4998
|
+
for (const [provider, stats] of Object.entries(health.providerBreakdown)) {
|
|
4999
|
+
const pRate = (stats.citedRate * 100).toFixed(1);
|
|
5000
|
+
console.log(` ${provider.padEnd(15)} ${pRate}% (${stats.cited}/${stats.total})`);
|
|
5001
|
+
}
|
|
5002
|
+
}
|
|
5003
|
+
}
|
|
5004
|
+
|
|
5005
|
+
// src/cli-commands/intelligence.ts
|
|
5006
|
+
var INTELLIGENCE_CLI_COMMANDS = [
|
|
5007
|
+
{
|
|
5008
|
+
path: ["insights"],
|
|
5009
|
+
usage: "canonry insights <project> [--dismissed] [--format json]",
|
|
5010
|
+
options: {
|
|
5011
|
+
dismissed: { type: "boolean" }
|
|
5012
|
+
},
|
|
5013
|
+
run: async (input) => {
|
|
5014
|
+
const usage = "canonry insights <project> [--dismissed] [--format json]";
|
|
5015
|
+
const project = requireProject(input, "insights", usage);
|
|
5016
|
+
const dismissed = input.values.dismissed === true;
|
|
5017
|
+
await listInsights(project, { dismissed, format: input.format });
|
|
5018
|
+
}
|
|
5019
|
+
},
|
|
5020
|
+
{
|
|
5021
|
+
path: ["insights", "dismiss"],
|
|
5022
|
+
usage: "canonry insights dismiss <project> <id> [--format json]",
|
|
5023
|
+
options: {},
|
|
5024
|
+
run: async (input) => {
|
|
5025
|
+
const usage = "canonry insights dismiss <project> <id> [--format json]";
|
|
5026
|
+
const project = requireProject(input, "insights dismiss", usage);
|
|
5027
|
+
const id = requirePositional(input, 1, { command: "insights dismiss", usage, message: "insight ID is required" });
|
|
5028
|
+
await dismissInsight(project, id, { format: input.format });
|
|
5029
|
+
}
|
|
5030
|
+
},
|
|
5031
|
+
{
|
|
5032
|
+
path: ["health"],
|
|
5033
|
+
usage: "canonry health <project> [--history] [--limit <n>] [--format json]",
|
|
5034
|
+
options: {
|
|
5035
|
+
history: { type: "boolean" },
|
|
5036
|
+
limit: { type: "string" }
|
|
5037
|
+
},
|
|
5038
|
+
run: async (input) => {
|
|
5039
|
+
const usage = "canonry health <project> [--history] [--limit <n>] [--format json]";
|
|
5040
|
+
const project = requireProject(input, "health", usage);
|
|
5041
|
+
const history = input.values.history === true;
|
|
5042
|
+
const limitStr = getString(input.values, "limit");
|
|
5043
|
+
const limit = limitStr ? Number.parseInt(limitStr, 10) : void 0;
|
|
5044
|
+
await showHealth(project, { history, limit, format: input.format });
|
|
5045
|
+
}
|
|
5046
|
+
}
|
|
5047
|
+
];
|
|
5048
|
+
|
|
4858
5049
|
// src/commands/bootstrap.ts
|
|
4859
5050
|
import crypto from "crypto";
|
|
4860
5051
|
import path2 from "path";
|
|
@@ -5035,15 +5226,17 @@ async function bootstrapCommand(_opts) {
|
|
|
5035
5226
|
const keyPrefix = rawApiKey.slice(0, 9);
|
|
5036
5227
|
const db = createClient(databasePath);
|
|
5037
5228
|
migrate(db);
|
|
5038
|
-
db.
|
|
5039
|
-
|
|
5040
|
-
|
|
5041
|
-
|
|
5042
|
-
|
|
5043
|
-
|
|
5044
|
-
|
|
5045
|
-
|
|
5046
|
-
|
|
5229
|
+
db.transaction((tx) => {
|
|
5230
|
+
tx.delete(apiKeys).where(eq2(apiKeys.name, "default")).run();
|
|
5231
|
+
tx.insert(apiKeys).values({
|
|
5232
|
+
id: crypto.randomUUID(),
|
|
5233
|
+
name: "default",
|
|
5234
|
+
keyHash,
|
|
5235
|
+
keyPrefix,
|
|
5236
|
+
scopes: '["*"]',
|
|
5237
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
5238
|
+
}).run();
|
|
5239
|
+
});
|
|
5047
5240
|
saveConfig({
|
|
5048
5241
|
apiUrl: env.apiUrl || existingConfig?.apiUrl || `http://localhost:${process.env.CANONRY_PORT || "4100"}`,
|
|
5049
5242
|
database: databasePath,
|
|
@@ -5454,6 +5647,15 @@ async function serveCommand(format = "text") {
|
|
|
5454
5647
|
const db = createClient(config.database);
|
|
5455
5648
|
migrate(db);
|
|
5456
5649
|
const app = await createServer({ config, db });
|
|
5650
|
+
const shutdown = () => {
|
|
5651
|
+
app.close().then(() => {
|
|
5652
|
+
process.exit(0);
|
|
5653
|
+
}).catch(() => {
|
|
5654
|
+
process.exit(1);
|
|
5655
|
+
});
|
|
5656
|
+
};
|
|
5657
|
+
process.on("SIGTERM", shutdown);
|
|
5658
|
+
process.on("SIGINT", shutdown);
|
|
5457
5659
|
try {
|
|
5458
5660
|
await app.listen({ host, port });
|
|
5459
5661
|
const url = `http://${host === "0.0.0.0" ? "localhost" : host}:${port}`;
|
|
@@ -5758,16 +5960,6 @@ import fs6 from "fs";
|
|
|
5758
5960
|
function getClient17() {
|
|
5759
5961
|
return createApiClient();
|
|
5760
5962
|
}
|
|
5761
|
-
async function promptForAppPassword() {
|
|
5762
|
-
const readline2 = await import("readline");
|
|
5763
|
-
const rl = readline2.createInterface({ input: process.stdin, output: process.stderr });
|
|
5764
|
-
return new Promise((resolve) => {
|
|
5765
|
-
rl.question("WordPress Application Password: ", (answer) => {
|
|
5766
|
-
rl.close();
|
|
5767
|
-
resolve(answer.trim());
|
|
5768
|
-
});
|
|
5769
|
-
});
|
|
5770
|
-
}
|
|
5771
5963
|
function printJson(value) {
|
|
5772
5964
|
console.log(JSON.stringify(value, null, 2));
|
|
5773
5965
|
}
|
|
@@ -5903,12 +6095,12 @@ Staging:`);
|
|
|
5903
6095
|
console.log(` Snippet: ${diff.staging.contentSnippet || "(empty)"}`);
|
|
5904
6096
|
}
|
|
5905
6097
|
async function wordpressConnect(project, opts) {
|
|
5906
|
-
const appPassword = opts.appPassword
|
|
6098
|
+
const appPassword = opts.appPassword;
|
|
5907
6099
|
if (!appPassword) {
|
|
5908
6100
|
throw new CliError({
|
|
5909
6101
|
code: "WORDPRESS_APP_PASSWORD_REQUIRED",
|
|
5910
6102
|
message: "WordPress Application Password is required",
|
|
5911
|
-
displayMessage: "Error: WordPress Application Password is required
|
|
6103
|
+
displayMessage: "Error: WordPress Application Password is required. Pass --app-password <password>.",
|
|
5912
6104
|
details: { project }
|
|
5913
6105
|
});
|
|
5914
6106
|
}
|
|
@@ -6200,12 +6392,12 @@ async function wordpressSchemaStatus(project, opts) {
|
|
|
6200
6392
|
}
|
|
6201
6393
|
}
|
|
6202
6394
|
async function wordpressOnboard(project, opts) {
|
|
6203
|
-
const appPassword = opts.appPassword
|
|
6395
|
+
const appPassword = opts.appPassword;
|
|
6204
6396
|
if (!appPassword) {
|
|
6205
6397
|
throw new CliError({
|
|
6206
6398
|
code: "WORDPRESS_APP_PASSWORD_REQUIRED",
|
|
6207
6399
|
message: "WordPress Application Password is required",
|
|
6208
|
-
displayMessage: "Error: WordPress Application Password is required
|
|
6400
|
+
displayMessage: "Error: WordPress Application Password is required. Pass --app-password <password>.",
|
|
6209
6401
|
details: { project }
|
|
6210
6402
|
});
|
|
6211
6403
|
}
|
|
@@ -6815,7 +7007,8 @@ var REGISTERED_CLI_COMMANDS = [
|
|
|
6815
7007
|
...BING_CLI_COMMANDS,
|
|
6816
7008
|
...WORDPRESS_CLI_COMMANDS,
|
|
6817
7009
|
...CDP_CLI_COMMANDS,
|
|
6818
|
-
...GA_CLI_COMMANDS
|
|
7010
|
+
...GA_CLI_COMMANDS,
|
|
7011
|
+
...INTELLIGENCE_CLI_COMMANDS
|
|
6819
7012
|
];
|
|
6820
7013
|
|
|
6821
7014
|
// src/cli.ts
|
|
@@ -6828,6 +7021,7 @@ Usage:
|
|
|
6828
7021
|
canonry init --gemini-key <key> Initialize non-interactively (also reads env vars)
|
|
6829
7022
|
canonry bootstrap [--force] Bootstrap config/database from env vars
|
|
6830
7023
|
canonry backfill answer-visibility Backfill answer-level visibility from stored answers
|
|
7024
|
+
canonry backfill insights <project> Backfill intelligence insights [--from-run <id>] [--to-run <id>]
|
|
6831
7025
|
canonry serve Start the local server (foreground)
|
|
6832
7026
|
canonry start Start the server as a background daemon
|
|
6833
7027
|
canonry stop Stop the background daemon
|
|
@@ -6857,10 +7051,13 @@ Usage:
|
|
|
6857
7051
|
canonry run --all Trigger runs for all projects
|
|
6858
7052
|
canonry run show <id> Show run details and snapshots
|
|
6859
7053
|
canonry runs <project> List runs for a project (--limit <n>)
|
|
6860
|
-
canonry status <project> Show project summary
|
|
6861
|
-
canonry evidence <project> Show per-phrase results
|
|
7054
|
+
canonry status <project> Show project summary [--format json]
|
|
7055
|
+
canonry evidence <project> Show per-phrase results [--format json]
|
|
6862
7056
|
canonry analytics <project> Show analytics (--feature metrics|gaps|sources, --window 7d|30d|90d|all)
|
|
6863
|
-
canonry
|
|
7057
|
+
canonry insights <project> Show intelligence insights [--dismissed] [--format json]
|
|
7058
|
+
canonry insights dismiss <project> <id> Dismiss an insight [--format json]
|
|
7059
|
+
canonry health <project> Show citation health [--history] [--limit <n>] [--format json]
|
|
7060
|
+
canonry history <project> Show audit trail [--format json]
|
|
6864
7061
|
canonry export <project> Export project as YAML
|
|
6865
7062
|
canonry apply <file...> Apply declarative config (multi-doc YAML supported)
|
|
6866
7063
|
canonry schedule set <project> Set schedule (--preset or --cron)
|
|
@@ -7019,7 +7216,7 @@ Run "canonry --help" for usage.`, {
|
|
|
7019
7216
|
});
|
|
7020
7217
|
} catch (err) {
|
|
7021
7218
|
printCliError(err, format);
|
|
7022
|
-
return
|
|
7219
|
+
return err instanceof CliError ? err.exitCode : EXIT_SYSTEM_ERROR;
|
|
7023
7220
|
}
|
|
7024
7221
|
}
|
|
7025
7222
|
async function main(args = process.argv.slice(2)) {
|
package/dist/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ainyc/canonry",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.39.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "The ultimate open-source AEO monitoring tool - track how answer engines cite your domain",
|
|
6
6
|
"license": "FSL-1.1-ALv2",
|
|
@@ -55,17 +55,18 @@
|
|
|
55
55
|
"tsup": "^8.5.1",
|
|
56
56
|
"tsx": "^4.19.0",
|
|
57
57
|
"@ainyc/canonry-api-routes": "0.0.0",
|
|
58
|
-
"@ainyc/canonry-contracts": "0.0.0",
|
|
59
|
-
"@ainyc/canonry-integration-google": "0.0.0",
|
|
60
|
-
"@ainyc/canonry-db": "0.0.0",
|
|
61
58
|
"@ainyc/canonry-config": "0.0.0",
|
|
59
|
+
"@ainyc/canonry-db": "0.0.0",
|
|
60
|
+
"@ainyc/canonry-contracts": "0.0.0",
|
|
62
61
|
"@ainyc/canonry-integration-bing": "0.0.0",
|
|
62
|
+
"@ainyc/canonry-intelligence": "0.0.0",
|
|
63
|
+
"@ainyc/canonry-integration-google": "0.0.0",
|
|
63
64
|
"@ainyc/canonry-integration-wordpress": "0.0.0",
|
|
64
65
|
"@ainyc/canonry-provider-cdp": "0.0.0",
|
|
65
|
-
"@ainyc/canonry-provider-local": "0.0.0",
|
|
66
|
-
"@ainyc/canonry-provider-openai": "0.0.0",
|
|
67
66
|
"@ainyc/canonry-provider-claude": "0.0.0",
|
|
68
67
|
"@ainyc/canonry-provider-gemini": "0.0.0",
|
|
68
|
+
"@ainyc/canonry-provider-local": "0.0.0",
|
|
69
|
+
"@ainyc/canonry-provider-openai": "0.0.0",
|
|
69
70
|
"@ainyc/canonry-provider-perplexity": "0.0.0"
|
|
70
71
|
},
|
|
71
72
|
"scripts": {
|