@ainyc/canonry 4.99.0 → 4.100.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.
Files changed (27) hide show
  1. package/assets/agent-workspace/skills/canonry/references/canonry-cli.md +13 -1
  2. package/assets/assets/{BacklinksPage-CuWs7I1D.js → BacklinksPage-DWZdWEm3.js} +1 -1
  3. package/assets/assets/{ChartPrimitives-bm37tprp.js → ChartPrimitives-C-ky5206.js} +1 -1
  4. package/assets/assets/ProjectPage-CpbFpi6J.js +6 -0
  5. package/assets/assets/{RunRow-DzhQl87g.js → RunRow-Bj-D4kZe.js} +1 -1
  6. package/assets/assets/{RunsPage-wOaewhbF.js → RunsPage-GktitsAV.js} +1 -1
  7. package/assets/assets/{SettingsPage-CJdhhkkE.js → SettingsPage-AsFLFDS3.js} +1 -1
  8. package/assets/assets/{TrafficPage-BJBeN5Ph.js → TrafficPage-Cf3uCkxs.js} +1 -1
  9. package/assets/assets/{TrafficSourceDetailPage-D3PmH0B1.js → TrafficSourceDetailPage-B5t7nf9C.js} +1 -1
  10. package/assets/assets/{arrow-left-BmITMI6O.js → arrow-left-CZFQuQO_.js} +1 -1
  11. package/assets/assets/{extract-error-message-BGwnyAfP.js → extract-error-message--bieZ2IX.js} +1 -1
  12. package/assets/assets/index-9hI0ODQU.css +1 -0
  13. package/assets/assets/index-Cb2h7KkD.js +210 -0
  14. package/assets/assets/{trash-2-Bd2mkP_w.js → trash-2-sKO0k75f.js} +1 -1
  15. package/assets/index.html +2 -2
  16. package/dist/{chunk-CEHD33NQ.js → chunk-74SOIAIY.js} +262 -119
  17. package/dist/{chunk-CWFTMFEU.js → chunk-ADLS64PG.js} +117 -3
  18. package/dist/{chunk-DJVUNURL.js → chunk-SNHYRSUF.js} +401 -40
  19. package/dist/{chunk-VQN74WWA.js → chunk-UVIHWF45.js} +27 -1
  20. package/dist/cli.js +110 -4
  21. package/dist/index.js +4 -4
  22. package/dist/{intelligence-service-QHBXW4KI.js → intelligence-service-3CRNJLUZ.js} +2 -2
  23. package/dist/mcp.js +2 -2
  24. package/package.json +10 -10
  25. package/assets/assets/ProjectPage-BksYe8FP.js +0 -6
  26. package/assets/assets/index-BSX6r6ju.js +0 -210
  27. package/assets/assets/index-BsZNCwg8.css +0 -1
@@ -9,7 +9,7 @@ import {
9
9
  loadConfig,
10
10
  loadConfigRaw,
11
11
  saveConfigPatch
12
- } from "./chunk-VQN74WWA.js";
12
+ } from "./chunk-UVIHWF45.js";
13
13
  import {
14
14
  CC_CACHE_DIR,
15
15
  DUCKDB_SPEC,
@@ -111,7 +111,7 @@ import {
111
111
  siteAuditPages,
112
112
  siteAuditSnapshots,
113
113
  usageCounters
114
- } from "./chunk-DJVUNURL.js";
114
+ } from "./chunk-SNHYRSUF.js";
115
115
  import {
116
116
  AGENT_MEMORY_VALUE_MAX_BYTES,
117
117
  AGENT_PROVIDER_IDS,
@@ -171,7 +171,7 @@ import {
171
171
  validationError,
172
172
  winnabilityClassLabel,
173
173
  withRetry
174
- } from "./chunk-CWFTMFEU.js";
174
+ } from "./chunk-ADLS64PG.js";
175
175
 
176
176
  // src/telemetry.ts
177
177
  import crypto from "crypto";
