@ainyc/canonry 2.2.1 → 2.2.3

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/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-J73csS93.js"></script>
15
+ <script type="module" crossorigin src="./assets/index-Bmnz-wvT.js"></script>
16
16
  <link rel="stylesheet" crossorigin href="./assets/index-yF1fs-OW.css">
17
17
  </head>
18
18
  <body>
@@ -292,6 +292,11 @@ function usageError(displayMessage, options) {
292
292
  details: options?.details
293
293
  });
294
294
  }
295
+ function isEndpointMissing(err) {
296
+ if (!(err instanceof CliError)) return false;
297
+ const status = err.details?.httpStatus;
298
+ return status === 404 || status === 405;
299
+ }
295
300
  function printCliError(err, format) {
296
301
  if (format === "json") {
297
302
  if (err instanceof CliError) {
@@ -987,6 +992,13 @@ var querySnapshotDtoSchema = z8.object({
987
992
  location: z8.string().nullable().optional(),
988
993
  createdAt: z8.string()
989
994
  });
995
+ var runDetailDtoSchema = runDtoSchema.extend({
996
+ snapshots: z8.array(querySnapshotDtoSchema).optional()
997
+ });
998
+ var latestProjectRunDtoSchema = z8.object({
999
+ totalRuns: z8.number().int().nonnegative(),
1000
+ run: runDetailDtoSchema.nullable()
1001
+ });
990
1002
  var auditLogEntrySchema = z8.object({
991
1003
  id: z8.string(),
992
1004
  projectId: z8.string().nullable().optional(),
@@ -1996,7 +2008,7 @@ async function competitorRoutes(app) {
1996
2008
 
1997
2009
  // ../api-routes/src/runs.ts
1998
2010
  import crypto8 from "crypto";
1999
- import { eq as eq7, asc, desc } from "drizzle-orm";
2011
+ import { eq as eq7, asc, desc, sql as sql2 } from "drizzle-orm";
2000
2012
 
2001
2013
  // ../api-routes/src/run-queue.ts
2002
2014
  import crypto7 from "crypto";
@@ -2136,6 +2148,19 @@ async function runRoutes(app, opts) {
2136
2148
  const rows = limit == null ? app.db.select().from(runs).where(eq7(runs.projectId, project.id)).orderBy(asc(runs.createdAt)).all() : app.db.select().from(runs).where(eq7(runs.projectId, project.id)).orderBy(desc(runs.createdAt)).limit(limit).all().reverse();
2137
2149
  return reply.send(rows.map(formatRun));
2138
2150
  });
2151
+ app.get("/projects/:name/runs/latest", async (request, reply) => {
2152
+ const project = resolveProject(app.db, request.params.name);
2153
+ const countRow = app.db.select({ count: sql2`count(*)` }).from(runs).where(eq7(runs.projectId, project.id)).get();
2154
+ const totalRuns = countRow?.count ?? 0;
2155
+ const latestRun = app.db.select().from(runs).where(eq7(runs.projectId, project.id)).orderBy(desc(runs.createdAt)).limit(1).get();
2156
+ if (!latestRun) {
2157
+ return reply.send({ totalRuns: 0, run: null });
2158
+ }
2159
+ return reply.send({
2160
+ totalRuns,
2161
+ run: loadRunDetail(app, latestRun)
2162
+ });
2163
+ });
2139
2164
  app.get("/runs", async (_request, reply) => {
2140
2165
  const rows = app.db.select().from(runs).all();
2141
2166
  return reply.send(rows.map(formatRun));
@@ -2224,55 +2249,7 @@ async function runRoutes(app, opts) {
2224
2249
  app.get("/runs/:id", async (request, reply) => {
2225
2250
  const run = app.db.select().from(runs).where(eq7(runs.id, request.params.id)).get();
2226
2251
  if (!run) throw notFound("Run", request.params.id);
2227
- const project = app.db.select({
2228
- displayName: projects.displayName,
2229
- canonicalDomain: projects.canonicalDomain,
2230
- ownedDomains: projects.ownedDomains
2231
- }).from(projects).where(eq7(projects.id, run.projectId)).get();
2232
- const snapshots = app.db.select({
2233
- id: querySnapshots.id,
2234
- runId: querySnapshots.runId,
2235
- keywordId: querySnapshots.keywordId,
2236
- keyword: keywords.keyword,
2237
- provider: querySnapshots.provider,
2238
- model: querySnapshots.model,
2239
- citationState: querySnapshots.citationState,
2240
- answerMentioned: querySnapshots.answerMentioned,
2241
- answerText: querySnapshots.answerText,
2242
- citedDomains: querySnapshots.citedDomains,
2243
- competitorOverlap: querySnapshots.competitorOverlap,
2244
- recommendedCompetitors: querySnapshots.recommendedCompetitors,
2245
- location: querySnapshots.location,
2246
- rawResponse: querySnapshots.rawResponse,
2247
- createdAt: querySnapshots.createdAt
2248
- }).from(querySnapshots).leftJoin(keywords, eq7(querySnapshots.keywordId, keywords.id)).where(eq7(querySnapshots.runId, run.id)).all();
2249
- return reply.send({
2250
- ...formatRun(run),
2251
- snapshots: snapshots.map((s) => {
2252
- const rawParsed = parseSnapshotRawResponse(s.rawResponse);
2253
- const answerMentioned = project ? resolveSnapshotAnswerMentioned(s, project) : s.answerMentioned ?? false;
2254
- return {
2255
- id: s.id,
2256
- runId: s.runId,
2257
- keywordId: s.keywordId,
2258
- keyword: s.keyword,
2259
- provider: s.provider,
2260
- citationState: s.citationState,
2261
- answerMentioned,
2262
- visibilityState: project ? resolveSnapshotVisibilityState(s, project) : answerMentioned ? "visible" : "not-visible",
2263
- answerText: s.answerText,
2264
- citedDomains: parseJsonColumn(s.citedDomains, []),
2265
- competitorOverlap: parseJsonColumn(s.competitorOverlap, []),
2266
- recommendedCompetitors: parseJsonColumn(s.recommendedCompetitors, []),
2267
- matchedTerms: project ? resolveSnapshotMatchedTerms(s, project) : [],
2268
- model: s.model ?? rawParsed.model,
2269
- location: s.location,
2270
- groundingSources: rawParsed.groundingSources,
2271
- searchQueries: rawParsed.searchQueries,
2272
- createdAt: s.createdAt
2273
- };
2274
- })
2275
- });
2252
+ return reply.send(loadRunDetail(app, run));
2276
2253
  });
2277
2254
  }
2278
2255
  function formatRun(row) {
@@ -2297,6 +2274,57 @@ function parseSnapshotRawResponse(raw) {
2297
2274
  model: parsed.model ?? null
2298
2275
  };
2299
2276
  }
2277
+ function loadRunDetail(app, run) {
2278
+ const project = app.db.select({
2279
+ displayName: projects.displayName,
2280
+ canonicalDomain: projects.canonicalDomain,
2281
+ ownedDomains: projects.ownedDomains
2282
+ }).from(projects).where(eq7(projects.id, run.projectId)).get();
2283
+ const snapshots = app.db.select({
2284
+ id: querySnapshots.id,
2285
+ runId: querySnapshots.runId,
2286
+ keywordId: querySnapshots.keywordId,
2287
+ keyword: keywords.keyword,
2288
+ provider: querySnapshots.provider,
2289
+ model: querySnapshots.model,
2290
+ citationState: querySnapshots.citationState,
2291
+ answerMentioned: querySnapshots.answerMentioned,
2292
+ answerText: querySnapshots.answerText,
2293
+ citedDomains: querySnapshots.citedDomains,
2294
+ competitorOverlap: querySnapshots.competitorOverlap,
2295
+ recommendedCompetitors: querySnapshots.recommendedCompetitors,
2296
+ location: querySnapshots.location,
2297
+ rawResponse: querySnapshots.rawResponse,
2298
+ createdAt: querySnapshots.createdAt
2299
+ }).from(querySnapshots).leftJoin(keywords, eq7(querySnapshots.keywordId, keywords.id)).where(eq7(querySnapshots.runId, run.id)).all();
2300
+ return {
2301
+ ...formatRun(run),
2302
+ snapshots: snapshots.map((s) => {
2303
+ const rawParsed = parseSnapshotRawResponse(s.rawResponse);
2304
+ const answerMentioned = project ? resolveSnapshotAnswerMentioned(s, project) : s.answerMentioned ?? false;
2305
+ return {
2306
+ id: s.id,
2307
+ runId: s.runId,
2308
+ keywordId: s.keywordId,
2309
+ keyword: s.keyword,
2310
+ provider: s.provider,
2311
+ citationState: s.citationState,
2312
+ answerMentioned,
2313
+ visibilityState: project ? resolveSnapshotVisibilityState(s, project) : answerMentioned ? "visible" : "not-visible",
2314
+ answerText: s.answerText,
2315
+ citedDomains: parseJsonColumn(s.citedDomains, []),
2316
+ competitorOverlap: parseJsonColumn(s.competitorOverlap, []),
2317
+ recommendedCompetitors: parseJsonColumn(s.recommendedCompetitors, []),
2318
+ matchedTerms: project ? resolveSnapshotMatchedTerms(s, project) : [],
2319
+ model: s.model ?? rawParsed.model,
2320
+ location: s.location,
2321
+ groundingSources: rawParsed.groundingSources,
2322
+ searchQueries: rawParsed.searchQueries,
2323
+ createdAt: s.createdAt
2324
+ };
2325
+ })
2326
+ };
2327
+ }
2300
2328
 
2301
2329
  // ../api-routes/src/apply.ts
2302
2330
  import crypto10 from "crypto";
@@ -3930,6 +3958,16 @@ var routeCatalog = [
3930
3958
  200: { description: "Runs returned." }
3931
3959
  }
3932
3960
  },
3961
+ {
3962
+ method: "get",
3963
+ path: "/api/v1/projects/{name}/runs/latest",
3964
+ summary: "Get the latest project run",
3965
+ tags: ["runs"],
3966
+ parameters: [nameParameter],
3967
+ responses: {
3968
+ 200: { description: "Latest run returned." }
3969
+ }
3970
+ },
3933
3971
  {
3934
3972
  method: "get",
3935
3973
  path: "/api/v1/runs",
@@ -5918,10 +5956,9 @@ async function snapshotRoutes(app, opts) {
5918
5956
 
5919
5957
  // ../api-routes/src/telemetry.ts
5920
5958
  async function telemetryRoutes(app, opts) {
5921
- app.get("/telemetry", async (_request, reply) => {
5959
+ app.get("/telemetry", async () => {
5922
5960
  if (!opts.getTelemetryStatus) {
5923
- const err = notImplemented("Telemetry status is not available in this deployment");
5924
- return reply.status(err.statusCode).send(err.toJSON());
5961
+ throw notImplemented("Telemetry status is not available in this deployment");
5925
5962
  }
5926
5963
  const status = opts.getTelemetryStatus();
5927
5964
  return {
@@ -5929,15 +5966,13 @@ async function telemetryRoutes(app, opts) {
5929
5966
  anonymousId: status.anonymousId ? status.anonymousId.slice(0, 8) + "..." : void 0
5930
5967
  };
5931
5968
  });
5932
- app.put("/telemetry", async (request, reply) => {
5969
+ app.put("/telemetry", async (request) => {
5933
5970
  if (!opts.setTelemetryEnabled) {
5934
- const err = notImplemented("Telemetry configuration is not available in this deployment");
5935
- return reply.status(err.statusCode).send(err.toJSON());
5971
+ throw notImplemented("Telemetry configuration is not available in this deployment");
5936
5972
  }
5937
5973
  const { enabled } = request.body ?? {};
5938
5974
  if (typeof enabled !== "boolean") {
5939
- const err = validationError("enabled (boolean) is required");
5940
- return reply.status(err.statusCode).send(err.toJSON());
5975
+ throw validationError("enabled (boolean) is required");
5941
5976
  }
5942
5977
  opts.setTelemetryEnabled(enabled);
5943
5978
  const status = opts.getTelemetryStatus?.();
@@ -6190,7 +6225,7 @@ function formatNotification(row) {
6190
6225
 
6191
6226
  // ../api-routes/src/google.ts
6192
6227
  import crypto14 from "crypto";
6193
- import { eq as eq14, and as and3, desc as desc5, sql as sql2 } from "drizzle-orm";
6228
+ import { eq as eq14, and as and3, desc as desc5, sql as sql3 } from "drizzle-orm";
6194
6229
 
6195
6230
  // ../integration-google/src/constants.ts
6196
6231
  var GOOGLE_AUTH_URL = "https://accounts.google.com/o/oauth2/v2/auth";
@@ -7313,11 +7348,11 @@ async function googleRoutes(app, opts) {
7313
7348
  const { startDate, endDate, query, page, limit } = request.query;
7314
7349
  const cutoffDate = !startDate ? windowCutoff(parseWindow(request.query.window))?.slice(0, 10) ?? null : null;
7315
7350
  const conditions = [eq14(gscSearchData.projectId, project.id)];
7316
- if (startDate) conditions.push(sql2`${gscSearchData.date} >= ${startDate}`);
7317
- else if (cutoffDate) conditions.push(sql2`${gscSearchData.date} >= ${cutoffDate}`);
7318
- if (endDate) conditions.push(sql2`${gscSearchData.date} <= ${endDate}`);
7319
- if (query) conditions.push(sql2`${gscSearchData.query} LIKE ${"%" + query + "%"}`);
7320
- if (page) conditions.push(sql2`${gscSearchData.page} LIKE ${"%" + page + "%"}`);
7351
+ if (startDate) conditions.push(sql3`${gscSearchData.date} >= ${startDate}`);
7352
+ else if (cutoffDate) conditions.push(sql3`${gscSearchData.date} >= ${cutoffDate}`);
7353
+ if (endDate) conditions.push(sql3`${gscSearchData.date} <= ${endDate}`);
7354
+ if (query) conditions.push(sql3`${gscSearchData.query} LIKE ${"%" + query + "%"}`);
7355
+ if (page) conditions.push(sql3`${gscSearchData.page} LIKE ${"%" + page + "%"}`);
7321
7356
  const rows = app.db.select().from(gscSearchData).where(and3(...conditions)).orderBy(desc5(gscSearchData.date)).limit(parseInt(limit ?? "500", 10)).all();
7322
7357
  return rows.map((r) => ({
7323
7358
  date: r.date,
@@ -8543,7 +8578,7 @@ async function cdpRoutes(app, opts) {
8543
8578
 
8544
8579
  // ../api-routes/src/ga.ts
8545
8580
  import crypto16 from "crypto";
8546
- import { eq as eq17, desc as desc7, and as and6, sql as sql3 } from "drizzle-orm";
8581
+ import { eq as eq17, desc as desc7, and as and6, sql as sql4 } from "drizzle-orm";
8547
8582
  function gaLog(level, action, ctx) {
8548
8583
  const entry = { ts: (/* @__PURE__ */ new Date()).toISOString(), level, module: "GA4Routes", action, ...ctx };
8549
8584
  const stream = level === "error" ? process.stderr : process.stdout;
@@ -8785,8 +8820,8 @@ async function ga4Routes(app, opts) {
8785
8820
  tx.delete(gaTrafficSnapshots).where(
8786
8821
  and6(
8787
8822
  eq17(gaTrafficSnapshots.projectId, project.id),
8788
- sql3`${gaTrafficSnapshots.date} >= ${summary.periodStart}`,
8789
- sql3`${gaTrafficSnapshots.date} <= ${summary.periodEnd}`
8823
+ sql4`${gaTrafficSnapshots.date} >= ${summary.periodStart}`,
8824
+ sql4`${gaTrafficSnapshots.date} <= ${summary.periodEnd}`
8790
8825
  )
8791
8826
  ).run();
8792
8827
  for (const row of rows) {
@@ -8807,8 +8842,8 @@ async function ga4Routes(app, opts) {
8807
8842
  tx.delete(gaAiReferrals).where(
8808
8843
  and6(
8809
8844
  eq17(gaAiReferrals.projectId, project.id),
8810
- sql3`${gaAiReferrals.date} >= ${summary.periodStart}`,
8811
- sql3`${gaAiReferrals.date} <= ${summary.periodEnd}`
8845
+ sql4`${gaAiReferrals.date} >= ${summary.periodStart}`,
8846
+ sql4`${gaAiReferrals.date} <= ${summary.periodEnd}`
8812
8847
  )
8813
8848
  ).run();
8814
8849
  for (const row of aiReferrals) {
@@ -8830,8 +8865,8 @@ async function ga4Routes(app, opts) {
8830
8865
  tx.delete(gaSocialReferrals).where(
8831
8866
  and6(
8832
8867
  eq17(gaSocialReferrals.projectId, project.id),
8833
- sql3`${gaSocialReferrals.date} >= ${summary.periodStart}`,
8834
- sql3`${gaSocialReferrals.date} <= ${summary.periodEnd}`
8868
+ sql4`${gaSocialReferrals.date} >= ${summary.periodStart}`,
8869
+ sql4`${gaSocialReferrals.date} <= ${summary.periodEnd}`
8835
8870
  )
8836
8871
  ).run();
8837
8872
  for (const row of socialReferrals) {
@@ -8900,15 +8935,15 @@ async function ga4Routes(app, opts) {
8900
8935
  const cutoff = windowCutoff(window);
8901
8936
  const cutoffDate = cutoff?.slice(0, 10) ?? null;
8902
8937
  const snapshotConditions = [eq17(gaTrafficSnapshots.projectId, project.id)];
8903
- if (cutoffDate) snapshotConditions.push(sql3`${gaTrafficSnapshots.date} >= ${cutoffDate}`);
8938
+ if (cutoffDate) snapshotConditions.push(sql4`${gaTrafficSnapshots.date} >= ${cutoffDate}`);
8904
8939
  const aiConditions = [eq17(gaAiReferrals.projectId, project.id)];
8905
- if (cutoffDate) aiConditions.push(sql3`${gaAiReferrals.date} >= ${cutoffDate}`);
8940
+ if (cutoffDate) aiConditions.push(sql4`${gaAiReferrals.date} >= ${cutoffDate}`);
8906
8941
  const socialConditions = [eq17(gaSocialReferrals.projectId, project.id)];
8907
- if (cutoffDate) socialConditions.push(sql3`${gaSocialReferrals.date} >= ${cutoffDate}`);
8942
+ if (cutoffDate) socialConditions.push(sql4`${gaSocialReferrals.date} >= ${cutoffDate}`);
8908
8943
  const summaryRow = cutoffDate ? app.db.select({
8909
- totalSessions: sql3`COALESCE(SUM(${gaTrafficSnapshots.sessions}), 0)`,
8910
- totalOrganicSessions: sql3`COALESCE(SUM(${gaTrafficSnapshots.organicSessions}), 0)`,
8911
- totalUsers: sql3`COALESCE(SUM(${gaTrafficSnapshots.users}), 0)`
8944
+ totalSessions: sql4`COALESCE(SUM(${gaTrafficSnapshots.sessions}), 0)`,
8945
+ totalOrganicSessions: sql4`COALESCE(SUM(${gaTrafficSnapshots.organicSessions}), 0)`,
8946
+ totalUsers: sql4`COALESCE(SUM(${gaTrafficSnapshots.users}), 0)`
8912
8947
  }).from(gaTrafficSnapshots).where(and6(...snapshotConditions)).get() : app.db.select({
8913
8948
  totalSessions: gaTrafficSummaries.totalSessions,
8914
8949
  totalOrganicSessions: gaTrafficSummaries.totalOrganicSessions,
@@ -8920,27 +8955,27 @@ async function ga4Routes(app, opts) {
8920
8955
  }).from(gaTrafficSummaries).where(eq17(gaTrafficSummaries.projectId, project.id)).get();
8921
8956
  const rows = app.db.select({
8922
8957
  landingPage: gaTrafficSnapshots.landingPage,
8923
- sessions: sql3`SUM(${gaTrafficSnapshots.sessions})`,
8924
- organicSessions: sql3`SUM(${gaTrafficSnapshots.organicSessions})`,
8925
- users: sql3`SUM(${gaTrafficSnapshots.users})`
8926
- }).from(gaTrafficSnapshots).where(and6(...snapshotConditions)).groupBy(gaTrafficSnapshots.landingPage).orderBy(sql3`SUM(${gaTrafficSnapshots.sessions}) DESC`).limit(limit).all();
8958
+ sessions: sql4`SUM(${gaTrafficSnapshots.sessions})`,
8959
+ organicSessions: sql4`SUM(${gaTrafficSnapshots.organicSessions})`,
8960
+ users: sql4`SUM(${gaTrafficSnapshots.users})`
8961
+ }).from(gaTrafficSnapshots).where(and6(...snapshotConditions)).groupBy(gaTrafficSnapshots.landingPage).orderBy(sql4`SUM(${gaTrafficSnapshots.sessions}) DESC`).limit(limit).all();
8927
8962
  const aiReferrals = app.db.select({
8928
8963
  source: gaAiReferrals.source,
8929
8964
  medium: gaAiReferrals.medium,
8930
8965
  sourceDimension: gaAiReferrals.sourceDimension,
8931
- sessions: sql3`SUM(${gaAiReferrals.sessions})`,
8932
- users: sql3`SUM(${gaAiReferrals.users})`
8933
- }).from(gaAiReferrals).where(and6(...aiConditions)).groupBy(gaAiReferrals.source, gaAiReferrals.medium, gaAiReferrals.sourceDimension).orderBy(sql3`SUM(${gaAiReferrals.sessions}) DESC`).all();
8966
+ sessions: sql4`SUM(${gaAiReferrals.sessions})`,
8967
+ users: sql4`SUM(${gaAiReferrals.users})`
8968
+ }).from(gaAiReferrals).where(and6(...aiConditions)).groupBy(gaAiReferrals.source, gaAiReferrals.medium, gaAiReferrals.sourceDimension).orderBy(sql4`SUM(${gaAiReferrals.sessions}) DESC`).all();
8934
8969
  const aiDeduped = app.db.select({
8935
- sessions: sql3`SUM(max_sessions)`,
8936
- users: sql3`SUM(max_users)`
8970
+ sessions: sql4`SUM(max_sessions)`,
8971
+ users: sql4`SUM(max_users)`
8937
8972
  }).from(
8938
- sql3`(
8973
+ sql4`(
8939
8974
  SELECT date, source, medium,
8940
8975
  MAX(sessions) AS max_sessions,
8941
8976
  MAX(users) AS max_users
8942
8977
  FROM ga_ai_referrals
8943
- WHERE project_id = ${project.id}${cutoffDate ? sql3` AND date >= ${cutoffDate}` : sql3``}
8978
+ WHERE project_id = ${project.id}${cutoffDate ? sql4` AND date >= ${cutoffDate}` : sql4``}
8944
8979
  GROUP BY date, source, medium
8945
8980
  )`
8946
8981
  ).get();
@@ -8948,12 +8983,12 @@ async function ga4Routes(app, opts) {
8948
8983
  source: gaSocialReferrals.source,
8949
8984
  medium: gaSocialReferrals.medium,
8950
8985
  channelGroup: gaSocialReferrals.channelGroup,
8951
- sessions: sql3`SUM(${gaSocialReferrals.sessions})`,
8952
- users: sql3`SUM(${gaSocialReferrals.users})`
8953
- }).from(gaSocialReferrals).where(and6(...socialConditions)).groupBy(gaSocialReferrals.source, gaSocialReferrals.medium, gaSocialReferrals.channelGroup).orderBy(sql3`SUM(${gaSocialReferrals.sessions}) DESC`).all();
8986
+ sessions: sql4`SUM(${gaSocialReferrals.sessions})`,
8987
+ users: sql4`SUM(${gaSocialReferrals.users})`
8988
+ }).from(gaSocialReferrals).where(and6(...socialConditions)).groupBy(gaSocialReferrals.source, gaSocialReferrals.medium, gaSocialReferrals.channelGroup).orderBy(sql4`SUM(${gaSocialReferrals.sessions}) DESC`).all();
8954
8989
  const socialTotals = app.db.select({
8955
- sessions: sql3`SUM(${gaSocialReferrals.sessions})`,
8956
- users: sql3`SUM(${gaSocialReferrals.users})`
8990
+ sessions: sql4`SUM(${gaSocialReferrals.sessions})`,
8991
+ users: sql4`SUM(${gaSocialReferrals.users})`
8957
8992
  }).from(gaSocialReferrals).where(and6(...socialConditions)).get();
8958
8993
  const latestSync = app.db.select({ syncedAt: gaTrafficSummaries.syncedAt }).from(gaTrafficSummaries).where(eq17(gaTrafficSummaries.projectId, project.id)).orderBy(desc7(gaTrafficSummaries.syncedAt)).limit(1).get();
8959
8994
  const total = summaryRow?.totalSessions ?? 0;
@@ -9003,7 +9038,7 @@ async function ga4Routes(app, opts) {
9003
9038
  requireGa4Connection(opts, project.name, project.canonicalDomain);
9004
9039
  const cutoffDate = windowCutoff(parseWindow(request.query.window))?.slice(0, 10) ?? null;
9005
9040
  const conditions = [eq17(gaAiReferrals.projectId, project.id)];
9006
- if (cutoffDate) conditions.push(sql3`${gaAiReferrals.date} >= ${cutoffDate}`);
9041
+ if (cutoffDate) conditions.push(sql4`${gaAiReferrals.date} >= ${cutoffDate}`);
9007
9042
  const rows = app.db.select({
9008
9043
  date: gaAiReferrals.date,
9009
9044
  source: gaAiReferrals.source,
@@ -9019,7 +9054,7 @@ async function ga4Routes(app, opts) {
9019
9054
  requireGa4Connection(opts, project.name, project.canonicalDomain);
9020
9055
  const cutoffDate = windowCutoff(parseWindow(request.query.window))?.slice(0, 10) ?? null;
9021
9056
  const conditions = [eq17(gaSocialReferrals.projectId, project.id)];
9022
- if (cutoffDate) conditions.push(sql3`${gaSocialReferrals.date} >= ${cutoffDate}`);
9057
+ if (cutoffDate) conditions.push(sql4`${gaSocialReferrals.date} >= ${cutoffDate}`);
9023
9058
  const rows = app.db.select({
9024
9059
  date: gaSocialReferrals.date,
9025
9060
  source: gaSocialReferrals.source,
@@ -9040,10 +9075,10 @@ async function ga4Routes(app, opts) {
9040
9075
  d.setDate(d.getDate() - n);
9041
9076
  return fmt(d);
9042
9077
  };
9043
- const sumSocial = (from, to) => app.db.select({ sessions: sql3`COALESCE(SUM(${gaSocialReferrals.sessions}), 0)` }).from(gaSocialReferrals).where(and6(
9078
+ const sumSocial = (from, to) => app.db.select({ sessions: sql4`COALESCE(SUM(${gaSocialReferrals.sessions}), 0)` }).from(gaSocialReferrals).where(and6(
9044
9079
  eq17(gaSocialReferrals.projectId, project.id),
9045
- sql3`${gaSocialReferrals.date} >= ${from}`,
9046
- sql3`${gaSocialReferrals.date} < ${to}`
9080
+ sql4`${gaSocialReferrals.date} >= ${from}`,
9081
+ sql4`${gaSocialReferrals.date} < ${to}`
9047
9082
  )).get();
9048
9083
  const current7d = sumSocial(daysAgo2(7), fmt(today));
9049
9084
  const prev7d = sumSocial(daysAgo2(14), daysAgo2(7));
@@ -9052,19 +9087,19 @@ async function ga4Routes(app, opts) {
9052
9087
  const pct = (cur, prev) => prev === 0 ? null : Math.round((cur - prev) / prev * 100);
9053
9088
  const sourceCurrent = app.db.select({
9054
9089
  source: gaSocialReferrals.source,
9055
- sessions: sql3`SUM(${gaSocialReferrals.sessions})`
9090
+ sessions: sql4`SUM(${gaSocialReferrals.sessions})`
9056
9091
  }).from(gaSocialReferrals).where(and6(
9057
9092
  eq17(gaSocialReferrals.projectId, project.id),
9058
- sql3`${gaSocialReferrals.date} >= ${daysAgo2(7)}`,
9059
- sql3`${gaSocialReferrals.date} < ${fmt(today)}`
9093
+ sql4`${gaSocialReferrals.date} >= ${daysAgo2(7)}`,
9094
+ sql4`${gaSocialReferrals.date} < ${fmt(today)}`
9060
9095
  )).groupBy(gaSocialReferrals.source).all();
9061
9096
  const sourcePrev = app.db.select({
9062
9097
  source: gaSocialReferrals.source,
9063
- sessions: sql3`SUM(${gaSocialReferrals.sessions})`
9098
+ sessions: sql4`SUM(${gaSocialReferrals.sessions})`
9064
9099
  }).from(gaSocialReferrals).where(and6(
9065
9100
  eq17(gaSocialReferrals.projectId, project.id),
9066
- sql3`${gaSocialReferrals.date} >= ${daysAgo2(14)}`,
9067
- sql3`${gaSocialReferrals.date} < ${daysAgo2(7)}`
9101
+ sql4`${gaSocialReferrals.date} >= ${daysAgo2(14)}`,
9102
+ sql4`${gaSocialReferrals.date} < ${daysAgo2(7)}`
9068
9103
  )).groupBy(gaSocialReferrals.source).all();
9069
9104
  const prevMap = new Map(sourcePrev.map((r) => [r.source, r.sessions]));
9070
9105
  let biggestMover = null;
@@ -9103,15 +9138,15 @@ async function ga4Routes(app, opts) {
9103
9138
  return fmt(d);
9104
9139
  };
9105
9140
  const pct = (cur, prev) => prev === 0 ? null : Math.round((cur - prev) / prev * 100);
9106
- const sumTotal = (from, to) => app.db.select({ sessions: sql3`COALESCE(SUM(${gaTrafficSnapshots.sessions}), 0)` }).from(gaTrafficSnapshots).where(and6(eq17(gaTrafficSnapshots.projectId, project.id), sql3`${gaTrafficSnapshots.date} >= ${from}`, sql3`${gaTrafficSnapshots.date} < ${to}`)).get();
9107
- const sumOrganic = (from, to) => app.db.select({ sessions: sql3`COALESCE(SUM(${gaTrafficSnapshots.organicSessions}), 0)` }).from(gaTrafficSnapshots).where(and6(eq17(gaTrafficSnapshots.projectId, project.id), sql3`${gaTrafficSnapshots.date} >= ${from}`, sql3`${gaTrafficSnapshots.date} < ${to}`)).get();
9108
- const sumAi = (from, to) => app.db.select({ sessions: sql3`COALESCE(SUM(max_sessions), 0)` }).from(sql3`(
9141
+ const sumTotal = (from, to) => app.db.select({ sessions: sql4`COALESCE(SUM(${gaTrafficSnapshots.sessions}), 0)` }).from(gaTrafficSnapshots).where(and6(eq17(gaTrafficSnapshots.projectId, project.id), sql4`${gaTrafficSnapshots.date} >= ${from}`, sql4`${gaTrafficSnapshots.date} < ${to}`)).get();
9142
+ const sumOrganic = (from, to) => app.db.select({ sessions: sql4`COALESCE(SUM(${gaTrafficSnapshots.organicSessions}), 0)` }).from(gaTrafficSnapshots).where(and6(eq17(gaTrafficSnapshots.projectId, project.id), sql4`${gaTrafficSnapshots.date} >= ${from}`, sql4`${gaTrafficSnapshots.date} < ${to}`)).get();
9143
+ const sumAi = (from, to) => app.db.select({ sessions: sql4`COALESCE(SUM(max_sessions), 0)` }).from(sql4`(
9109
9144
  SELECT date, source, medium, MAX(sessions) AS max_sessions
9110
9145
  FROM ga_ai_referrals
9111
9146
  WHERE project_id = ${project.id} AND date >= ${from} AND date < ${to}
9112
9147
  GROUP BY date, source, medium
9113
9148
  )`).get();
9114
- const sumSocial = (from, to) => app.db.select({ sessions: sql3`COALESCE(SUM(${gaSocialReferrals.sessions}), 0)` }).from(gaSocialReferrals).where(and6(eq17(gaSocialReferrals.projectId, project.id), sql3`${gaSocialReferrals.date} >= ${from}`, sql3`${gaSocialReferrals.date} < ${to}`)).get();
9149
+ const sumSocial = (from, to) => app.db.select({ sessions: sql4`COALESCE(SUM(${gaSocialReferrals.sessions}), 0)` }).from(gaSocialReferrals).where(and6(eq17(gaSocialReferrals.projectId, project.id), sql4`${gaSocialReferrals.date} >= ${from}`, sql4`${gaSocialReferrals.date} < ${to}`)).get();
9115
9150
  const todayStr = fmt(today);
9116
9151
  const buildTrend = (sum) => {
9117
9152
  const c7 = sum(daysAgo2(7), todayStr)?.sessions ?? 0;
@@ -9120,18 +9155,18 @@ async function ga4Routes(app, opts) {
9120
9155
  const p30 = sum(daysAgo2(60), daysAgo2(30))?.sessions ?? 0;
9121
9156
  return { sessions7d: c7, sessionsPrev7d: p7, trend7dPct: pct(c7, p7), sessions30d: c30, sessionsPrev30d: p30, trend30dPct: pct(c30, p30) };
9122
9157
  };
9123
- const aiSourceCurrent = app.db.select({ source: sql3`source`, sessions: sql3`COALESCE(SUM(max_sessions), 0)` }).from(sql3`(
9158
+ const aiSourceCurrent = app.db.select({ source: sql4`source`, sessions: sql4`COALESCE(SUM(max_sessions), 0)` }).from(sql4`(
9124
9159
  SELECT date, source, medium, MAX(sessions) AS max_sessions
9125
9160
  FROM ga_ai_referrals
9126
9161
  WHERE project_id = ${project.id} AND date >= ${daysAgo2(7)} AND date < ${todayStr}
9127
9162
  GROUP BY date, source, medium
9128
- )`).groupBy(sql3`source`).all();
9129
- const aiSourcePrev = app.db.select({ source: sql3`source`, sessions: sql3`COALESCE(SUM(max_sessions), 0)` }).from(sql3`(
9163
+ )`).groupBy(sql4`source`).all();
9164
+ const aiSourcePrev = app.db.select({ source: sql4`source`, sessions: sql4`COALESCE(SUM(max_sessions), 0)` }).from(sql4`(
9130
9165
  SELECT date, source, medium, MAX(sessions) AS max_sessions
9131
9166
  FROM ga_ai_referrals
9132
9167
  WHERE project_id = ${project.id} AND date >= ${daysAgo2(14)} AND date < ${daysAgo2(7)}
9133
9168
  GROUP BY date, source, medium
9134
- )`).groupBy(sql3`source`).all();
9169
+ )`).groupBy(sql4`source`).all();
9135
9170
  const findBiggestMover = (current, prev) => {
9136
9171
  const prevMap = new Map(prev.map((r) => [r.source, r.sessions]));
9137
9172
  let mover = null;
@@ -9146,8 +9181,8 @@ async function ga4Routes(app, opts) {
9146
9181
  }
9147
9182
  return mover;
9148
9183
  };
9149
- const socialSourceCurrent = app.db.select({ source: gaSocialReferrals.source, sessions: sql3`SUM(${gaSocialReferrals.sessions})` }).from(gaSocialReferrals).where(and6(eq17(gaSocialReferrals.projectId, project.id), sql3`${gaSocialReferrals.date} >= ${daysAgo2(7)}`, sql3`${gaSocialReferrals.date} < ${todayStr}`)).groupBy(gaSocialReferrals.source).all();
9150
- const socialSourcePrev = app.db.select({ source: gaSocialReferrals.source, sessions: sql3`SUM(${gaSocialReferrals.sessions})` }).from(gaSocialReferrals).where(and6(eq17(gaSocialReferrals.projectId, project.id), sql3`${gaSocialReferrals.date} >= ${daysAgo2(14)}`, sql3`${gaSocialReferrals.date} < ${daysAgo2(7)}`)).groupBy(gaSocialReferrals.source).all();
9184
+ const socialSourceCurrent = app.db.select({ source: gaSocialReferrals.source, sessions: sql4`SUM(${gaSocialReferrals.sessions})` }).from(gaSocialReferrals).where(and6(eq17(gaSocialReferrals.projectId, project.id), sql4`${gaSocialReferrals.date} >= ${daysAgo2(7)}`, sql4`${gaSocialReferrals.date} < ${todayStr}`)).groupBy(gaSocialReferrals.source).all();
9185
+ const socialSourcePrev = app.db.select({ source: gaSocialReferrals.source, sessions: sql4`SUM(${gaSocialReferrals.sessions})` }).from(gaSocialReferrals).where(and6(eq17(gaSocialReferrals.projectId, project.id), sql4`${gaSocialReferrals.date} >= ${daysAgo2(14)}`, sql4`${gaSocialReferrals.date} < ${daysAgo2(7)}`)).groupBy(gaSocialReferrals.source).all();
9151
9186
  return {
9152
9187
  total: buildTrend(sumTotal),
9153
9188
  organic: buildTrend(sumOrganic),
@@ -9162,12 +9197,12 @@ async function ga4Routes(app, opts) {
9162
9197
  requireGa4Connection(opts, project.name, project.canonicalDomain);
9163
9198
  const cutoffDate = windowCutoff(parseWindow(request.query.window))?.slice(0, 10) ?? null;
9164
9199
  const conditions = [eq17(gaTrafficSnapshots.projectId, project.id)];
9165
- if (cutoffDate) conditions.push(sql3`${gaTrafficSnapshots.date} >= ${cutoffDate}`);
9200
+ if (cutoffDate) conditions.push(sql4`${gaTrafficSnapshots.date} >= ${cutoffDate}`);
9166
9201
  const rows = app.db.select({
9167
9202
  date: gaTrafficSnapshots.date,
9168
- sessions: sql3`SUM(${gaTrafficSnapshots.sessions})`,
9169
- organicSessions: sql3`SUM(${gaTrafficSnapshots.organicSessions})`,
9170
- users: sql3`SUM(${gaTrafficSnapshots.users})`
9203
+ sessions: sql4`SUM(${gaTrafficSnapshots.sessions})`,
9204
+ organicSessions: sql4`SUM(${gaTrafficSnapshots.organicSessions})`,
9205
+ users: sql4`SUM(${gaTrafficSnapshots.users})`
9171
9206
  }).from(gaTrafficSnapshots).where(and6(...conditions)).groupBy(gaTrafficSnapshots.date).orderBy(gaTrafficSnapshots.date).all();
9172
9207
  return rows.map((r) => ({
9173
9208
  date: r.date,
@@ -9181,10 +9216,10 @@ async function ga4Routes(app, opts) {
9181
9216
  requireGa4Connection(opts, project.name, project.canonicalDomain);
9182
9217
  const trafficPages = app.db.select({
9183
9218
  landingPage: gaTrafficSnapshots.landingPage,
9184
- sessions: sql3`SUM(${gaTrafficSnapshots.sessions})`,
9185
- organicSessions: sql3`SUM(${gaTrafficSnapshots.organicSessions})`,
9186
- users: sql3`SUM(${gaTrafficSnapshots.users})`
9187
- }).from(gaTrafficSnapshots).where(eq17(gaTrafficSnapshots.projectId, project.id)).groupBy(gaTrafficSnapshots.landingPage).orderBy(sql3`SUM(${gaTrafficSnapshots.sessions}) DESC`).all();
9219
+ sessions: sql4`SUM(${gaTrafficSnapshots.sessions})`,
9220
+ organicSessions: sql4`SUM(${gaTrafficSnapshots.organicSessions})`,
9221
+ users: sql4`SUM(${gaTrafficSnapshots.users})`
9222
+ }).from(gaTrafficSnapshots).where(eq17(gaTrafficSnapshots.projectId, project.id)).groupBy(gaTrafficSnapshots.landingPage).orderBy(sql4`SUM(${gaTrafficSnapshots.sessions}) DESC`).all();
9188
9223
  return {
9189
9224
  pages: trafficPages.map((r) => ({
9190
9225
  landingPage: r.landingPage,
@@ -13457,7 +13492,7 @@ import crypto18 from "crypto";
13457
13492
  import fs4 from "fs";
13458
13493
  import path5 from "path";
13459
13494
  import os4 from "os";
13460
- import { and as and7, eq as eq18, inArray as inArray3, sql as sql4 } from "drizzle-orm";
13495
+ import { and as and7, eq as eq18, inArray as inArray3, sql as sql5 } from "drizzle-orm";
13461
13496
 
13462
13497
  // src/citation-utils.ts
13463
13498
  function domainMatches(domain, canonicalDomain) {
@@ -13971,7 +14006,7 @@ var JobRunner = class {
13971
14006
  updatedAt: now
13972
14007
  }).onConflictDoUpdate({
13973
14008
  target: [usageCounters.scope, usageCounters.period, usageCounters.metric],
13974
- set: { count: sql4`${usageCounters.count} + ${count}`, updatedAt: now }
14009
+ set: { count: sql5`${usageCounters.count} + ${count}`, updatedAt: now }
13975
14010
  }).run();
13976
14011
  }
13977
14012
  flushProviderUsage(projectId, providerDispatchCounts) {
@@ -14024,7 +14059,7 @@ function getCurrentUsageDay() {
14024
14059
 
14025
14060
  // src/gsc-sync.ts
14026
14061
  import crypto19 from "crypto";
14027
- import { eq as eq19, and as and8, sql as sql5 } from "drizzle-orm";
14062
+ import { eq as eq19, and as and8, sql as sql6 } from "drizzle-orm";
14028
14063
  var log2 = createLogger("GscSync");
14029
14064
  function formatDate2(d) {
14030
14065
  return d.toISOString().split("T")[0];
@@ -14078,8 +14113,8 @@ async function executeGscSync(db, runId, projectId, opts) {
14078
14113
  db.delete(gscSearchData).where(
14079
14114
  and8(
14080
14115
  eq19(gscSearchData.projectId, projectId),
14081
- sql5`${gscSearchData.date} >= ${startDate}`,
14082
- sql5`${gscSearchData.date} <= ${endDate}`
14116
+ sql6`${gscSearchData.date} >= ${startDate}`,
14117
+ sql6`${gscSearchData.date} <= ${endDate}`
14083
14118
  )
14084
14119
  ).run();
14085
14120
  const batchSize = 500;
@@ -15023,7 +15058,7 @@ import { Type as Type2 } from "@sinclair/typebox";
15023
15058
 
15024
15059
  // src/agent/memory-store.ts
15025
15060
  import crypto22 from "crypto";
15026
- import { and as and11, desc as desc9, eq as eq23, like, sql as sql6 } from "drizzle-orm";
15061
+ import { and as and11, desc as desc9, eq as eq23, like, sql as sql7 } from "drizzle-orm";
15027
15062
  var COMPACTION_KEY_PREFIX = "compaction:";
15028
15063
  var COMPACTION_NOTES_PER_SESSION = 3;
15029
15064
  function rowToDto(row) {
@@ -15109,7 +15144,7 @@ function writeCompactionNote(db, args) {
15109
15144
  ).orderBy(desc9(agentMemory.updatedAt)).all();
15110
15145
  const stale = existing.slice(COMPACTION_NOTES_PER_SESSION).map((r) => r.id);
15111
15146
  if (stale.length > 0) {
15112
- tx.delete(agentMemory).where(sql6`${agentMemory.id} IN (${sql6.join(stale.map((s) => sql6`${s}`), sql6`, `)})`).run();
15147
+ tx.delete(agentMemory).where(sql7`${agentMemory.id} IN (${sql7.join(stale.map((s) => sql7`${s}`), sql7`, `)})`).run();
15113
15148
  }
15114
15149
  const row = tx.select().from(agentMemory).where(and11(eq23(agentMemory.projectId, args.projectId), eq23(agentMemory.key, key))).get();
15115
15150
  if (row) inserted = rowToDto(row);
@@ -16402,7 +16437,7 @@ var ApiClient = class {
16402
16437
  const msg = errorObj?.message ? String(errorObj.message) : `HTTP ${res.status}: ${res.statusText}`;
16403
16438
  const code = errorObj?.code ? String(errorObj.code) : "API_ERROR";
16404
16439
  const exitCode = res.status >= 500 ? EXIT_SYSTEM_ERROR : EXIT_USER_ERROR;
16405
- throw new CliError({ code, message: msg, exitCode });
16440
+ throw new CliError({ code, message: msg, exitCode, details: { httpStatus: res.status } });
16406
16441
  }
16407
16442
  if (res.status === 204) {
16408
16443
  return void 0;
@@ -16492,7 +16527,7 @@ var ApiClient = class {
16492
16527
  const msg = errorObj?.message ? String(errorObj.message) : `HTTP ${res.status}: ${res.statusText}`;
16493
16528
  const code = errorObj?.code ? String(errorObj.code) : "API_ERROR";
16494
16529
  const exitCode = res.status >= 500 ? EXIT_SYSTEM_ERROR : EXIT_USER_ERROR;
16495
- throw new CliError({ code, message: msg, exitCode });
16530
+ throw new CliError({ code, message: msg, exitCode, details: { httpStatus: res.status } });
16496
16531
  }
16497
16532
  return res;
16498
16533
  }
@@ -16533,6 +16568,9 @@ var ApiClient = class {
16533
16568
  const query = limit != null ? `?limit=${encodeURIComponent(String(limit))}` : "";
16534
16569
  return this.request("GET", `/projects/${encodeURIComponent(project)}/runs${query}`);
16535
16570
  }
16571
+ async getLatestRun(project) {
16572
+ return this.request("GET", `/projects/${encodeURIComponent(project)}/runs/latest`);
16573
+ }
16536
16574
  async getRun(id) {
16537
16575
  return this.request("GET", `/runs/${encodeURIComponent(id)}`);
16538
16576
  }
@@ -18422,6 +18460,7 @@ export {
18422
18460
  EXIT_SYSTEM_ERROR,
18423
18461
  CliError,
18424
18462
  usageError,
18463
+ isEndpointMissing,
18425
18464
  printCliError,
18426
18465
  providerQuotaPolicySchema,
18427
18466
  ProviderNames,