@ainyc/canonry 1.34.0 → 1.36.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/assets/assets/{index-CxMuEW6I.css → index-CborW-lk.css} +1 -1
- package/assets/assets/index-Du_w835k.js +281 -0
- package/assets/index.html +2 -2
- package/dist/{chunk-4SRBJCNX.js → chunk-PZKK53EX.js} +169 -60
- package/dist/cli.js +43 -4
- package/dist/index.js +1 -1
- package/package.json +5 -5
- package/assets/assets/index-BPuIj1DV.js +0 -246
package/assets/index.html
CHANGED
|
@@ -12,8 +12,8 @@
|
|
|
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-
|
|
16
|
-
<link rel="stylesheet" crossorigin href="./assets/index-
|
|
15
|
+
<script type="module" crossorigin src="./assets/index-Du_w835k.js"></script>
|
|
16
|
+
<link rel="stylesheet" crossorigin href="./assets/index-CborW-lk.css">
|
|
17
17
|
</head>
|
|
18
18
|
<body>
|
|
19
19
|
<div id="root"></div>
|
|
@@ -1074,6 +1074,19 @@ var ga4TrafficSummaryDtoSchema = z11.object({
|
|
|
1074
1074
|
aiReferrals: z11.array(ga4AiReferralDtoSchema),
|
|
1075
1075
|
lastSyncedAt: z11.string().nullable()
|
|
1076
1076
|
});
|
|
1077
|
+
var ga4AiReferralHistoryEntrySchema = z11.object({
|
|
1078
|
+
date: z11.string(),
|
|
1079
|
+
source: z11.string(),
|
|
1080
|
+
medium: z11.string(),
|
|
1081
|
+
sessions: z11.number(),
|
|
1082
|
+
users: z11.number()
|
|
1083
|
+
});
|
|
1084
|
+
var ga4SessionHistoryEntrySchema = z11.object({
|
|
1085
|
+
date: z11.string(),
|
|
1086
|
+
sessions: z11.number(),
|
|
1087
|
+
organicSessions: z11.number(),
|
|
1088
|
+
users: z11.number()
|
|
1089
|
+
});
|
|
1077
1090
|
|
|
1078
1091
|
// ../contracts/src/answer-visibility.ts
|
|
1079
1092
|
var GENERIC_TOKENS = /* @__PURE__ */ new Set([
|
|
@@ -1512,6 +1525,7 @@ function createClient(databasePath) {
|
|
|
1512
1525
|
const sqlite = new Database(databasePath);
|
|
1513
1526
|
sqlite.pragma("journal_mode = WAL");
|
|
1514
1527
|
sqlite.pragma("foreign_keys = ON");
|
|
1528
|
+
sqlite.pragma("busy_timeout = 5000");
|
|
1515
1529
|
return drizzle(sqlite, { schema: schema_exports });
|
|
1516
1530
|
}
|
|
1517
1531
|
|
|
@@ -1839,7 +1853,14 @@ var MIGRATIONS = [
|
|
|
1839
1853
|
`CREATE INDEX IF NOT EXISTS idx_ga_ai_ref_source ON ga_ai_referrals(source)`,
|
|
1840
1854
|
`CREATE UNIQUE INDEX IF NOT EXISTS idx_ga_ai_ref_unique ON ga_ai_referrals(project_id, date, source, medium)`,
|
|
1841
1855
|
// v18: Answer-level visibility derived from answer text
|
|
1842
|
-
`ALTER TABLE query_snapshots ADD COLUMN answer_mentioned INTEGER
|
|
1856
|
+
`ALTER TABLE query_snapshots ADD COLUMN answer_mentioned INTEGER`,
|
|
1857
|
+
// v19: Add named unique indexes and missing columns from early tables
|
|
1858
|
+
`CREATE UNIQUE INDEX IF NOT EXISTS idx_keywords_project_keyword ON keywords(project_id, keyword)`,
|
|
1859
|
+
`CREATE UNIQUE INDEX IF NOT EXISTS idx_competitors_project_domain ON competitors(project_id, domain)`,
|
|
1860
|
+
`CREATE UNIQUE INDEX IF NOT EXISTS idx_schedules_project ON schedules(project_id)`,
|
|
1861
|
+
`CREATE UNIQUE INDEX IF NOT EXISTS idx_usage_scope_period_metric ON usage_counters(scope, period, metric)`,
|
|
1862
|
+
`ALTER TABLE projects ADD COLUMN config_source TEXT NOT NULL DEFAULT 'cli'`,
|
|
1863
|
+
`ALTER TABLE projects ADD COLUMN config_revision INTEGER NOT NULL DEFAULT 1`
|
|
1843
1864
|
];
|
|
1844
1865
|
function migrate(db) {
|
|
1845
1866
|
const statements = MIGRATION_SQL.split(";").map((s) => s.trim()).filter((s) => s.length > 0);
|
|
@@ -5754,6 +5775,30 @@ var routeCatalog = [
|
|
|
5754
5775
|
404: { description: "Project not found." }
|
|
5755
5776
|
}
|
|
5756
5777
|
},
|
|
5778
|
+
{
|
|
5779
|
+
method: "get",
|
|
5780
|
+
path: "/api/v1/projects/{name}/ga/ai-referral-history",
|
|
5781
|
+
summary: "Get AI referral sessions per day grouped by source",
|
|
5782
|
+
tags: ["ga4"],
|
|
5783
|
+
parameters: [nameParameter],
|
|
5784
|
+
responses: {
|
|
5785
|
+
200: { description: "AI referral history returned." },
|
|
5786
|
+
400: { description: "GA4 is not connected." },
|
|
5787
|
+
404: { description: "Project not found." }
|
|
5788
|
+
}
|
|
5789
|
+
},
|
|
5790
|
+
{
|
|
5791
|
+
method: "get",
|
|
5792
|
+
path: "/api/v1/projects/{name}/ga/session-history",
|
|
5793
|
+
summary: "Get total sessions per day for the project",
|
|
5794
|
+
tags: ["ga4"],
|
|
5795
|
+
parameters: [nameParameter],
|
|
5796
|
+
responses: {
|
|
5797
|
+
200: { description: "Session history returned." },
|
|
5798
|
+
400: { description: "GA4 is not connected." },
|
|
5799
|
+
404: { description: "Project not found." }
|
|
5800
|
+
}
|
|
5801
|
+
},
|
|
5757
5802
|
{
|
|
5758
5803
|
method: "get",
|
|
5759
5804
|
path: "/api/v1/projects/{name}/ga/coverage",
|
|
@@ -6263,6 +6308,7 @@ var GSC_MAX_ROWS_PER_REQUEST = 25e3;
|
|
|
6263
6308
|
var GSC_DATA_LAG_DAYS = 3;
|
|
6264
6309
|
var INDEXING_API_BASE = "https://indexing.googleapis.com/v3";
|
|
6265
6310
|
var INDEXING_API_DAILY_LIMIT = 200;
|
|
6311
|
+
var GOOGLE_REQUEST_TIMEOUT_MS = 3e4;
|
|
6266
6312
|
|
|
6267
6313
|
// ../integration-google/src/types.ts
|
|
6268
6314
|
var GoogleAuthError = class extends Error {
|
|
@@ -6303,7 +6349,8 @@ async function exchangeCode(clientId, clientSecret, code, redirectUri) {
|
|
|
6303
6349
|
code,
|
|
6304
6350
|
redirect_uri: redirectUri,
|
|
6305
6351
|
grant_type: "authorization_code"
|
|
6306
|
-
})
|
|
6352
|
+
}),
|
|
6353
|
+
signal: AbortSignal.timeout(GOOGLE_REQUEST_TIMEOUT_MS)
|
|
6307
6354
|
});
|
|
6308
6355
|
if (!res.ok) {
|
|
6309
6356
|
const body = await res.text();
|
|
@@ -6320,7 +6367,8 @@ async function refreshAccessToken(clientId, clientSecret, currentRefreshToken) {
|
|
|
6320
6367
|
client_secret: clientSecret,
|
|
6321
6368
|
refresh_token: currentRefreshToken,
|
|
6322
6369
|
grant_type: "refresh_token"
|
|
6323
|
-
})
|
|
6370
|
+
}),
|
|
6371
|
+
signal: AbortSignal.timeout(GOOGLE_REQUEST_TIMEOUT_MS)
|
|
6324
6372
|
});
|
|
6325
6373
|
if (!res.ok) {
|
|
6326
6374
|
const body = await res.text();
|
|
@@ -6344,7 +6392,8 @@ async function gscFetch(accessToken, url, opts) {
|
|
|
6344
6392
|
const res = await fetch(url, {
|
|
6345
6393
|
method,
|
|
6346
6394
|
headers,
|
|
6347
|
-
body: opts?.body != null ? JSON.stringify(opts.body) : void 0
|
|
6395
|
+
body: opts?.body != null ? JSON.stringify(opts.body) : void 0,
|
|
6396
|
+
signal: AbortSignal.timeout(GOOGLE_REQUEST_TIMEOUT_MS)
|
|
6348
6397
|
});
|
|
6349
6398
|
if (res.status === 401) {
|
|
6350
6399
|
const body = await res.text().catch(() => "");
|
|
@@ -6446,6 +6495,7 @@ var GA4_SCOPE = "https://www.googleapis.com/auth/analytics.readonly";
|
|
|
6446
6495
|
var GOOGLE_TOKEN_URL2 = "https://oauth2.googleapis.com/token";
|
|
6447
6496
|
var GA4_DEFAULT_SYNC_DAYS = 30;
|
|
6448
6497
|
var GA4_MAX_SYNC_DAYS = 90;
|
|
6498
|
+
var GA4_REQUEST_TIMEOUT_MS = 3e4;
|
|
6449
6499
|
|
|
6450
6500
|
// ../integration-google-analytics/src/types.ts
|
|
6451
6501
|
var GA4ApiError = class extends Error {
|
|
@@ -6491,7 +6541,8 @@ async function getAccessToken(clientEmail, privateKey) {
|
|
|
6491
6541
|
body: new URLSearchParams({
|
|
6492
6542
|
grant_type: "urn:ietf:params:oauth:grant-type:jwt-bearer",
|
|
6493
6543
|
assertion: jwt
|
|
6494
|
-
})
|
|
6544
|
+
}),
|
|
6545
|
+
signal: AbortSignal.timeout(GA4_REQUEST_TIMEOUT_MS)
|
|
6495
6546
|
});
|
|
6496
6547
|
if (!res.ok) {
|
|
6497
6548
|
const body = await res.text().catch(() => "");
|
|
@@ -6509,7 +6560,8 @@ async function runReport(accessToken, propertyId, request) {
|
|
|
6509
6560
|
"Authorization": `Bearer ${accessToken}`,
|
|
6510
6561
|
"Content-Type": "application/json"
|
|
6511
6562
|
},
|
|
6512
|
-
body: JSON.stringify(request)
|
|
6563
|
+
body: JSON.stringify(request),
|
|
6564
|
+
signal: AbortSignal.timeout(GA4_REQUEST_TIMEOUT_MS)
|
|
6513
6565
|
});
|
|
6514
6566
|
if (res.status === 401 || res.status === 403) {
|
|
6515
6567
|
const body = await res.text().catch(() => "");
|
|
@@ -6549,7 +6601,8 @@ async function batchRunReports(accessToken, propertyId, requests) {
|
|
|
6549
6601
|
"Authorization": `Bearer ${accessToken}`,
|
|
6550
6602
|
"Content-Type": "application/json"
|
|
6551
6603
|
},
|
|
6552
|
-
body: JSON.stringify({ requests })
|
|
6604
|
+
body: JSON.stringify({ requests }),
|
|
6605
|
+
signal: AbortSignal.timeout(GA4_REQUEST_TIMEOUT_MS)
|
|
6553
6606
|
});
|
|
6554
6607
|
if (res.status === 401 || res.status === 403) {
|
|
6555
6608
|
const body = await res.text().catch(() => "");
|
|
@@ -7488,6 +7541,7 @@ import { eq as eq14, and as and3, desc as desc5 } from "drizzle-orm";
|
|
|
7488
7541
|
var BING_WMT_API_BASE = "https://ssl.bing.com/webmaster/api.svc/json";
|
|
7489
7542
|
var BING_SUBMIT_URL_BATCH_LIMIT = 500;
|
|
7490
7543
|
var BING_SUBMIT_URL_DAILY_LIMIT = 1e4;
|
|
7544
|
+
var BING_REQUEST_TIMEOUT_MS = 3e4;
|
|
7491
7545
|
|
|
7492
7546
|
// ../integration-bing/src/types.ts
|
|
7493
7547
|
var BingApiError = class extends Error {
|
|
@@ -7515,7 +7569,8 @@ async function bingFetch(apiKey, endpoint, opts) {
|
|
|
7515
7569
|
const res = await fetch(url, {
|
|
7516
7570
|
method,
|
|
7517
7571
|
headers,
|
|
7518
|
-
body: opts?.body != null ? JSON.stringify(opts.body) : void 0
|
|
7572
|
+
body: opts?.body != null ? JSON.stringify(opts.body) : void 0,
|
|
7573
|
+
signal: AbortSignal.timeout(BING_REQUEST_TIMEOUT_MS)
|
|
7519
7574
|
});
|
|
7520
7575
|
if (res.status === 401 || res.status === 403) {
|
|
7521
7576
|
const body = await res.text().catch(() => "");
|
|
@@ -8463,6 +8518,34 @@ async function ga4Routes(app, opts) {
|
|
|
8463
8518
|
lastSyncedAt: latestSync?.syncedAt ?? null
|
|
8464
8519
|
};
|
|
8465
8520
|
});
|
|
8521
|
+
app.get("/projects/:name/ga/ai-referral-history", async (request, _reply) => {
|
|
8522
|
+
const project = resolveProject(app.db, request.params.name);
|
|
8523
|
+
requireGa4Connection(opts, project.name, project.canonicalDomain);
|
|
8524
|
+
const rows = app.db.select({
|
|
8525
|
+
date: gaAiReferrals.date,
|
|
8526
|
+
source: gaAiReferrals.source,
|
|
8527
|
+
medium: gaAiReferrals.medium,
|
|
8528
|
+
sessions: gaAiReferrals.sessions,
|
|
8529
|
+
users: gaAiReferrals.users
|
|
8530
|
+
}).from(gaAiReferrals).where(eq16(gaAiReferrals.projectId, project.id)).orderBy(gaAiReferrals.date).all();
|
|
8531
|
+
return rows;
|
|
8532
|
+
});
|
|
8533
|
+
app.get("/projects/:name/ga/session-history", async (request, _reply) => {
|
|
8534
|
+
const project = resolveProject(app.db, request.params.name);
|
|
8535
|
+
requireGa4Connection(opts, project.name, project.canonicalDomain);
|
|
8536
|
+
const rows = app.db.select({
|
|
8537
|
+
date: gaTrafficSnapshots.date,
|
|
8538
|
+
sessions: sql4`SUM(${gaTrafficSnapshots.sessions})`,
|
|
8539
|
+
organicSessions: sql4`SUM(${gaTrafficSnapshots.organicSessions})`,
|
|
8540
|
+
users: sql4`SUM(${gaTrafficSnapshots.users})`
|
|
8541
|
+
}).from(gaTrafficSnapshots).where(eq16(gaTrafficSnapshots.projectId, project.id)).groupBy(gaTrafficSnapshots.date).orderBy(gaTrafficSnapshots.date).all();
|
|
8542
|
+
return rows.map((r) => ({
|
|
8543
|
+
date: r.date,
|
|
8544
|
+
sessions: r.sessions ?? 0,
|
|
8545
|
+
organicSessions: r.organicSessions ?? 0,
|
|
8546
|
+
users: r.users ?? 0
|
|
8547
|
+
}));
|
|
8548
|
+
});
|
|
8466
8549
|
app.get("/projects/:name/ga/coverage", async (request, _reply) => {
|
|
8467
8550
|
const project = resolveProject(app.db, request.params.name);
|
|
8468
8551
|
requireGa4Connection(opts, project.name, project.canonicalDomain);
|
|
@@ -8606,6 +8689,8 @@ function parseSchemaPageEntry(entry) {
|
|
|
8606
8689
|
|
|
8607
8690
|
// ../integration-wordpress/src/wordpress-client.ts
|
|
8608
8691
|
import crypto17 from "crypto";
|
|
8692
|
+
var WP_REQUEST_TIMEOUT_MS = 3e4;
|
|
8693
|
+
var WP_FETCH_TEXT_TIMEOUT_MS = 15e3;
|
|
8609
8694
|
var PAGE_FIELDS = "id,slug,status,link,modified,modified_gmt,title,content,meta";
|
|
8610
8695
|
var PAGE_LIST_FIELDS = "id,slug,status,link,modified,modified_gmt,title";
|
|
8611
8696
|
var VERIFY_PAGE_FIELDS = "id,status";
|
|
@@ -8662,7 +8747,8 @@ async function fetchJson(connection, siteUrl, path7, init) {
|
|
|
8662
8747
|
"Authorization": `Basic ${encodeBasicAuth(connection.username, connection.appPassword)}`,
|
|
8663
8748
|
...init?.body != null ? { "Content-Type": "application/json" } : {},
|
|
8664
8749
|
...init?.headers ?? {}
|
|
8665
|
-
}
|
|
8750
|
+
},
|
|
8751
|
+
signal: AbortSignal.timeout(WP_REQUEST_TIMEOUT_MS)
|
|
8666
8752
|
});
|
|
8667
8753
|
if (res.status === 401 || res.status === 403) {
|
|
8668
8754
|
const text2 = await res.text().catch(() => "");
|
|
@@ -8705,7 +8791,7 @@ async function fetchPageCollectionSummary(connection, siteUrl, options) {
|
|
|
8705
8791
|
}
|
|
8706
8792
|
async function fetchText(url) {
|
|
8707
8793
|
try {
|
|
8708
|
-
const res = await fetch(url);
|
|
8794
|
+
const res = await fetch(url, { signal: AbortSignal.timeout(WP_FETCH_TEXT_TIMEOUT_MS) });
|
|
8709
8795
|
if (!res.ok) return null;
|
|
8710
8796
|
return await res.text();
|
|
8711
8797
|
} catch {
|
|
@@ -8886,12 +8972,20 @@ async function getSiteStatus(connection, env) {
|
|
|
8886
8972
|
async function listActivePlugins(connection, env) {
|
|
8887
8973
|
const site = resolveEnvironment(connection, env);
|
|
8888
8974
|
try {
|
|
8889
|
-
const
|
|
8890
|
-
|
|
8891
|
-
|
|
8892
|
-
|
|
8893
|
-
|
|
8894
|
-
|
|
8975
|
+
const allPlugins = [];
|
|
8976
|
+
let page = 1;
|
|
8977
|
+
let totalPages = 1;
|
|
8978
|
+
while (page <= totalPages) {
|
|
8979
|
+
const { body, response } = await fetchJson(
|
|
8980
|
+
connection,
|
|
8981
|
+
site.siteUrl,
|
|
8982
|
+
`/wp-json/wp/v2/plugins?per_page=100&page=${page}&_fields=plugin,status`
|
|
8983
|
+
);
|
|
8984
|
+
totalPages = Number.parseInt(response.headers.get("x-wp-totalpages") ?? "1", 10) || 1;
|
|
8985
|
+
allPlugins.push(...body);
|
|
8986
|
+
page += 1;
|
|
8987
|
+
}
|
|
8988
|
+
return allPlugins.filter((plugin) => plugin.status === "active").map((plugin) => plugin.plugin).sort();
|
|
8895
8989
|
} catch (error) {
|
|
8896
8990
|
if (error instanceof WordpressApiError && (error.statusCode === 403 || error.statusCode === 404)) {
|
|
8897
8991
|
return null;
|
|
@@ -10699,7 +10793,7 @@ function extractCitedDomains2(raw) {
|
|
|
10699
10793
|
function extractDomainFromUri2(uri) {
|
|
10700
10794
|
try {
|
|
10701
10795
|
const url = new URL(uri);
|
|
10702
|
-
return url.hostname.replace(/^www\./, "");
|
|
10796
|
+
return url.hostname.replace(/^www\./, "").toLowerCase();
|
|
10703
10797
|
} catch {
|
|
10704
10798
|
return null;
|
|
10705
10799
|
}
|
|
@@ -10707,11 +10801,11 @@ function extractDomainFromUri2(uri) {
|
|
|
10707
10801
|
async function generateText2(prompt, config) {
|
|
10708
10802
|
const model = config.model ?? DEFAULT_MODEL2;
|
|
10709
10803
|
const client = new OpenAI({ apiKey: config.apiKey });
|
|
10710
|
-
const response = await client.
|
|
10804
|
+
const response = await client.responses.create({
|
|
10711
10805
|
model,
|
|
10712
|
-
|
|
10806
|
+
input: prompt
|
|
10713
10807
|
});
|
|
10714
|
-
return response
|
|
10808
|
+
return extractResponseText(response);
|
|
10715
10809
|
}
|
|
10716
10810
|
function responseToRecord2(response) {
|
|
10717
10811
|
try {
|
|
@@ -10960,7 +11054,7 @@ function extractCitedDomains3(raw) {
|
|
|
10960
11054
|
function extractDomainFromUri3(uri) {
|
|
10961
11055
|
try {
|
|
10962
11056
|
const url = new URL(uri);
|
|
10963
|
-
return url.hostname.replace(/^www\./, "");
|
|
11057
|
+
return url.hostname.replace(/^www\./, "").toLowerCase();
|
|
10964
11058
|
} catch {
|
|
10965
11059
|
return null;
|
|
10966
11060
|
}
|
|
@@ -11126,7 +11220,7 @@ async function executeTrackedQuery4(input) {
|
|
|
11126
11220
|
});
|
|
11127
11221
|
return {
|
|
11128
11222
|
provider: "local",
|
|
11129
|
-
rawResponse:
|
|
11223
|
+
rawResponse: responseToRecord4(response),
|
|
11130
11224
|
model,
|
|
11131
11225
|
groundingSources: [],
|
|
11132
11226
|
searchQueries: []
|
|
@@ -11181,6 +11275,13 @@ function extractDomainMentions(text2) {
|
|
|
11181
11275
|
}
|
|
11182
11276
|
return [...domains];
|
|
11183
11277
|
}
|
|
11278
|
+
function responseToRecord4(response) {
|
|
11279
|
+
try {
|
|
11280
|
+
return JSON.parse(JSON.stringify(response));
|
|
11281
|
+
} catch {
|
|
11282
|
+
return { error: "failed to serialize response" };
|
|
11283
|
+
}
|
|
11284
|
+
}
|
|
11184
11285
|
|
|
11185
11286
|
// ../provider-local/src/adapter.ts
|
|
11186
11287
|
function toLocalConfig(config) {
|
|
@@ -11710,6 +11811,10 @@ function getConnection(config) {
|
|
|
11710
11811
|
if (parts.length >= 1 && parts[0]) host = parts[0];
|
|
11711
11812
|
if (parts.length >= 2 && parts[1]) port = parseInt(parts[1], 10) || 9222;
|
|
11712
11813
|
if (!sharedConnection || sharedConnection.endpoint !== `${host}:${port}`) {
|
|
11814
|
+
if (sharedConnection) {
|
|
11815
|
+
sharedConnection.disconnect().catch(() => {
|
|
11816
|
+
});
|
|
11817
|
+
}
|
|
11713
11818
|
sharedConnection = new CDPConnectionManager(host, port);
|
|
11714
11819
|
}
|
|
11715
11820
|
return sharedConnection;
|
|
@@ -11859,7 +11964,7 @@ async function executeTrackedQuery5(input) {
|
|
|
11859
11964
|
{ role: "user", content: prompt }
|
|
11860
11965
|
]
|
|
11861
11966
|
});
|
|
11862
|
-
const rawResponse =
|
|
11967
|
+
const rawResponse = responseToRecord5(response);
|
|
11863
11968
|
const citations = extractCitations(rawResponse);
|
|
11864
11969
|
const groundingSources = citations.map((url) => ({
|
|
11865
11970
|
uri: url,
|
|
@@ -11923,7 +12028,7 @@ function extractCitedDomains5(groundingSources) {
|
|
|
11923
12028
|
function extractDomainFromUri4(uri) {
|
|
11924
12029
|
try {
|
|
11925
12030
|
const url = new URL(uri);
|
|
11926
|
-
return url.hostname.replace(/^www\./, "");
|
|
12031
|
+
return url.hostname.replace(/^www\./, "").toLowerCase();
|
|
11927
12032
|
} catch {
|
|
11928
12033
|
return null;
|
|
11929
12034
|
}
|
|
@@ -11937,7 +12042,7 @@ async function generateText5(prompt, config) {
|
|
|
11937
12042
|
});
|
|
11938
12043
|
return response.choices[0]?.message?.content ?? "";
|
|
11939
12044
|
}
|
|
11940
|
-
function
|
|
12045
|
+
function responseToRecord5(response) {
|
|
11941
12046
|
try {
|
|
11942
12047
|
return JSON.parse(JSON.stringify(response));
|
|
11943
12048
|
} catch {
|
|
@@ -12569,7 +12674,7 @@ var JobRunner = class {
|
|
|
12569
12674
|
});
|
|
12570
12675
|
if (this.onRunCompleted) {
|
|
12571
12676
|
this.onRunCompleted(runId, projectId).catch((notifErr) => {
|
|
12572
|
-
|
|
12677
|
+
log.error("notification.callback-failed", { runId, error: notifErr instanceof Error ? notifErr.message : String(notifErr) });
|
|
12573
12678
|
});
|
|
12574
12679
|
}
|
|
12575
12680
|
}
|
|
@@ -13269,45 +13374,49 @@ var Scheduler = class {
|
|
|
13269
13374
|
log4.info("cron.registered", { projectId, schedule: label, timezone });
|
|
13270
13375
|
}
|
|
13271
13376
|
triggerRun(scheduleId, projectId) {
|
|
13272
|
-
|
|
13273
|
-
|
|
13274
|
-
|
|
13275
|
-
|
|
13276
|
-
|
|
13277
|
-
|
|
13278
|
-
|
|
13279
|
-
|
|
13280
|
-
|
|
13281
|
-
|
|
13282
|
-
|
|
13283
|
-
|
|
13284
|
-
|
|
13285
|
-
|
|
13286
|
-
|
|
13287
|
-
|
|
13288
|
-
|
|
13289
|
-
|
|
13290
|
-
|
|
13291
|
-
|
|
13292
|
-
|
|
13293
|
-
|
|
13294
|
-
|
|
13377
|
+
try {
|
|
13378
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
13379
|
+
const currentSchedule = this.db.select().from(schedules).where(eq20(schedules.id, scheduleId)).get();
|
|
13380
|
+
if (!currentSchedule || currentSchedule.enabled !== 1) {
|
|
13381
|
+
log4.warn("schedule.stale", { scheduleId, projectId, msg: "schedule no longer exists or is disabled" });
|
|
13382
|
+
this.remove(projectId);
|
|
13383
|
+
return;
|
|
13384
|
+
}
|
|
13385
|
+
const task = this.tasks.get(projectId);
|
|
13386
|
+
const nextRunAt = task?.getNextRun()?.toISOString() ?? null;
|
|
13387
|
+
const project = this.db.select().from(projects).where(eq20(projects.id, projectId)).get();
|
|
13388
|
+
if (!project) {
|
|
13389
|
+
log4.error("project.not-found", { projectId, msg: "skipping scheduled run" });
|
|
13390
|
+
this.remove(projectId);
|
|
13391
|
+
return;
|
|
13392
|
+
}
|
|
13393
|
+
const queueResult = queueRunIfProjectIdle(this.db, {
|
|
13394
|
+
createdAt: now,
|
|
13395
|
+
kind: "answer-visibility",
|
|
13396
|
+
projectId,
|
|
13397
|
+
trigger: "scheduled"
|
|
13398
|
+
});
|
|
13399
|
+
if (queueResult.conflict) {
|
|
13400
|
+
log4.info("run.skipped-active", { projectName: project.name, activeRunId: queueResult.activeRunId });
|
|
13401
|
+
this.db.update(schedules).set({
|
|
13402
|
+
nextRunAt,
|
|
13403
|
+
updatedAt: now
|
|
13404
|
+
}).where(eq20(schedules.id, currentSchedule.id)).run();
|
|
13405
|
+
return;
|
|
13406
|
+
}
|
|
13407
|
+
const runId = queueResult.runId;
|
|
13295
13408
|
this.db.update(schedules).set({
|
|
13409
|
+
lastRunAt: now,
|
|
13296
13410
|
nextRunAt,
|
|
13297
13411
|
updatedAt: now
|
|
13298
13412
|
}).where(eq20(schedules.id, currentSchedule.id)).run();
|
|
13299
|
-
|
|
13413
|
+
const scheduleProviders = JSON.parse(currentSchedule.providers);
|
|
13414
|
+
const providers = scheduleProviders.length > 0 ? scheduleProviders : void 0;
|
|
13415
|
+
log4.info("run.triggered", { runId, projectName: project.name, providers: providers ?? "all" });
|
|
13416
|
+
this.callbacks.onRunCreated(runId, projectId, providers);
|
|
13417
|
+
} catch (err) {
|
|
13418
|
+
log4.error("trigger.error", { scheduleId, projectId, error: err instanceof Error ? err.message : String(err) });
|
|
13300
13419
|
}
|
|
13301
|
-
const runId = queueResult.runId;
|
|
13302
|
-
this.db.update(schedules).set({
|
|
13303
|
-
lastRunAt: now,
|
|
13304
|
-
nextRunAt,
|
|
13305
|
-
updatedAt: now
|
|
13306
|
-
}).where(eq20(schedules.id, currentSchedule.id)).run();
|
|
13307
|
-
const scheduleProviders = JSON.parse(currentSchedule.providers);
|
|
13308
|
-
const providers = scheduleProviders.length > 0 ? scheduleProviders : void 0;
|
|
13309
|
-
log4.info("run.triggered", { runId, projectName: project.name, providers: providers ?? "all" });
|
|
13310
|
-
this.callbacks.onRunCreated(runId, projectId, providers);
|
|
13311
13420
|
}
|
|
13312
13421
|
};
|
|
13313
13422
|
|
package/dist/cli.js
CHANGED
|
@@ -26,7 +26,7 @@ import {
|
|
|
26
26
|
setGoogleAuthConfig,
|
|
27
27
|
showFirstRunNotice,
|
|
28
28
|
trackEvent
|
|
29
|
-
} from "./chunk-
|
|
29
|
+
} from "./chunk-PZKK53EX.js";
|
|
30
30
|
|
|
31
31
|
// src/cli.ts
|
|
32
32
|
import { pathToFileURL } from "url";
|
|
@@ -667,6 +667,12 @@ var ApiClient = class {
|
|
|
667
667
|
async gaCoverage(project) {
|
|
668
668
|
return this.request("GET", `/projects/${encodeURIComponent(project)}/ga/coverage`);
|
|
669
669
|
}
|
|
670
|
+
async gaAiReferralHistory(project) {
|
|
671
|
+
return this.request("GET", `/projects/${encodeURIComponent(project)}/ga/ai-referral-history`);
|
|
672
|
+
}
|
|
673
|
+
async gaSessionHistory(project) {
|
|
674
|
+
return this.request("GET", `/projects/${encodeURIComponent(project)}/ga/session-history`);
|
|
675
|
+
}
|
|
670
676
|
async wordpressConnect(project, body) {
|
|
671
677
|
return this.request("POST", `/projects/${encodeURIComponent(project)}/wordpress/connect`, body);
|
|
672
678
|
}
|
|
@@ -1516,6 +1522,29 @@ async function gaTraffic(project, opts) {
|
|
|
1516
1522
|
Last synced: ${result.lastSyncedAt}`);
|
|
1517
1523
|
}
|
|
1518
1524
|
}
|
|
1525
|
+
async function gaAiReferralHistory(project, format) {
|
|
1526
|
+
const client = getClient3();
|
|
1527
|
+
const result = await client.gaAiReferralHistory(project);
|
|
1528
|
+
if (format === "json") {
|
|
1529
|
+
console.log(JSON.stringify(result, null, 2));
|
|
1530
|
+
return;
|
|
1531
|
+
}
|
|
1532
|
+
if (result.length === 0) {
|
|
1533
|
+
console.log('No AI referral history. Run "canonry ga sync <project>" first.');
|
|
1534
|
+
return;
|
|
1535
|
+
}
|
|
1536
|
+
const dateWidth = 12;
|
|
1537
|
+
const sourceWidth = Math.min(30, Math.max(10, ...result.map((r) => r.source.length)));
|
|
1538
|
+
console.log(`GA4 AI Referral History for "${project}":
|
|
1539
|
+
`);
|
|
1540
|
+
console.log(` ${"DATE".padEnd(dateWidth)} ${"SOURCE".padEnd(sourceWidth)} ${"SESSIONS".padEnd(10)}${"USERS".padEnd(8)}`);
|
|
1541
|
+
console.log(` ${"\u2500".repeat(dateWidth)} ${"\u2500".repeat(sourceWidth)} ${"\u2500".repeat(10)}${"\u2500".repeat(8)}`);
|
|
1542
|
+
for (const row of result) {
|
|
1543
|
+
console.log(
|
|
1544
|
+
` ${row.date.padEnd(dateWidth)} ${row.source.padEnd(sourceWidth)} ${String(row.sessions).padEnd(10)}${String(row.users).padEnd(8)}`
|
|
1545
|
+
);
|
|
1546
|
+
}
|
|
1547
|
+
}
|
|
1519
1548
|
async function gaCoverage(project, format) {
|
|
1520
1549
|
const client = getClient3();
|
|
1521
1550
|
const result = await client.gaCoverage(project);
|
|
@@ -1620,14 +1649,22 @@ var GA_CLI_COMMANDS = [
|
|
|
1620
1649
|
await gaCoverage(project, input.format);
|
|
1621
1650
|
}
|
|
1622
1651
|
},
|
|
1652
|
+
{
|
|
1653
|
+
path: ["ga", "ai-referral-history"],
|
|
1654
|
+
usage: "canonry ga ai-referral-history <project> [--format json]",
|
|
1655
|
+
run: async (input) => {
|
|
1656
|
+
const project = requireProject(input, "ga.ai-referral-history", "canonry ga ai-referral-history <project> [--format json]");
|
|
1657
|
+
await gaAiReferralHistory(project, input.format);
|
|
1658
|
+
}
|
|
1659
|
+
},
|
|
1623
1660
|
{
|
|
1624
1661
|
path: ["ga"],
|
|
1625
|
-
usage: "canonry ga <connect|disconnect|status|sync|traffic|coverage> <project> [args]",
|
|
1662
|
+
usage: "canonry ga <connect|disconnect|status|sync|traffic|coverage|ai-referral-history> <project> [args]",
|
|
1626
1663
|
run: async (input) => {
|
|
1627
1664
|
unknownSubcommand(input.positionals[0], {
|
|
1628
1665
|
command: "ga",
|
|
1629
|
-
usage: "canonry ga <connect|disconnect|status|sync|traffic|coverage> <project> [args]",
|
|
1630
|
-
available: ["connect", "disconnect", "status", "sync", "traffic", "coverage"]
|
|
1666
|
+
usage: "canonry ga <connect|disconnect|status|sync|traffic|coverage|ai-referral-history> <project> [args]",
|
|
1667
|
+
available: ["connect", "disconnect", "status", "sync", "traffic", "coverage", "ai-referral-history"]
|
|
1631
1668
|
});
|
|
1632
1669
|
}
|
|
1633
1670
|
}
|
|
@@ -2597,6 +2634,7 @@ async function importKeywords(project, filePath, format) {
|
|
|
2597
2634
|
throw new CliError({
|
|
2598
2635
|
code: "KEYWORD_IMPORT_FILE_NOT_FOUND",
|
|
2599
2636
|
message: `File not found: ${filePath}`,
|
|
2637
|
+
displayMessage: `Error: file not found: ${filePath}`,
|
|
2600
2638
|
details: {
|
|
2601
2639
|
project,
|
|
2602
2640
|
filePath
|
|
@@ -4818,6 +4856,7 @@ var envSchema = z.object({
|
|
|
4818
4856
|
WORKER_PORT: z.coerce.number().int().positive().default(3001),
|
|
4819
4857
|
WEB_PORT: z.coerce.number().int().positive().default(4173),
|
|
4820
4858
|
BOOTSTRAP_SECRET: z.string().default("change-me"),
|
|
4859
|
+
CANONRY_BASE_PATH: z.string().default("/"),
|
|
4821
4860
|
// Gemini
|
|
4822
4861
|
GEMINI_API_KEY: z.string().optional(),
|
|
4823
4862
|
GEMINI_MODEL: z.string().optional(),
|
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.36.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,18 +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-config": "0.0.0",
|
|
58
59
|
"@ainyc/canonry-contracts": "0.0.0",
|
|
59
60
|
"@ainyc/canonry-integration-bing": "0.0.0",
|
|
60
61
|
"@ainyc/canonry-db": "0.0.0",
|
|
61
62
|
"@ainyc/canonry-integration-google": "0.0.0",
|
|
62
63
|
"@ainyc/canonry-integration-wordpress": "0.0.0",
|
|
63
|
-
"@ainyc/canonry-
|
|
64
|
+
"@ainyc/canonry-provider-cdp": "0.0.0",
|
|
64
65
|
"@ainyc/canonry-provider-claude": "0.0.0",
|
|
65
|
-
"@ainyc/canonry-provider-
|
|
66
|
+
"@ainyc/canonry-provider-openai": "0.0.0",
|
|
66
67
|
"@ainyc/canonry-provider-gemini": "0.0.0",
|
|
67
68
|
"@ainyc/canonry-provider-perplexity": "0.0.0",
|
|
68
|
-
"@ainyc/canonry-provider-
|
|
69
|
-
"@ainyc/canonry-provider-cdp": "0.0.0"
|
|
69
|
+
"@ainyc/canonry-provider-local": "0.0.0"
|
|
70
70
|
},
|
|
71
71
|
"scripts": {
|
|
72
72
|
"build": "tsup && tsx build-web.ts",
|