@@ -1473,7 +1473,8 @@ function extractWebSearchToolErrors(rawResponse) {
1473
1473
  if (!content) return [];
1474
1474
  for (const block of content) {
1475
1475
  if (block.type !== "web_search_tool_result") continue;
1476
- if (block.content === null || typeof block.content !== "object" || Array.isArray(block.content)) continue;
1476
+ if (block.content === null || typeof block.content !== "object" || Array.isArray(block.content))
1477
+ continue;
1477
1478
  const errorCode = block.content.error_code;
1478
1479
  if (typeof errorCode === "string" && errorCode.length > 0) {
1479
1480
  errors.add(errorCode);
@@ -6402,7 +6403,7 @@ function readStoredGroundingSources(rawResponse) {
6402
6403
  return result;
6403
6404
  }
6404
6405
  async function backfillInsightsCommand(project, opts) {
6405
- const { IntelligenceService: IntelligenceService2 } = await import("./intelligence-service-QHBXW4KI.js");
6406
+ const { IntelligenceService: IntelligenceService2 } = await import("./intelligence-service-3CRNJLUZ.js");
6406
6407
  const config = loadConfig();
6407
6408
  const db = createClient(config.database);
6408
6409
  migrate(db);
@@ -8019,12 +8020,13 @@ var PROVIDER_MODELS = {
8019
8020
  [LlmCapabilities.classify]: "glm-5-turbo"
8020
8021
  },
8021
8022
  [AgentProviderIds.deepinfra]: {
8022
- // DeepInfra `org/Model` slugs. GLM-5.2 drives the agent loop; the much
8023
- // cheaper DeepSeek-V4-Flash fills analyze + classify. Resolved into a
8024
- // custom openai-completions model, not pi-ai's catalog.
8023
+ // DeepInfra `org/Model` slugs. GLM-5.2 (Sonnet-class) on all three tiers
8024
+ // it undercuts the Claude tiers it replaces, so the cheaper tiers take no
8025
+ // quality hit. Resolved into a custom openai-completions model, not pi-ai's
8026
+ // catalog.
8025
8027
  [LlmCapabilities.agent]: "zai-org/GLM-5.2",
8026
- [LlmCapabilities.analyze]: "deepseek-ai/DeepSeek-V4-Flash",
8027
- [LlmCapabilities.classify]: "deepseek-ai/DeepSeek-V4-Flash"
8028
+ [LlmCapabilities.analyze]: "zai-org/GLM-5.2",
8029
+ [LlmCapabilities.classify]: "zai-org/GLM-5.2"
8028
8030
  }
8029
8031
  };
8030
8032
  var AGENT_PROVIDERS = {
@@ -8129,7 +8131,8 @@ function resolveModelForCapability(provider, capability, modelIdOverride) {
8129
8131
  const entry = AGENT_PROVIDERS[provider];
8130
8132
  const id = modelIdOverride ?? PROVIDER_MODELS[provider][capability];
8131
8133
  if (entry.openaiCompatible) {
8132
- return buildOpenAiCompatibleModel(entry, id);
8134
+ const suppressThinking = capability !== LlmCapabilities.agent;
8135
+ return buildOpenAiCompatibleModel(entry, id, suppressThinking);
8133
8136
  }
8134
8137
  const model = getModel(entry.piAiProvider, id);
8135
8138
  if (!model) {
@@ -8139,13 +8142,14 @@ function resolveModelForCapability(provider, capability, modelIdOverride) {
8139
8142
  }
8140
8143
  return model;
8141
8144
  }
8142
- function buildOpenAiCompatibleModel(entry, id) {
8145
+ function buildOpenAiCompatibleModel(entry, id, suppressThinking = false) {
8143
8146
  const host = entry.openaiCompatible;
8144
8147
  if (!host) {
8145
8148
  throw new Error(`buildOpenAiCompatibleModel called for non-custom provider '${entry.piAiProvider}'`);
8146
8149
  }
8147
8150
  const meta = host.knownModels[id] ?? host.defaultModelMeta;
8148
8151
  const baseUrl = (host.baseUrlEnvVar ? process.env[host.baseUrlEnvVar] : void 0) || host.baseUrl;
8152
+ const compat = suppressThinking ? { ...host.compat, thinkingFormat: "qwen-chat-template" } : host.compat;
8149
8153
  return {
8150
8154
  id,
8151
8155
  name: id,
@@ -8157,7 +8161,7 @@ function buildOpenAiCompatibleModel(entry, id) {
8157
8161
  cost: meta.cost,
8158
8162
  contextWindow: meta.contextWindow,
8159
8163
  maxTokens: meta.maxTokens,
8160
- ...host.compat ? { compat: host.compat } : {}
8164
+ ...compat ? { compat } : {}
8161
8165
  };
8162
8166
  }
8163
8167
  function validateAgentProviderRegistry() {
@@ -10425,9 +10429,7 @@ var API_ADAPTERS = [
10425
10429
  localAdapter,
10426
10430
  perplexityAdapter
10427
10431
  ];
10428
- var BROWSER_ADAPTERS = [
10429
- cdpChatgptAdapter
10430
- ];
10432
+ var BROWSER_ADAPTERS = [cdpChatgptAdapter];
10431
10433
  var adapterMap = Object.fromEntries(
10432
10434
  API_ADAPTERS.map((a) => [a.name, a])
10433
10435
  );
@@ -10477,13 +10479,18 @@ function verifyDashboardPassword(password, storedHash) {
10477
10479
  N: DASHBOARD_SCRYPT_COST,
10478
10480
  maxmem: DASHBOARD_SCRYPT_MAXMEM
10479
10481
  });
10480
- if (derived.length !== expected.length) return { ok: false, needsRehash: false };
10481
- return { ok: crypto20.timingSafeEqual(derived, expected), needsRehash: false };
10482
+ if (derived.length !== expected.length)
10483
+ return { ok: false, needsRehash: false };
10484
+ return {
10485
+ ok: crypto20.timingSafeEqual(derived, expected),
10486
+ needsRehash: false
10487
+ };
10482
10488
  }
10483
10489
  if (/^[a-f0-9]{64}$/i.test(storedHash)) {
10484
10490
  const candidate = Buffer.from(hashApiKey(password), "hex");
10485
10491
  const expected = Buffer.from(storedHash, "hex");
10486
- if (candidate.length !== expected.length) return { ok: false, needsRehash: false };
10492
+ if (candidate.length !== expected.length)
10493
+ return { ok: false, needsRehash: false };
10487
10494
  const ok = crypto20.timingSafeEqual(candidate, expected);
10488
10495
  return { ok, needsRehash: ok };
10489
10496
  }
@@ -10525,7 +10532,11 @@ function serializeSessionCookie(opts) {
10525
10532
  function applyLegacyCredentials(rows, config) {
10526
10533
  let migratedGoogle = 0;
10527
10534
  for (const row of rows.google) {
10528
- const existing = getGoogleConnection(config, row.domain, row.connectionType);
10535
+ const existing = getGoogleConnection(
10536
+ config,
10537
+ row.domain,
10538
+ row.connectionType
10539
+ );
10529
10540
  if (existing?.refreshToken) continue;
10530
10541
  upsertGoogleConnection(config, {
10531
10542
  domain: row.domain,
@@ -10604,10 +10615,12 @@ async function createServer(opts) {
10604
10615
  error: err instanceof Error ? err.message : String(err)
10605
10616
  });
10606
10617
  }
10607
- log21.info("providers.configured", { providers: Object.keys(providers).filter((k) => {
10608
- const p = providers[k];
10609
- return p?.apiKey || p?.baseUrl || p?.vertexProject;
10610
- }) });
10618
+ log21.info("providers.configured", {
10619
+ providers: Object.keys(providers).filter((k) => {
10620
+ const p = providers[k];
10621
+ return p?.apiKey || p?.baseUrl || p?.vertexProject;
10622
+ })
10623
+ });
10611
10624
  for (const adapter of API_ADAPTERS) {
10612
10625
  const entry = providers[adapter.name];
10613
10626
  if (!entry) continue;
@@ -10627,7 +10640,11 @@ async function createServer(opts) {
10627
10640
  }
10628
10641
  const cdpConfig = opts.config.cdp;
10629
10642
  if (cdpConfig?.host || cdpConfig?.port) {
10630
- const CDP_DEFAULT_QUOTA = { maxConcurrency: 1, maxRequestsPerMinute: 4, maxRequestsPerDay: 200 };
10643
+ const CDP_DEFAULT_QUOTA = {
10644
+ maxConcurrency: 1,
10645
+ maxRequestsPerMinute: 4,
10646
+ maxRequestsPerDay: 200
10647
+ };
10631
10648
  const cdpEndpoint = `ws://${cdpConfig.host ?? "localhost"}:${cdpConfig.port ?? 9222}`;
10632
10649
  registry.register(cdpChatgptAdapter, {
10633
10650
  provider: "cdp:chatgpt",
@@ -10641,7 +10658,9 @@ async function createServer(opts) {
10641
10658
  jobRunner.recoverStaleRuns();
10642
10659
  const notifier = new Notifier(opts.db, serverUrl);
10643
10660
  const intelligenceService = new IntelligenceService(opts.db);
10644
- const aeroClient = new ApiClient(opts.config.apiUrl, opts.config.apiKey, { skipProbe: true });
10661
+ const aeroClient = new ApiClient(opts.config.apiUrl, opts.config.apiKey, {
10662
+ skipProbe: true
10663
+ });
10645
10664
  const agentEnabled = resolveAgentEnabled(process.env, opts.config);
10646
10665
  const sessionRegistry = agentEnabled ? new SessionRegistry({
10647
10666
  db: opts.db,
@@ -10688,7 +10707,9 @@ async function createServer(opts) {
10688
10707
  const runGbpSync = (runId, projectId, syncOpts) => {
10689
10708
  const { clientId: googleClientId, clientSecret: googleClientSecret } = getGoogleAuthConfig(opts.config);
10690
10709
  if (!googleClientId || !googleClientSecret) {
10691
- app.log.error("GBP sync requested but Google OAuth credentials are not configured in the local config");
10710
+ app.log.error(
10711
+ "GBP sync requested but Google OAuth credentials are not configured in the local config"
10712
+ );
10692
10713
  return;
10693
10714
  }
10694
10715
  executeGbpSync(opts.db, runId, projectId, {
@@ -10741,7 +10762,14 @@ async function createServer(opts) {
10741
10762
  },
10742
10763
  onTrafficSyncRequested: (projectName, sourceId) => {
10743
10764
  aeroClient.trafficSync(projectName, sourceId).catch((err) => {
10744
- app.log.error({ projectName, sourceId, err: err instanceof Error ? err.message : String(err) }, "Scheduled traffic sync failed");
10765
+ app.log.error(
10766
+ {
10767
+ projectName,
10768
+ sourceId,
10769
+ err: err instanceof Error ? err.message : String(err)
10770
+ },
10771
+ "Scheduled traffic sync failed"
10772
+ );
10745
10773
  });
10746
10774
  },
10747
10775
  onGbpSyncRequested: (runId, projectId) => {
@@ -10756,21 +10784,33 @@ async function createServer(opts) {
10756
10784
  onBacklinksSyncRequested: (projectName) => {
10757
10785
  void (async () => {
10758
10786
  const probed = await probeLatestRelease().catch((err) => {
10759
- app.log.warn({ projectName, err }, "Scheduled backlinks sync: latest-release probe failed");
10787
+ app.log.warn(
10788
+ { projectName, err },
10789
+ "Scheduled backlinks sync: latest-release probe failed"
10790
+ );
10760
10791
  return null;
10761
10792
  });
10762
10793
  if (!probed) return;
10763
- const alreadySynced = opts.db.select().from(ccReleaseSyncs).where(and14(
10764
- eq20(ccReleaseSyncs.release, probed.release),
10765
- eq20(ccReleaseSyncs.status, CcReleaseSyncStatuses.ready)
10766
- )).limit(1).get();
10794
+ const alreadySynced = opts.db.select().from(ccReleaseSyncs).where(
10795
+ and14(
10796
+ eq20(ccReleaseSyncs.release, probed.release),
10797
+ eq20(ccReleaseSyncs.status, CcReleaseSyncStatuses.ready)
10798
+ )
10799
+ ).limit(1).get();
10767
10800
  if (alreadySynced) {
10768
- app.log.info({ projectName, release: probed.release }, "Scheduled backlinks sync: already up to date, skipping");
10801
+ app.log.info(
10802
+ { projectName, release: probed.release },
10803
+ "Scheduled backlinks sync: already up to date, skipping"
10804
+ );
10769
10805
  return;
10770
10806
  }
10771
10807
  aeroClient.backlinksTriggerSync(probed.release).catch((err) => {
10772
10808
  app.log.error(
10773
- { projectName, release: probed.release, err: err instanceof Error ? err.message : String(err) },
10809
+ {
10810
+ projectName,
10811
+ release: probed.release,
10812
+ err: err instanceof Error ? err.message : String(err)
10813
+ },
10774
10814
  "Scheduled backlinks sync failed"
10775
10815
  );
10776
10816
  });
@@ -10779,7 +10819,10 @@ async function createServer(opts) {
10779
10819
  if (project && bingConnectionStore.getConnection(project.canonicalDomain)) {
10780
10820
  aeroClient.backlinksBingSync(projectName).catch((err) => {
10781
10821
  app.log.error(
10782
- { projectName, err: err instanceof Error ? err.message : String(err) },
10822
+ {
10823
+ projectName,
10824
+ err: err instanceof Error ? err.message : String(err)
10825
+ },
10783
10826
  "Scheduled Bing backlinks sync failed"
10784
10827
  );
10785
10828
  });
@@ -10801,7 +10844,9 @@ async function createServer(opts) {
10801
10844
  vertexConfigured: adapter.name === "gemini" ? !!opts.config.providers?.gemini?.vertexProject : void 0
10802
10845
  }));
10803
10846
  const googleSettingsSummary = {
10804
- configured: Boolean(opts.config.google?.clientId && opts.config.google?.clientSecret)
10847
+ configured: Boolean(
10848
+ opts.config.google?.clientId && opts.config.google?.clientSecret
10849
+ )
10805
10850
  };
10806
10851
  const bingSettingsSummary = {
10807
10852
  // Treat Bing as configured if there is at least one connection with an API key,
@@ -10818,8 +10863,13 @@ async function createServer(opts) {
10818
10863
  upsertConnection: (connection) => {
10819
10864
  if (!opts.config.bing) opts.config.bing = {};
10820
10865
  if (!opts.config.bing.connections) opts.config.bing.connections = [];
10821
- const idx = opts.config.bing.connections.findIndex((c) => c.domain === connection.domain);
10822
- const normalized = { ...connection, createdByProjectId: connection.createdByProjectId ?? null };
10866
+ const idx = opts.config.bing.connections.findIndex(
10867
+ (c) => c.domain === connection.domain
10868
+ );
10869
+ const normalized = {
10870
+ ...connection,
10871
+ createdByProjectId: connection.createdByProjectId ?? null
10872
+ };
10823
10873
  if (idx >= 0) {
10824
10874
  opts.config.bing.connections[idx] = normalized;
10825
10875
  } else {
@@ -10829,7 +10879,9 @@ async function createServer(opts) {
10829
10879
  return normalized;
10830
10880
  },
10831
10881
  updateConnection: (domain, patch) => {
10832
- const conn = opts.config.bing?.connections?.find((c) => c.domain === domain);
10882
+ const conn = opts.config.bing?.connections?.find(
10883
+ (c) => c.domain === domain
10884
+ );
10833
10885
  if (!conn) return void 0;
10834
10886
  Object.assign(conn, patch);
10835
10887
  saveConfigPatch(opts.config);
@@ -10837,7 +10889,9 @@ async function createServer(opts) {
10837
10889
  },
10838
10890
  deleteConnection: (domain) => {
10839
10891
  if (!opts.config.bing?.connections) return false;
10840
- const idx = opts.config.bing.connections.findIndex((c) => c.domain === domain);
10892
+ const idx = opts.config.bing.connections.findIndex(
10893
+ (c) => c.domain === domain
10894
+ );
10841
10895
  if (idx < 0) return false;
10842
10896
  opts.config.bing.connections.splice(idx, 1);
10843
10897
  saveConfigPatch(opts.config);
@@ -10884,7 +10938,10 @@ async function createServer(opts) {
10884
10938
  return updated;
10885
10939
  },
10886
10940
  deleteConnection: (projectName) => {
10887
- const removed = removeWordpressTrafficConnection(opts.config, projectName);
10941
+ const removed = removeWordpressTrafficConnection(
10942
+ opts.config,
10943
+ projectName
10944
+ );
10888
10945
  if (removed) saveConfigPatch(opts.config);
10889
10946
  return removed;
10890
10947
  }
@@ -10914,12 +10971,21 @@ async function createServer(opts) {
10914
10971
  return updated;
10915
10972
  },
10916
10973
  updateConnection: (domain, connectionType, patch) => {
10917
- const updated = patchGoogleConnection(opts.config, domain, connectionType, patch);
10974
+ const updated = patchGoogleConnection(
10975
+ opts.config,
10976
+ domain,
10977
+ connectionType,
10978
+ patch
10979
+ );
10918
10980
  if (updated) saveConfigPatch(opts.config);
10919
10981
  return updated;
10920
10982
  },
10921
10983
  deleteConnection: (domain, connectionType) => {
10922
- const removed = removeGoogleConnection(opts.config, domain, connectionType);
10984
+ const removed = removeGoogleConnection(
10985
+ opts.config,
10986
+ domain,
10987
+ connectionType
10988
+ );
10923
10989
  if (removed) saveConfigPatch(opts.config);
10924
10990
  return removed;
10925
10991
  }
@@ -11010,13 +11076,16 @@ async function createServer(opts) {
11010
11076
  const key = getDefaultApiKey();
11011
11077
  if (!key || key.revokedAt) return false;
11012
11078
  const sessionId = createSession(key.id);
11013
- reply.header("set-cookie", serializeSessionCookie({
11014
- name: SESSION_COOKIE_NAME,
11015
- value: sessionId,
11016
- path: sessionCookiePath,
11017
- secure: sessionCookieSecure,
11018
- ttlMs: SESSION_TTL_MS
11019
- }));
11079
+ reply.header(
11080
+ "set-cookie",
11081
+ serializeSessionCookie({
11082
+ name: SESSION_COOKIE_NAME,
11083
+ value: sessionId,
11084
+ path: sessionCookiePath,
11085
+ secure: sessionCookieSecure,
11086
+ ttlMs: SESSION_TTL_MS
11087
+ })
11088
+ );
11020
11089
  return true;
11021
11090
  };
11022
11091
  const boundToLoopback = isLoopbackBindHost(opts.host);
@@ -11037,7 +11106,9 @@ async function createServer(opts) {
11037
11106
  });
11038
11107
  app.post(apiPrefix + "/session/setup", async (request, reply) => {
11039
11108
  if (!boundToLoopback && !requestHasValidApiKey(request)) {
11040
- const err = authRequired("This server is network-reachable; setting the dashboard password requires a valid API key.");
11109
+ const err = authRequired(
11110
+ "This server is network-reachable; setting the dashboard password requires a valid API key."
11111
+ );
11041
11112
  return reply.status(err.statusCode).send(err.toJSON());
11042
11113
  }
11043
11114
  if (opts.config.dashboardPasswordHash) {
@@ -11062,19 +11133,31 @@ async function createServer(opts) {
11062
11133
  const apiKey = request.body?.apiKey?.trim();
11063
11134
  if (password) {
11064
11135
  if (!opts.config.dashboardPasswordHash) {
11065
- const err2 = validationError("No dashboard password configured \u2014 use /session/setup first");
11136
+ const err2 = validationError(
11137
+ "No dashboard password configured \u2014 use /session/setup first"
11138
+ );
11066
11139
  return reply.status(err2.statusCode).send(err2.toJSON());
11067
11140
  }
11068
- const verification = verifyDashboardPassword(password, opts.config.dashboardPasswordHash);
11141
+ const verification = verifyDashboardPassword(
11142
+ password,
11143
+ opts.config.dashboardPasswordHash
11144
+ );
11069
11145
  if (!verification.ok) {
11070
- return reply.status(401).send({ error: { code: "AUTH_INVALID", message: "Incorrect password" } });
11146
+ return reply.status(401).send({
11147
+ error: { code: "AUTH_INVALID", message: "Incorrect password" }
11148
+ });
11071
11149
  }
11072
11150
  if (verification.needsRehash) {
11073
11151
  opts.config.dashboardPasswordHash = hashDashboardPassword(password);
11074
11152
  saveConfigPatch(opts.config);
11075
11153
  }
11076
11154
  if (!createPasswordSession(reply)) {
11077
- return reply.status(401).send({ error: { code: "AUTH_INVALID", message: "Server API key not found \u2014 re-run canonry init" } });
11155
+ return reply.status(401).send({
11156
+ error: {
11157
+ code: "AUTH_INVALID",
11158
+ message: "Server API key not found \u2014 re-run canonry init"
11159
+ }
11160
+ });
11078
11161
  }
11079
11162
  return reply.send({ authenticated: true });
11080
11163
  }
@@ -11086,13 +11169,16 @@ async function createServer(opts) {
11086
11169
  }
11087
11170
  opts.db.update(apiKeys).set({ lastUsedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(eq20(apiKeys.id, key.id)).run();
11088
11171
  const sessionId = createSession(key.id);
11089
- reply.header("set-cookie", serializeSessionCookie({
11090
- name: SESSION_COOKIE_NAME,
11091
- value: sessionId,
11092
- path: sessionCookiePath,
11093
- secure: sessionCookieSecure,
11094
- ttlMs: SESSION_TTL_MS
11095
- }));
11172
+ reply.header(
11173
+ "set-cookie",
11174
+ serializeSessionCookie({
11175
+ name: SESSION_COOKIE_NAME,
11176
+ value: sessionId,
11177
+ path: sessionCookiePath,
11178
+ secure: sessionCookieSecure,
11179
+ ttlMs: SESSION_TTL_MS
11180
+ })
11181
+ );
11096
11182
  return reply.send({ authenticated: true });
11097
11183
  }
11098
11184
  const err = validationError("Either password or apiKey is required");
@@ -11101,13 +11187,16 @@ async function createServer(opts) {
11101
11187
  app.delete(apiPrefix + "/session", async (request, reply) => {
11102
11188
  const sessionId = parseCookies(request.headers.cookie)[SESSION_COOKIE_NAME];
11103
11189
  clearSession(sessionId);
11104
- reply.header("set-cookie", serializeSessionCookie({
11105
- name: SESSION_COOKIE_NAME,
11106
- value: null,
11107
- path: sessionCookiePath,
11108
- secure: sessionCookieSecure,
11109
- ttlMs: SESSION_TTL_MS
11110
- }));
11190
+ reply.header(
11191
+ "set-cookie",
11192
+ serializeSessionCookie({
11193
+ name: SESSION_COOKIE_NAME,
11194
+ value: null,
11195
+ path: sessionCookiePath,
11196
+ secure: sessionCookieSecure,
11197
+ ttlMs: SESSION_TTL_MS
11198
+ })
11199
+ );
11111
11200
  return reply.status(204).send();
11112
11201
  });
11113
11202
  const LATEST_RELEASE_TTL_MS = 5 * 60 * 1e3;
@@ -11132,8 +11221,12 @@ async function createServer(opts) {
11132
11221
  latestReleaseCache = { value, expiresAt: now + LATEST_RELEASE_TTL_MS };
11133
11222
  return value;
11134
11223
  };
11135
- const explainContentRecommendation = createRecommendationExplainer({ config: opts.config });
11136
- const briefContentRecommendation = createRecommendationBriefSynthesizer({ config: opts.config });
11224
+ const explainContentRecommendation = createRecommendationExplainer({
11225
+ config: opts.config
11226
+ });
11227
+ const briefContentRecommendation = createRecommendationBriefSynthesizer({
11228
+ config: opts.config
11229
+ });
11137
11230
  await app.register(apiRoutes, {
11138
11231
  db: opts.db,
11139
11232
  routePrefix: apiPrefix,
@@ -11195,7 +11288,9 @@ async function createServer(opts) {
11195
11288
  onGscSyncRequested: (runId, projectId, syncOpts) => {
11196
11289
  const { clientId: googleClientId, clientSecret: googleClientSecret } = getGoogleAuthConfig(opts.config);
11197
11290
  if (!googleClientId || !googleClientSecret) {
11198
- app.log.error("GSC sync requested but Google OAuth credentials are not configured in the local config");
11291
+ app.log.error(
11292
+ "GSC sync requested but Google OAuth credentials are not configured in the local config"
11293
+ );
11199
11294
  return;
11200
11295
  }
11201
11296
  executeGscSync(opts.db, runId, projectId, {
@@ -11208,7 +11303,9 @@ async function createServer(opts) {
11208
11303
  onInspectSitemapRequested: (runId, projectId, inspectOpts) => {
11209
11304
  const { clientId: googleClientId, clientSecret: googleClientSecret } = getGoogleAuthConfig(opts.config);
11210
11305
  if (!googleClientId || !googleClientSecret) {
11211
- app.log.error("Inspect sitemap requested but Google OAuth credentials are not configured");
11306
+ app.log.error(
11307
+ "Inspect sitemap requested but Google OAuth credentials are not configured"
11308
+ );
11212
11309
  return;
11213
11310
  }
11214
11311
  executeInspectSitemap(opts.db, runId, projectId, {
@@ -11233,7 +11330,9 @@ async function createServer(opts) {
11233
11330
  pluginDir: PLUGIN_DIR
11234
11331
  }),
11235
11332
  onInstallBacklinks: async () => {
11236
- const result = await installDuckdb({ onLog: (line) => app.log.info({ line }, "duckdb install") });
11333
+ const result = await installDuckdb({
11334
+ onLog: (line) => app.log.info({ line }, "duckdb install")
11335
+ });
11237
11336
  return {
11238
11337
  installed: true,
11239
11338
  version: result.version,
@@ -11256,8 +11355,13 @@ async function createServer(opts) {
11256
11355
  trigger: RunTriggers.scheduled,
11257
11356
  createdAt: now
11258
11357
  }).run();
11259
- executeBacklinkExtract(opts.db, runId, projectId, { release: r }).catch((err) => {
11260
- app.log.error({ runId, projectId, err }, "Auto backlink extract failed");
11358
+ executeBacklinkExtract(opts.db, runId, projectId, {
11359
+ release: r
11360
+ }).catch((err) => {
11361
+ app.log.error(
11362
+ { runId, projectId, err },
11363
+ "Auto backlink extract failed"
11364
+ );
11261
11365
  });
11262
11366
  }
11263
11367
  }
@@ -11266,9 +11370,11 @@ async function createServer(opts) {
11266
11370
  });
11267
11371
  },
11268
11372
  onBacklinkExtractRequested: (runId, projectId, release) => {
11269
- executeBacklinkExtract(opts.db, runId, projectId, { release }).catch((err) => {
11270
- app.log.error({ runId, err }, "Backlink extract failed");
11271
- });
11373
+ executeBacklinkExtract(opts.db, runId, projectId, { release }).catch(
11374
+ (err) => {
11375
+ app.log.error({ runId, err }, "Backlink extract failed");
11376
+ }
11377
+ );
11272
11378
  },
11273
11379
  onBingBacklinkSyncRequested: (runId, projectId) => {
11274
11380
  executeBingBacklinkSync(opts.db, runId, projectId, {
@@ -11303,9 +11409,16 @@ async function createServer(opts) {
11303
11409
  embedQueries: (queriesToEmbed) => {
11304
11410
  const cfg = registry.get("gemini")?.config;
11305
11411
  if (!cfg?.apiKey) {
11306
- return Promise.reject(new Error("Gemini API key not configured; harvest semantic novelty unavailable"));
11412
+ return Promise.reject(
11413
+ new Error(
11414
+ "Gemini API key not configured; harvest semantic novelty unavailable"
11415
+ )
11416
+ );
11307
11417
  }
11308
- return embedQueries(queriesToEmbed, { apiKey: cfg.apiKey, baseUrl: cfg.baseUrl });
11418
+ return embedQueries(queriesToEmbed, {
11419
+ apiKey: cfg.apiKey,
11420
+ baseUrl: cfg.baseUrl
11421
+ });
11309
11422
  },
11310
11423
  onSiteAuditRequested: (runId, projectId, auditOpts) => {
11311
11424
  runSiteAudit(runId, projectId, auditOpts);
@@ -11321,7 +11434,10 @@ async function createServer(opts) {
11321
11434
  const cached = listCachedReleases();
11322
11435
  const syncByRelease = /* @__PURE__ */ new Map();
11323
11436
  for (const row of opts.db.select().from(ccReleaseSyncs).all()) {
11324
- syncByRelease.set(row.release, { status: row.status, updatedAt: row.updatedAt });
11437
+ syncByRelease.set(row.release, {
11438
+ status: row.status,
11439
+ updatedAt: row.updatedAt
11440
+ });
11325
11441
  }
11326
11442
  return cached.map((entry) => {
11327
11443
  const sync = syncByRelease.get(entry.release);
@@ -11370,16 +11486,20 @@ async function createServer(opts) {
11370
11486
  wordpressTrafficCredentialStore,
11371
11487
  vercelTrafficCredentialStore,
11372
11488
  onTrafficSynced: (event) => {
11373
- trackEvent("traffic.synced", {
11374
- status: event.status,
11375
- sourceType: event.sourceType,
11376
- sourceId: event.sourceId,
11377
- pulledEvents: event.pulledEvents,
11378
- selfTrafficExcluded: event.selfTrafficExcluded,
11379
- crawlerHits: event.crawlerHits,
11380
- aiReferralHits: event.aiReferralHits,
11381
- durationMs: event.durationMs
11382
- }, event.errorCode ? { errorCode: event.errorCode } : void 0);
11489
+ trackEvent(
11490
+ "traffic.synced",
11491
+ {
11492
+ status: event.status,
11493
+ sourceType: event.sourceType,
11494
+ sourceId: event.sourceId,
11495
+ pulledEvents: event.pulledEvents,
11496
+ selfTrafficExcluded: event.selfTrafficExcluded,
11497
+ crawlerHits: event.crawlerHits,
11498
+ aiReferralHits: event.aiReferralHits,
11499
+ durationMs: event.durationMs
11500
+ },
11501
+ event.errorCode ? { errorCode: event.errorCode } : void 0
11502
+ );
11383
11503
  },
11384
11504
  onRunCreated: (runId, projectId, providers2, location) => {
11385
11505
  jobRunner.executeRun(runId, projectId, providers2, location).catch((err) => {
@@ -11442,16 +11562,18 @@ async function createServer(opts) {
11442
11562
  }).map((project) => project.id);
11443
11563
  const targetProjectIds = affectedProjectIds.length > 0 ? affectedProjectIds : [null];
11444
11564
  const createdAt = (/* @__PURE__ */ new Date()).toISOString();
11445
- opts.db.insert(auditLog).values(targetProjectIds.map((projectId) => ({
11446
- id: crypto20.randomUUID(),
11447
- projectId,
11448
- actor: "api",
11449
- action: existing ? "provider.updated" : "provider.created",
11450
- entityType: "provider",
11451
- entityId: name,
11452
- diff,
11453
- createdAt
11454
- }))).run();
11565
+ opts.db.insert(auditLog).values(
11566
+ targetProjectIds.map((projectId) => ({
11567
+ id: crypto20.randomUUID(),
11568
+ projectId,
11569
+ actor: "api",
11570
+ action: existing ? "provider.updated" : "provider.created",
11571
+ entityType: "provider",
11572
+ entityId: name,
11573
+ diff,
11574
+ createdAt
11575
+ }))
11576
+ ).run();
11455
11577
  }
11456
11578
  return {
11457
11579
  name,
@@ -11499,7 +11621,10 @@ async function createServer(opts) {
11499
11621
  "aliases changed \u2014 recomputed mention fields on historical snapshots"
11500
11622
  );
11501
11623
  } catch (err) {
11502
- app.log.error({ err, projectId, projectName }, "alias-triggered backfill failed");
11624
+ app.log.error(
11625
+ { err, projectId, projectName },
11626
+ "alias-triggered backfill failed"
11627
+ );
11503
11628
  }
11504
11629
  });
11505
11630
  },
@@ -11528,7 +11653,11 @@ async function createServer(opts) {
11528
11653
  app.log.error({ err }, "Failed to save CDP config");
11529
11654
  throw err;
11530
11655
  }
11531
- const CDP_DEFAULT_QUOTA = { maxConcurrency: 1, maxRequestsPerMinute: 4, maxRequestsPerDay: 200 };
11656
+ const CDP_DEFAULT_QUOTA = {
11657
+ maxConcurrency: 1,
11658
+ maxRequestsPerMinute: 4,
11659
+ maxRequestsPerDay: 200
11660
+ };
11532
11661
  registry.register(cdpChatgptAdapter, {
11533
11662
  provider: "cdp:chatgpt",
11534
11663
  cdpEndpoint: `ws://${host}:${port2}`,
@@ -11560,16 +11689,19 @@ async function createServer(opts) {
11560
11689
  conn.config
11561
11690
  );
11562
11691
  const raw = result.rawResponse;
11563
- return [{
11564
- target: targets?.[0] ?? "chatgpt",
11565
- screenshotPath: result.screenshotPath ?? "",
11566
- answerText: raw.answerText ?? "",
11567
- citations: raw.groundingSources ?? []
11568
- }];
11692
+ return [
11693
+ {
11694
+ target: targets?.[0] ?? "chatgpt",
11695
+ screenshotPath: result.screenshotPath ?? "",
11696
+ answerText: raw.answerText ?? "",
11697
+ citations: raw.groundingSources ?? []
11698
+ }
11699
+ ];
11569
11700
  },
11570
11701
  onGenerateQueries: async (providerName, count, project) => {
11571
11702
  const provider = registry.get(providerName);
11572
- if (!provider) throw new Error(`Provider "${providerName}" is not configured`);
11703
+ if (!provider)
11704
+ throw new Error(`Provider "${providerName}" is not configured`);
11573
11705
  const siteText = await fetchSiteText(project.domain);
11574
11706
  const prompt = buildQueryGenerationPrompt({
11575
11707
  domain: project.domain,
@@ -11622,11 +11754,11 @@ async function createServer(opts) {
11622
11754
  // Without this, the browser hits the server for every JS chunk on
11623
11755
  // every page load, defeating most of the dashboard's first-paint
11624
11756
  // budget.
11625
- setHeaders: (reply, filePath) => {
11626
- if (filePath.endsWith(".html")) {
11627
- reply.setHeader("Cache-Control", "no-cache, must-revalidate");
11757
+ setHeaders: (res, path10) => {
11758
+ if (path10.endsWith(".html")) {
11759
+ res.setHeader("Cache-Control", "no-cache, must-revalidate");
11628
11760
  } else {
11629
- reply.setHeader("Cache-Control", "public, max-age=31536000, immutable");
11761
+ res.setHeader("Cache-Control", "public, max-age=31536000, immutable");
11630
11762
  }
11631
11763
  }
11632
11764
  });
@@ -11690,10 +11822,18 @@ function buildQueryGenerationPrompt(ctx) {
11690
11822
  lines.push(`Country: ${ctx.country}`);
11691
11823
  lines.push(`Language: ${ctx.language}`);
11692
11824
  if (ctx.siteText) {
11693
- lines.push("", "--- Site Content ---", ctx.siteText, "--- End Site Content ---");
11825
+ lines.push(
11826
+ "",
11827
+ "--- Site Content ---",
11828
+ ctx.siteText,
11829
+ "--- End Site Content ---"
11830
+ );
11694
11831
  }
11695
11832
  if (ctx.existingQueries.length > 0) {
11696
- lines.push("", `Already tracking (do NOT duplicate): ${ctx.existingQueries.join(", ")}`);
11833
+ lines.push(
11834
+ "",
11835
+ `Already tracking (do NOT duplicate): ${ctx.existingQueries.join(", ")}`
11836
+ );
11697
11837
  }
11698
11838
  lines.push(
11699
11839
  "",
@@ -11715,7 +11855,10 @@ function parseQueryResponse(raw, count) {
11715
11855
  let cleaned = line.replace(/^\s*(?:\d+[.)]\s*|[-*•]\s*)/, "").trim();
11716
11856
  cleaned = cleaned.replace(/^["']|["']$/g, "").trim();
11717
11857
  if (!cleaned) continue;
11718
- if (/^(?:here are|sure|certainly|of course|i['’]ve|these are|below are)/i.test(cleaned)) continue;
11858
+ if (/^(?:here are|sure|certainly|of course|i['’]ve|these are|below are)/i.test(
11859
+ cleaned
11860
+ ))
11861
+ continue;
11719
11862
  if (cleaned.split(/\s+/).length > 8) continue;
11720
11863
  const key = cleaned.toLowerCase();
11721
11864
  if (seen.has(key)) continue;