@ainyc/canonry 4.33.1 → 4.35.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/assets/assets/index-Cfv0_lwq.css +1 -0
- package/assets/assets/index-u7ZXZ5mA.js +302 -0
- package/assets/index.html +2 -2
- package/dist/{chunk-5EBN7736.js → chunk-B3FBOECD.js} +1 -1
- package/dist/{chunk-XW3F5EEW.js → chunk-EM5GVF3C.js} +73 -20
- package/dist/{chunk-BJXHETQW.js → chunk-MLS5KJWK.js} +243 -39
- package/dist/{chunk-DZENHID5.js → chunk-NLV4MZZF.js} +1105 -294
- package/dist/cli.js +100 -563
- package/dist/index.d.ts +3 -0
- package/dist/index.js +4 -4
- package/dist/{intelligence-service-XKOUBRCE.js → intelligence-service-WAJOEOJV.js} +2 -2
- package/dist/mcp.js +2 -2
- package/package.json +8 -8
- package/assets/assets/index-47V0U52s.js +0 -302
- package/assets/assets/index-CNKAwZMB.css +0 -1
package/dist/cli.js
CHANGED
|
@@ -1,26 +1,27 @@
|
|
|
1
1
|
#!/usr/bin/env node --import tsx
|
|
2
2
|
import {
|
|
3
|
+
backfillAiReferralPaths,
|
|
4
|
+
backfillAiReferralPathsCommand,
|
|
5
|
+
backfillAnswerMentionsCommand,
|
|
6
|
+
backfillAnswerVisibilityCommand,
|
|
7
|
+
backfillInsightsCommand,
|
|
8
|
+
backfillNormalizedPaths,
|
|
9
|
+
backfillNormalizedPathsCommand,
|
|
10
|
+
checkLatestVersionForCli,
|
|
3
11
|
coerceAgentProvider,
|
|
4
|
-
computeCompetitorOverlap,
|
|
5
12
|
createServer,
|
|
6
13
|
detectAndTrackUpgrade,
|
|
7
|
-
determineCitationState,
|
|
8
|
-
extractRecommendedCompetitors,
|
|
9
14
|
formatAuditFactorScore,
|
|
10
15
|
getOrCreateAnonymousId,
|
|
11
16
|
isFirstRun,
|
|
12
17
|
isTelemetryEnabled,
|
|
13
18
|
listAgentProviders,
|
|
14
19
|
renderReportHtml,
|
|
15
|
-
reparseStoredResult,
|
|
16
|
-
reparseStoredResult2,
|
|
17
|
-
reparseStoredResult3,
|
|
18
|
-
reparseStoredResult4,
|
|
19
20
|
setGoogleAuthConfig,
|
|
20
21
|
setTelemetrySource,
|
|
21
22
|
showFirstRunNotice,
|
|
22
23
|
trackEvent
|
|
23
|
-
} from "./chunk-
|
|
24
|
+
} from "./chunk-NLV4MZZF.js";
|
|
24
25
|
import {
|
|
25
26
|
CliError,
|
|
26
27
|
EXIT_SYSTEM_ERROR,
|
|
@@ -36,42 +37,33 @@ import {
|
|
|
36
37
|
saveConfig,
|
|
37
38
|
saveConfigPatch,
|
|
38
39
|
usageError
|
|
39
|
-
} from "./chunk-
|
|
40
|
+
} from "./chunk-B3FBOECD.js";
|
|
40
41
|
import {
|
|
41
42
|
apiKeys,
|
|
42
|
-
competitors,
|
|
43
43
|
createClient,
|
|
44
|
-
gaAiReferrals,
|
|
45
|
-
gaTrafficSnapshots,
|
|
46
44
|
migrate,
|
|
47
|
-
parseJsonColumn,
|
|
48
45
|
projects,
|
|
49
|
-
queries
|
|
50
|
-
|
|
51
|
-
runs
|
|
52
|
-
} from "./chunk-BJXHETQW.js";
|
|
46
|
+
queries
|
|
47
|
+
} from "./chunk-MLS5KJWK.js";
|
|
53
48
|
import {
|
|
54
49
|
CcReleaseSyncStatuses,
|
|
55
50
|
CheckScopes,
|
|
56
51
|
CheckStatuses,
|
|
57
52
|
CitationStates,
|
|
58
53
|
CodingAgents,
|
|
59
|
-
ProviderNames,
|
|
60
|
-
RunKinds,
|
|
61
54
|
RunStatuses,
|
|
62
55
|
SkillsClients,
|
|
63
56
|
TrafficEventKinds,
|
|
64
|
-
determineAnswerMentioned,
|
|
65
57
|
discoveryBucketSchema,
|
|
66
58
|
discoveryCompetitorTypeSchema,
|
|
67
59
|
effectiveDomains,
|
|
68
60
|
formatRunErrorOneLine,
|
|
69
|
-
|
|
61
|
+
normalizeProjectAliases,
|
|
70
62
|
notificationEventSchema,
|
|
71
63
|
providerQuotaPolicySchema,
|
|
72
64
|
resolveProviderInput,
|
|
73
65
|
skillsClientSchema
|
|
74
|
-
} from "./chunk-
|
|
66
|
+
} from "./chunk-EM5GVF3C.js";
|
|
75
67
|
|
|
76
68
|
// src/cli.ts
|
|
77
69
|
import { pathToFileURL } from "url";
|
|
@@ -211,501 +203,6 @@ Usage: ${spec.usage}`, {
|
|
|
211
203
|
return true;
|
|
212
204
|
}
|
|
213
205
|
|
|
214
|
-
// src/commands/backfill.ts
|
|
215
|
-
import { and, eq, inArray } from "drizzle-orm";
|
|
216
|
-
var SNAPSHOT_BATCH_SIZE = 500;
|
|
217
|
-
async function backfillAnswerVisibilityCommand(opts) {
|
|
218
|
-
const config = loadConfig();
|
|
219
|
-
const db = createClient(config.database);
|
|
220
|
-
migrate(db);
|
|
221
|
-
const projectFilter = opts?.project?.trim();
|
|
222
|
-
const scopedProjects = projectFilter ? db.select().from(projects).where(eq(projects.name, projectFilter)).all() : db.select().from(projects).all();
|
|
223
|
-
let examined = 0;
|
|
224
|
-
let updated = 0;
|
|
225
|
-
let mentioned = 0;
|
|
226
|
-
let reparsed = 0;
|
|
227
|
-
let providerErrors = 0;
|
|
228
|
-
if (scopedProjects.length > 0) {
|
|
229
|
-
const runRows = projectFilter ? db.select({ id: runs.id, projectId: runs.projectId }).from(runs).where(and(
|
|
230
|
-
eq(runs.kind, RunKinds["answer-visibility"]),
|
|
231
|
-
inArray(runs.projectId, scopedProjects.map((project) => project.id))
|
|
232
|
-
)).all() : db.select({ id: runs.id, projectId: runs.projectId }).from(runs).where(eq(runs.kind, RunKinds["answer-visibility"])).all();
|
|
233
|
-
const runIdsByProject = /* @__PURE__ */ new Map();
|
|
234
|
-
for (const run of runRows) {
|
|
235
|
-
const existing = runIdsByProject.get(run.projectId);
|
|
236
|
-
if (existing) existing.push(run.id);
|
|
237
|
-
else runIdsByProject.set(run.projectId, [run.id]);
|
|
238
|
-
}
|
|
239
|
-
for (const project of scopedProjects) {
|
|
240
|
-
const competitorDomains = db.select({ domain: competitors.domain }).from(competitors).where(eq(competitors.projectId, project.id)).all().map((row) => row.domain);
|
|
241
|
-
const runIds = runIdsByProject.get(project.id) ?? [];
|
|
242
|
-
if (runIds.length === 0) continue;
|
|
243
|
-
const projectDomains = effectiveDomains({
|
|
244
|
-
canonicalDomain: project.canonicalDomain,
|
|
245
|
-
ownedDomains: parseJsonColumn(project.ownedDomains, [])
|
|
246
|
-
});
|
|
247
|
-
for (let offset = 0; offset < runIds.length; offset += SNAPSHOT_BATCH_SIZE) {
|
|
248
|
-
const batchRunIds = runIds.slice(offset, offset + SNAPSHOT_BATCH_SIZE);
|
|
249
|
-
const snapshotRows = db.select({
|
|
250
|
-
id: querySnapshots.id,
|
|
251
|
-
provider: querySnapshots.provider,
|
|
252
|
-
citationState: querySnapshots.citationState,
|
|
253
|
-
answerMentioned: querySnapshots.answerMentioned,
|
|
254
|
-
answerText: querySnapshots.answerText,
|
|
255
|
-
citedDomains: querySnapshots.citedDomains,
|
|
256
|
-
competitorOverlap: querySnapshots.competitorOverlap,
|
|
257
|
-
recommendedCompetitors: querySnapshots.recommendedCompetitors,
|
|
258
|
-
rawResponse: querySnapshots.rawResponse
|
|
259
|
-
}).from(querySnapshots).where(inArray(querySnapshots.runId, batchRunIds)).all();
|
|
260
|
-
const pendingUpdates = [];
|
|
261
|
-
for (const snapshot of snapshotRows) {
|
|
262
|
-
examined++;
|
|
263
|
-
const reparsedResult = reparseProviderSnapshot(snapshot.provider, snapshot.rawResponse);
|
|
264
|
-
if (reparsedResult) reparsed++;
|
|
265
|
-
if (reparsedResult?.providerError) providerErrors++;
|
|
266
|
-
const answerText = reparsedResult?.answerText ?? snapshot.answerText ?? "";
|
|
267
|
-
const nextValue = determineAnswerMentioned(answerText, project.displayName, projectDomains);
|
|
268
|
-
if (nextValue) mentioned++;
|
|
269
|
-
const nextPatch = {};
|
|
270
|
-
if (snapshot.answerMentioned !== nextValue) {
|
|
271
|
-
nextPatch.answerMentioned = nextValue;
|
|
272
|
-
}
|
|
273
|
-
if ((snapshot.answerText ?? "") !== answerText) {
|
|
274
|
-
nextPatch.answerText = answerText;
|
|
275
|
-
}
|
|
276
|
-
if (reparsedResult) {
|
|
277
|
-
const normalized = {
|
|
278
|
-
provider: snapshot.provider,
|
|
279
|
-
answerText,
|
|
280
|
-
citedDomains: reparsedResult.citedDomains,
|
|
281
|
-
groundingSources: reparsedResult.groundingSources,
|
|
282
|
-
searchQueries: reparsedResult.searchQueries
|
|
283
|
-
};
|
|
284
|
-
const nextCitationState = determineCitationState(normalized, projectDomains);
|
|
285
|
-
const nextCitedDomains = JSON.stringify(reparsedResult.citedDomains);
|
|
286
|
-
const nextCompetitorOverlap = JSON.stringify(
|
|
287
|
-
computeCompetitorOverlap(normalized, competitorDomains)
|
|
288
|
-
);
|
|
289
|
-
const nextRecommendedCompetitors = JSON.stringify(
|
|
290
|
-
extractRecommendedCompetitors(
|
|
291
|
-
normalized.answerText,
|
|
292
|
-
projectDomains,
|
|
293
|
-
normalized.citedDomains,
|
|
294
|
-
competitorDomains
|
|
295
|
-
)
|
|
296
|
-
);
|
|
297
|
-
const nextRawResponse = stringifyStoredSnapshotEnvelope(
|
|
298
|
-
snapshot.rawResponse,
|
|
299
|
-
reparsedResult
|
|
300
|
-
);
|
|
301
|
-
if (snapshot.citationState !== nextCitationState) {
|
|
302
|
-
nextPatch.citationState = nextCitationState;
|
|
303
|
-
}
|
|
304
|
-
if (snapshot.citedDomains !== nextCitedDomains) {
|
|
305
|
-
nextPatch.citedDomains = nextCitedDomains;
|
|
306
|
-
}
|
|
307
|
-
if (snapshot.competitorOverlap !== nextCompetitorOverlap) {
|
|
308
|
-
nextPatch.competitorOverlap = nextCompetitorOverlap;
|
|
309
|
-
}
|
|
310
|
-
if (snapshot.recommendedCompetitors !== nextRecommendedCompetitors) {
|
|
311
|
-
nextPatch.recommendedCompetitors = nextRecommendedCompetitors;
|
|
312
|
-
}
|
|
313
|
-
if (snapshot.rawResponse !== nextRawResponse) {
|
|
314
|
-
nextPatch.rawResponse = nextRawResponse;
|
|
315
|
-
}
|
|
316
|
-
}
|
|
317
|
-
if (Object.keys(nextPatch).length > 0) {
|
|
318
|
-
pendingUpdates.push({ id: snapshot.id, patch: nextPatch });
|
|
319
|
-
}
|
|
320
|
-
}
|
|
321
|
-
if (pendingUpdates.length > 0) {
|
|
322
|
-
db.transaction((tx) => {
|
|
323
|
-
for (const update of pendingUpdates) {
|
|
324
|
-
tx.update(querySnapshots).set(update.patch).where(eq(querySnapshots.id, update.id)).run();
|
|
325
|
-
}
|
|
326
|
-
});
|
|
327
|
-
updated += pendingUpdates.length;
|
|
328
|
-
}
|
|
329
|
-
}
|
|
330
|
-
}
|
|
331
|
-
}
|
|
332
|
-
const result = {
|
|
333
|
-
project: projectFilter ?? null,
|
|
334
|
-
projects: scopedProjects.length,
|
|
335
|
-
examined,
|
|
336
|
-
updated,
|
|
337
|
-
mentioned,
|
|
338
|
-
reparsed,
|
|
339
|
-
providerErrors
|
|
340
|
-
};
|
|
341
|
-
if (opts?.format === "json") {
|
|
342
|
-
console.log(JSON.stringify(result, null, 2));
|
|
343
|
-
return;
|
|
344
|
-
}
|
|
345
|
-
console.log("Answer visibility backfill complete.\n");
|
|
346
|
-
if (projectFilter) {
|
|
347
|
-
console.log(` Project: ${projectFilter}`);
|
|
348
|
-
}
|
|
349
|
-
console.log(` Projects: ${scopedProjects.length}`);
|
|
350
|
-
console.log(` Examined: ${examined}`);
|
|
351
|
-
console.log(` Updated: ${updated}`);
|
|
352
|
-
console.log(` Mentioned: ${mentioned}`);
|
|
353
|
-
console.log(` Reparsed: ${reparsed}`);
|
|
354
|
-
console.log(` Errors: ${providerErrors}`);
|
|
355
|
-
}
|
|
356
|
-
function backfillNormalizedPaths(db, opts) {
|
|
357
|
-
const baseConditions = [];
|
|
358
|
-
if (opts?.projectId) {
|
|
359
|
-
baseConditions.push(eq(gaTrafficSnapshots.projectId, opts.projectId));
|
|
360
|
-
}
|
|
361
|
-
const rows = db.select({
|
|
362
|
-
id: gaTrafficSnapshots.id,
|
|
363
|
-
landingPage: gaTrafficSnapshots.landingPage,
|
|
364
|
-
landingPageNormalized: gaTrafficSnapshots.landingPageNormalized
|
|
365
|
-
}).from(gaTrafficSnapshots).where(baseConditions.length > 0 ? and(...baseConditions) : void 0).all();
|
|
366
|
-
let updated = 0;
|
|
367
|
-
let unchanged = 0;
|
|
368
|
-
if (rows.length > 0) {
|
|
369
|
-
db.transaction((tx) => {
|
|
370
|
-
for (const row of rows) {
|
|
371
|
-
const next = normalizeUrlPath(row.landingPage);
|
|
372
|
-
if (next === null) {
|
|
373
|
-
unchanged++;
|
|
374
|
-
continue;
|
|
375
|
-
}
|
|
376
|
-
if (row.landingPageNormalized === next) {
|
|
377
|
-
unchanged++;
|
|
378
|
-
continue;
|
|
379
|
-
}
|
|
380
|
-
tx.update(gaTrafficSnapshots).set({ landingPageNormalized: next }).where(eq(gaTrafficSnapshots.id, row.id)).run();
|
|
381
|
-
updated++;
|
|
382
|
-
}
|
|
383
|
-
});
|
|
384
|
-
}
|
|
385
|
-
return { examined: rows.length, updated, unchanged };
|
|
386
|
-
}
|
|
387
|
-
async function backfillNormalizedPathsCommand(opts) {
|
|
388
|
-
const config = loadConfig();
|
|
389
|
-
const db = createClient(config.database);
|
|
390
|
-
migrate(db);
|
|
391
|
-
const projectFilter = opts?.project?.trim();
|
|
392
|
-
let projectId;
|
|
393
|
-
if (projectFilter) {
|
|
394
|
-
const project = db.select({ id: projects.id }).from(projects).where(eq(projects.name, projectFilter)).get();
|
|
395
|
-
if (!project) {
|
|
396
|
-
const result2 = {
|
|
397
|
-
project: projectFilter,
|
|
398
|
-
examined: 0,
|
|
399
|
-
updated: 0,
|
|
400
|
-
unchanged: 0
|
|
401
|
-
};
|
|
402
|
-
if (opts?.format === "json") {
|
|
403
|
-
console.log(JSON.stringify(result2, null, 2));
|
|
404
|
-
return;
|
|
405
|
-
}
|
|
406
|
-
console.log(`Backfill normalized-paths: project "${projectFilter}" not found.`);
|
|
407
|
-
return;
|
|
408
|
-
}
|
|
409
|
-
projectId = project.id;
|
|
410
|
-
}
|
|
411
|
-
const { examined, updated, unchanged } = backfillNormalizedPaths(db, { projectId });
|
|
412
|
-
const result = {
|
|
413
|
-
project: projectFilter ?? null,
|
|
414
|
-
examined,
|
|
415
|
-
updated,
|
|
416
|
-
unchanged
|
|
417
|
-
};
|
|
418
|
-
if (opts?.format === "json") {
|
|
419
|
-
console.log(JSON.stringify(result, null, 2));
|
|
420
|
-
return;
|
|
421
|
-
}
|
|
422
|
-
console.log("Normalized-path backfill complete.\n");
|
|
423
|
-
if (projectFilter) console.log(` Project: ${projectFilter}`);
|
|
424
|
-
console.log(` Examined: ${examined}`);
|
|
425
|
-
console.log(` Updated: ${updated}`);
|
|
426
|
-
console.log(` Unchanged: ${unchanged}`);
|
|
427
|
-
}
|
|
428
|
-
function backfillAiReferralPaths(db, opts) {
|
|
429
|
-
const baseConditions = [];
|
|
430
|
-
if (opts?.projectId) {
|
|
431
|
-
baseConditions.push(eq(gaAiReferrals.projectId, opts.projectId));
|
|
432
|
-
}
|
|
433
|
-
const rows = db.select({
|
|
434
|
-
id: gaAiReferrals.id,
|
|
435
|
-
landingPage: gaAiReferrals.landingPage,
|
|
436
|
-
landingPageNormalized: gaAiReferrals.landingPageNormalized
|
|
437
|
-
}).from(gaAiReferrals).where(baseConditions.length > 0 ? and(...baseConditions) : void 0).all();
|
|
438
|
-
let updated = 0;
|
|
439
|
-
let unchanged = 0;
|
|
440
|
-
if (rows.length > 0) {
|
|
441
|
-
db.transaction((tx) => {
|
|
442
|
-
for (const row of rows) {
|
|
443
|
-
const next = normalizeUrlPath(row.landingPage);
|
|
444
|
-
if (next === null) {
|
|
445
|
-
unchanged++;
|
|
446
|
-
continue;
|
|
447
|
-
}
|
|
448
|
-
if (row.landingPageNormalized === next) {
|
|
449
|
-
unchanged++;
|
|
450
|
-
continue;
|
|
451
|
-
}
|
|
452
|
-
tx.update(gaAiReferrals).set({ landingPageNormalized: next }).where(eq(gaAiReferrals.id, row.id)).run();
|
|
453
|
-
updated++;
|
|
454
|
-
}
|
|
455
|
-
});
|
|
456
|
-
}
|
|
457
|
-
return { examined: rows.length, updated, unchanged };
|
|
458
|
-
}
|
|
459
|
-
async function backfillAiReferralPathsCommand(opts) {
|
|
460
|
-
const config = loadConfig();
|
|
461
|
-
const db = createClient(config.database);
|
|
462
|
-
migrate(db);
|
|
463
|
-
const projectFilter = opts?.project?.trim();
|
|
464
|
-
let projectId;
|
|
465
|
-
if (projectFilter) {
|
|
466
|
-
const project = db.select({ id: projects.id }).from(projects).where(eq(projects.name, projectFilter)).get();
|
|
467
|
-
if (!project) {
|
|
468
|
-
const result2 = {
|
|
469
|
-
project: projectFilter,
|
|
470
|
-
examined: 0,
|
|
471
|
-
updated: 0,
|
|
472
|
-
unchanged: 0
|
|
473
|
-
};
|
|
474
|
-
if (opts?.format === "json") {
|
|
475
|
-
console.log(JSON.stringify(result2, null, 2));
|
|
476
|
-
return;
|
|
477
|
-
}
|
|
478
|
-
console.log(`Backfill ai-referral-paths: project "${projectFilter}" not found.`);
|
|
479
|
-
return;
|
|
480
|
-
}
|
|
481
|
-
projectId = project.id;
|
|
482
|
-
}
|
|
483
|
-
const { examined, updated, unchanged } = backfillAiReferralPaths(db, { projectId });
|
|
484
|
-
const result = {
|
|
485
|
-
project: projectFilter ?? null,
|
|
486
|
-
examined,
|
|
487
|
-
updated,
|
|
488
|
-
unchanged
|
|
489
|
-
};
|
|
490
|
-
if (opts?.format === "json") {
|
|
491
|
-
console.log(JSON.stringify(result, null, 2));
|
|
492
|
-
return;
|
|
493
|
-
}
|
|
494
|
-
console.log("AI referral landing-page backfill complete.\n");
|
|
495
|
-
if (projectFilter) console.log(` Project: ${projectFilter}`);
|
|
496
|
-
console.log(` Examined: ${examined}`);
|
|
497
|
-
console.log(` Updated: ${updated}`);
|
|
498
|
-
console.log(` Unchanged: ${unchanged}`);
|
|
499
|
-
}
|
|
500
|
-
async function backfillAnswerMentionsCommand(opts) {
|
|
501
|
-
const config = loadConfig();
|
|
502
|
-
const db = createClient(config.database);
|
|
503
|
-
migrate(db);
|
|
504
|
-
const projectFilter = opts?.project?.trim();
|
|
505
|
-
const scopedProjects = projectFilter ? db.select().from(projects).where(eq(projects.name, projectFilter)).all() : db.select().from(projects).all();
|
|
506
|
-
let examined = 0;
|
|
507
|
-
let updated = 0;
|
|
508
|
-
let mentioned = 0;
|
|
509
|
-
if (scopedProjects.length > 0) {
|
|
510
|
-
const runRows = projectFilter ? db.select({ id: runs.id, projectId: runs.projectId }).from(runs).where(and(
|
|
511
|
-
eq(runs.kind, RunKinds["answer-visibility"]),
|
|
512
|
-
inArray(runs.projectId, scopedProjects.map((project) => project.id))
|
|
513
|
-
)).all() : db.select({ id: runs.id, projectId: runs.projectId }).from(runs).where(eq(runs.kind, RunKinds["answer-visibility"])).all();
|
|
514
|
-
const runIdsByProject = /* @__PURE__ */ new Map();
|
|
515
|
-
for (const run of runRows) {
|
|
516
|
-
const existing = runIdsByProject.get(run.projectId);
|
|
517
|
-
if (existing) existing.push(run.id);
|
|
518
|
-
else runIdsByProject.set(run.projectId, [run.id]);
|
|
519
|
-
}
|
|
520
|
-
for (const project of scopedProjects) {
|
|
521
|
-
const competitorDomains = db.select({ domain: competitors.domain }).from(competitors).where(eq(competitors.projectId, project.id)).all().map((row) => row.domain);
|
|
522
|
-
const runIds = runIdsByProject.get(project.id) ?? [];
|
|
523
|
-
if (runIds.length === 0) continue;
|
|
524
|
-
const projectDomains = effectiveDomains({
|
|
525
|
-
canonicalDomain: project.canonicalDomain,
|
|
526
|
-
ownedDomains: parseJsonColumn(project.ownedDomains, [])
|
|
527
|
-
});
|
|
528
|
-
for (let offset = 0; offset < runIds.length; offset += SNAPSHOT_BATCH_SIZE) {
|
|
529
|
-
const batchRunIds = runIds.slice(offset, offset + SNAPSHOT_BATCH_SIZE);
|
|
530
|
-
const snapshotRows = db.select({
|
|
531
|
-
id: querySnapshots.id,
|
|
532
|
-
provider: querySnapshots.provider,
|
|
533
|
-
answerMentioned: querySnapshots.answerMentioned,
|
|
534
|
-
answerText: querySnapshots.answerText,
|
|
535
|
-
citedDomains: querySnapshots.citedDomains,
|
|
536
|
-
competitorOverlap: querySnapshots.competitorOverlap,
|
|
537
|
-
recommendedCompetitors: querySnapshots.recommendedCompetitors,
|
|
538
|
-
rawResponse: querySnapshots.rawResponse
|
|
539
|
-
}).from(querySnapshots).where(inArray(querySnapshots.runId, batchRunIds)).all();
|
|
540
|
-
const pendingUpdates = [];
|
|
541
|
-
for (const snapshot of snapshotRows) {
|
|
542
|
-
examined++;
|
|
543
|
-
const answerText = snapshot.answerText ?? "";
|
|
544
|
-
const nextAnswerMentioned = determineAnswerMentioned(answerText, project.displayName, projectDomains);
|
|
545
|
-
if (nextAnswerMentioned) mentioned++;
|
|
546
|
-
const citedDomains = parseJsonColumn(snapshot.citedDomains, []);
|
|
547
|
-
const groundingSources = readStoredGroundingSources(snapshot.rawResponse);
|
|
548
|
-
const normalized = {
|
|
549
|
-
provider: snapshot.provider,
|
|
550
|
-
answerText,
|
|
551
|
-
citedDomains,
|
|
552
|
-
groundingSources,
|
|
553
|
-
searchQueries: []
|
|
554
|
-
};
|
|
555
|
-
const nextCompetitorOverlap = JSON.stringify(
|
|
556
|
-
computeCompetitorOverlap(normalized, competitorDomains)
|
|
557
|
-
);
|
|
558
|
-
const nextRecommendedCompetitors = JSON.stringify(
|
|
559
|
-
extractRecommendedCompetitors(
|
|
560
|
-
answerText,
|
|
561
|
-
projectDomains,
|
|
562
|
-
citedDomains,
|
|
563
|
-
competitorDomains
|
|
564
|
-
)
|
|
565
|
-
);
|
|
566
|
-
const nextPatch = {};
|
|
567
|
-
if (snapshot.answerMentioned !== nextAnswerMentioned) {
|
|
568
|
-
nextPatch.answerMentioned = nextAnswerMentioned;
|
|
569
|
-
}
|
|
570
|
-
if (snapshot.competitorOverlap !== nextCompetitorOverlap) {
|
|
571
|
-
nextPatch.competitorOverlap = nextCompetitorOverlap;
|
|
572
|
-
}
|
|
573
|
-
if (snapshot.recommendedCompetitors !== nextRecommendedCompetitors) {
|
|
574
|
-
nextPatch.recommendedCompetitors = nextRecommendedCompetitors;
|
|
575
|
-
}
|
|
576
|
-
if (Object.keys(nextPatch).length > 0) {
|
|
577
|
-
pendingUpdates.push({ id: snapshot.id, patch: nextPatch });
|
|
578
|
-
}
|
|
579
|
-
}
|
|
580
|
-
if (pendingUpdates.length > 0) {
|
|
581
|
-
db.transaction((tx) => {
|
|
582
|
-
for (const update of pendingUpdates) {
|
|
583
|
-
tx.update(querySnapshots).set(update.patch).where(eq(querySnapshots.id, update.id)).run();
|
|
584
|
-
}
|
|
585
|
-
});
|
|
586
|
-
updated += pendingUpdates.length;
|
|
587
|
-
}
|
|
588
|
-
}
|
|
589
|
-
}
|
|
590
|
-
}
|
|
591
|
-
const result = {
|
|
592
|
-
project: projectFilter ?? null,
|
|
593
|
-
projects: scopedProjects.length,
|
|
594
|
-
examined,
|
|
595
|
-
updated,
|
|
596
|
-
mentioned
|
|
597
|
-
};
|
|
598
|
-
if (opts?.format === "json") {
|
|
599
|
-
console.log(JSON.stringify(result, null, 2));
|
|
600
|
-
return;
|
|
601
|
-
}
|
|
602
|
-
console.log("Answer mentions backfill complete.\n");
|
|
603
|
-
if (projectFilter) console.log(` Project: ${projectFilter}`);
|
|
604
|
-
console.log(` Projects: ${scopedProjects.length}`);
|
|
605
|
-
console.log(` Examined: ${examined}`);
|
|
606
|
-
console.log(` Updated: ${updated}`);
|
|
607
|
-
console.log(` Mentioned: ${mentioned}`);
|
|
608
|
-
}
|
|
609
|
-
function readStoredGroundingSources(rawResponse) {
|
|
610
|
-
const envelope = parseJsonColumn(rawResponse, {});
|
|
611
|
-
const sources = envelope.groundingSources;
|
|
612
|
-
if (!Array.isArray(sources)) return [];
|
|
613
|
-
const result = [];
|
|
614
|
-
for (const source of sources) {
|
|
615
|
-
if (source && typeof source === "object") {
|
|
616
|
-
const uri = source.uri;
|
|
617
|
-
const title = source.title;
|
|
618
|
-
if (typeof uri === "string") {
|
|
619
|
-
result.push({ uri, title: typeof title === "string" ? title : "" });
|
|
620
|
-
}
|
|
621
|
-
}
|
|
622
|
-
}
|
|
623
|
-
return result;
|
|
624
|
-
}
|
|
625
|
-
async function backfillInsightsCommand(project, opts) {
|
|
626
|
-
const { IntelligenceService } = await import("./intelligence-service-XKOUBRCE.js");
|
|
627
|
-
const config = loadConfig();
|
|
628
|
-
const db = createClient(config.database);
|
|
629
|
-
migrate(db);
|
|
630
|
-
const service = new IntelligenceService(db);
|
|
631
|
-
const isJson = opts?.format === "json";
|
|
632
|
-
if (!isJson) {
|
|
633
|
-
process.stderr.write(`Backfilling insights for "${project}"...
|
|
634
|
-
`);
|
|
635
|
-
}
|
|
636
|
-
const result = service.backfill(project, {
|
|
637
|
-
fromRunId: opts?.fromRun,
|
|
638
|
-
toRunId: opts?.toRun
|
|
639
|
-
}, (info) => {
|
|
640
|
-
if (!isJson) {
|
|
641
|
-
process.stderr.write(` [${info.index}/${info.total}] ${info.runId} \u2014 ${info.insights} insights
|
|
642
|
-
`);
|
|
643
|
-
}
|
|
644
|
-
});
|
|
645
|
-
const output = {
|
|
646
|
-
project,
|
|
647
|
-
processed: result.processed,
|
|
648
|
-
skipped: result.skipped,
|
|
649
|
-
totalInsights: result.totalInsights
|
|
650
|
-
};
|
|
651
|
-
if (isJson) {
|
|
652
|
-
console.log(JSON.stringify(output, null, 2));
|
|
653
|
-
return;
|
|
654
|
-
}
|
|
655
|
-
console.log(`
|
|
656
|
-
Backfill complete.`);
|
|
657
|
-
console.log(` Processed: ${result.processed}`);
|
|
658
|
-
console.log(` Skipped: ${result.skipped}`);
|
|
659
|
-
console.log(` Insights: ${result.totalInsights}`);
|
|
660
|
-
}
|
|
661
|
-
function reparseProviderSnapshot(provider, rawResponse) {
|
|
662
|
-
const envelope = parseJsonColumn(rawResponse, {});
|
|
663
|
-
const apiResponse = resolveStoredApiResponse(envelope);
|
|
664
|
-
if (!apiResponse) return null;
|
|
665
|
-
switch (provider) {
|
|
666
|
-
case ProviderNames.openai:
|
|
667
|
-
return reparseStoredResult(apiResponse);
|
|
668
|
-
case ProviderNames.claude:
|
|
669
|
-
return reparseStoredResult2(apiResponse);
|
|
670
|
-
case ProviderNames.gemini:
|
|
671
|
-
return reparseStoredResult3(apiResponse);
|
|
672
|
-
case ProviderNames.perplexity:
|
|
673
|
-
return reparseStoredResult4(apiResponse);
|
|
674
|
-
default:
|
|
675
|
-
return null;
|
|
676
|
-
}
|
|
677
|
-
}
|
|
678
|
-
function resolveStoredApiResponse(parsed) {
|
|
679
|
-
const nested = parsed.apiResponse;
|
|
680
|
-
if (nested !== null && typeof nested === "object" && !Array.isArray(nested)) {
|
|
681
|
-
return nested;
|
|
682
|
-
}
|
|
683
|
-
if (looksLikeProviderApiResponse(parsed)) {
|
|
684
|
-
return parsed;
|
|
685
|
-
}
|
|
686
|
-
return null;
|
|
687
|
-
}
|
|
688
|
-
function looksLikeProviderApiResponse(value) {
|
|
689
|
-
return Array.isArray(value.output) || Array.isArray(value.content) || Array.isArray(value.candidates) || Array.isArray(value.choices);
|
|
690
|
-
}
|
|
691
|
-
function stringifyStoredSnapshotEnvelope(rawResponse, reparsed) {
|
|
692
|
-
const parsed = parseJsonColumn(rawResponse, {});
|
|
693
|
-
const apiResponse = resolveStoredApiResponse(parsed);
|
|
694
|
-
const envelope = apiResponse === parsed ? {} : { ...parsed };
|
|
695
|
-
delete envelope.answerText;
|
|
696
|
-
delete envelope.citedDomains;
|
|
697
|
-
delete envelope.competitorOverlap;
|
|
698
|
-
delete envelope.recommendedCompetitors;
|
|
699
|
-
delete envelope.providerError;
|
|
700
|
-
return JSON.stringify({
|
|
701
|
-
...envelope,
|
|
702
|
-
groundingSources: reparsed.groundingSources,
|
|
703
|
-
searchQueries: reparsed.searchQueries,
|
|
704
|
-
...reparsed.providerError ? { providerError: reparsed.providerError } : {},
|
|
705
|
-
...apiResponse ? { apiResponse } : {}
|
|
706
|
-
});
|
|
707
|
-
}
|
|
708
|
-
|
|
709
206
|
// src/cli-command-helpers.ts
|
|
710
207
|
function getString(values, key) {
|
|
711
208
|
const value = values[key];
|
|
@@ -1996,18 +1493,18 @@ function errorMessage(err) {
|
|
|
1996
1493
|
async function discoverRun(project, opts) {
|
|
1997
1494
|
const client = getClient4();
|
|
1998
1495
|
const { angles, multiAngle } = resolveIcpAngles(opts);
|
|
1999
|
-
const
|
|
1496
|
+
const runs = [];
|
|
2000
1497
|
for (const angle of angles) {
|
|
2001
1498
|
try {
|
|
2002
1499
|
const start = await client.triggerDiscoveryRun(project, buildRunBody(opts, angle));
|
|
2003
|
-
|
|
1500
|
+
runs.push({ angle, start });
|
|
2004
1501
|
} catch (err) {
|
|
2005
|
-
if (
|
|
1502
|
+
if (runs.length > 0) {
|
|
2006
1503
|
process.stderr.write(
|
|
2007
1504
|
`
|
|
2008
1505
|
Failed to start ${angle ? `angle "${angle}"` : "discovery run"}: ${errorMessage(err)}
|
|
2009
1506
|
Sessions already started (recover with \`canonry discover show ${project} <id>\`):
|
|
2010
|
-
` +
|
|
1507
|
+
` + runs.map((r) => ` ${r.start.sessionId}`).join("\n") + "\n"
|
|
2011
1508
|
);
|
|
2012
1509
|
}
|
|
2013
1510
|
throw err;
|
|
@@ -2015,10 +1512,10 @@ Sessions already started (recover with \`canonry discover show ${project} <id>\`
|
|
|
2015
1512
|
}
|
|
2016
1513
|
if (!opts.wait) {
|
|
2017
1514
|
if (opts.format === "json") {
|
|
2018
|
-
console.log(JSON.stringify(multiAngle ?
|
|
1515
|
+
console.log(JSON.stringify(multiAngle ? runs.map((r) => r.start) : runs[0].start, null, 2));
|
|
2019
1516
|
return;
|
|
2020
1517
|
}
|
|
2021
|
-
for (const { angle, start } of
|
|
1518
|
+
for (const { angle, start } of runs) {
|
|
2022
1519
|
if (angle) console.log(`[${angle}]`);
|
|
2023
1520
|
if (start.consolidated) {
|
|
2024
1521
|
console.log(`Reusing in-flight discovery session: ${start.sessionId}`);
|
|
@@ -2031,20 +1528,20 @@ Sessions already started (recover with \`canonry discover show ${project} <id>\`
|
|
|
2031
1528
|
console.log(` Status: ${start.status}`);
|
|
2032
1529
|
console.log(` Tail: canonry discover show ${project} ${start.sessionId}`);
|
|
2033
1530
|
}
|
|
2034
|
-
if (
|
|
1531
|
+
if (runs.length > 1) console.log();
|
|
2035
1532
|
}
|
|
2036
1533
|
return;
|
|
2037
1534
|
}
|
|
2038
|
-
const parallel =
|
|
2039
|
-
if (parallel) process.stderr.write(`Waiting for ${
|
|
1535
|
+
const parallel = runs.length > 1;
|
|
1536
|
+
if (parallel) process.stderr.write(`Waiting for ${runs.length} discovery sessions...
|
|
2040
1537
|
`);
|
|
2041
1538
|
const settled = await Promise.allSettled(
|
|
2042
|
-
|
|
1539
|
+
runs.map((r) => pollSession(client, project, r.start.sessionId, parallel))
|
|
2043
1540
|
);
|
|
2044
1541
|
const results = [];
|
|
2045
1542
|
const failures = [];
|
|
2046
1543
|
settled.forEach((outcome, i) => {
|
|
2047
|
-
const run =
|
|
1544
|
+
const run = runs[i];
|
|
2048
1545
|
if (outcome.status === "fulfilled") {
|
|
2049
1546
|
results.push({ angle: run.angle, session: outcome.value });
|
|
2050
1547
|
} else {
|
|
@@ -2084,7 +1581,7 @@ Sessions already started (recover with \`canonry discover show ${project} <id>\`
|
|
|
2084
1581
|
}
|
|
2085
1582
|
throw new CliError({
|
|
2086
1583
|
code: "DISCOVERY_INCOMPLETE",
|
|
2087
|
-
message: `${failures.length} of ${
|
|
1584
|
+
message: `${failures.length} of ${runs.length} discovery session(s) did not complete`,
|
|
2088
1585
|
details: { failed: failures.map((f) => f.sessionId) }
|
|
2089
1586
|
});
|
|
2090
1587
|
}
|
|
@@ -6069,10 +5566,10 @@ Brand Gap Analysis`);
|
|
|
6069
5566
|
console.log(`
|
|
6070
5567
|
Opportunity Gaps (competitors cited, you're not):`);
|
|
6071
5568
|
for (const q of data.gap) {
|
|
6072
|
-
const
|
|
5569
|
+
const competitors = q.competitorsCiting.join(", ");
|
|
6073
5570
|
const cons = q.consistency.totalRuns > 0 ? ` [cited ${q.consistency.citedRuns}/${q.consistency.totalRuns} runs]` : "";
|
|
6074
5571
|
console.log(` \u2022 ${q.query}${cons}`);
|
|
6075
|
-
console.log(` Competitors: ${
|
|
5572
|
+
console.log(` Competitors: ${competitors}`);
|
|
6076
5573
|
}
|
|
6077
5574
|
}
|
|
6078
5575
|
if (data.cited.length > 0) {
|
|
@@ -6155,9 +5652,9 @@ async function loadLatestRunForExport(client, project) {
|
|
|
6155
5652
|
} catch (err) {
|
|
6156
5653
|
if (!isEndpointMissing(err)) throw err;
|
|
6157
5654
|
}
|
|
6158
|
-
const
|
|
6159
|
-
if (
|
|
6160
|
-
const latestRun =
|
|
5655
|
+
const runs = await client.listRuns(project);
|
|
5656
|
+
if (runs.length === 0) return null;
|
|
5657
|
+
const latestRun = runs.reduce(
|
|
6161
5658
|
(current, candidate) => candidate.createdAt > current.createdAt ? candidate : current
|
|
6162
5659
|
);
|
|
6163
5660
|
return client.getRun(latestRun.id);
|
|
@@ -6211,14 +5708,14 @@ async function showStatus(project, format) {
|
|
|
6211
5708
|
const projectData = await client.getProject(project);
|
|
6212
5709
|
const latest = await getLatestRunSummary(client, project);
|
|
6213
5710
|
if (format === "json") {
|
|
6214
|
-
let
|
|
5711
|
+
let runs = [];
|
|
6215
5712
|
try {
|
|
6216
|
-
|
|
5713
|
+
runs = await client.listRuns(project);
|
|
6217
5714
|
} catch {
|
|
6218
5715
|
}
|
|
6219
5716
|
console.log(JSON.stringify({
|
|
6220
5717
|
project: projectData,
|
|
6221
|
-
runs
|
|
5718
|
+
runs,
|
|
6222
5719
|
latestRun: latest.run,
|
|
6223
5720
|
totalRuns: latest.totalRuns
|
|
6224
5721
|
}, null, 2));
|
|
@@ -6249,15 +5746,15 @@ async function getLatestRunSummary(client, project) {
|
|
|
6249
5746
|
return await client.getLatestRun(project);
|
|
6250
5747
|
} catch (err) {
|
|
6251
5748
|
if (!isEndpointMissing(err)) throw err;
|
|
6252
|
-
const
|
|
6253
|
-
if (
|
|
5749
|
+
const runs = await client.listRuns(project);
|
|
5750
|
+
if (runs.length === 0) {
|
|
6254
5751
|
return { totalRuns: 0, run: null };
|
|
6255
5752
|
}
|
|
6256
|
-
const latestRun =
|
|
5753
|
+
const latestRun = runs.reduce(
|
|
6257
5754
|
(current, candidate) => candidate.createdAt > current.createdAt ? candidate : current
|
|
6258
5755
|
);
|
|
6259
5756
|
return {
|
|
6260
|
-
totalRuns:
|
|
5757
|
+
totalRuns: runs.length,
|
|
6261
5758
|
run: latestRun
|
|
6262
5759
|
};
|
|
6263
5760
|
}
|
|
@@ -6347,6 +5844,7 @@ async function createProject(name, opts) {
|
|
|
6347
5844
|
displayName: opts.displayName,
|
|
6348
5845
|
canonicalDomain: opts.domain,
|
|
6349
5846
|
ownedDomains: opts.ownedDomains ?? [],
|
|
5847
|
+
aliases: normalizeProjectAliases(opts.displayName, opts.aliases ?? []),
|
|
6350
5848
|
country: opts.country,
|
|
6351
5849
|
language: opts.language
|
|
6352
5850
|
});
|
|
@@ -6400,6 +5898,9 @@ async function showProject(name, format) {
|
|
|
6400
5898
|
if (secondaryDomains.length > 0) {
|
|
6401
5899
|
console.log(` Owned domains: ${secondaryDomains.join(", ")}`);
|
|
6402
5900
|
}
|
|
5901
|
+
if (project.aliases && project.aliases.length > 0) {
|
|
5902
|
+
console.log(` Aliases: ${project.aliases.join(", ")}`);
|
|
5903
|
+
}
|
|
6403
5904
|
console.log(` Country: ${project.country}`);
|
|
6404
5905
|
console.log(` Language: ${project.language}`);
|
|
6405
5906
|
console.log(` Config source: ${project.configSource}`);
|
|
@@ -6422,10 +5923,22 @@ async function updateProjectSettings(name, opts) {
|
|
|
6422
5923
|
const toRemove = new Set(opts.removeOwnedDomain);
|
|
6423
5924
|
ownedDomains = ownedDomains.filter((d) => !toRemove.has(d));
|
|
6424
5925
|
}
|
|
5926
|
+
const nextDisplayName = opts.displayName ?? project.displayName ?? project.name;
|
|
5927
|
+
let aliases = opts.aliases ?? project.aliases ?? [];
|
|
5928
|
+
if (opts.addAlias) {
|
|
5929
|
+
const existingKeys = new Set(aliases.map((a) => a.toLowerCase()));
|
|
5930
|
+
const toAdd = opts.addAlias.filter((a) => !existingKeys.has(a.toLowerCase()));
|
|
5931
|
+
aliases = [...aliases, ...toAdd];
|
|
5932
|
+
}
|
|
5933
|
+
if (opts.removeAlias) {
|
|
5934
|
+
const toRemove = new Set(opts.removeAlias.map((a) => a.toLowerCase()));
|
|
5935
|
+
aliases = aliases.filter((a) => !toRemove.has(a.toLowerCase()));
|
|
5936
|
+
}
|
|
6425
5937
|
const result = await client.putProject(name, {
|
|
6426
|
-
displayName:
|
|
5938
|
+
displayName: nextDisplayName,
|
|
6427
5939
|
canonicalDomain: opts.domain ?? project.canonicalDomain,
|
|
6428
5940
|
ownedDomains,
|
|
5941
|
+
aliases: normalizeProjectAliases(nextDisplayName, aliases),
|
|
6429
5942
|
country: opts.country ?? project.country,
|
|
6430
5943
|
language: opts.language ?? project.language
|
|
6431
5944
|
});
|
|
@@ -6508,10 +6021,11 @@ async function setDefaultLocation(project, label, format) {
|
|
|
6508
6021
|
var PROJECT_CLI_COMMANDS = [
|
|
6509
6022
|
{
|
|
6510
6023
|
path: ["project", "create"],
|
|
6511
|
-
usage: "canonry project create <name> [--domain <domain>] [--owned-domain <domain>...] [--country <code>] [--language <lang>] [--display-name <name>] [--format json]",
|
|
6024
|
+
usage: "canonry project create <name> [--domain <domain>] [--owned-domain <domain>...] [--alias <name>...] [--country <code>] [--language <lang>] [--display-name <name>] [--format json]",
|
|
6512
6025
|
options: {
|
|
6513
6026
|
domain: { type: "string", short: "d" },
|
|
6514
6027
|
"owned-domain": multiStringOption(),
|
|
6028
|
+
alias: multiStringOption(),
|
|
6515
6029
|
country: stringOption(),
|
|
6516
6030
|
language: stringOption(),
|
|
6517
6031
|
"display-name": stringOption()
|
|
@@ -6520,11 +6034,12 @@ var PROJECT_CLI_COMMANDS = [
|
|
|
6520
6034
|
const name = requireProject(
|
|
6521
6035
|
input,
|
|
6522
6036
|
"project.create",
|
|
6523
|
-
"canonry project create <name> [--domain <domain>] [--owned-domain <domain>...] [--country <code>] [--language <lang>] [--display-name <name>] [--format json]"
|
|
6037
|
+
"canonry project create <name> [--domain <domain>] [--owned-domain <domain>...] [--alias <name>...] [--country <code>] [--language <lang>] [--display-name <name>] [--format json]"
|
|
6524
6038
|
);
|
|
6525
6039
|
await createProject(name, {
|
|
6526
6040
|
domain: getString(input.values, "domain") ?? name,
|
|
6527
6041
|
ownedDomains: getStringArray(input.values, "owned-domain") ?? [],
|
|
6042
|
+
aliases: getStringArray(input.values, "alias") ?? [],
|
|
6528
6043
|
country: getString(input.values, "country") ?? "US",
|
|
6529
6044
|
language: getString(input.values, "language") ?? "en",
|
|
6530
6045
|
displayName: getString(input.values, "display-name") ?? name,
|
|
@@ -6534,12 +6049,15 @@ var PROJECT_CLI_COMMANDS = [
|
|
|
6534
6049
|
},
|
|
6535
6050
|
{
|
|
6536
6051
|
path: ["project", "update"],
|
|
6537
|
-
usage: "canonry project update <name> [--domain <domain>] [--owned-domain <domain>...] [--add-domain <domain>...] [--remove-domain <domain>...] [--country <code>] [--language <lang>] [--display-name <name>] [--format json]",
|
|
6052
|
+
usage: "canonry project update <name> [--domain <domain>] [--owned-domain <domain>...] [--add-domain <domain>...] [--remove-domain <domain>...] [--alias <name>...] [--add-alias <name>...] [--remove-alias <name>...] [--country <code>] [--language <lang>] [--display-name <name>] [--format json]",
|
|
6538
6053
|
options: {
|
|
6539
6054
|
domain: { type: "string", short: "d" },
|
|
6540
6055
|
"owned-domain": multiStringOption(),
|
|
6541
6056
|
"add-domain": multiStringOption(),
|
|
6542
6057
|
"remove-domain": multiStringOption(),
|
|
6058
|
+
alias: multiStringOption(),
|
|
6059
|
+
"add-alias": multiStringOption(),
|
|
6060
|
+
"remove-alias": multiStringOption(),
|
|
6543
6061
|
country: stringOption(),
|
|
6544
6062
|
language: stringOption(),
|
|
6545
6063
|
"display-name": stringOption()
|
|
@@ -6548,7 +6066,7 @@ var PROJECT_CLI_COMMANDS = [
|
|
|
6548
6066
|
const name = requireProject(
|
|
6549
6067
|
input,
|
|
6550
6068
|
"project.update",
|
|
6551
|
-
"canonry project update <name> [--domain <domain>] [--owned-domain <domain>...] [--add-domain <domain>...] [--remove-domain <domain>...] [--country <code>] [--language <lang>] [--display-name <name>] [--format json]"
|
|
6069
|
+
"canonry project update <name> [--domain <domain>] [--owned-domain <domain>...] [--add-domain <domain>...] [--remove-domain <domain>...] [--alias <name>...] [--add-alias <name>...] [--remove-alias <name>...] [--country <code>] [--language <lang>] [--display-name <name>] [--format json]"
|
|
6552
6070
|
);
|
|
6553
6071
|
await updateProjectSettings(name, {
|
|
6554
6072
|
displayName: getString(input.values, "display-name"),
|
|
@@ -6556,6 +6074,9 @@ var PROJECT_CLI_COMMANDS = [
|
|
|
6556
6074
|
ownedDomains: getStringArray(input.values, "owned-domain"),
|
|
6557
6075
|
addOwnedDomain: getStringArray(input.values, "add-domain"),
|
|
6558
6076
|
removeOwnedDomain: getStringArray(input.values, "remove-domain"),
|
|
6077
|
+
aliases: getStringArray(input.values, "alias"),
|
|
6078
|
+
addAlias: getStringArray(input.values, "add-alias"),
|
|
6079
|
+
removeAlias: getStringArray(input.values, "remove-alias"),
|
|
6559
6080
|
country: getString(input.values, "country"),
|
|
6560
6081
|
language: getString(input.values, "language"),
|
|
6561
6082
|
format: input.format
|
|
@@ -6892,8 +6413,8 @@ async function cancelRun(project, runId, format) {
|
|
|
6892
6413
|
const client = getClient17();
|
|
6893
6414
|
let targetId = runId;
|
|
6894
6415
|
if (!targetId) {
|
|
6895
|
-
const
|
|
6896
|
-
const active =
|
|
6416
|
+
const runs = await client.listRuns(project);
|
|
6417
|
+
const active = runs.find((r) => r.status === "queued" || r.status === "running");
|
|
6897
6418
|
if (!active) {
|
|
6898
6419
|
throw new CliError({
|
|
6899
6420
|
code: "NO_ACTIVE_RUN",
|
|
@@ -6931,20 +6452,20 @@ async function showRun(id, format) {
|
|
|
6931
6452
|
}
|
|
6932
6453
|
async function listRuns(project, opts) {
|
|
6933
6454
|
const client = getClient17();
|
|
6934
|
-
const
|
|
6455
|
+
const runs = await client.listRuns(project, opts?.limit);
|
|
6935
6456
|
if (opts?.format === "json") {
|
|
6936
|
-
console.log(JSON.stringify(
|
|
6457
|
+
console.log(JSON.stringify(runs, null, 2));
|
|
6937
6458
|
return;
|
|
6938
6459
|
}
|
|
6939
|
-
if (
|
|
6460
|
+
if (runs.length === 0) {
|
|
6940
6461
|
console.log(`No runs found for "${project}".`);
|
|
6941
6462
|
return;
|
|
6942
6463
|
}
|
|
6943
|
-
console.log(`Runs for "${project}" (${
|
|
6464
|
+
console.log(`Runs for "${project}" (${runs.length}):
|
|
6944
6465
|
`);
|
|
6945
6466
|
console.log(" ID STATUS KIND TRIGGER CREATED");
|
|
6946
6467
|
console.log(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
|
|
6947
|
-
for (const run of
|
|
6468
|
+
for (const run of runs) {
|
|
6948
6469
|
console.log(
|
|
6949
6470
|
` ${run.id} ${run.status.padEnd(10)} ${run.kind.padEnd(18)} ${run.trigger.padEnd(9)} ${run.createdAt}`
|
|
6950
6471
|
);
|
|
@@ -8042,8 +7563,8 @@ function renderQueries(pdf, report) {
|
|
|
8042
7563
|
for (const result of queryResult.providerResults) {
|
|
8043
7564
|
const status = result.error ? "error" : result.mentioned ? result.cited ? "mentioned and cited" : "mentioned" : "not mentioned";
|
|
8044
7565
|
const accuracy = result.describedAccurately === "not-mentioned" ? "" : `; accuracy: ${result.describedAccurately}`;
|
|
8045
|
-
const
|
|
8046
|
-
const line = `${result.displayName}: ${status}${accuracy}${
|
|
7566
|
+
const competitors = result.recommendedCompetitors.length > 0 ? `; recommended instead: ${result.recommendedCompetitors.join(", ")}` : "";
|
|
7567
|
+
const line = `${result.displayName}: ${status}${accuracy}${competitors}`;
|
|
8047
7568
|
pdf.bullet(line);
|
|
8048
7569
|
if (result.error) {
|
|
8049
7570
|
pdf.paragraph(`Error: ${result.error}`, { size: 9, color: FAIL, lineHeight: 12 });
|
|
@@ -8190,8 +7711,8 @@ function formatSnapshotMarkdown(report) {
|
|
|
8190
7711
|
const mentioned = result.mentioned ? "Yes" : "No";
|
|
8191
7712
|
const cited = result.cited ? "Yes" : "No";
|
|
8192
7713
|
const accuracy = result.describedAccurately === "not-mentioned" ? "-" : result.describedAccurately;
|
|
8193
|
-
const
|
|
8194
|
-
lines.push(`| ${result.displayName} | ${mentioned} | ${cited} | ${accuracy} | ${
|
|
7714
|
+
const competitors = result.recommendedCompetitors.length > 0 ? result.recommendedCompetitors.join(", ") : "-";
|
|
7715
|
+
lines.push(`| ${result.displayName} | ${mentioned} | ${cited} | ${accuracy} | ${competitors} |`);
|
|
8195
7716
|
}
|
|
8196
7717
|
lines.push("");
|
|
8197
7718
|
}
|
|
@@ -8421,7 +7942,7 @@ function renderHuman(overview) {
|
|
|
8421
7942
|
transitions,
|
|
8422
7943
|
scores,
|
|
8423
7944
|
movementSummary,
|
|
8424
|
-
competitors
|
|
7945
|
+
competitors,
|
|
8425
7946
|
providerScores,
|
|
8426
7947
|
attentionItems,
|
|
8427
7948
|
runHistory,
|
|
@@ -8469,9 +7990,9 @@ function renderHuman(overview) {
|
|
|
8469
7990
|
console.log(`
|
|
8470
7991
|
Transitions since ${transitions.since}: +${transitions.gained} gained, -${transitions.lost} lost, ${transitions.emerging} emerging`);
|
|
8471
7992
|
}
|
|
8472
|
-
if (
|
|
7993
|
+
if (competitors.length > 0) {
|
|
8473
7994
|
console.log("\n Competitors:");
|
|
8474
|
-
for (const c of
|
|
7995
|
+
for (const c of competitors) {
|
|
8475
7996
|
console.log(` ${c.domain.padEnd(28)} ${c.citationCount}/${c.totalQueries} ${c.pressureLabel}`);
|
|
8476
7997
|
}
|
|
8477
7998
|
}
|
|
@@ -8860,7 +8381,7 @@ var CONTENT_CLI_COMMANDS = [
|
|
|
8860
8381
|
// src/commands/bootstrap.ts
|
|
8861
8382
|
import crypto from "crypto";
|
|
8862
8383
|
import path7 from "path";
|
|
8863
|
-
import { eq
|
|
8384
|
+
import { eq } from "drizzle-orm";
|
|
8864
8385
|
|
|
8865
8386
|
// ../config/src/index.ts
|
|
8866
8387
|
import { z } from "zod";
|
|
@@ -8898,7 +8419,11 @@ var envSchema = z.object({
|
|
|
8898
8419
|
PERPLEXITY_MODEL: z.string().optional(),
|
|
8899
8420
|
PERPLEXITY_MAX_CONCURRENCY: z.coerce.number().int().positive().default(2),
|
|
8900
8421
|
PERPLEXITY_MAX_REQUESTS_PER_MINUTE: z.coerce.number().int().positive().default(10),
|
|
8901
|
-
PERPLEXITY_MAX_REQUESTS_PER_DAY: z.coerce.number().int().positive().default(1e3)
|
|
8422
|
+
PERPLEXITY_MAX_REQUESTS_PER_DAY: z.coerce.number().int().positive().default(1e3),
|
|
8423
|
+
// Secret for HMAC-signing Google OAuth state parameters. Required for
|
|
8424
|
+
// cloud deployments that mount googleRoutes; the plugin refuses to register
|
|
8425
|
+
// without it (see packages/api-routes/src/google.ts).
|
|
8426
|
+
GOOGLE_STATE_SECRET: z.string().optional()
|
|
8902
8427
|
});
|
|
8903
8428
|
var bootstrapEnvSchema = z.object({
|
|
8904
8429
|
CANONRY_API_KEY: z.string().optional(),
|
|
@@ -9038,7 +8563,7 @@ async function bootstrapCommand(_opts) {
|
|
|
9038
8563
|
const db = createClient(databasePath);
|
|
9039
8564
|
migrate(db);
|
|
9040
8565
|
db.transaction((tx) => {
|
|
9041
|
-
tx.delete(apiKeys).where(
|
|
8566
|
+
tx.delete(apiKeys).where(eq(apiKeys.name, "default")).run();
|
|
9042
8567
|
tx.insert(apiKeys).values({
|
|
9043
8568
|
id: crypto.randomUUID(),
|
|
9044
8569
|
name: "default",
|
|
@@ -11558,6 +11083,18 @@ async function runCli(args = process.argv.slice(2)) {
|
|
|
11558
11083
|
...setupState ? { setup_state: setupState } : {}
|
|
11559
11084
|
});
|
|
11560
11085
|
}
|
|
11086
|
+
if (!isHelpRequest && command !== "telemetry") {
|
|
11087
|
+
void checkLatestVersionForCli().then((update) => {
|
|
11088
|
+
if (!update) return;
|
|
11089
|
+
process.stderr.write(
|
|
11090
|
+
`
|
|
11091
|
+
\u2192 canonry ${update.latest} is available (you have ${update.current}).
|
|
11092
|
+
Upgrade: ${update.upgradeCommand}
|
|
11093
|
+
|
|
11094
|
+
`
|
|
11095
|
+
);
|
|
11096
|
+
});
|
|
11097
|
+
}
|
|
11561
11098
|
try {
|
|
11562
11099
|
if (await dispatchRegisteredCommand(args, format, REGISTERED_CLI_COMMANDS)) {
|
|
11563
11100
|
return 0;
|