@ainyc/canonry 4.55.3 → 4.56.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/assets/agent-workspace/skills/aero/SKILL.md +2 -0
- package/assets/agent-workspace/skills/canonry/SKILL.md +2 -0
- package/assets/agent-workspace/skills/canonry/references/server-side-traffic.md +46 -3
- package/assets/assets/{BacklinksPage-buvZ4ZOd.js → BacklinksPage-CmeFZ8UJ.js} +1 -1
- package/assets/assets/ChartPrimitives-D7C1Cp8w.js +1 -0
- package/assets/assets/ProjectPage-Y6uCyjGb.js +6 -0
- package/assets/assets/{RunRow-D-DTu1PA.js → RunRow-BntNdrgM.js} +1 -1
- package/assets/assets/{RunsPage-CrBpgwkO.js → RunsPage-Btp6qn10.js} +1 -1
- package/assets/assets/{SettingsPage-Bgsi9tZ2.js → SettingsPage-DkyNiU2i.js} +1 -1
- package/assets/assets/{TrafficPage-DAHXrzqz.js → TrafficPage-CBl4Mwdc.js} +1 -1
- package/assets/assets/TrafficSourceDetailPage-BZzuWCn-.js +1 -0
- package/assets/assets/{extract-error-message-BGhWiJPr.js → extract-error-message-De8_qAzs.js} +1 -1
- package/assets/assets/index-9NRlymgj.css +1 -0
- package/assets/assets/{index-CbDkoDBH.js → index-XUKhruAg.js} +79 -79
- package/assets/assets/{server-traffic-3xxyOEIX.js → server-traffic-bn9LSZN9.js} +1 -1
- package/assets/assets/{trash-2-dppRdHYI.js → trash-2-B5clF2rU.js} +1 -1
- package/assets/index.html +2 -2
- package/dist/{chunk-XB6Y63NI.js → chunk-4KWPOVIT.js} +1 -1
- package/dist/{chunk-UOQ62KDD.js → chunk-6X5TF73A.js} +42 -1
- package/dist/{chunk-5EAGNVCJ.js → chunk-I2LAM5IM.js} +207 -63
- package/dist/{chunk-XHU35P3S.js → chunk-WFVUZVJD.js} +9 -0
- package/dist/cli.js +58 -5
- package/dist/index.js +4 -4
- package/dist/{intelligence-service-4PT22FED.js → intelligence-service-NY3MAVPB.js} +2 -2
- package/dist/mcp.js +2 -2
- package/package.json +8 -8
- package/assets/assets/ChartPrimitives-9Kx3gzQL.js +0 -1
- package/assets/assets/ProjectPage-D0UqSqe7.js +0 -6
- package/assets/assets/TrafficSourceDetailPage-DCcDN3VD.js +0 -1
- package/assets/assets/index-dxdJhCQO.css +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
import{c as d,j as a,
|
|
1
|
+
import{c as d,j as a,bI as S,bC as l,bJ as v,aj as m,l as t,bK as y,bD as i,bL as T,bM as h,bN as p,bO as b}from"./index-XUKhruAg.js";import{u as s,r as C,n as c,o as u}from"./vendor-tanstack-Dq7p98wZ.js";const g=[["path",{d:"M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8",key:"v9h5vc"}],["path",{d:"M21 3v5h-5",key:"1q7to0"}],["path",{d:"M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16",key:"3uifl3"}],["path",{d:"M8 16H3v5",key:"1cv678"}]],B=d("refresh-cw",g);function P(e){switch(e){case i.connected:return"positive";case i.paused:return"caution";case i.error:return"negative";case i.archived:return"neutral"}}function w(e){const r={};return e.kind&&e.kind!=="all"&&(r.kind=e.kind),e.sourceId&&(r.sourceId=e.sourceId),e.sinceMinutes!==void 0&&(r.since=new Date(Date.now()-e.sinceMinutes*6e4).toISOString()),e.limit!==void 0&&(r.limit=String(e.limit)),r}function o(e){e.invalidateQueries({predicate:r=>{const n=r.queryKey[0];return typeof n?._id=="string"&&n._id.startsWith("getApiV1ProjectsByNameTraffic")}})}function k(e){return s({...S({client:t,path:{name:e??""}}),enabled:!!e,staleTime:a})}function F(e){return s({...b({client:t,path:{name:e??""}}),enabled:!!e,staleTime:a})}function V(e,r){return s({...l({client:t,path:{name:e??"",id:r??""}}),enabled:!!(e&&r),staleTime:a})}function A(e,r){const n=C.useMemo(()=>w(r),[r.kind,r.sourceId,r.sinceMinutes,r.limit]);return s({...v({client:t,path:{name:e??""},query:n}),enabled:!!e,staleTime:a})}function E(e){const r=c();return u({mutationFn:n=>{if(!e)throw new Error("Project is required to connect a Cloud Run source");return p(e,n)},onSuccess:()=>{e&&o(r)}})}function I(e){const r=c();return u({mutationFn:n=>{if(!e)throw new Error("Project is required to connect a WordPress source");return T(e,n)},onSuccess:()=>{e&&o(r)}})}function N(e){const r=c();return u({mutationFn:n=>{if(!e)throw new Error("Project is required to connect a Vercel source");return h(e,n)},onSuccess:()=>{e&&o(r)}})}function Q(e,r){const n=c();return u({mutationFn:f=>{if(!e||!r)throw new Error("Project and sourceId are required to sync");return y(e,r,f??void 0)},onSuccess:()=>{e&&(o(n),n.invalidateQueries({queryKey:m({client:t})}))}})}export{B as R,I as a,N as b,E as c,k as d,V as e,A as f,Q as g,P as t,F as u};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{c}from"./index-
|
|
1
|
+
import{c}from"./index-XUKhruAg.js";const a=[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}],["path",{d:"m9 12 2 2 4-4",key:"dzmm74"}]],h=c("circle-check",a);const e=[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}],["path",{d:"M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3",key:"1u773s"}],["path",{d:"M12 17h.01",key:"p32p05"}]],n=c("circle-question-mark",e);const o=[["path",{d:"M12 15V3",key:"m9g1x1"}],["path",{d:"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4",key:"ih7n3h"}],["path",{d:"m7 10 5 5 5-5",key:"brsn70"}]],s=c("download",o);const t=[["path",{d:"M10 11v6",key:"nco0om"}],["path",{d:"M14 11v6",key:"outv1u"}],["path",{d:"M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6",key:"miytrc"}],["path",{d:"M3 6h18",key:"d0wm0j"}],["path",{d:"M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2",key:"e791ji"}]],r=c("trash-2",t);export{h as C,s as D,r as T,n as a};
|
package/assets/index.html
CHANGED
|
@@ -12,12 +12,12 @@
|
|
|
12
12
|
<link rel="icon" type="image/png" sizes="32x32" href="./favicon-32.png" />
|
|
13
13
|
<link rel="apple-touch-icon" href="./apple-touch-icon.png" />
|
|
14
14
|
<title>Canonry</title>
|
|
15
|
-
<script type="module" crossorigin src="./assets/index-
|
|
15
|
+
<script type="module" crossorigin src="./assets/index-XUKhruAg.js"></script>
|
|
16
16
|
<link rel="modulepreload" crossorigin href="./assets/vendor-tanstack-Dq7p98wZ.js">
|
|
17
17
|
<link rel="modulepreload" crossorigin href="./assets/vendor-radix-B57xfQbP.js">
|
|
18
18
|
<link rel="modulepreload" crossorigin href="./assets/vendor-recharts-DWvKDyBF.js">
|
|
19
19
|
<link rel="modulepreload" crossorigin href="./assets/vendor-markdown-DK7fbRNb.js">
|
|
20
|
-
<link rel="stylesheet" crossorigin href="./assets/index-
|
|
20
|
+
<link rel="stylesheet" crossorigin href="./assets/index-9NRlymgj.css">
|
|
21
21
|
</head>
|
|
22
22
|
<body>
|
|
23
23
|
<div id="root"></div>
|
|
@@ -22,7 +22,7 @@ import {
|
|
|
22
22
|
trafficConnectVercelRequestSchema,
|
|
23
23
|
trafficConnectWordpressRequestSchema,
|
|
24
24
|
trafficEventKindSchema
|
|
25
|
-
} from "./chunk-
|
|
25
|
+
} from "./chunk-WFVUZVJD.js";
|
|
26
26
|
|
|
27
27
|
// src/config.ts
|
|
28
28
|
import fs from "fs";
|
|
@@ -3001,6 +3001,22 @@ var postApiV1ProjectsByNameTrafficSourcesByIdBackfill = (options) => {
|
|
|
3001
3001
|
}
|
|
3002
3002
|
});
|
|
3003
3003
|
};
|
|
3004
|
+
var postApiV1ProjectsByNameTrafficSourcesByIdReset = (options) => {
|
|
3005
|
+
return (options.client ?? client).post({
|
|
3006
|
+
security: [
|
|
3007
|
+
{
|
|
3008
|
+
scheme: "bearer",
|
|
3009
|
+
type: "http"
|
|
3010
|
+
}
|
|
3011
|
+
],
|
|
3012
|
+
url: "/api/v1/projects/{name}/traffic/sources/{id}/reset",
|
|
3013
|
+
...options,
|
|
3014
|
+
headers: {
|
|
3015
|
+
"Content-Type": "application/json",
|
|
3016
|
+
...options.headers
|
|
3017
|
+
}
|
|
3018
|
+
});
|
|
3019
|
+
};
|
|
3004
3020
|
var getApiV1ProjectsByNameTrafficSources = (options) => {
|
|
3005
3021
|
return (options.client ?? client).get({
|
|
3006
3022
|
security: [
|
|
@@ -4110,6 +4126,15 @@ var ApiClient = class {
|
|
|
4110
4126
|
() => getApiV1ProjectsByNameTrafficSources({ client: this.heyClient, path: { name: project } })
|
|
4111
4127
|
);
|
|
4112
4128
|
}
|
|
4129
|
+
async trafficReset(project, sourceId) {
|
|
4130
|
+
return this.invoke(
|
|
4131
|
+
() => postApiV1ProjectsByNameTrafficSourcesByIdReset({
|
|
4132
|
+
client: this.heyClient,
|
|
4133
|
+
path: { name: project, id: sourceId },
|
|
4134
|
+
body: { advanceToNow: true }
|
|
4135
|
+
})
|
|
4136
|
+
);
|
|
4137
|
+
}
|
|
4113
4138
|
async trafficStatus(project) {
|
|
4114
4139
|
return this.invoke(
|
|
4115
4140
|
() => getApiV1ProjectsByNameTrafficStatus({ client: this.heyClient, path: { name: project } })
|
|
@@ -4714,6 +4739,11 @@ var trafficBackfillInputSchema = z2.object({
|
|
|
4714
4739
|
sourceId: z2.string().min(1).describe("Traffic source ID returned by canonry_traffic_sources_list."),
|
|
4715
4740
|
days: z2.number().int().positive().max(30).optional().describe("Lookback window in days. Default 30, capped server-side at the upstream log retention ceiling (Cloud Logging _Default = 30d).")
|
|
4716
4741
|
});
|
|
4742
|
+
var trafficResetInputSchema = z2.object({
|
|
4743
|
+
project: projectNameSchema,
|
|
4744
|
+
sourceId: z2.string().min(1).describe("Traffic source ID returned by canonry_traffic_sources_list."),
|
|
4745
|
+
advanceToNow: z2.literal(true).describe("Must be `true`. Explicit gate against accidental resets. Advances lastSyncedAt to NOW and clears the source's error state.")
|
|
4746
|
+
});
|
|
4717
4747
|
var trafficEventsInputSchema = z2.object({
|
|
4718
4748
|
project: projectNameSchema,
|
|
4719
4749
|
since: z2.string().optional().describe("ISO 8601 lower bound. Defaults to 24h ago when omitted."),
|
|
@@ -5395,6 +5425,17 @@ var canonryMcpTools = [
|
|
|
5395
5425
|
openApiOperations: ["POST /api/v1/projects/{name}/traffic/sources/{id}/backfill"],
|
|
5396
5426
|
handler: (client2, input) => client2.trafficBackfill(input.project, input.sourceId, input.days !== void 0 ? { days: input.days } : void 0)
|
|
5397
5427
|
}),
|
|
5428
|
+
defineTool({
|
|
5429
|
+
name: "canonry_traffic_reset",
|
|
5430
|
+
title: "Advance traffic source lastSyncedAt to NOW",
|
|
5431
|
+
description: "Operator recovery for a stuck traffic source. Advances `lastSyncedAt` to NOW, sets `status` back to `connected`, and clears `last_error`. Accepts any non-archived source: the `lastSyncedAt` advance determines the next sync window for time-windowed sources (Vercel, Cloud Run); cursor-based sources (WordPress) keep their `last_cursor` so the advance is informational. Common trigger: an idle Vercel/Cloud Run source whose `lastSyncedAt` aged past the upstream retention boundary and every sync now throws a retention error. Historical events in the gap are unrecoverable from the sync path; run canonry_traffic_backfill separately if any of them are needed. Archived sources are rejected \u2014 re-connect via the appropriate canonry_traffic_connect_* tool instead.",
|
|
5432
|
+
access: "write",
|
|
5433
|
+
tier: "traffic",
|
|
5434
|
+
inputSchema: trafficResetInputSchema,
|
|
5435
|
+
annotations: writeAnnotations({ idempotentHint: true, destructiveHint: true }),
|
|
5436
|
+
openApiOperations: ["POST /api/v1/projects/{name}/traffic/sources/{id}/reset"],
|
|
5437
|
+
handler: (client2, input) => client2.trafficReset(input.project, input.sourceId)
|
|
5438
|
+
}),
|
|
5398
5439
|
defineTool({
|
|
5399
5440
|
name: "canonry_project_upsert",
|
|
5400
5441
|
title: "Create or replace project",
|
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
loadConfig,
|
|
7
7
|
loadConfigRaw,
|
|
8
8
|
saveConfigPatch
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-6X5TF73A.js";
|
|
10
10
|
import {
|
|
11
11
|
DEFAULT_RUN_HISTORY_LIMIT,
|
|
12
12
|
IntelligenceService,
|
|
@@ -87,7 +87,7 @@ import {
|
|
|
87
87
|
smoothedRunDelta,
|
|
88
88
|
trafficSources,
|
|
89
89
|
usageCounters
|
|
90
|
-
} from "./chunk-
|
|
90
|
+
} from "./chunk-4KWPOVIT.js";
|
|
91
91
|
import {
|
|
92
92
|
AGENT_MEMORY_VALUE_MAX_BYTES,
|
|
93
93
|
AGENT_PROVIDER_IDS,
|
|
@@ -267,6 +267,7 @@ import {
|
|
|
267
267
|
trafficConnectVercelRequestSchema,
|
|
268
268
|
trafficConnectWordpressRequestSchema,
|
|
269
269
|
trafficEventsResponseSchema,
|
|
270
|
+
trafficResetRequestSchema,
|
|
270
271
|
trafficSourceDetailDtoSchema,
|
|
271
272
|
trafficSourceDtoSchema,
|
|
272
273
|
trafficSourceListResponseSchema,
|
|
@@ -288,7 +289,7 @@ import {
|
|
|
288
289
|
wordpressSchemaDeployResultDtoSchema,
|
|
289
290
|
wordpressSchemaStatusResultDtoSchema,
|
|
290
291
|
wordpressStatusDtoSchema
|
|
291
|
-
} from "./chunk-
|
|
292
|
+
} from "./chunk-WFVUZVJD.js";
|
|
292
293
|
|
|
293
294
|
// src/telemetry.ts
|
|
294
295
|
import crypto from "crypto";
|
|
@@ -11502,6 +11503,36 @@ var routeCatalog = [
|
|
|
11502
11503
|
404: errorResponse("Project or traffic source not found.")
|
|
11503
11504
|
}
|
|
11504
11505
|
},
|
|
11506
|
+
{
|
|
11507
|
+
method: "post",
|
|
11508
|
+
path: "/api/v1/projects/{name}/traffic/sources/{id}/reset",
|
|
11509
|
+
summary: "Advance lastSyncedAt to NOW and clear the error state",
|
|
11510
|
+
description: "Operator recovery: advances `lastSyncedAt` to NOW, sets `status` back to `connected`, and clears `last_error`. Accepts any non-archived source \u2014 the `lastSyncedAt` advance determines the next sync window for time-windowed sources (Vercel, Cloud Run) and is informational for cursor-based sources (WordPress, where `last_cursor` governs the next drain and is preserved). Common trigger: an idle Vercel/Cloud Run source whose `lastSyncedAt` has aged past the upstream retention window (`request-logs` ~14d, Cloud Logging 30d) and now throws on every sync. Any pre-existing rollup history stays in place; the skipped history is the explicit trade-off \u2014 run `traffic backfill` separately to recover any of it. `advanceToNow: true` is required (no implicit reset). Archived sources are rejected with 400 \u2014 re-connect them via the appropriate `traffic/connect/*` endpoint instead.",
|
|
11511
|
+
tags: ["traffic"],
|
|
11512
|
+
parameters: [
|
|
11513
|
+
nameParameter,
|
|
11514
|
+
{ name: "id", in: "path", required: true, description: "Traffic source ID.", schema: stringSchema }
|
|
11515
|
+
],
|
|
11516
|
+
requestBody: {
|
|
11517
|
+
required: true,
|
|
11518
|
+
content: {
|
|
11519
|
+
"application/json": {
|
|
11520
|
+
schema: {
|
|
11521
|
+
type: "object",
|
|
11522
|
+
required: ["advanceToNow"],
|
|
11523
|
+
properties: {
|
|
11524
|
+
advanceToNow: { type: "boolean", enum: [true], description: "Must be `true` \u2014 explicit gate against accidental resets." }
|
|
11525
|
+
}
|
|
11526
|
+
}
|
|
11527
|
+
}
|
|
11528
|
+
}
|
|
11529
|
+
},
|
|
11530
|
+
responses: {
|
|
11531
|
+
200: jsonResponse("Source reset; lastSyncedAt advanced to NOW.", "TrafficSourceDetailDto"),
|
|
11532
|
+
400: errorResponse("Missing or invalid `advanceToNow` flag, or the source is archived."),
|
|
11533
|
+
404: errorResponse("Project or traffic source not found.")
|
|
11534
|
+
}
|
|
11535
|
+
},
|
|
11505
11536
|
{
|
|
11506
11537
|
method: "get",
|
|
11507
11538
|
path: "/api/v1/projects/{name}/traffic/sources",
|
|
@@ -21590,14 +21621,34 @@ var VERCEL_REQUEST_LOGS_URL = "https://vercel.com/api/logs/request-logs";
|
|
|
21590
21621
|
var DEFAULT_ENVIRONMENT = "production";
|
|
21591
21622
|
var DEFAULT_MAX_PAGES3 = 1;
|
|
21592
21623
|
var DEFAULT_TIMEOUT_MS3 = 3e4;
|
|
21624
|
+
var DEFAULT_MAX_RETRIES = 3;
|
|
21625
|
+
var DEFAULT_INITIAL_RETRY_DELAY_MS = 1e3;
|
|
21593
21626
|
var VercelLogsApiError = class extends Error {
|
|
21594
|
-
constructor(message, status, body) {
|
|
21627
|
+
constructor(message, status, body, retryAfterSeconds) {
|
|
21595
21628
|
super(message);
|
|
21596
21629
|
this.status = status;
|
|
21597
21630
|
this.body = body;
|
|
21631
|
+
this.retryAfterSeconds = retryAfterSeconds;
|
|
21598
21632
|
this.name = "VercelLogsApiError";
|
|
21599
21633
|
}
|
|
21600
21634
|
};
|
|
21635
|
+
function parseRetryAfter2(headerValue) {
|
|
21636
|
+
if (!headerValue) return void 0;
|
|
21637
|
+
const trimmed = headerValue.trim();
|
|
21638
|
+
const asNum = Number(trimmed);
|
|
21639
|
+
if (Number.isFinite(asNum) && asNum >= 0) return asNum;
|
|
21640
|
+
const asDate = Date.parse(trimmed);
|
|
21641
|
+
if (!Number.isNaN(asDate)) {
|
|
21642
|
+
return Math.max(0, (asDate - Date.now()) / 1e3);
|
|
21643
|
+
}
|
|
21644
|
+
return void 0;
|
|
21645
|
+
}
|
|
21646
|
+
function isRetryableVercelError(error) {
|
|
21647
|
+
if (error instanceof VercelLogsApiError) {
|
|
21648
|
+
return error.status === 429 || error.status >= 500;
|
|
21649
|
+
}
|
|
21650
|
+
return true;
|
|
21651
|
+
}
|
|
21601
21652
|
function trimRequired2(name, value) {
|
|
21602
21653
|
const trimmed = value.trim();
|
|
21603
21654
|
if (!trimmed) {
|
|
@@ -21624,6 +21675,24 @@ async function readErrorBody3(response) {
|
|
|
21624
21675
|
if (!text) return void 0;
|
|
21625
21676
|
return text.length <= 500 ? text : `${text.slice(0, 500)}... [truncated]`;
|
|
21626
21677
|
}
|
|
21678
|
+
async function withVercelRetry(attempt, maxRetries, initialDelayMs) {
|
|
21679
|
+
let lastError;
|
|
21680
|
+
for (let attemptNumber = 0; attemptNumber <= maxRetries; attemptNumber += 1) {
|
|
21681
|
+
try {
|
|
21682
|
+
return await attempt();
|
|
21683
|
+
} catch (error) {
|
|
21684
|
+
lastError = error;
|
|
21685
|
+
if (attemptNumber >= maxRetries || !isRetryableVercelError(error)) throw error;
|
|
21686
|
+
const retryAfterSeconds = error instanceof VercelLogsApiError ? error.retryAfterSeconds : void 0;
|
|
21687
|
+
const computedDelayMs = initialDelayMs * Math.pow(2, attemptNumber);
|
|
21688
|
+
const delayMs = retryAfterSeconds !== void 0 ? Math.max(0, retryAfterSeconds * 1e3) : computedDelayMs;
|
|
21689
|
+
if (delayMs > 0) {
|
|
21690
|
+
await new Promise((resolve) => setTimeout(resolve, delayMs));
|
|
21691
|
+
}
|
|
21692
|
+
}
|
|
21693
|
+
}
|
|
21694
|
+
throw lastError;
|
|
21695
|
+
}
|
|
21627
21696
|
async function listVercelTrafficEvents(options) {
|
|
21628
21697
|
const token = trimRequired2("token", options.token);
|
|
21629
21698
|
const projectId = trimRequired2("projectId", options.projectId);
|
|
@@ -21633,6 +21702,8 @@ async function listVercelTrafficEvents(options) {
|
|
|
21633
21702
|
const endDate = toEpochMs("endDate", options.endDate);
|
|
21634
21703
|
const maxPages = normalizeMaxPages3(options.maxPages);
|
|
21635
21704
|
const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS3;
|
|
21705
|
+
const maxRetries = options.maxRetries ?? DEFAULT_MAX_RETRIES;
|
|
21706
|
+
const initialRetryDelayMs = options.initialRetryDelayMs ?? DEFAULT_INITIAL_RETRY_DELAY_MS;
|
|
21636
21707
|
let rawEntryCount = 0;
|
|
21637
21708
|
let skippedEntryCount = 0;
|
|
21638
21709
|
let hasMore = false;
|
|
@@ -21646,23 +21717,27 @@ async function listVercelTrafficEvents(options) {
|
|
|
21646
21717
|
url.searchParams.set("startDate", startDate);
|
|
21647
21718
|
url.searchParams.set("endDate", endDate);
|
|
21648
21719
|
url.searchParams.set("environment", environment);
|
|
21649
|
-
const
|
|
21650
|
-
|
|
21651
|
-
|
|
21652
|
-
|
|
21653
|
-
|
|
21654
|
-
|
|
21655
|
-
|
|
21656
|
-
|
|
21657
|
-
|
|
21658
|
-
|
|
21659
|
-
|
|
21660
|
-
|
|
21661
|
-
|
|
21662
|
-
|
|
21663
|
-
|
|
21664
|
-
|
|
21665
|
-
|
|
21720
|
+
const body = await withVercelRetry(async () => {
|
|
21721
|
+
const response = await fetch(url, {
|
|
21722
|
+
method: "GET",
|
|
21723
|
+
headers: {
|
|
21724
|
+
Authorization: `Bearer ${token}`,
|
|
21725
|
+
Accept: "application/json"
|
|
21726
|
+
},
|
|
21727
|
+
signal: AbortSignal.timeout(timeoutMs)
|
|
21728
|
+
});
|
|
21729
|
+
if (!response.ok) {
|
|
21730
|
+
const errorBody = await readErrorBody3(response);
|
|
21731
|
+
const retryAfterSeconds = parseRetryAfter2(response.headers.get("retry-after"));
|
|
21732
|
+
throw new VercelLogsApiError(
|
|
21733
|
+
`Vercel request-logs endpoint returned HTTP ${response.status}`,
|
|
21734
|
+
response.status,
|
|
21735
|
+
errorBody,
|
|
21736
|
+
retryAfterSeconds
|
|
21737
|
+
);
|
|
21738
|
+
}
|
|
21739
|
+
return await response.json();
|
|
21740
|
+
}, maxRetries, initialRetryDelayMs);
|
|
21666
21741
|
const rows = body.rows ?? [];
|
|
21667
21742
|
rawEntryCount += rows.length;
|
|
21668
21743
|
for (const row of rows) {
|
|
@@ -21686,8 +21761,10 @@ async function listVercelTrafficEvents(options) {
|
|
|
21686
21761
|
}
|
|
21687
21762
|
|
|
21688
21763
|
// ../integration-vercel/src/drain.ts
|
|
21689
|
-
var MIN_SUB_WINDOW_MS =
|
|
21764
|
+
var MIN_SUB_WINDOW_MS = 1e3;
|
|
21690
21765
|
var FLOOR_SLICE_MAX_PAGES = 1e3;
|
|
21766
|
+
var FLOOR_CONGESTION_PROBE_INTERVAL = 60;
|
|
21767
|
+
var RETENTION_PROBE_WINDOW_MS = 6e4;
|
|
21691
21768
|
var RETENTION_BOUNDARY_TOLERANCE_MS = 60 * 6e4;
|
|
21692
21769
|
function toMs(value) {
|
|
21693
21770
|
return typeof value === "number" ? value : value.getTime();
|
|
@@ -21713,7 +21790,7 @@ async function isServable(options, windowStartMs, windowEndMs) {
|
|
|
21713
21790
|
}
|
|
21714
21791
|
}
|
|
21715
21792
|
async function resolveRetainedStart(options, unservableStartMs, endMs) {
|
|
21716
|
-
const tailStartMs = Math.max(unservableStartMs, endMs -
|
|
21793
|
+
const tailStartMs = Math.max(unservableStartMs, endMs - RETENTION_PROBE_WINDOW_MS);
|
|
21717
21794
|
if (!await isServable(options, tailStartMs, endMs)) {
|
|
21718
21795
|
return endMs;
|
|
21719
21796
|
}
|
|
@@ -21743,6 +21820,8 @@ async function drainVercelTrafficEvents(options) {
|
|
|
21743
21820
|
let effectiveStartMs = startMs;
|
|
21744
21821
|
let retentionClamped = false;
|
|
21745
21822
|
let retentionResolved = false;
|
|
21823
|
+
let floorSpanProbeCountdown = 0;
|
|
21824
|
+
let floorPageBudgetCountdown = 0;
|
|
21746
21825
|
while (cursorMs < endMs) {
|
|
21747
21826
|
if (subWindowCount >= options.maxSubWindows) {
|
|
21748
21827
|
throw new Error(
|
|
@@ -21750,6 +21829,9 @@ async function drainVercelTrafficEvents(options) {
|
|
|
21750
21829
|
);
|
|
21751
21830
|
}
|
|
21752
21831
|
const subEndMs = Math.min(cursorMs + spanMs, endMs);
|
|
21832
|
+
const subSpanMs = subEndMs - cursorMs;
|
|
21833
|
+
const useFloorPageBudget = subSpanMs <= MIN_SUB_WINDOW_MS && floorPageBudgetCountdown > 0;
|
|
21834
|
+
const pageBudget = useFloorPageBudget ? FLOOR_SLICE_MAX_PAGES : options.pagesPerSubWindow;
|
|
21753
21835
|
let page;
|
|
21754
21836
|
try {
|
|
21755
21837
|
page = await options.pull({
|
|
@@ -21759,7 +21841,7 @@ async function drainVercelTrafficEvents(options) {
|
|
|
21759
21841
|
environment: options.environment,
|
|
21760
21842
|
startDate: cursorMs,
|
|
21761
21843
|
endDate: subEndMs,
|
|
21762
|
-
maxPages:
|
|
21844
|
+
maxPages: pageBudget
|
|
21763
21845
|
});
|
|
21764
21846
|
} catch (error) {
|
|
21765
21847
|
if (isRetentionError(error) && !retentionResolved) {
|
|
@@ -21775,11 +21857,18 @@ async function drainVercelTrafficEvents(options) {
|
|
|
21775
21857
|
}
|
|
21776
21858
|
subWindowCount += 1;
|
|
21777
21859
|
if (page.hasMore) {
|
|
21778
|
-
const subSpanMs = subEndMs - cursorMs;
|
|
21779
21860
|
if (subSpanMs > MIN_SUB_WINDOW_MS) {
|
|
21780
21861
|
spanMs = Math.max(Math.floor(subSpanMs / 2), MIN_SUB_WINDOW_MS);
|
|
21862
|
+
if (spanMs === MIN_SUB_WINDOW_MS) {
|
|
21863
|
+
floorSpanProbeCountdown = FLOOR_CONGESTION_PROBE_INTERVAL;
|
|
21864
|
+
}
|
|
21781
21865
|
continue;
|
|
21782
21866
|
}
|
|
21867
|
+
if (pageBudget >= FLOOR_SLICE_MAX_PAGES) {
|
|
21868
|
+
throw new Error(
|
|
21869
|
+
`Vercel ${MIN_SUB_WINDOW_MS / 1e3}-second slice holds more than ${FLOOR_SLICE_MAX_PAGES} pages and cannot be drained further`
|
|
21870
|
+
);
|
|
21871
|
+
}
|
|
21783
21872
|
page = await options.pull({
|
|
21784
21873
|
token: options.token,
|
|
21785
21874
|
projectId: options.projectId,
|
|
@@ -21790,9 +21879,11 @@ async function drainVercelTrafficEvents(options) {
|
|
|
21790
21879
|
maxPages: FLOOR_SLICE_MAX_PAGES
|
|
21791
21880
|
});
|
|
21792
21881
|
subWindowCount += 1;
|
|
21882
|
+
floorSpanProbeCountdown = FLOOR_CONGESTION_PROBE_INTERVAL;
|
|
21883
|
+
floorPageBudgetCountdown = FLOOR_CONGESTION_PROBE_INTERVAL;
|
|
21793
21884
|
if (page.hasMore) {
|
|
21794
21885
|
throw new Error(
|
|
21795
|
-
`Vercel ${MIN_SUB_WINDOW_MS /
|
|
21886
|
+
`Vercel ${MIN_SUB_WINDOW_MS / 1e3}-second slice holds more than ${FLOOR_SLICE_MAX_PAGES} pages and cannot be drained further`
|
|
21796
21887
|
);
|
|
21797
21888
|
}
|
|
21798
21889
|
}
|
|
@@ -21805,7 +21896,16 @@ async function drainVercelTrafficEvents(options) {
|
|
|
21805
21896
|
cursorMs = subEndMs;
|
|
21806
21897
|
const remainingMs = endMs - cursorMs;
|
|
21807
21898
|
if (remainingMs > 0) {
|
|
21808
|
-
|
|
21899
|
+
if (spanMs === MIN_SUB_WINDOW_MS && floorSpanProbeCountdown > 0) {
|
|
21900
|
+
floorSpanProbeCountdown -= 1;
|
|
21901
|
+
if (floorPageBudgetCountdown > 0) floorPageBudgetCountdown -= 1;
|
|
21902
|
+
spanMs = Math.min(MIN_SUB_WINDOW_MS, remainingMs);
|
|
21903
|
+
} else {
|
|
21904
|
+
spanMs = Math.min(spanMs * 2, remainingMs);
|
|
21905
|
+
if (spanMs > MIN_SUB_WINDOW_MS) {
|
|
21906
|
+
floorPageBudgetCountdown = 0;
|
|
21907
|
+
}
|
|
21908
|
+
}
|
|
21809
21909
|
}
|
|
21810
21910
|
}
|
|
21811
21911
|
return { events, subWindowCount, effectiveStartMs, retentionClamped };
|
|
@@ -21820,6 +21920,7 @@ var DEFAULT_WP_PAGE_SIZE = 500;
|
|
|
21820
21920
|
var DEFAULT_WP_MAX_PAGES = 20;
|
|
21821
21921
|
var DEFAULT_VERCEL_MAX_PAGES = 50;
|
|
21822
21922
|
var VERCEL_MAX_SUB_WINDOWS = 5e3;
|
|
21923
|
+
var VERCEL_BACKFILL_CHUNK_MS = 60 * 6e4;
|
|
21823
21924
|
var MAX_TRACKED_EVENT_IDS = 1e3;
|
|
21824
21925
|
var DEFAULT_BACKFILL_DAYS = 30;
|
|
21825
21926
|
var MAX_BACKFILL_DAYS = 90;
|
|
@@ -21855,6 +21956,11 @@ async function defaultResolveAccessToken(record) {
|
|
|
21855
21956
|
"OAuth-mode Cloud Run sync is not yet supported in v1. Provide a service-account key file."
|
|
21856
21957
|
);
|
|
21857
21958
|
}
|
|
21959
|
+
function vercelRetentionClampError(requestedStartMs, effectiveStartMs) {
|
|
21960
|
+
return new Error(
|
|
21961
|
+
`Vercel request-logs retention starts at ${new Date(effectiveStartMs).toISOString()}, after requested start ${new Date(requestedStartMs).toISOString()}; refusing to advance because historical traffic would be skipped`
|
|
21962
|
+
);
|
|
21963
|
+
}
|
|
21858
21964
|
async function runBackfillTask(options) {
|
|
21859
21965
|
const {
|
|
21860
21966
|
app,
|
|
@@ -22285,7 +22391,15 @@ async function trafficRoutes(app, opts) {
|
|
|
22285
22391
|
sourceType: TrafficSourceTypes.vercel,
|
|
22286
22392
|
displayName: fallbackName,
|
|
22287
22393
|
status: TrafficSourceStatuses.connected,
|
|
22288
|
-
lastSyncedAt
|
|
22394
|
+
// Seed lastSyncedAt to NOW so the first sync uses a tight window.
|
|
22395
|
+
// Leaving this null would make the first sync fall back to
|
|
22396
|
+
// DEFAULT_SYNC_WINDOW_MINUTES (30 days) — which exceeds Vercel's
|
|
22397
|
+
// request-logs retention (~14 days), causing the first sync to
|
|
22398
|
+
// throw a retention error and leaving the source permanently
|
|
22399
|
+
// stuck before it ever drained an event. New users opt into
|
|
22400
|
+
// historical recovery via the explicit `traffic backfill` command;
|
|
22401
|
+
// they do not silently inherit a 30-day pull on connect.
|
|
22402
|
+
lastSyncedAt: now,
|
|
22289
22403
|
lastCursor: null,
|
|
22290
22404
|
lastError: null,
|
|
22291
22405
|
archivedAt: null,
|
|
@@ -22495,14 +22609,7 @@ async function trafficRoutes(app, opts) {
|
|
|
22495
22609
|
maxSubWindows: VERCEL_MAX_SUB_WINDOWS
|
|
22496
22610
|
});
|
|
22497
22611
|
if (drained.retentionClamped) {
|
|
22498
|
-
|
|
22499
|
-
{
|
|
22500
|
-
sourceId: sourceRow.id,
|
|
22501
|
-
requestedStart: windowStart.toISOString(),
|
|
22502
|
-
servedStart: new Date(drained.effectiveStartMs).toISOString()
|
|
22503
|
-
},
|
|
22504
|
-
"Vercel request-logs retention clamp: sync window predated plan retention; ingested only the portion Vercel still serves"
|
|
22505
|
-
);
|
|
22612
|
+
throw vercelRetentionClampError(windowStart.getTime(), drained.effectiveStartMs);
|
|
22506
22613
|
}
|
|
22507
22614
|
allEvents = drained.events;
|
|
22508
22615
|
} catch (e) {
|
|
@@ -22850,33 +22957,32 @@ async function trafficRoutes(app, opts) {
|
|
|
22850
22957
|
const vercelEnvironment = config.environment ?? credential.environment;
|
|
22851
22958
|
pullErrorPrefix = "Vercel pull failed";
|
|
22852
22959
|
pullForBackfill = async () => {
|
|
22853
|
-
const
|
|
22854
|
-
|
|
22855
|
-
|
|
22856
|
-
|
|
22857
|
-
|
|
22858
|
-
|
|
22859
|
-
|
|
22860
|
-
|
|
22861
|
-
|
|
22862
|
-
|
|
22863
|
-
|
|
22864
|
-
|
|
22865
|
-
|
|
22866
|
-
|
|
22867
|
-
|
|
22868
|
-
|
|
22869
|
-
|
|
22870
|
-
|
|
22871
|
-
|
|
22872
|
-
|
|
22873
|
-
|
|
22874
|
-
|
|
22875
|
-
|
|
22876
|
-
|
|
22877
|
-
);
|
|
22960
|
+
const collected = [];
|
|
22961
|
+
const seenEventIds = /* @__PURE__ */ new Set();
|
|
22962
|
+
const backfillEndMs = windowEnd.getTime();
|
|
22963
|
+
for (let chunkStartMs = windowStart.getTime(); chunkStartMs < backfillEndMs; chunkStartMs += VERCEL_BACKFILL_CHUNK_MS) {
|
|
22964
|
+
const chunkEndMs = Math.min(chunkStartMs + VERCEL_BACKFILL_CHUNK_MS, backfillEndMs);
|
|
22965
|
+
const drained = await drainVercelTrafficEvents({
|
|
22966
|
+
pull: pullVercelEvents,
|
|
22967
|
+
token: credential.token,
|
|
22968
|
+
projectId: vercelProjectId,
|
|
22969
|
+
teamId: vercelTeamId,
|
|
22970
|
+
environment: vercelEnvironment,
|
|
22971
|
+
startDate: chunkStartMs,
|
|
22972
|
+
endDate: chunkEndMs,
|
|
22973
|
+
pagesPerSubWindow: BACKFILL_MAX_PAGES,
|
|
22974
|
+
maxSubWindows: VERCEL_MAX_SUB_WINDOWS
|
|
22975
|
+
});
|
|
22976
|
+
if (drained.retentionClamped) {
|
|
22977
|
+
throw vercelRetentionClampError(chunkStartMs, drained.effectiveStartMs);
|
|
22978
|
+
}
|
|
22979
|
+
for (const event of drained.events) {
|
|
22980
|
+
if (seenEventIds.has(event.eventId)) continue;
|
|
22981
|
+
seenEventIds.add(event.eventId);
|
|
22982
|
+
collected.push(event);
|
|
22983
|
+
}
|
|
22878
22984
|
}
|
|
22879
|
-
return
|
|
22985
|
+
return collected;
|
|
22880
22986
|
};
|
|
22881
22987
|
}
|
|
22882
22988
|
const startedAt = windowEnd.toISOString();
|
|
@@ -22962,6 +23068,43 @@ async function trafficRoutes(app, opts) {
|
|
|
22962
23068
|
} : null
|
|
22963
23069
|
};
|
|
22964
23070
|
}
|
|
23071
|
+
app.post("/projects/:name/traffic/sources/:id/reset", async (request) => {
|
|
23072
|
+
const project = resolveProject(app.db, request.params.name);
|
|
23073
|
+
const parsed = trafficResetRequestSchema.safeParse(request.body ?? {});
|
|
23074
|
+
if (!parsed.success) {
|
|
23075
|
+
throw validationError(
|
|
23076
|
+
"`advanceToNow` must be `true`. There is no implicit reset."
|
|
23077
|
+
);
|
|
23078
|
+
}
|
|
23079
|
+
const sourceRow = app.db.select().from(trafficSources).where(and19(eq24(trafficSources.projectId, project.id), eq24(trafficSources.id, request.params.id))).get();
|
|
23080
|
+
if (!sourceRow) {
|
|
23081
|
+
throw notFound("traffic source", request.params.id);
|
|
23082
|
+
}
|
|
23083
|
+
if (sourceRow.status === TrafficSourceStatuses.archived) {
|
|
23084
|
+
throw validationError(
|
|
23085
|
+
`Traffic source "${sourceRow.id}" is archived. Re-connect via "canonry traffic connect ..." to start tracking it again.`
|
|
23086
|
+
);
|
|
23087
|
+
}
|
|
23088
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
23089
|
+
let updatedRow;
|
|
23090
|
+
app.db.transaction((tx) => {
|
|
23091
|
+
tx.update(trafficSources).set({
|
|
23092
|
+
lastSyncedAt: now,
|
|
23093
|
+
status: TrafficSourceStatuses.connected,
|
|
23094
|
+
lastError: null,
|
|
23095
|
+
updatedAt: now
|
|
23096
|
+
}).where(eq24(trafficSources.id, sourceRow.id)).run();
|
|
23097
|
+
writeAuditLog(tx, auditFromRequest(request, {
|
|
23098
|
+
projectId: project.id,
|
|
23099
|
+
actor: "api",
|
|
23100
|
+
action: "traffic.source.reset",
|
|
23101
|
+
entityType: "traffic_source",
|
|
23102
|
+
entityId: sourceRow.id
|
|
23103
|
+
}));
|
|
23104
|
+
updatedRow = tx.select().from(trafficSources).where(eq24(trafficSources.id, sourceRow.id)).get();
|
|
23105
|
+
});
|
|
23106
|
+
return buildSourceDetail(project.id, updatedRow, new Date(Date.now() - 24 * 60 * 6e4).toISOString());
|
|
23107
|
+
});
|
|
22965
23108
|
app.get("/projects/:name/traffic/sources", async (request) => {
|
|
22966
23109
|
const project = resolveProject(app.db, request.params.name);
|
|
22967
23110
|
const rows = app.db.select().from(trafficSources).where(eq24(trafficSources.projectId, project.id)).orderBy(desc13(trafficSources.createdAt)).all();
|
|
@@ -30006,7 +30149,7 @@ function readStoredGroundingSources(rawResponse) {
|
|
|
30006
30149
|
return result;
|
|
30007
30150
|
}
|
|
30008
30151
|
async function backfillInsightsCommand(project, opts) {
|
|
30009
|
-
const { IntelligenceService: IntelligenceService2 } = await import("./intelligence-service-
|
|
30152
|
+
const { IntelligenceService: IntelligenceService2 } = await import("./intelligence-service-NY3MAVPB.js");
|
|
30010
30153
|
const config = loadConfig();
|
|
30011
30154
|
const db = createClient(config.database);
|
|
30012
30155
|
migrate(db);
|
|
@@ -33408,6 +33551,7 @@ async function createServer(opts) {
|
|
|
33408
33551
|
keyUrl: adapter.keyUrl,
|
|
33409
33552
|
modelHint: `e.g. ${adapter.modelRegistry.defaultModel}`,
|
|
33410
33553
|
model: registry.get(adapter.name)?.config.model,
|
|
33554
|
+
defaultModel: adapter.modelRegistry.defaultModel,
|
|
33411
33555
|
configured: !!registry.get(adapter.name),
|
|
33412
33556
|
quota: registry.get(adapter.name)?.config.quotaPolicy,
|
|
33413
33557
|
vertexConfigured: adapter.name === "gemini" ? !!opts.config.providers?.gemini?.vertexProject : void 0
|
|
@@ -1116,6 +1116,11 @@ var providerSummaryEntryDtoSchema = z10.object({
|
|
|
1116
1116
|
keyUrl: z10.string().optional(),
|
|
1117
1117
|
modelHint: z10.string().optional(),
|
|
1118
1118
|
model: z10.string().optional(),
|
|
1119
|
+
/**
|
|
1120
|
+
* The adapter's built-in default model. Surfaced so the UI and CLI can show
|
|
1121
|
+
* the effective model even when no explicit `model` override is configured.
|
|
1122
|
+
*/
|
|
1123
|
+
defaultModel: z10.string().optional(),
|
|
1119
1124
|
configured: z10.boolean(),
|
|
1120
1125
|
quota: providerQuotaPolicySchema.optional(),
|
|
1121
1126
|
/** Whether Vertex AI is configured for this provider (Gemini only). */
|
|
@@ -3225,6 +3230,9 @@ var trafficBackfillRequestSchema = z23.object({
|
|
|
3225
3230
|
/** Lookback window in days. Capped server-side at the upstream log retention ceiling (Cloud Logging _Default = 30d). Default: 30. */
|
|
3226
3231
|
days: z23.number().int().positive().optional()
|
|
3227
3232
|
});
|
|
3233
|
+
var trafficResetRequestSchema = z23.object({
|
|
3234
|
+
advanceToNow: z23.literal(true)
|
|
3235
|
+
});
|
|
3228
3236
|
var trafficBackfillResponseSchema = z23.object({
|
|
3229
3237
|
sourceId: z23.string(),
|
|
3230
3238
|
runId: z23.string(),
|
|
@@ -3773,6 +3781,7 @@ export {
|
|
|
3773
3781
|
trafficConnectWordpressRequestSchema,
|
|
3774
3782
|
trafficConnectVercelRequestSchema,
|
|
3775
3783
|
trafficSyncResponseSchema,
|
|
3784
|
+
trafficResetRequestSchema,
|
|
3776
3785
|
trafficBackfillResponseSchema,
|
|
3777
3786
|
trafficSourceListResponseSchema,
|
|
3778
3787
|
trafficSourceDetailDtoSchema,
|