@ainyc/canonry 1.44.0 → 1.45.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/assets/assets/index-BsF7MVAu.js +281 -0
- package/assets/index.html +1 -1
- package/dist/{chunk-C3LF36DQ.js → chunk-6HJVQBQM.js} +116 -2
- package/dist/{chunk-AATIMNOX.js → chunk-WBV2D7FB.js} +29 -7
- package/dist/cli.js +79 -19
- package/dist/index.js +2 -2
- package/dist/{intelligence-service-36ERONKI.js → intelligence-service-QF6E4HBV.js} +1 -1
- package/package.json +7 -7
- package/assets/assets/index-BAzKj_9S.js +0 -281
package/assets/index.html
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
<link rel="icon" type="image/png" sizes="32x32" href="./favicon-32.png" />
|
|
13
13
|
<link rel="apple-touch-icon" href="./apple-touch-icon.png" />
|
|
14
14
|
<title>Canonry</title>
|
|
15
|
-
<script type="module" crossorigin src="./assets/index-
|
|
15
|
+
<script type="module" crossorigin src="./assets/index-BsF7MVAu.js"></script>
|
|
16
16
|
<link rel="stylesheet" crossorigin href="./assets/index--ev1Bjls.css">
|
|
17
17
|
</head>
|
|
18
18
|
<body>
|
|
@@ -2,6 +2,7 @@ import {
|
|
|
2
2
|
IntelligenceService,
|
|
3
3
|
apiKeys,
|
|
4
4
|
auditLog,
|
|
5
|
+
bingCoverageSnapshots,
|
|
5
6
|
bingUrlInspections,
|
|
6
7
|
competitors,
|
|
7
8
|
createLogger,
|
|
@@ -22,7 +23,7 @@ import {
|
|
|
22
23
|
runs,
|
|
23
24
|
schedules,
|
|
24
25
|
usageCounters
|
|
25
|
-
} from "./chunk-
|
|
26
|
+
} from "./chunk-WBV2D7FB.js";
|
|
26
27
|
|
|
27
28
|
// src/config.ts
|
|
28
29
|
import fs from "fs";
|
|
@@ -267,7 +268,7 @@ import crypto22 from "crypto";
|
|
|
267
268
|
import fs5 from "fs";
|
|
268
269
|
import path6 from "path";
|
|
269
270
|
import { fileURLToPath } from "url";
|
|
270
|
-
import { eq as eq23 } from "drizzle-orm";
|
|
271
|
+
import { eq as eq23, sql as sql6 } from "drizzle-orm";
|
|
271
272
|
import Fastify from "fastify";
|
|
272
273
|
|
|
273
274
|
// ../contracts/src/config-schema.ts
|
|
@@ -651,6 +652,12 @@ var bingKeywordStatsDtoSchema = z6.object({
|
|
|
651
652
|
ctr: z6.number(),
|
|
652
653
|
averagePosition: z6.number()
|
|
653
654
|
});
|
|
655
|
+
var bingCoverageSnapshotDtoSchema = z6.object({
|
|
656
|
+
date: z6.string(),
|
|
657
|
+
indexed: z6.number(),
|
|
658
|
+
notIndexed: z6.number(),
|
|
659
|
+
unknown: z6.number()
|
|
660
|
+
});
|
|
654
661
|
var bingSubmitResultDtoSchema = z6.object({
|
|
655
662
|
url: z6.string(),
|
|
656
663
|
status: z6.enum(["success", "error"]),
|
|
@@ -4741,6 +4748,18 @@ var routeCatalog = [
|
|
|
4741
4748
|
404: { description: "Project not found." }
|
|
4742
4749
|
}
|
|
4743
4750
|
},
|
|
4751
|
+
{
|
|
4752
|
+
method: "get",
|
|
4753
|
+
path: "/api/v1/projects/{name}/bing/coverage/history",
|
|
4754
|
+
summary: "Get Bing coverage history snapshots",
|
|
4755
|
+
tags: ["bing"],
|
|
4756
|
+
parameters: [nameParameter, limitQueryParameter],
|
|
4757
|
+
responses: {
|
|
4758
|
+
200: { description: "Bing coverage history returned." },
|
|
4759
|
+
400: { description: "Bing is not configured for this project." },
|
|
4760
|
+
404: { description: "Project not found." }
|
|
4761
|
+
}
|
|
4762
|
+
},
|
|
4744
4763
|
{
|
|
4745
4764
|
method: "get",
|
|
4746
4765
|
path: "/api/v1/projects/{name}/bing/inspections",
|
|
@@ -7802,6 +7821,22 @@ async function bingRoutes(app, opts) {
|
|
|
7802
7821
|
anchorCount: r.anchorCount ?? null,
|
|
7803
7822
|
discoveryDate: r.discoveryDate ?? null
|
|
7804
7823
|
});
|
|
7824
|
+
if (total > 0) {
|
|
7825
|
+
const snapshotDate = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
7826
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
7827
|
+
app.db.insert(bingCoverageSnapshots).values({
|
|
7828
|
+
id: crypto15.randomUUID(),
|
|
7829
|
+
projectId: project.id,
|
|
7830
|
+
date: snapshotDate,
|
|
7831
|
+
indexed,
|
|
7832
|
+
notIndexed,
|
|
7833
|
+
unknown,
|
|
7834
|
+
createdAt: now
|
|
7835
|
+
}).onConflictDoUpdate({
|
|
7836
|
+
target: [bingCoverageSnapshots.projectId, bingCoverageSnapshots.date],
|
|
7837
|
+
set: { indexed, notIndexed, unknown, createdAt: now }
|
|
7838
|
+
}).run();
|
|
7839
|
+
}
|
|
7805
7840
|
return {
|
|
7806
7841
|
summary: {
|
|
7807
7842
|
total,
|
|
@@ -7816,6 +7851,20 @@ async function bingRoutes(app, opts) {
|
|
|
7816
7851
|
unknown: unknownUrls.map(formatRow)
|
|
7817
7852
|
};
|
|
7818
7853
|
});
|
|
7854
|
+
app.get("/projects/:name/bing/coverage/history", async (request, reply) => {
|
|
7855
|
+
const store = requireConnectionStore(reply);
|
|
7856
|
+
if (!store) return;
|
|
7857
|
+
const project = resolveProject(app.db, request.params.name);
|
|
7858
|
+
const parsed = parseInt(request.query.limit ?? "90", 10);
|
|
7859
|
+
const limit = Number.isNaN(parsed) || parsed <= 0 ? 90 : parsed;
|
|
7860
|
+
const rows = app.db.select().from(bingCoverageSnapshots).where(eq15(bingCoverageSnapshots.projectId, project.id)).orderBy(desc6(bingCoverageSnapshots.date)).limit(limit).all();
|
|
7861
|
+
return rows.map((r) => ({
|
|
7862
|
+
date: r.date,
|
|
7863
|
+
indexed: r.indexed,
|
|
7864
|
+
notIndexed: r.notIndexed,
|
|
7865
|
+
unknown: r.unknown
|
|
7866
|
+
}));
|
|
7867
|
+
});
|
|
7819
7868
|
app.get("/projects/:name/bing/inspections", async (request, reply) => {
|
|
7820
7869
|
const store = requireConnectionStore(reply);
|
|
7821
7870
|
if (!store) return;
|
|
@@ -15036,6 +15085,70 @@ function serializeSessionCookie(opts) {
|
|
|
15036
15085
|
}
|
|
15037
15086
|
return parts.join("; ");
|
|
15038
15087
|
}
|
|
15088
|
+
function migrateDbCredentialsToConfig(db, config) {
|
|
15089
|
+
try {
|
|
15090
|
+
const googleColCheck = db.all(sql6.raw(
|
|
15091
|
+
`SELECT COUNT(*) as c FROM pragma_table_info('google_connections') WHERE name = 'access_token'`
|
|
15092
|
+
));
|
|
15093
|
+
if (googleColCheck[0]?.c) {
|
|
15094
|
+
const rows = db.all(sql6.raw(
|
|
15095
|
+
`SELECT domain, connection_type, property_id, sitemap_url, access_token, refresh_token, token_expires_at, scopes, created_at, updated_at FROM google_connections WHERE refresh_token IS NOT NULL AND refresh_token != ''`
|
|
15096
|
+
));
|
|
15097
|
+
let migrated = 0;
|
|
15098
|
+
for (const row of rows) {
|
|
15099
|
+
const connType = row.connection_type;
|
|
15100
|
+
const existing = getGoogleConnection(config, row.domain, connType);
|
|
15101
|
+
if (existing?.refreshToken) continue;
|
|
15102
|
+
upsertGoogleConnection(config, {
|
|
15103
|
+
domain: row.domain,
|
|
15104
|
+
connectionType: connType,
|
|
15105
|
+
propertyId: row.property_id ?? null,
|
|
15106
|
+
sitemapUrl: row.sitemap_url ?? null,
|
|
15107
|
+
accessToken: row.access_token ?? void 0,
|
|
15108
|
+
refreshToken: row.refresh_token ?? null,
|
|
15109
|
+
tokenExpiresAt: row.token_expires_at ?? null,
|
|
15110
|
+
scopes: parseJsonColumn(row.scopes, []),
|
|
15111
|
+
createdAt: row.created_at,
|
|
15112
|
+
updatedAt: row.updated_at
|
|
15113
|
+
});
|
|
15114
|
+
migrated++;
|
|
15115
|
+
}
|
|
15116
|
+
if (migrated > 0) {
|
|
15117
|
+
saveConfigPatch({ google: config.google });
|
|
15118
|
+
log8.info("credentials.migrated", { type: "google", count: migrated });
|
|
15119
|
+
}
|
|
15120
|
+
}
|
|
15121
|
+
const gaColCheck = db.all(sql6.raw(
|
|
15122
|
+
`SELECT COUNT(*) as c FROM pragma_table_info('ga_connections') WHERE name = 'private_key'`
|
|
15123
|
+
));
|
|
15124
|
+
if (gaColCheck[0]?.c) {
|
|
15125
|
+
const rows = db.all(sql6.raw(
|
|
15126
|
+
`SELECT id, project_id, property_id, client_email, private_key, created_at, updated_at FROM ga_connections WHERE private_key IS NOT NULL AND private_key != ''`
|
|
15127
|
+
));
|
|
15128
|
+
let migrated = 0;
|
|
15129
|
+
for (const row of rows) {
|
|
15130
|
+
const project = db.select({ name: projects.name }).from(projects).where(eq23(projects.id, row.project_id)).get();
|
|
15131
|
+
if (!project) continue;
|
|
15132
|
+
const existing = getGa4Connection(config, project.name);
|
|
15133
|
+
if (existing?.privateKey) continue;
|
|
15134
|
+
upsertGa4Connection(config, {
|
|
15135
|
+
projectName: project.name,
|
|
15136
|
+
propertyId: row.property_id,
|
|
15137
|
+
clientEmail: row.client_email,
|
|
15138
|
+
privateKey: row.private_key,
|
|
15139
|
+
createdAt: row.created_at,
|
|
15140
|
+
updatedAt: row.updated_at
|
|
15141
|
+
});
|
|
15142
|
+
migrated++;
|
|
15143
|
+
}
|
|
15144
|
+
if (migrated > 0) {
|
|
15145
|
+
saveConfigPatch({ ga4: config.ga4 });
|
|
15146
|
+
log8.info("credentials.migrated", { type: "ga4", count: migrated });
|
|
15147
|
+
}
|
|
15148
|
+
}
|
|
15149
|
+
} catch {
|
|
15150
|
+
}
|
|
15151
|
+
}
|
|
15039
15152
|
async function createServer(opts) {
|
|
15040
15153
|
const logger = opts.logger === false ? false : process.stdout.isTTY ? {
|
|
15041
15154
|
transport: {
|
|
@@ -15060,6 +15173,7 @@ async function createServer(opts) {
|
|
|
15060
15173
|
quota: opts.config.geminiQuota
|
|
15061
15174
|
};
|
|
15062
15175
|
}
|
|
15176
|
+
migrateDbCredentialsToConfig(opts.db, opts.config);
|
|
15063
15177
|
log8.info("providers.configured", { providers: Object.keys(providers).filter((k) => {
|
|
15064
15178
|
const p = providers[k];
|
|
15065
15179
|
return p?.apiKey || p?.baseUrl || p?.vertexProject;
|
|
@@ -19,6 +19,7 @@ __export(schema_exports, {
|
|
|
19
19
|
apiKeys: () => apiKeys,
|
|
20
20
|
auditLog: () => auditLog,
|
|
21
21
|
bingConnections: () => bingConnections,
|
|
22
|
+
bingCoverageSnapshots: () => bingCoverageSnapshots,
|
|
22
23
|
bingKeywordStats: () => bingKeywordStats,
|
|
23
24
|
bingUrlInspections: () => bingUrlInspections,
|
|
24
25
|
competitors: () => competitors,
|
|
@@ -174,10 +175,6 @@ var googleConnections = sqliteTable("google_connections", {
|
|
|
174
175
|
connectionType: text("connection_type").notNull(),
|
|
175
176
|
propertyId: text("property_id"),
|
|
176
177
|
sitemapUrl: text("sitemap_url"),
|
|
177
|
-
// WARNING: Authentication material should be stored in config.yaml per CLAUDE.md
|
|
178
|
-
accessToken: text("access_token"),
|
|
179
|
-
refreshToken: text("refresh_token"),
|
|
180
|
-
tokenExpiresAt: text("token_expires_at"),
|
|
181
178
|
scopes: text("scopes").notNull().default("[]"),
|
|
182
179
|
createdAt: text("created_at").notNull(),
|
|
183
180
|
updatedAt: text("updated_at").notNull()
|
|
@@ -238,6 +235,17 @@ var gscCoverageSnapshots = sqliteTable("gsc_coverage_snapshots", {
|
|
|
238
235
|
index("idx_gsc_coverage_snap_project_date").on(table.projectId, table.date),
|
|
239
236
|
index("idx_gsc_coverage_snap_run").on(table.syncRunId)
|
|
240
237
|
]);
|
|
238
|
+
var bingCoverageSnapshots = sqliteTable("bing_coverage_snapshots", {
|
|
239
|
+
id: text("id").primaryKey(),
|
|
240
|
+
projectId: text("project_id").notNull().references(() => projects.id, { onDelete: "cascade" }),
|
|
241
|
+
date: text("date").notNull(),
|
|
242
|
+
indexed: integer("indexed").notNull().default(0),
|
|
243
|
+
notIndexed: integer("not_indexed").notNull().default(0),
|
|
244
|
+
unknown: integer("unknown").notNull().default(0),
|
|
245
|
+
createdAt: text("created_at").notNull()
|
|
246
|
+
}, (table) => [
|
|
247
|
+
uniqueIndex("idx_bing_coverage_snap_project_date").on(table.projectId, table.date)
|
|
248
|
+
]);
|
|
241
249
|
var bingConnections = sqliteTable("bing_connections", {
|
|
242
250
|
id: text("id").primaryKey(),
|
|
243
251
|
domain: text("domain").notNull(),
|
|
@@ -283,8 +291,6 @@ var gaConnections = sqliteTable("ga_connections", {
|
|
|
283
291
|
projectId: text("project_id").notNull().references(() => projects.id, { onDelete: "cascade" }),
|
|
284
292
|
propertyId: text("property_id").notNull(),
|
|
285
293
|
clientEmail: text("client_email").notNull(),
|
|
286
|
-
// WARNING: Authentication material should be stored in config.yaml per CLAUDE.md
|
|
287
|
-
privateKey: text("private_key").notNull(),
|
|
288
294
|
createdAt: text("created_at").notNull(),
|
|
289
295
|
updatedAt: text("updated_at").notNull()
|
|
290
296
|
}, (table) => [
|
|
@@ -795,7 +801,22 @@ var MIGRATIONS = [
|
|
|
795
801
|
)`,
|
|
796
802
|
`CREATE INDEX IF NOT EXISTS idx_ga_social_ref_project_date ON ga_social_referrals(project_id, date)`,
|
|
797
803
|
`CREATE INDEX IF NOT EXISTS idx_ga_social_ref_source ON ga_social_referrals(source)`,
|
|
798
|
-
`CREATE UNIQUE INDEX IF NOT EXISTS idx_ga_social_ref_unique ON ga_social_referrals(project_id, date, source, medium, channel_group)
|
|
804
|
+
`CREATE UNIQUE INDEX IF NOT EXISTS idx_ga_social_ref_unique ON ga_social_referrals(project_id, date, source, medium, channel_group)`,
|
|
805
|
+
// v26: Bing coverage snapshots for historical tracking (mirrors gsc_coverage_snapshots)
|
|
806
|
+
`CREATE TABLE IF NOT EXISTS bing_coverage_snapshots (
|
|
807
|
+
id TEXT PRIMARY KEY,
|
|
808
|
+
project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
|
|
809
|
+
date TEXT NOT NULL,
|
|
810
|
+
indexed INTEGER NOT NULL DEFAULT 0,
|
|
811
|
+
not_indexed INTEGER NOT NULL DEFAULT 0,
|
|
812
|
+
unknown INTEGER NOT NULL DEFAULT 0,
|
|
813
|
+
created_at TEXT NOT NULL
|
|
814
|
+
)`,
|
|
815
|
+
`CREATE UNIQUE INDEX IF NOT EXISTS idx_bing_coverage_snap_project_date ON bing_coverage_snapshots(project_id, date)`
|
|
816
|
+
// v27: Credential columns removed from Drizzle schema — credentials now live in config.yaml.
|
|
817
|
+
// Physical columns (access_token, refresh_token, token_expires_at on google_connections;
|
|
818
|
+
// private_key on ga_connections) intentionally retained in DB for one-time migration in server.ts.
|
|
819
|
+
// SQLite does not support DROP COLUMN; no SQL to execute.
|
|
799
820
|
];
|
|
800
821
|
function isDuplicateColumnError(err) {
|
|
801
822
|
if (!(err instanceof Error)) return false;
|
|
@@ -1239,6 +1260,7 @@ export {
|
|
|
1239
1260
|
gscSearchData,
|
|
1240
1261
|
gscUrlInspections,
|
|
1241
1262
|
gscCoverageSnapshots,
|
|
1263
|
+
bingCoverageSnapshots,
|
|
1242
1264
|
bingUrlInspections,
|
|
1243
1265
|
gaTrafficSnapshots,
|
|
1244
1266
|
gaAiReferrals,
|
package/dist/cli.js
CHANGED
|
@@ -28,7 +28,7 @@ import {
|
|
|
28
28
|
setGoogleAuthConfig,
|
|
29
29
|
showFirstRunNotice,
|
|
30
30
|
trackEvent
|
|
31
|
-
} from "./chunk-
|
|
31
|
+
} from "./chunk-6HJVQBQM.js";
|
|
32
32
|
import {
|
|
33
33
|
apiKeys,
|
|
34
34
|
competitors,
|
|
@@ -38,7 +38,7 @@ import {
|
|
|
38
38
|
projects,
|
|
39
39
|
querySnapshots,
|
|
40
40
|
runs
|
|
41
|
-
} from "./chunk-
|
|
41
|
+
} from "./chunk-WBV2D7FB.js";
|
|
42
42
|
|
|
43
43
|
// src/cli.ts
|
|
44
44
|
import { pathToFileURL } from "url";
|
|
@@ -355,7 +355,7 @@ async function backfillAnswerVisibilityCommand(opts) {
|
|
|
355
355
|
console.log(` Errors: ${providerErrors}`);
|
|
356
356
|
}
|
|
357
357
|
async function backfillInsightsCommand(project, opts) {
|
|
358
|
-
const { IntelligenceService } = await import("./intelligence-service-
|
|
358
|
+
const { IntelligenceService } = await import("./intelligence-service-QF6E4HBV.js");
|
|
359
359
|
const config = loadConfig();
|
|
360
360
|
const db = createClient(config.database);
|
|
361
361
|
migrate(db);
|
|
@@ -619,6 +619,7 @@ var ApiClient = class {
|
|
|
619
619
|
const serializedBody = body != null ? JSON.stringify(body) : void 0;
|
|
620
620
|
const headers = {
|
|
621
621
|
"Authorization": `Bearer ${this.apiKey}`,
|
|
622
|
+
"Accept": "application/json",
|
|
622
623
|
...serializedBody != null ? { "Content-Type": "application/json" } : {}
|
|
623
624
|
};
|
|
624
625
|
let res;
|
|
@@ -860,6 +861,10 @@ var ApiClient = class {
|
|
|
860
861
|
async bingCoverage(project) {
|
|
861
862
|
return this.request("GET", `/projects/${encodeURIComponent(project)}/bing/coverage`);
|
|
862
863
|
}
|
|
864
|
+
async bingCoverageHistory(project, params) {
|
|
865
|
+
const qs = params?.limit != null ? `?limit=${params.limit}` : "";
|
|
866
|
+
return this.request("GET", `/projects/${encodeURIComponent(project)}/bing/coverage/history${qs}`);
|
|
867
|
+
}
|
|
863
868
|
async bingInspections(project, params) {
|
|
864
869
|
const qs = params ? "?" + new URLSearchParams(params).toString() : "";
|
|
865
870
|
return this.request("GET", `/projects/${encodeURIComponent(project)}/bing/inspections${qs}`);
|
|
@@ -1152,6 +1157,26 @@ Bing Index Coverage for "${project}"
|
|
|
1152
1157
|
console.log(` Last inspected: ${result.lastInspectedAt}`);
|
|
1153
1158
|
}
|
|
1154
1159
|
}
|
|
1160
|
+
async function bingCoverageHistory(project, opts) {
|
|
1161
|
+
const client = getClient();
|
|
1162
|
+
const rows = await client.bingCoverageHistory(project, { limit: opts.limit });
|
|
1163
|
+
if (opts.format === "json") {
|
|
1164
|
+
console.log(JSON.stringify(rows, null, 2));
|
|
1165
|
+
return;
|
|
1166
|
+
}
|
|
1167
|
+
if (rows.length === 0) {
|
|
1168
|
+
console.log('No coverage history found. Run "canonry bing coverage" or "canonry bing refresh" first to capture a snapshot.');
|
|
1169
|
+
return;
|
|
1170
|
+
}
|
|
1171
|
+
console.log(`
|
|
1172
|
+
Bing Coverage History for "${project}" (${rows.length} snapshots):
|
|
1173
|
+
`);
|
|
1174
|
+
console.log(` ${"DATE".padEnd(12)}${"INDEXED".padEnd(10)}${"NOT INDEXED".padEnd(14)}UNKNOWN`);
|
|
1175
|
+
console.log(` ${"\u2500".repeat(12)}${"\u2500".repeat(10)}${"\u2500".repeat(14)}${"\u2500".repeat(10)}`);
|
|
1176
|
+
for (const row of rows) {
|
|
1177
|
+
console.log(` ${row.date.padEnd(12)}${String(row.indexed).padEnd(10)}${String(row.notIndexed).padEnd(14)}${String(row.unknown)}`);
|
|
1178
|
+
}
|
|
1179
|
+
}
|
|
1155
1180
|
async function bingInspect(project, url, format) {
|
|
1156
1181
|
const client = getClient();
|
|
1157
1182
|
const result = await client.bingInspectUrl(project, url);
|
|
@@ -1352,6 +1377,22 @@ var BING_CLI_COMMANDS = [
|
|
|
1352
1377
|
await bingSetSite(project, siteUrl, input.format);
|
|
1353
1378
|
}
|
|
1354
1379
|
},
|
|
1380
|
+
{
|
|
1381
|
+
path: ["bing", "coverage-history"],
|
|
1382
|
+
usage: "canonry bing coverage-history <project> [--limit <n>] [--format json]",
|
|
1383
|
+
options: { limit: stringOption() },
|
|
1384
|
+
run: async (input) => {
|
|
1385
|
+
const project = requireProject(input, "bing.coverage-history", "canonry bing coverage-history <project> [--limit <n>] [--format json]");
|
|
1386
|
+
await bingCoverageHistory(project, {
|
|
1387
|
+
limit: parseIntegerOption(input, "limit", {
|
|
1388
|
+
command: "bing.coverage-history",
|
|
1389
|
+
message: "--limit must be a positive integer",
|
|
1390
|
+
usage: "canonry bing coverage-history <project> [--limit <n>] [--format json]"
|
|
1391
|
+
}),
|
|
1392
|
+
format: input.format
|
|
1393
|
+
});
|
|
1394
|
+
}
|
|
1395
|
+
},
|
|
1355
1396
|
{
|
|
1356
1397
|
path: ["bing", "coverage"],
|
|
1357
1398
|
usage: "canonry bing coverage <project> [--format json]",
|
|
@@ -1431,12 +1472,12 @@ var BING_CLI_COMMANDS = [
|
|
|
1431
1472
|
},
|
|
1432
1473
|
{
|
|
1433
1474
|
path: ["bing"],
|
|
1434
|
-
usage: "canonry bing <connect|disconnect|status|sites|set-site|coverage|inspect|inspections|request-indexing|performance|refresh> <project> [args]",
|
|
1475
|
+
usage: "canonry bing <connect|disconnect|status|sites|set-site|coverage|coverage-history|inspect|inspections|request-indexing|performance|refresh> <project> [args]",
|
|
1435
1476
|
run: async (input) => {
|
|
1436
1477
|
unknownSubcommand(input.positionals[0], {
|
|
1437
1478
|
command: "bing",
|
|
1438
|
-
usage: "canonry bing <connect|disconnect|status|sites|set-site|coverage|inspect|inspections|request-indexing|performance|refresh> <project> [args]",
|
|
1439
|
-
available: ["connect", "disconnect", "status", "sites", "set-site", "coverage", "inspect", "inspections", "request-indexing", "performance", "refresh"]
|
|
1479
|
+
usage: "canonry bing <connect|disconnect|status|sites|set-site|coverage|coverage-history|inspect|inspections|request-indexing|performance|refresh> <project> [args]",
|
|
1480
|
+
available: ["connect", "disconnect", "status", "sites", "set-site", "coverage", "coverage-history", "inspect", "inspections", "request-indexing", "performance", "refresh"]
|
|
1440
1481
|
});
|
|
1441
1482
|
}
|
|
1442
1483
|
}
|
|
@@ -4289,7 +4330,7 @@ async function triggerRun(project, opts) {
|
|
|
4289
4330
|
console.log(` ${loc} ${id} ${r.status}`);
|
|
4290
4331
|
}
|
|
4291
4332
|
if (opts?.wait) {
|
|
4292
|
-
const pending = locationRuns.filter((r) => r.id && r.status !== "conflict");
|
|
4333
|
+
const pending = locationRuns.filter((r) => r.id && r.status !== "conflict" && !TERMINAL_STATUSES.has(r.status));
|
|
4293
4334
|
if (pending.length > 0) {
|
|
4294
4335
|
process.stderr.write(`Waiting for ${pending.length} run(s)`);
|
|
4295
4336
|
await Promise.all(
|
|
@@ -4309,7 +4350,7 @@ async function triggerRun(project, opts) {
|
|
|
4309
4350
|
return;
|
|
4310
4351
|
}
|
|
4311
4352
|
const run = response;
|
|
4312
|
-
if (opts?.wait) {
|
|
4353
|
+
if (opts?.wait && run.id && !TERMINAL_STATUSES.has(run.status)) {
|
|
4313
4354
|
process.stderr.write(`Run ${run.id} started`);
|
|
4314
4355
|
const result = await pollRun(client, run.id);
|
|
4315
4356
|
if (opts?.format === "json") {
|
|
@@ -4320,6 +4361,15 @@ async function triggerRun(project, opts) {
|
|
|
4320
4361
|
}
|
|
4321
4362
|
return;
|
|
4322
4363
|
}
|
|
4364
|
+
if (opts?.wait && (TERMINAL_STATUSES.has(run.status) || !run.id)) {
|
|
4365
|
+
const result = run.id ? await client.getRun(run.id) : run;
|
|
4366
|
+
if (opts?.format === "json") {
|
|
4367
|
+
console.log(JSON.stringify(result, null, 2));
|
|
4368
|
+
} else {
|
|
4369
|
+
printRunDetail(result);
|
|
4370
|
+
}
|
|
4371
|
+
return;
|
|
4372
|
+
}
|
|
4323
4373
|
if (opts?.format === "json") {
|
|
4324
4374
|
console.log(JSON.stringify(run, null, 2));
|
|
4325
4375
|
return;
|
|
@@ -5492,7 +5542,7 @@ var SNAPSHOT_CLI_COMMANDS = [
|
|
|
5492
5542
|
// src/commands/insights.ts
|
|
5493
5543
|
async function listInsights(project, opts) {
|
|
5494
5544
|
const client = createApiClient();
|
|
5495
|
-
const insights = await client.getInsights(project, { dismissed: opts.dismissed });
|
|
5545
|
+
const insights = await client.getInsights(project, { dismissed: opts.dismissed, runId: opts.runId });
|
|
5496
5546
|
if (opts.format === "json") {
|
|
5497
5547
|
console.log(JSON.stringify(insights, null, 2));
|
|
5498
5548
|
return;
|
|
@@ -5569,15 +5619,17 @@ async function showHealth(project, opts) {
|
|
|
5569
5619
|
var INTELLIGENCE_CLI_COMMANDS = [
|
|
5570
5620
|
{
|
|
5571
5621
|
path: ["insights"],
|
|
5572
|
-
usage: "canonry insights <project> [--dismissed] [--format json]",
|
|
5622
|
+
usage: "canonry insights <project> [--dismissed] [--run-id <id>] [--format json]",
|
|
5573
5623
|
options: {
|
|
5574
|
-
dismissed: { type: "boolean" }
|
|
5624
|
+
dismissed: { type: "boolean" },
|
|
5625
|
+
"run-id": { type: "string" }
|
|
5575
5626
|
},
|
|
5576
5627
|
run: async (input) => {
|
|
5577
|
-
const usage = "canonry insights <project> [--dismissed] [--format json]";
|
|
5628
|
+
const usage = "canonry insights <project> [--dismissed] [--run-id <id>] [--format json]";
|
|
5578
5629
|
const project = requireProject(input, "insights", usage);
|
|
5579
5630
|
const dismissed = input.values.dismissed === true;
|
|
5580
|
-
|
|
5631
|
+
const runId = getString(input.values, "run-id");
|
|
5632
|
+
await listInsights(project, { dismissed, runId, format: input.format });
|
|
5581
5633
|
}
|
|
5582
5634
|
},
|
|
5583
5635
|
{
|
|
@@ -5602,8 +5654,11 @@ var INTELLIGENCE_CLI_COMMANDS = [
|
|
|
5602
5654
|
const usage = "canonry health <project> [--history] [--limit <n>] [--format json]";
|
|
5603
5655
|
const project = requireProject(input, "health", usage);
|
|
5604
5656
|
const history = input.values.history === true;
|
|
5605
|
-
const
|
|
5606
|
-
|
|
5657
|
+
const limit = parseIntegerOption(input, "limit", {
|
|
5658
|
+
command: "health",
|
|
5659
|
+
usage,
|
|
5660
|
+
message: "--limit must be an integer"
|
|
5661
|
+
});
|
|
5607
5662
|
await showHealth(project, { history, limit, format: input.format });
|
|
5608
5663
|
}
|
|
5609
5664
|
}
|
|
@@ -6211,17 +6266,22 @@ async function serveCommand(format = "text") {
|
|
|
6211
6266
|
migrate(db);
|
|
6212
6267
|
const app = await createServer({ config, db });
|
|
6213
6268
|
let shuttingDown = false;
|
|
6214
|
-
const shutdown = () => {
|
|
6269
|
+
const shutdown = (signal) => {
|
|
6215
6270
|
if (shuttingDown) return;
|
|
6216
6271
|
shuttingDown = true;
|
|
6272
|
+
if (format === "text") {
|
|
6273
|
+
console.log(`
|
|
6274
|
+
Received ${signal}, stopping server...`);
|
|
6275
|
+
}
|
|
6217
6276
|
app.close().then(() => {
|
|
6218
6277
|
process.exit(0);
|
|
6219
|
-
}).catch(() => {
|
|
6278
|
+
}).catch((err) => {
|
|
6279
|
+
console.error("Error during shutdown:", err);
|
|
6220
6280
|
process.exit(1);
|
|
6221
6281
|
});
|
|
6222
6282
|
};
|
|
6223
|
-
process.on("SIGTERM", shutdown);
|
|
6224
|
-
process.on("SIGINT", shutdown);
|
|
6283
|
+
process.on("SIGTERM", () => shutdown("SIGTERM"));
|
|
6284
|
+
process.on("SIGINT", () => shutdown("SIGINT"));
|
|
6225
6285
|
try {
|
|
6226
6286
|
await app.listen({ host, port });
|
|
6227
6287
|
const url = `http://${host === "0.0.0.0" ? "localhost" : host}:${port}`;
|
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.45.1",
|
|
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",
|
|
@@ -54,20 +54,20 @@
|
|
|
54
54
|
"@types/node-cron": "^3.0.11",
|
|
55
55
|
"tsup": "^8.5.1",
|
|
56
56
|
"tsx": "^4.19.0",
|
|
57
|
-
"@ainyc/canonry-
|
|
57
|
+
"@ainyc/canonry-api-routes": "0.0.0",
|
|
58
58
|
"@ainyc/canonry-config": "0.0.0",
|
|
59
59
|
"@ainyc/canonry-db": "0.0.0",
|
|
60
|
+
"@ainyc/canonry-contracts": "0.0.0",
|
|
60
61
|
"@ainyc/canonry-intelligence": "0.0.0",
|
|
61
|
-
"@ainyc/canonry-api-routes": "0.0.0",
|
|
62
|
-
"@ainyc/canonry-integration-google": "0.0.0",
|
|
63
62
|
"@ainyc/canonry-integration-bing": "0.0.0",
|
|
64
63
|
"@ainyc/canonry-integration-wordpress": "0.0.0",
|
|
65
64
|
"@ainyc/canonry-provider-cdp": "0.0.0",
|
|
65
|
+
"@ainyc/canonry-provider-local": "0.0.0",
|
|
66
|
+
"@ainyc/canonry-integration-google": "0.0.0",
|
|
66
67
|
"@ainyc/canonry-provider-claude": "0.0.0",
|
|
67
|
-
"@ainyc/canonry-provider-gemini": "0.0.0",
|
|
68
68
|
"@ainyc/canonry-provider-openai": "0.0.0",
|
|
69
|
-
"@ainyc/canonry-provider-
|
|
70
|
-
"@ainyc/canonry-provider-
|
|
69
|
+
"@ainyc/canonry-provider-perplexity": "0.0.0",
|
|
70
|
+
"@ainyc/canonry-provider-gemini": "0.0.0"
|
|
71
71
|
},
|
|
72
72
|
"scripts": {
|
|
73
73
|
"build": "tsup && tsx build-web.ts",
|