@agentsean/keywords 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +661 -0
- package/dist/cluster.d.ts +10 -0
- package/dist/cluster.d.ts.map +1 -0
- package/dist/cluster.js +119 -0
- package/dist/cluster.js.map +1 -0
- package/dist/difficulty.d.ts +8 -0
- package/dist/difficulty.d.ts.map +1 -0
- package/dist/difficulty.js +103 -0
- package/dist/difficulty.js.map +1 -0
- package/dist/embeddings.d.ts +28 -0
- package/dist/embeddings.d.ts.map +1 -0
- package/dist/embeddings.js +103 -0
- package/dist/embeddings.js.map +1 -0
- package/dist/engine.d.ts +4 -0
- package/dist/engine.d.ts.map +1 -0
- package/dist/engine.js +84 -0
- package/dist/engine.js.map +1 -0
- package/dist/gsc.d.ts +4 -0
- package/dist/gsc.d.ts.map +1 -0
- package/dist/gsc.js +17 -0
- package/dist/gsc.js.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -0
- package/dist/opportunities.d.ts +13 -0
- package/dist/opportunities.d.ts.map +1 -0
- package/dist/opportunities.js +79 -0
- package/dist/opportunities.js.map +1 -0
- package/dist/persist.d.ts +43 -0
- package/dist/persist.d.ts.map +1 -0
- package/dist/persist.js +127 -0
- package/dist/persist.js.map +1 -0
- package/dist/ranks.d.ts +27 -0
- package/dist/ranks.d.ts.map +1 -0
- package/dist/ranks.js +70 -0
- package/dist/ranks.js.map +1 -0
- package/dist/types.d.ts +81 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/vectors.d.ts +8 -0
- package/dist/vectors.d.ts.map +1 -0
- package/dist/vectors.js +24 -0
- package/dist/vectors.js.map +1 -0
- package/package.json +53 -0
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
const STRIKE_LO = 8;
|
|
2
|
+
const STRIKE_HI = 20;
|
|
3
|
+
export function aggregateQueries(rows) {
|
|
4
|
+
const map = new Map();
|
|
5
|
+
for (const r of rows) {
|
|
6
|
+
const cur = map.get(r.query) ?? {
|
|
7
|
+
query: r.query,
|
|
8
|
+
clicks: 0,
|
|
9
|
+
impressions: 0,
|
|
10
|
+
position: 0,
|
|
11
|
+
n: 0,
|
|
12
|
+
page: r.page ?? null,
|
|
13
|
+
};
|
|
14
|
+
cur.clicks += r.clicks;
|
|
15
|
+
cur.impressions += r.impressions;
|
|
16
|
+
if (r.position !== null) {
|
|
17
|
+
cur.position += r.position;
|
|
18
|
+
cur.n++;
|
|
19
|
+
}
|
|
20
|
+
if (!cur.page && r.page)
|
|
21
|
+
cur.page = r.page;
|
|
22
|
+
map.set(r.query, cur);
|
|
23
|
+
}
|
|
24
|
+
return [...map.values()].map((v) => ({
|
|
25
|
+
query: v.query,
|
|
26
|
+
clicks: v.clicks,
|
|
27
|
+
impressions: v.impressions,
|
|
28
|
+
position: v.n ? v.position / v.n : null,
|
|
29
|
+
page: v.page,
|
|
30
|
+
}));
|
|
31
|
+
}
|
|
32
|
+
export function strikingDistance(rows) {
|
|
33
|
+
return aggregateQueries(rows)
|
|
34
|
+
.filter((r) => r.position !== null && r.position >= STRIKE_LO && r.position <= STRIKE_HI)
|
|
35
|
+
.toSorted((a, b) => b.clicks - a.clicks)
|
|
36
|
+
.map((r) => ({
|
|
37
|
+
query: r.query,
|
|
38
|
+
kind: "striking_distance",
|
|
39
|
+
clicks: r.clicks,
|
|
40
|
+
impressions: r.impressions,
|
|
41
|
+
position: r.position,
|
|
42
|
+
page: r.page,
|
|
43
|
+
volume: null,
|
|
44
|
+
difficulty: null,
|
|
45
|
+
source: "gsc",
|
|
46
|
+
}));
|
|
47
|
+
}
|
|
48
|
+
export function demandOpportunities(rows) {
|
|
49
|
+
return aggregateQueries(rows)
|
|
50
|
+
.filter((r) => r.clicks > 0 || r.impressions > 0)
|
|
51
|
+
.toSorted((a, b) => b.clicks - a.clicks || b.impressions - a.impressions)
|
|
52
|
+
.map((r) => ({
|
|
53
|
+
query: r.query,
|
|
54
|
+
kind: "demand",
|
|
55
|
+
clicks: r.clicks,
|
|
56
|
+
impressions: r.impressions,
|
|
57
|
+
position: r.position,
|
|
58
|
+
page: r.page,
|
|
59
|
+
volume: null,
|
|
60
|
+
difficulty: null,
|
|
61
|
+
source: "gsc",
|
|
62
|
+
}));
|
|
63
|
+
}
|
|
64
|
+
export function expansionOpportunities(related, known) {
|
|
65
|
+
return related
|
|
66
|
+
.filter((r) => !known.has(r.query.toLowerCase()))
|
|
67
|
+
.map((r) => ({
|
|
68
|
+
query: r.query,
|
|
69
|
+
kind: "expansion",
|
|
70
|
+
clicks: r.clicks ?? 0,
|
|
71
|
+
impressions: r.impressions ?? 0,
|
|
72
|
+
position: r.position ?? null,
|
|
73
|
+
page: r.page ?? null,
|
|
74
|
+
volume: r.volume ?? null,
|
|
75
|
+
difficulty: null,
|
|
76
|
+
source: r.source,
|
|
77
|
+
}));
|
|
78
|
+
}
|
|
79
|
+
//# sourceMappingURL=opportunities.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"opportunities.js","sourceRoot":"","sources":["../src/opportunities.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { type SqliteDatabase } from "@agentsean/db";
|
|
2
|
+
import type { Cluster, Opportunity, RankRow } from "./types.js";
|
|
3
|
+
export declare function saveKeywords(db: SqliteDatabase, siteId: string, rows: Opportunity[], now?: Date): void;
|
|
4
|
+
export declare function saveClusters(db: SqliteDatabase, siteId: string, clusters: Cluster[], now?: Date): void;
|
|
5
|
+
export declare function saveRanks(db: SqliteDatabase, siteId: string, ranks: RankRow[], now?: Date): void;
|
|
6
|
+
export declare function listKeywords(db: SqliteDatabase, siteId: string): {
|
|
7
|
+
id: string;
|
|
8
|
+
siteId: string;
|
|
9
|
+
query: string;
|
|
10
|
+
source: string;
|
|
11
|
+
volume: number | null;
|
|
12
|
+
volumeSource: string | null;
|
|
13
|
+
difficulty: number | null;
|
|
14
|
+
clicks: number;
|
|
15
|
+
impressions: number;
|
|
16
|
+
position: number | null;
|
|
17
|
+
page: string | null;
|
|
18
|
+
clusterId: string | null;
|
|
19
|
+
createdAt: string;
|
|
20
|
+
updatedAt: string;
|
|
21
|
+
}[];
|
|
22
|
+
export declare function listClusters(db: SqliteDatabase, siteId: string): {
|
|
23
|
+
id: string;
|
|
24
|
+
siteId: string;
|
|
25
|
+
label: string;
|
|
26
|
+
memberCount: number;
|
|
27
|
+
centroidJson: string | null;
|
|
28
|
+
serpConfirmed: number;
|
|
29
|
+
createdAt: string;
|
|
30
|
+
}[];
|
|
31
|
+
export declare function listRanks(db: SqliteDatabase, siteId: string): {
|
|
32
|
+
id: string;
|
|
33
|
+
siteId: string;
|
|
34
|
+
query: string;
|
|
35
|
+
url: string | null;
|
|
36
|
+
position: number | null;
|
|
37
|
+
date: string;
|
|
38
|
+
provider: string;
|
|
39
|
+
estimatedUsd: number;
|
|
40
|
+
actualUsd: number;
|
|
41
|
+
createdAt: string;
|
|
42
|
+
}[];
|
|
43
|
+
//# sourceMappingURL=persist.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"persist.d.ts","sourceRoot":"","sources":["../src/persist.ts"],"names":[],"mappings":"AAEA,OAAO,EAIL,KAAK,cAAc,EACpB,MAAM,eAAe,CAAC;AACvB,OAAO,KAAK,EAAE,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAEhE,wBAAgB,YAAY,CAC1B,EAAE,EAAE,cAAc,EAClB,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,WAAW,EAAE,EACnB,GAAG,OAAa,GACf,IAAI,CA4CN;AAED,wBAAgB,YAAY,CAC1B,EAAE,EAAE,cAAc,EAClB,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,OAAO,EAAE,EACnB,GAAG,OAAa,GACf,IAAI,CA0BN;AAED,wBAAgB,SAAS,CACvB,EAAE,EAAE,cAAc,EAClB,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,OAAO,EAAE,EAChB,GAAG,OAAa,GACf,IAAI,CAwCN;AAED,wBAAgB,YAAY,CAAC,EAAE,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM;;;;;;;;;;;;;;;IAE9D;AAED,wBAAgB,YAAY,CAAC,EAAE,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM;;;;;;;;IAM9D;AAED,wBAAgB,SAAS,CAAC,EAAE,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM;;;;;;;;;;;IAE3D"}
|
package/dist/persist.js
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { eq } from "drizzle-orm";
|
|
3
|
+
import { keywordClusters, keywords, rankSnapshots, } from "@agentsean/db";
|
|
4
|
+
export function saveKeywords(db, siteId, rows, now = new Date()) {
|
|
5
|
+
const ts = now.toISOString();
|
|
6
|
+
for (const row of rows) {
|
|
7
|
+
const existing = db
|
|
8
|
+
.select()
|
|
9
|
+
.from(keywords)
|
|
10
|
+
.where(eq(keywords.siteId, siteId))
|
|
11
|
+
.all()
|
|
12
|
+
.find((r) => r.query === row.query);
|
|
13
|
+
if (existing) {
|
|
14
|
+
db.update(keywords)
|
|
15
|
+
.set({
|
|
16
|
+
source: row.source,
|
|
17
|
+
volume: row.volume,
|
|
18
|
+
difficulty: row.difficulty,
|
|
19
|
+
clicks: row.clicks,
|
|
20
|
+
impressions: row.impressions,
|
|
21
|
+
position: row.position,
|
|
22
|
+
page: row.page,
|
|
23
|
+
updatedAt: ts,
|
|
24
|
+
})
|
|
25
|
+
.where(eq(keywords.id, existing.id))
|
|
26
|
+
.run();
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
db.insert(keywords)
|
|
30
|
+
.values({
|
|
31
|
+
id: randomUUID(),
|
|
32
|
+
siteId,
|
|
33
|
+
query: row.query,
|
|
34
|
+
source: row.source,
|
|
35
|
+
volume: row.volume,
|
|
36
|
+
volumeSource: row.volume !== null ? row.source : null,
|
|
37
|
+
difficulty: row.difficulty,
|
|
38
|
+
clicks: row.clicks,
|
|
39
|
+
impressions: row.impressions,
|
|
40
|
+
position: row.position,
|
|
41
|
+
page: row.page,
|
|
42
|
+
clusterId: null,
|
|
43
|
+
createdAt: ts,
|
|
44
|
+
updatedAt: ts,
|
|
45
|
+
})
|
|
46
|
+
.run();
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
export function saveClusters(db, siteId, clusters, now = new Date()) {
|
|
50
|
+
const ts = now.toISOString();
|
|
51
|
+
db.delete(keywordClusters).where(eq(keywordClusters.siteId, siteId)).run();
|
|
52
|
+
const allKw = db.select().from(keywords).where(eq(keywords.siteId, siteId)).all();
|
|
53
|
+
for (const c of clusters) {
|
|
54
|
+
db.insert(keywordClusters)
|
|
55
|
+
.values({
|
|
56
|
+
id: c.id,
|
|
57
|
+
siteId,
|
|
58
|
+
label: c.label,
|
|
59
|
+
memberCount: c.members.length,
|
|
60
|
+
centroidJson: null,
|
|
61
|
+
serpConfirmed: c.serpConfirmed ? 1 : 0,
|
|
62
|
+
createdAt: ts,
|
|
63
|
+
})
|
|
64
|
+
.run();
|
|
65
|
+
for (const member of c.members) {
|
|
66
|
+
const row = allKw.find((k) => k.query === member);
|
|
67
|
+
if (row) {
|
|
68
|
+
db.update(keywords)
|
|
69
|
+
.set({ clusterId: c.id, updatedAt: ts })
|
|
70
|
+
.where(eq(keywords.id, row.id))
|
|
71
|
+
.run();
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
export function saveRanks(db, siteId, ranks, now = new Date()) {
|
|
77
|
+
const ts = now.toISOString();
|
|
78
|
+
const date = ts.slice(0, 10);
|
|
79
|
+
for (const r of ranks) {
|
|
80
|
+
const existing = db
|
|
81
|
+
.select()
|
|
82
|
+
.from(rankSnapshots)
|
|
83
|
+
.where(eq(rankSnapshots.siteId, siteId))
|
|
84
|
+
.all()
|
|
85
|
+
.find((row) => row.query === r.query && row.date === date && row.provider === r.provider);
|
|
86
|
+
if (existing) {
|
|
87
|
+
db.update(rankSnapshots)
|
|
88
|
+
.set({
|
|
89
|
+
url: r.url,
|
|
90
|
+
position: r.position,
|
|
91
|
+
estimatedUsd: r.estimatedUsd,
|
|
92
|
+
actualUsd: r.actualUsd,
|
|
93
|
+
})
|
|
94
|
+
.where(eq(rankSnapshots.id, existing.id))
|
|
95
|
+
.run();
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
db.insert(rankSnapshots)
|
|
99
|
+
.values({
|
|
100
|
+
id: randomUUID(),
|
|
101
|
+
siteId,
|
|
102
|
+
query: r.query,
|
|
103
|
+
url: r.url,
|
|
104
|
+
position: r.position,
|
|
105
|
+
date,
|
|
106
|
+
provider: r.provider,
|
|
107
|
+
estimatedUsd: r.estimatedUsd,
|
|
108
|
+
actualUsd: r.actualUsd,
|
|
109
|
+
createdAt: ts,
|
|
110
|
+
})
|
|
111
|
+
.run();
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
export function listKeywords(db, siteId) {
|
|
115
|
+
return db.select().from(keywords).where(eq(keywords.siteId, siteId)).all();
|
|
116
|
+
}
|
|
117
|
+
export function listClusters(db, siteId) {
|
|
118
|
+
return db
|
|
119
|
+
.select()
|
|
120
|
+
.from(keywordClusters)
|
|
121
|
+
.where(eq(keywordClusters.siteId, siteId))
|
|
122
|
+
.all();
|
|
123
|
+
}
|
|
124
|
+
export function listRanks(db, siteId) {
|
|
125
|
+
return db.select().from(rankSnapshots).where(eq(rankSnapshots.siteId, siteId)).all();
|
|
126
|
+
}
|
|
127
|
+
//# sourceMappingURL=persist.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"persist.js","sourceRoot":"","sources":["../src/persist.ts"],"names":[],"mappings":"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"}
|
package/dist/ranks.d.ts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { ProviderStack } from "@agentsean/providers";
|
|
2
|
+
import { type CostEstimate } from "@agentsean/providers";
|
|
3
|
+
import type { SqliteDatabase } from "@agentsean/db";
|
|
4
|
+
import type { RankRow } from "./types.js";
|
|
5
|
+
export type RankCheckResult = {
|
|
6
|
+
skipped: boolean;
|
|
7
|
+
reason?: string;
|
|
8
|
+
cadence: "weekly";
|
|
9
|
+
provider: string;
|
|
10
|
+
ranks: RankRow[];
|
|
11
|
+
quotes: CostEstimate[];
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* Weekly licensed rank check. Sean never scrapes Google.
|
|
15
|
+
* GSC position remains the free proxy; DataForSEO is the paid upgrade.
|
|
16
|
+
*/
|
|
17
|
+
export declare function runRankCheck(opts: {
|
|
18
|
+
db: SqliteDatabase;
|
|
19
|
+
siteId: string;
|
|
20
|
+
origin: string;
|
|
21
|
+
queries: string[];
|
|
22
|
+
stack: ProviderStack;
|
|
23
|
+
now?: Date;
|
|
24
|
+
dailyBudgetUsd?: number;
|
|
25
|
+
scrapeGoogle?: boolean;
|
|
26
|
+
}): Promise<RankCheckResult>;
|
|
27
|
+
//# sourceMappingURL=ranks.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ranks.d.ts","sourceRoot":"","sources":["../src/ranks.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,EAGL,KAAK,YAAY,EAClB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AACpD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAG1C,MAAM,MAAM,eAAe,GAAG;IAC5B,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,QAAQ,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,OAAO,EAAE,CAAC;IACjB,MAAM,EAAE,YAAY,EAAE,CAAC;CACxB,CAAC;AAEF;;;GAGG;AACH,wBAAsB,YAAY,CAAC,IAAI,EAAE;IACvC,EAAE,EAAE,cAAc,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,KAAK,EAAE,aAAa,CAAC;IACrB,GAAG,CAAC,EAAE,IAAI,CAAC;IACX,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB,GAAG,OAAO,CAAC,eAAe,CAAC,CA0D3B"}
|
package/dist/ranks.js
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { debitProvider, remainingBudgetUsd, } from "@agentsean/providers";
|
|
2
|
+
import { scrapeGoogleSerp } from "@agentsean/providers";
|
|
3
|
+
/**
|
|
4
|
+
* Weekly licensed rank check. Sean never scrapes Google.
|
|
5
|
+
* GSC position remains the free proxy; DataForSEO is the paid upgrade.
|
|
6
|
+
*/
|
|
7
|
+
export async function runRankCheck(opts) {
|
|
8
|
+
if (opts.scrapeGoogle)
|
|
9
|
+
scrapeGoogleSerp();
|
|
10
|
+
const now = opts.now ?? new Date();
|
|
11
|
+
if (!opts.stack.serp.available) {
|
|
12
|
+
return {
|
|
13
|
+
skipped: true,
|
|
14
|
+
reason: "no_licensed_rank_provider",
|
|
15
|
+
cadence: "weekly",
|
|
16
|
+
provider: "gsc",
|
|
17
|
+
ranks: [],
|
|
18
|
+
quotes: [],
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
const quotes = [];
|
|
22
|
+
const ranks = [];
|
|
23
|
+
const host = hostOf(opts.origin);
|
|
24
|
+
let remaining = remainingBudgetUsd(opts.db, opts.siteId, opts.dailyBudgetUsd ?? 8, now);
|
|
25
|
+
for (const query of opts.queries) {
|
|
26
|
+
const call = opts.stack.serp.serp(query);
|
|
27
|
+
quotes.push(call.estimate);
|
|
28
|
+
if (call.estimate.estimatedUsd > remaining) {
|
|
29
|
+
return {
|
|
30
|
+
skipped: ranks.length === 0,
|
|
31
|
+
reason: ranks.length === 0 ? "over_budget" : "partial_budget",
|
|
32
|
+
cadence: "weekly",
|
|
33
|
+
provider: opts.stack.serp.id,
|
|
34
|
+
ranks,
|
|
35
|
+
quotes,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
const result = await call.run();
|
|
39
|
+
debitProvider(opts.db, call.estimate, {
|
|
40
|
+
siteId: opts.siteId,
|
|
41
|
+
actualUsd: call.estimate.estimatedUsd,
|
|
42
|
+
});
|
|
43
|
+
remaining -= call.estimate.estimatedUsd;
|
|
44
|
+
const hit = result.items.find((i) => hostOf(i.url) === host) ?? null;
|
|
45
|
+
ranks.push({
|
|
46
|
+
query,
|
|
47
|
+
url: hit?.url ?? null,
|
|
48
|
+
position: hit?.rank ?? null,
|
|
49
|
+
provider: result.provider,
|
|
50
|
+
estimatedUsd: call.estimate.estimatedUsd,
|
|
51
|
+
actualUsd: call.estimate.estimatedUsd,
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
return {
|
|
55
|
+
skipped: false,
|
|
56
|
+
cadence: "weekly",
|
|
57
|
+
provider: opts.stack.serp.id,
|
|
58
|
+
ranks,
|
|
59
|
+
quotes,
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
function hostOf(value) {
|
|
63
|
+
try {
|
|
64
|
+
return new URL(value.includes("://") ? value : `https://${value}`).hostname.replace(/^www\./, "");
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
return value.replace(/^www\./, "");
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=ranks.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ranks.js","sourceRoot":"","sources":["../src/ranks.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import type { CostEstimate, KeywordRow, ProviderStack, SerpResult } from "@agentsean/providers";
|
|
2
|
+
export type QueryDaily = {
|
|
3
|
+
date: string;
|
|
4
|
+
query: string;
|
|
5
|
+
clicks: number;
|
|
6
|
+
impressions: number;
|
|
7
|
+
position: number | null;
|
|
8
|
+
page?: string | null;
|
|
9
|
+
};
|
|
10
|
+
export type Opportunity = {
|
|
11
|
+
query: string;
|
|
12
|
+
kind: "striking_distance" | "demand" | "expansion";
|
|
13
|
+
clicks: number;
|
|
14
|
+
impressions: number;
|
|
15
|
+
position: number | null;
|
|
16
|
+
page: string | null;
|
|
17
|
+
volume: number | null;
|
|
18
|
+
difficulty: number | null;
|
|
19
|
+
source: string;
|
|
20
|
+
};
|
|
21
|
+
export type Cluster = {
|
|
22
|
+
id: string;
|
|
23
|
+
label: string;
|
|
24
|
+
members: string[];
|
|
25
|
+
serpConfirmed: boolean;
|
|
26
|
+
};
|
|
27
|
+
export type RankRow = {
|
|
28
|
+
query: string;
|
|
29
|
+
url: string | null;
|
|
30
|
+
position: number | null;
|
|
31
|
+
provider: string;
|
|
32
|
+
estimatedUsd: number;
|
|
33
|
+
actualUsd: number;
|
|
34
|
+
};
|
|
35
|
+
export type Embeddings = {
|
|
36
|
+
model: string;
|
|
37
|
+
dim: number;
|
|
38
|
+
embed(text: string): number[] | Promise<number[]>;
|
|
39
|
+
};
|
|
40
|
+
export type VectorHit = {
|
|
41
|
+
id: string;
|
|
42
|
+
score: number;
|
|
43
|
+
};
|
|
44
|
+
export type VectorStore = {
|
|
45
|
+
upsert(id: string, vector: number[]): void;
|
|
46
|
+
knn(vector: number[], k: number): VectorHit[];
|
|
47
|
+
};
|
|
48
|
+
export type DifficultyModel = {
|
|
49
|
+
trained: boolean;
|
|
50
|
+
samples: number;
|
|
51
|
+
note: string;
|
|
52
|
+
predict(query: string, impressions: number): number | null;
|
|
53
|
+
};
|
|
54
|
+
export type KeywordsJobResult = {
|
|
55
|
+
skipped?: boolean;
|
|
56
|
+
reason?: string;
|
|
57
|
+
paidUpgrade: boolean;
|
|
58
|
+
opportunities: Opportunity[];
|
|
59
|
+
clusters: Cluster[];
|
|
60
|
+
strikingDistance: Opportunity[];
|
|
61
|
+
ranks: RankRow[];
|
|
62
|
+
quotes: CostEstimate[];
|
|
63
|
+
embeddingsModel: string;
|
|
64
|
+
difficultyNote: string;
|
|
65
|
+
};
|
|
66
|
+
export type KeywordsJobOptions = {
|
|
67
|
+
siteId: string;
|
|
68
|
+
origin: string;
|
|
69
|
+
now?: Date;
|
|
70
|
+
stack: ProviderStack;
|
|
71
|
+
gsc: QueryDaily[];
|
|
72
|
+
brandTerms?: string[];
|
|
73
|
+
embeddings?: Embeddings;
|
|
74
|
+
serpForCluster?: (query: string) => Promise<SerpResult | null>;
|
|
75
|
+
expandSeeds?: string[];
|
|
76
|
+
expand?: (seed: string) => Promise<KeywordRow[]>;
|
|
77
|
+
dailyBudgetUsd?: number;
|
|
78
|
+
trackLimit?: number;
|
|
79
|
+
};
|
|
80
|
+
export type { KeywordRow };
|
|
81
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { VectorStore } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Brute-force cosine KNN. sqlite-vec 0.1.9 is still alpha with brute-force only,
|
|
4
|
+
* so this is the honest default. LanceDB is the escape hatch above ~200k vectors.
|
|
5
|
+
*/
|
|
6
|
+
export declare function createMemoryVectorStore(): VectorStore;
|
|
7
|
+
export declare function createLanceVectorStore(): VectorStore;
|
|
8
|
+
//# sourceMappingURL=vectors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vectors.d.ts","sourceRoot":"","sources":["../src/vectors.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAa,WAAW,EAAE,MAAM,YAAY,CAAC;AAGzD;;;GAGG;AACH,wBAAgB,uBAAuB,IAAI,WAAW,CAcrD;AAED,wBAAgB,sBAAsB,IAAI,WAAW,CAIpD"}
|
package/dist/vectors.js
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { cosine } from "./embeddings.js";
|
|
2
|
+
/**
|
|
3
|
+
* Brute-force cosine KNN. sqlite-vec 0.1.9 is still alpha with brute-force only,
|
|
4
|
+
* so this is the honest default. LanceDB is the escape hatch above ~200k vectors.
|
|
5
|
+
*/
|
|
6
|
+
export function createMemoryVectorStore() {
|
|
7
|
+
const rows = new Map();
|
|
8
|
+
return {
|
|
9
|
+
upsert(id, vector) {
|
|
10
|
+
rows.set(id, vector);
|
|
11
|
+
},
|
|
12
|
+
knn(vector, k) {
|
|
13
|
+
const hits = [];
|
|
14
|
+
for (const [id, v] of rows) {
|
|
15
|
+
hits.push({ id, score: cosine(vector, v) });
|
|
16
|
+
}
|
|
17
|
+
return hits.toSorted((a, b) => b.score - a.score).slice(0, k);
|
|
18
|
+
},
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
export function createLanceVectorStore() {
|
|
22
|
+
throw new Error("LanceDB is the escape hatch above ~200k vectors and is not bundled. Stay on the in-memory/SQLite brute-force store until then.");
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=vectors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vectors.js","sourceRoot":"","sources":["../src/vectors.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAEzC;;;GAGG;AACH,MAAM,UAAU,uBAAuB;IACrC,MAAM,IAAI,GAAG,IAAI,GAAG,EAAoB,CAAC;IACzC,OAAO;QACL,MAAM,CAAC,EAAE,EAAE,MAAM;YACf,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC;QACvB,CAAC;QACD,GAAG,CAAC,MAAM,EAAE,CAAC;YACX,MAAM,IAAI,GAAgB,EAAE,CAAC;YAC7B,KAAK,MAAM,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,IAAI,EAAE,CAAC;gBAC3B,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC;YAC9C,CAAC;YACD,OAAO,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QAChE,CAAC;KACF,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,sBAAsB;IACpC,MAAM,IAAI,KAAK,CACb,gIAAgI,CACjI,CAAC;AACJ,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@agentsean/keywords",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"license": "AGPL-3.0-only",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"exports": {
|
|
7
|
+
".": {
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"import": "./dist/index.js"
|
|
10
|
+
}
|
|
11
|
+
},
|
|
12
|
+
"files": [
|
|
13
|
+
"dist"
|
|
14
|
+
],
|
|
15
|
+
"engines": {
|
|
16
|
+
"node": ">=22.19.0"
|
|
17
|
+
},
|
|
18
|
+
"dependencies": {
|
|
19
|
+
"drizzle-orm": "0.45.2",
|
|
20
|
+
"@agentsean/credentials": "1.0.0",
|
|
21
|
+
"@agentsean/providers": "1.0.0",
|
|
22
|
+
"@agentsean/db": "1.0.0"
|
|
23
|
+
},
|
|
24
|
+
"devDependencies": {
|
|
25
|
+
"typescript": "~5.9.3",
|
|
26
|
+
"vitest": "^4.1.11"
|
|
27
|
+
},
|
|
28
|
+
"publishConfig": {
|
|
29
|
+
"access": "public"
|
|
30
|
+
},
|
|
31
|
+
"repository": {
|
|
32
|
+
"type": "git",
|
|
33
|
+
"url": "git+https://github.com/seziro-team/agentsean.git",
|
|
34
|
+
"directory": "packages/keywords"
|
|
35
|
+
},
|
|
36
|
+
"homepage": "https://github.com/seziro-team/agentsean#readme",
|
|
37
|
+
"bugs": {
|
|
38
|
+
"url": "https://github.com/seziro-team/agentsean/issues"
|
|
39
|
+
},
|
|
40
|
+
"author": "Seziro (https://seziro.com)",
|
|
41
|
+
"description": "Agent Sean — keywords module",
|
|
42
|
+
"keywords": [
|
|
43
|
+
"seo",
|
|
44
|
+
"agent",
|
|
45
|
+
"agentsean",
|
|
46
|
+
"seo-automation"
|
|
47
|
+
],
|
|
48
|
+
"scripts": {
|
|
49
|
+
"build": "tsc -p tsconfig.json",
|
|
50
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
51
|
+
"test": "vitest run --config ../../vitest.config.ts"
|
|
52
|
+
}
|
|
53
|
+
}
|