@silverbackbase/root 0.2.1 → 0.3.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/README.md +4 -2
- package/dist/core/index.d.ts +2 -1
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +1 -0
- package/dist/core/index.js.map +1 -1
- package/dist/core/onboard.js +1 -1
- package/dist/core/onboard.js.map +1 -1
- package/dist/core/refresh.d.ts +18 -0
- package/dist/core/refresh.d.ts.map +1 -0
- package/dist/core/refresh.js +98 -0
- package/dist/core/refresh.js.map +1 -0
- package/dist/core/scheduler.d.ts +2 -0
- package/dist/core/scheduler.d.ts.map +1 -0
- package/dist/core/scheduler.js +37 -0
- package/dist/core/scheduler.js.map +1 -0
- package/dist/core/types.d.ts +9 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/hook.d.ts +8 -0
- package/dist/hook.d.ts.map +1 -0
- package/dist/hook.js +33 -0
- package/dist/hook.js.map +1 -0
- package/dist/init.js +52 -4
- package/dist/init.js.map +1 -1
- package/dist/mcp.d.ts.map +1 -1
- package/dist/mcp.js +8 -0
- package/dist/mcp.js.map +1 -1
- package/dist/server.js +4 -2
- package/dist/server.js.map +1 -1
- package/package.json +3 -1
package/README.md
CHANGED
|
@@ -18,6 +18,7 @@ URL → Firecrawl (site scraping) → LLM extraction → DataForSEO (local compe
|
|
|
18
18
|
| `root_get_category` | Read | Specific category (personas, objections, etc.) |
|
|
19
19
|
| `root_search` | Read | Text search across all knowledge |
|
|
20
20
|
| `root_onboard_from_url` | Write | Onboard a client from their website URL |
|
|
21
|
+
| `root_refresh` | Write | Re-query SERP and detect new/lost competitors — run weekly or on demand |
|
|
21
22
|
| `root_create_item` | Write | Add a knowledge item manually |
|
|
22
23
|
| `root_update_item` | Write | Update an existing item |
|
|
23
24
|
| `root_delete_item` | Write | Delete an item |
|
|
@@ -85,8 +86,9 @@ Root detects `DATABASE_URL` at startup and creates the table if it doesn't exist
|
|
|
85
86
|
| `DATABASE_URL` | No | PostgreSQL connection string. If set, Root uses Postgres instead of SQLite. |
|
|
86
87
|
| `ANTHROPIC_API_KEY` | For onboarding | LLM extraction from website content |
|
|
87
88
|
| `FIRECRAWL_API_KEY` | For onboarding | Website scraping |
|
|
88
|
-
| `DATAFORSEO_LOGIN` | For onboarding | Competitor analysis via SERP |
|
|
89
|
-
| `DATAFORSEO_PASSWORD` | For onboarding | Competitor analysis via SERP |
|
|
89
|
+
| `DATAFORSEO_LOGIN` | For onboarding + refresh | Competitor analysis via SERP |
|
|
90
|
+
| `DATAFORSEO_PASSWORD` | For onboarding + refresh | Competitor analysis via SERP |
|
|
91
|
+
| `ROOT_REFRESH_CRON` | No | Cron expression for automatic market refresh (e.g. `0 8 * * 1` = every Monday 8am). Scheduler is disabled if not set. |
|
|
90
92
|
|
|
91
93
|
---
|
|
92
94
|
|
package/dist/core/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export { getContext, getCategory, createItem, updateItem, deleteItem, searchKnowledge } from "./knowledge.js";
|
|
2
2
|
export { onboardFromUrl } from "./onboard.js";
|
|
3
|
-
export
|
|
3
|
+
export { refreshKnowledge } from "./refresh.js";
|
|
4
|
+
export type { KnowledgeCategory, KnowledgeItem, BusinessContext, OnboardResult, RefreshDiff } from "./types.js";
|
|
4
5
|
export { CATEGORIES } from "./types.js";
|
|
5
6
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/core/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAC9G,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,YAAY,EAAE,iBAAiB,EAAE,aAAa,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAC9G,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAChD,YAAY,EAAE,iBAAiB,EAAE,aAAa,EAAE,eAAe,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAChH,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC"}
|
package/dist/core/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export { getContext, getCategory, createItem, updateItem, deleteItem, searchKnowledge } from "./knowledge.js";
|
|
2
2
|
export { onboardFromUrl } from "./onboard.js";
|
|
3
|
+
export { refreshKnowledge } from "./refresh.js";
|
|
3
4
|
export { CATEGORIES } from "./types.js";
|
|
4
5
|
//# sourceMappingURL=index.js.map
|
package/dist/core/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAC9G,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAC9G,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAEhD,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC"}
|
package/dist/core/onboard.js
CHANGED
|
@@ -31,7 +31,7 @@ async function extractWithLLM(content) {
|
|
|
31
31
|
throw new Error("ANTHROPIC_API_KEY is required for onboarding");
|
|
32
32
|
const anthropic = new Anthropic({ apiKey });
|
|
33
33
|
const response = await anthropic.messages.create({
|
|
34
|
-
model: "claude-
|
|
34
|
+
model: "claude-haiku-4-5-20251001",
|
|
35
35
|
max_tokens: 4096,
|
|
36
36
|
messages: [
|
|
37
37
|
{
|
package/dist/core/onboard.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"onboard.js","sourceRoot":"","sources":["../../src/core/onboard.ts"],"names":[],"mappings":"AAAA,OAAO,SAAS,MAAM,mBAAmB,CAAC;AAC1C,OAAO,YAAY,MAAM,wBAAwB,CAAC;AAClD,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAGxD,MAAM,YAAY,GAA2B;IAC3C,OAAO,EAAE,2FAA2F;IACpG,QAAQ,EAAE,2FAA2F;IACrG,UAAU,EAAE,gFAAgF;IAC5F,eAAe,EAAE,+FAA+F;IAChH,MAAM,EAAE,uDAAuD;IAC/D,GAAG,EAAE,iEAAiE;CACvE,CAAC;AAEF,KAAK,UAAU,aAAa,CAAC,GAAW;IACtC,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC;IAC7C,IAAI,CAAC,MAAM;QAAE,MAAM,IAAI,KAAK,CAAC,8CAA8C,CAAC,CAAC;IAE7E,MAAM,SAAS,GAAG,IAAI,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC;IAE/C,MAAM,MAAM,GAAG,MAAM,SAAS,CAAC,QAAQ,CAAC,GAAG,EAAE;QAC3C,KAAK,EAAE,EAAE;QACT,aAAa,EAAE,EAAE,OAAO,EAAE,CAAC,UAAU,CAAC,EAAE;KACzC,CAAC,CAAC;IAEH,IAAI,CAAC,MAAM,CAAC,OAAO;QAAE,MAAM,IAAI,KAAK,CAAC,uCAAuC,CAAC,CAAC;IAE9E,OAAQ,MAAM,CAAC,IAA6E;SACzF,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,KAAK,IAAI,CAAC,QAAQ,EAAE,KAAK,IAAI,IAAI,CAAC,GAAG,IAAI,EAAE,OAAO,IAAI,CAAC,QAAQ,IAAI,EAAE,EAAE,CAAC;SACtF,IAAI,CAAC,aAAa,CAAC;SACnB,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;AACrB,CAAC;AAED,KAAK,UAAU,cAAc,CAAC,OAAe;IAC3C,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC;IAC7C,IAAI,CAAC,MAAM;QAAE,MAAM,IAAI,KAAK,CAAC,8CAA8C,CAAC,CAAC;IAE7E,MAAM,SAAS,GAAG,IAAI,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC;IAE5C,MAAM,QAAQ,GAAG,MAAM,SAAS,CAAC,QAAQ,CAAC,MAAM,CAAC;QAC/C,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"onboard.js","sourceRoot":"","sources":["../../src/core/onboard.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export interface CompetitorDiff {
|
|
2
|
+
domain: string;
|
|
3
|
+
name: string;
|
|
4
|
+
zone: string;
|
|
5
|
+
keyword: string;
|
|
6
|
+
positioning: string;
|
|
7
|
+
}
|
|
8
|
+
export interface RefreshDiff {
|
|
9
|
+
accountId: string;
|
|
10
|
+
refreshedAt: string;
|
|
11
|
+
newCompetitors: CompetitorDiff[];
|
|
12
|
+
lostCompetitors: Record<string, unknown>[];
|
|
13
|
+
unchanged: number;
|
|
14
|
+
summary: string;
|
|
15
|
+
skipped?: string;
|
|
16
|
+
}
|
|
17
|
+
export declare function refreshKnowledge(accountId: string): Promise<RefreshDiff>;
|
|
18
|
+
//# sourceMappingURL=refresh.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"refresh.d.ts","sourceRoot":"","sources":["../../src/core/refresh.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,WAAW;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,cAAc,EAAE,CAAC;IACjC,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;IAC3C,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AA4DD,wBAAsB,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAwD9E"}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { getContext, createItem } from "./knowledge.js";
|
|
2
|
+
async function fetchSerpCompetitors(geoZones, sector) {
|
|
3
|
+
const login = process.env.DATAFORSEO_LOGIN;
|
|
4
|
+
const password = process.env.DATAFORSEO_PASSWORD;
|
|
5
|
+
if (!login || !password || geoZones.length === 0 || !sector)
|
|
6
|
+
return [];
|
|
7
|
+
const credentials = Buffer.from(`${login}:${password}`).toString("base64");
|
|
8
|
+
const results = [];
|
|
9
|
+
for (const zone of geoZones.slice(0, 3)) {
|
|
10
|
+
const keyword = `${sector} ${zone}`.trim();
|
|
11
|
+
try {
|
|
12
|
+
const response = await fetch("https://api.dataforseo.com/v3/serp/google/organic/live/advanced", {
|
|
13
|
+
method: "POST",
|
|
14
|
+
headers: {
|
|
15
|
+
Authorization: `Basic ${credentials}`,
|
|
16
|
+
"Content-Type": "application/json",
|
|
17
|
+
},
|
|
18
|
+
body: JSON.stringify([{
|
|
19
|
+
keyword,
|
|
20
|
+
location_name: "France",
|
|
21
|
+
language_code: "fr",
|
|
22
|
+
device: "desktop",
|
|
23
|
+
os: "windows",
|
|
24
|
+
depth: 10,
|
|
25
|
+
}]),
|
|
26
|
+
});
|
|
27
|
+
if (!response.ok)
|
|
28
|
+
continue;
|
|
29
|
+
const data = await response.json();
|
|
30
|
+
const items = (data.tasks?.[0]?.result?.[0]?.items ?? [])
|
|
31
|
+
.filter(item => item.type === "organic" && item.domain)
|
|
32
|
+
.slice(0, 5)
|
|
33
|
+
.map(item => ({
|
|
34
|
+
domain: item.domain,
|
|
35
|
+
name: item.title ?? item.domain,
|
|
36
|
+
positioning: item.description ?? "",
|
|
37
|
+
zone,
|
|
38
|
+
keyword,
|
|
39
|
+
}));
|
|
40
|
+
results.push(...items);
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
// DataForSEO unavailable for this zone — continue
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return results;
|
|
47
|
+
}
|
|
48
|
+
export async function refreshKnowledge(accountId) {
|
|
49
|
+
const context = await getContext(accountId);
|
|
50
|
+
const geoZones = context.geo
|
|
51
|
+
.map(g => String(g.zone ?? ""))
|
|
52
|
+
.filter(Boolean);
|
|
53
|
+
const sector = String(context.profile?.sector ?? "");
|
|
54
|
+
if (!sector && geoZones.length === 0) {
|
|
55
|
+
return {
|
|
56
|
+
accountId,
|
|
57
|
+
refreshedAt: new Date().toISOString(),
|
|
58
|
+
newCompetitors: [],
|
|
59
|
+
lostCompetitors: [],
|
|
60
|
+
unchanged: context.competitors.length,
|
|
61
|
+
summary: "No changes",
|
|
62
|
+
skipped: "No sector or geo zones configured — run root_onboard_from_url first",
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
const freshSerp = await fetchSerpCompetitors(geoZones, sector);
|
|
66
|
+
const existingDomains = new Set(context.competitors
|
|
67
|
+
.map(c => String(c.domain ?? ""))
|
|
68
|
+
.filter(Boolean));
|
|
69
|
+
const freshDomains = new Set(freshSerp.map(c => c.domain).filter(Boolean));
|
|
70
|
+
const newCompetitors = freshSerp.filter(c => c.domain && !existingDomains.has(c.domain));
|
|
71
|
+
const lostCompetitors = context.competitors.filter(c => {
|
|
72
|
+
const domain = String(c.domain ?? "");
|
|
73
|
+
return domain && !freshDomains.has(domain);
|
|
74
|
+
});
|
|
75
|
+
for (const comp of newCompetitors) {
|
|
76
|
+
await createItem(accountId, "competitor", {
|
|
77
|
+
...comp,
|
|
78
|
+
source: "dataforseo_refresh",
|
|
79
|
+
detected_at: new Date().toISOString(),
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
const parts = [];
|
|
83
|
+
if (newCompetitors.length > 0)
|
|
84
|
+
parts.push(`${newCompetitors.length} new competitor(s) detected`);
|
|
85
|
+
if (lostCompetitors.length > 0)
|
|
86
|
+
parts.push(`${lostCompetitors.length} competitor(s) no longer in SERP`);
|
|
87
|
+
if (parts.length === 0)
|
|
88
|
+
parts.push("No changes detected");
|
|
89
|
+
return {
|
|
90
|
+
accountId,
|
|
91
|
+
refreshedAt: new Date().toISOString(),
|
|
92
|
+
newCompetitors,
|
|
93
|
+
lostCompetitors,
|
|
94
|
+
unchanged: context.competitors.length - lostCompetitors.length,
|
|
95
|
+
summary: parts.join(" — "),
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
//# sourceMappingURL=refresh.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"refresh.js","sourceRoot":"","sources":["../../src/core/refresh.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scheduler.d.ts","sourceRoot":"","sources":["../../src/core/scheduler.ts"],"names":[],"mappings":"AAMA,wBAAsB,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC,CAgCpD"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { refreshKnowledge } from "./refresh.js";
|
|
2
|
+
function accountId() {
|
|
3
|
+
return process.env.ROOT_ACCOUNT_ID ?? "default";
|
|
4
|
+
}
|
|
5
|
+
export async function startScheduler() {
|
|
6
|
+
const cronExpr = process.env.ROOT_REFRESH_CRON;
|
|
7
|
+
if (!cronExpr)
|
|
8
|
+
return;
|
|
9
|
+
let cron;
|
|
10
|
+
try {
|
|
11
|
+
cron = await import("node-cron");
|
|
12
|
+
}
|
|
13
|
+
catch {
|
|
14
|
+
console.error("[Root] node-cron not installed — scheduler disabled. Run: pnpm add node-cron");
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
if (!cron.validate(cronExpr)) {
|
|
18
|
+
console.error(`[Root] Invalid ROOT_REFRESH_CRON expression: "${cronExpr}"`);
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
cron.schedule(cronExpr, async () => {
|
|
22
|
+
const account = accountId();
|
|
23
|
+
console.log(`[Root] Scheduled refresh starting for account "${account}"`);
|
|
24
|
+
try {
|
|
25
|
+
const diff = await refreshKnowledge(account);
|
|
26
|
+
console.log(`[Root] Refresh complete — ${diff.summary}`);
|
|
27
|
+
if (diff.newCompetitors.length > 0) {
|
|
28
|
+
console.log(`[Root] New competitors: ${diff.newCompetitors.map(c => c.domain).join(", ")}`);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
catch (err) {
|
|
32
|
+
console.error("[Root] Scheduled refresh failed:", err);
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
console.log(`[Root] Market refresh scheduler active (cron: ${cronExpr})`);
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=scheduler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scheduler.js","sourceRoot":"","sources":["../../src/core/scheduler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAEhD,SAAS,SAAS;IAChB,OAAO,OAAO,CAAC,GAAG,CAAC,eAAe,IAAI,SAAS,CAAC;AAClD,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,cAAc;IAClC,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC;IAC/C,IAAI,CAAC,QAAQ;QAAE,OAAO;IAEtB,IAAI,IAAgC,CAAC;IACrC,IAAI,CAAC;QACH,IAAI,GAAG,MAAM,MAAM,CAAC,WAAW,CAAC,CAAC;IACnC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,CAAC,KAAK,CAAC,8EAA8E,CAAC,CAAC;QAC9F,OAAO;IACT,CAAC;IAED,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC7B,OAAO,CAAC,KAAK,CAAC,iDAAiD,QAAQ,GAAG,CAAC,CAAC;QAC5E,OAAO;IACT,CAAC;IAED,IAAI,CAAC,QAAQ,CAAC,QAAQ,EAAE,KAAK,IAAI,EAAE;QACjC,MAAM,OAAO,GAAG,SAAS,EAAE,CAAC;QAC5B,OAAO,CAAC,GAAG,CAAC,kDAAkD,OAAO,GAAG,CAAC,CAAC;QAC1E,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,MAAM,gBAAgB,CAAC,OAAO,CAAC,CAAC;YAC7C,OAAO,CAAC,GAAG,CAAC,6BAA6B,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC;YACzD,IAAI,IAAI,CAAC,cAAc,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACnC,OAAO,CAAC,GAAG,CAAC,2BAA2B,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YAC9F,CAAC;QACH,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,CAAC,KAAK,CAAC,kCAAkC,EAAE,GAAG,CAAC,CAAC;QACzD,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,OAAO,CAAC,GAAG,CAAC,iDAAiD,QAAQ,GAAG,CAAC,CAAC;AAC5E,CAAC"}
|
package/dist/core/types.d.ts
CHANGED
|
@@ -26,6 +26,15 @@ export interface OnboardResult {
|
|
|
26
26
|
followup_questions: string[];
|
|
27
27
|
saved_counts: Record<string, number>;
|
|
28
28
|
}
|
|
29
|
+
export interface RefreshDiff {
|
|
30
|
+
accountId: string;
|
|
31
|
+
refreshedAt: string;
|
|
32
|
+
newCompetitors: Record<string, unknown>[];
|
|
33
|
+
lostCompetitors: Record<string, unknown>[];
|
|
34
|
+
unchanged: number;
|
|
35
|
+
summary: string;
|
|
36
|
+
skipped?: string;
|
|
37
|
+
}
|
|
29
38
|
export interface ExtractedKnowledge {
|
|
30
39
|
profile?: Record<string, unknown>;
|
|
31
40
|
offers: Record<string, unknown>[];
|
package/dist/core/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/core/types.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,UAAU,gHAUb,CAAC;AAEX,MAAM,MAAM,iBAAiB,GAAG,CAAC,OAAO,UAAU,CAAC,CAAC,MAAM,CAAC,CAAC;AAE5D,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,iBAAiB,CAAC;IAC5B,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC9B,SAAS,EAAE,IAAI,CAAC;IAChB,SAAS,EAAE,IAAI,CAAC;CACjB;AAED,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IACxC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;IAClC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;IACpC,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;IACtC,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;IAC3C,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;IACvC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;IAC/B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;IACnC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;IAChC,IAAI,EAAE,MAAM,EAAE,CAAC;CAChB;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,eAAe,CAAC;IACzB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,kBAAkB,EAAE,MAAM,EAAE,CAAC;IAC7B,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACtC;AAED,MAAM,WAAW,kBAAkB;IACjC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;IAClC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;IACpC,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;IACtC,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;IAC3C,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;IAC/B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;IACnC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;IAChC,IAAI,EAAE,MAAM,EAAE,CAAC;CAChB"}
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/core/types.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,UAAU,gHAUb,CAAC;AAEX,MAAM,MAAM,iBAAiB,GAAG,CAAC,OAAO,UAAU,CAAC,CAAC,MAAM,CAAC,CAAC;AAE5D,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,iBAAiB,CAAC;IAC5B,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC9B,SAAS,EAAE,IAAI,CAAC;IAChB,SAAS,EAAE,IAAI,CAAC;CACjB;AAED,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IACxC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;IAClC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;IACpC,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;IACtC,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;IAC3C,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;IACvC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;IAC/B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;IACnC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;IAChC,IAAI,EAAE,MAAM,EAAE,CAAC;CAChB;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,eAAe,CAAC;IACzB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,kBAAkB,EAAE,MAAM,EAAE,CAAC;IAC7B,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACtC;AAED,MAAM,WAAW,WAAW;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;IAC1C,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;IAC3C,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,kBAAkB;IACjC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;IAClC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;IACpC,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;IACtC,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;IAC3C,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;IAC/B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;IACnC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;IAChC,IAAI,EAAE,MAAM,EAAE,CAAC;CAChB"}
|
package/dist/hook.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Root — UserPromptSubmit hook
|
|
4
|
+
* Detects content/SEO/prospection intent and reminds the agent to call root_get_context first.
|
|
5
|
+
* Installed at ~/.silverbackbase/root-context-hook.mjs by root-init.
|
|
6
|
+
*/
|
|
7
|
+
export {};
|
|
8
|
+
//# sourceMappingURL=hook.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hook.d.ts","sourceRoot":"","sources":["../src/hook.ts"],"names":[],"mappings":";AACA;;;;GAIG"}
|
package/dist/hook.js
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Root — UserPromptSubmit hook
|
|
4
|
+
* Detects content/SEO/prospection intent and reminds the agent to call root_get_context first.
|
|
5
|
+
* Installed at ~/.silverbackbase/root-context-hook.mjs by root-init.
|
|
6
|
+
*/
|
|
7
|
+
const CONTENT_KEYWORDS = [
|
|
8
|
+
// French
|
|
9
|
+
"rédige", "écris", "génère", "crée", "article", "blog", "email",
|
|
10
|
+
"prospection", "newsletter", "contenu", "annonce", "script", "pitch",
|
|
11
|
+
"landing", "page de vente", "post linkedin", "seo",
|
|
12
|
+
// English
|
|
13
|
+
"write", "draft", "generate", "create content", "blog post",
|
|
14
|
+
"email campaign", "ad copy", "sales page", "prospecting",
|
|
15
|
+
];
|
|
16
|
+
let raw = "";
|
|
17
|
+
process.stdin.on("data", (chunk) => { raw += chunk; });
|
|
18
|
+
process.stdin.on("end", () => {
|
|
19
|
+
try {
|
|
20
|
+
const payload = JSON.parse(raw);
|
|
21
|
+
const prompt = (payload.prompt ?? "").toLowerCase();
|
|
22
|
+
const isContentTask = CONTENT_KEYWORDS.some(kw => prompt.includes(kw));
|
|
23
|
+
if (isContentTask) {
|
|
24
|
+
process.stdout.write("[Root] Business context required — call root_get_context() BEFORE generating any content, email, SEO article, or prospection material.\n");
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
// Never interrupt the session
|
|
29
|
+
}
|
|
30
|
+
process.exit(0);
|
|
31
|
+
});
|
|
32
|
+
export {};
|
|
33
|
+
//# sourceMappingURL=hook.js.map
|
package/dist/hook.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hook.js","sourceRoot":"","sources":["../src/hook.ts"],"names":[],"mappings":";AACA;;;;GAIG;AAEH,MAAM,gBAAgB,GAAG;IACvB,SAAS;IACT,QAAQ,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO;IAC/D,aAAa,EAAE,YAAY,EAAE,SAAS,EAAE,SAAS,EAAE,QAAQ,EAAE,OAAO;IACpE,SAAS,EAAE,eAAe,EAAE,eAAe,EAAE,KAAK;IAClD,UAAU;IACV,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,gBAAgB,EAAE,WAAW;IAC3D,gBAAgB,EAAE,SAAS,EAAE,YAAY,EAAE,aAAa;CACzD,CAAC;AAEF,IAAI,GAAG,GAAG,EAAE,CAAC;AACb,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAa,EAAE,EAAE,GAAG,GAAG,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;AAC/D,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE;IAC3B,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAwB,CAAC;QACvD,MAAM,MAAM,GAAG,CAAC,OAAO,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC;QACpD,MAAM,aAAa,GAAG,gBAAgB,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC;QACvE,IAAI,aAAa,EAAE,CAAC;YAClB,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,0IAA0I,CAC3I,CAAC;QACJ,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,8BAA8B;IAChC,CAAC;IACD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
|
package/dist/init.js
CHANGED
|
@@ -26,6 +26,53 @@ function upsert(configPath) {
|
|
|
26
26
|
config.mcpServers = servers;
|
|
27
27
|
writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
28
28
|
}
|
|
29
|
+
const HOOK_SCRIPT = `#!/usr/bin/env node
|
|
30
|
+
// Root — UserPromptSubmit hook (installed by root-init, do not edit manually)
|
|
31
|
+
const KEYWORDS = [
|
|
32
|
+
"rédige","écris","génère","crée","article","blog","email","prospection",
|
|
33
|
+
"newsletter","contenu","annonce","script","pitch","landing","seo",
|
|
34
|
+
"write","draft","generate","create content","blog post","ad copy","sales page","prospecting",
|
|
35
|
+
];
|
|
36
|
+
let raw = "";
|
|
37
|
+
process.stdin.on("data", c => { raw += c; });
|
|
38
|
+
process.stdin.on("end", () => {
|
|
39
|
+
try {
|
|
40
|
+
const p = JSON.parse(raw);
|
|
41
|
+
const prompt = (p.prompt ?? "").toLowerCase();
|
|
42
|
+
if (KEYWORDS.some(kw => prompt.includes(kw))) {
|
|
43
|
+
process.stdout.write("[Root] Business context required — call root_get_context() BEFORE generating any content, email, SEO article, or prospection material.\\n");
|
|
44
|
+
}
|
|
45
|
+
} catch {}
|
|
46
|
+
process.exit(0);
|
|
47
|
+
});
|
|
48
|
+
`;
|
|
49
|
+
function installHook(sbbDir, claudeSettingsPath) {
|
|
50
|
+
mkdirSync(sbbDir, { recursive: true });
|
|
51
|
+
const hookPath = join(sbbDir, "root-context-hook.mjs");
|
|
52
|
+
writeFileSync(hookPath, HOOK_SCRIPT, "utf-8");
|
|
53
|
+
let settings = {};
|
|
54
|
+
if (existsSync(claudeSettingsPath)) {
|
|
55
|
+
try {
|
|
56
|
+
settings = JSON.parse(readFileSync(claudeSettingsPath, "utf-8"));
|
|
57
|
+
}
|
|
58
|
+
catch { }
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
mkdirSync(dirname(claudeSettingsPath), { recursive: true });
|
|
62
|
+
}
|
|
63
|
+
const hooks = settings.hooks ?? {};
|
|
64
|
+
const ups = hooks.UserPromptSubmit ?? [];
|
|
65
|
+
const alreadyInstalled = ups.some(h => h.hooks?.some(hook => hook.command?.includes("root-context-hook")));
|
|
66
|
+
if (!alreadyInstalled) {
|
|
67
|
+
ups.push({
|
|
68
|
+
matcher: "*",
|
|
69
|
+
hooks: [{ type: "command", command: `node ${hookPath}`, async: false }],
|
|
70
|
+
});
|
|
71
|
+
hooks.UserPromptSubmit = ups;
|
|
72
|
+
settings.hooks = hooks;
|
|
73
|
+
writeFileSync(claudeSettingsPath, JSON.stringify(settings, null, 2) + "\n", "utf-8");
|
|
74
|
+
}
|
|
75
|
+
}
|
|
29
76
|
const home = homedir();
|
|
30
77
|
const clients = [
|
|
31
78
|
{
|
|
@@ -38,10 +85,6 @@ const clients = [
|
|
|
38
85
|
? join(process.env.APPDATA ?? home, "Claude", "claude_desktop_config.json")
|
|
39
86
|
: join(home, "Library", "Application Support", "Claude", "claude_desktop_config.json"),
|
|
40
87
|
},
|
|
41
|
-
{
|
|
42
|
-
name: "Cursor",
|
|
43
|
-
path: join(process.cwd(), ".cursor", "mcp.json"),
|
|
44
|
-
},
|
|
45
88
|
];
|
|
46
89
|
const configured = [];
|
|
47
90
|
for (const client of clients) {
|
|
@@ -54,10 +97,15 @@ if (!configured.includes("Claude Code")) {
|
|
|
54
97
|
upsert(clients[0].path);
|
|
55
98
|
configured.push("Claude Code");
|
|
56
99
|
}
|
|
100
|
+
// Install UserPromptSubmit hook for Claude Code
|
|
101
|
+
const sbbDir = join(home, ".silverbackbase");
|
|
102
|
+
const claudeSettingsPath = join(home, ".claude", "settings.json");
|
|
103
|
+
installHook(sbbDir, claudeSettingsPath);
|
|
57
104
|
console.log("\n Root MCP configuré\n");
|
|
58
105
|
configured.forEach(name => console.log(` ✓ ${name}`));
|
|
59
106
|
console.log(`\n Mode : ${isLocal ? "local — SQLite, zéro config" : "cloud — root.silverbackbase.com"}`);
|
|
60
107
|
if (!isLocal)
|
|
61
108
|
console.log(` Token : ${token}`);
|
|
109
|
+
console.log(" Hook : UserPromptSubmit → auto-remind root_get_context");
|
|
62
110
|
console.log("\n Redémarrez votre agent IA pour activer Root.\n");
|
|
63
111
|
//# sourceMappingURL=init.js.map
|
package/dist/init.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"init.js","sourceRoot":"","sources":["../src/init.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAC7E,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAE1C,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AACnC,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;AACzC,MAAM,KAAK,GAAG,QAAQ,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;AAC7G,MAAM,OAAO,GAAG,CAAC,KAAK,CAAC;AAEvB,MAAM,YAAY,GAAG,qCAAqC,CAAC;AAE3D,MAAM,QAAQ,GAAG,OAAO;IACtB,CAAC,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,IAAI,EAAE,gCAAgC,EAAE,UAAU,CAAC,EAAE;IAChF,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,YAAY,EAAE,OAAO,EAAE,EAAE,aAAa,EAAE,UAAU,KAAK,EAAE,EAAE,EAAE,CAAC;AAEvF,SAAS,MAAM,CAAC,UAAkB;IAChC,IAAI,MAAM,GAA4B,EAAE,CAAC;IACzC,IAAI,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QAC3B,IAAI,CAAC;YAAC,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC,CAAC;QAAC,CAAC;QAAC,MAAM,CAAC,CAAA,CAAC;IAC1E,CAAC;SAAM,CAAC;QACN,SAAS,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACtD,CAAC;IACD,MAAM,OAAO,GAAI,MAAM,CAAC,UAAsC,IAAI,EAAE,CAAC;IACrE,OAAO,CAAC,IAAI,GAAG,QAAQ,CAAC;IACxB,MAAM,CAAC,UAAU,GAAG,OAAO,CAAC;IAC5B,aAAa,CAAC,UAAU,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,EAAE,OAAO,CAAC,CAAC;AAC7E,CAAC;AAED,MAAM,
|
|
1
|
+
{"version":3,"file":"init.js","sourceRoot":"","sources":["../src/init.ts"],"names":[],"mappings":"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"}
|
package/dist/mcp.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mcp.d.ts","sourceRoot":"","sources":["../src/mcp.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAGpE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"mcp.d.ts","sourceRoot":"","sources":["../src/mcp.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAGpE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AAcpC,wBAAgB,WAAW,IAAI,SAAS,CAkFvC;AAED,wBAAgB,gBAAgB,KAChB,GAAG,OAAO,uBAMzB"}
|
package/dist/mcp.js
CHANGED
|
@@ -3,6 +3,7 @@ import { WebStandardStreamableHTTPServerTransport } from "@modelcontextprotocol/
|
|
|
3
3
|
import { z } from "zod";
|
|
4
4
|
import { getContext, getCategory, createItem, updateItem, deleteItem, searchKnowledge } from "./core/knowledge.js";
|
|
5
5
|
import { onboardFromUrl } from "./core/onboard.js";
|
|
6
|
+
import { refreshKnowledge } from "./core/refresh.js";
|
|
6
7
|
const CATEGORY_ENUM = z.enum([
|
|
7
8
|
"offer", "persona", "objection", "differentiator",
|
|
8
9
|
"competitor", "geo", "pricing", "faq", "profile",
|
|
@@ -75,6 +76,13 @@ export function buildServer() {
|
|
|
75
76
|
const result = await deleteItem(accountId(), id);
|
|
76
77
|
return { content: [{ type: "text", text: JSON.stringify(result) }] };
|
|
77
78
|
});
|
|
79
|
+
server.registerTool("root_refresh", {
|
|
80
|
+
description: "Refresh market intelligence for this account. Re-queries DataForSEO SERP for the configured geo zones and sector, detects new competitors and competitors that disappeared from search results. Returns a structured diff. Run weekly or when you suspect market changes.",
|
|
81
|
+
inputSchema: {},
|
|
82
|
+
}, async () => {
|
|
83
|
+
const diff = await refreshKnowledge(accountId());
|
|
84
|
+
return { content: [{ type: "text", text: JSON.stringify(diff) }] };
|
|
85
|
+
});
|
|
78
86
|
return server;
|
|
79
87
|
}
|
|
80
88
|
export function createMcpHandler() {
|
package/dist/mcp.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mcp.js","sourceRoot":"","sources":["../src/mcp.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,wCAAwC,EAAE,MAAM,+DAA+D,CAAC;AACzH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AACnH,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"mcp.js","sourceRoot":"","sources":["../src/mcp.ts"],"names":[],"mappings":"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"}
|
package/dist/server.js
CHANGED
|
@@ -4,14 +4,16 @@ import { Hono } from "hono";
|
|
|
4
4
|
import { cors } from "hono/cors";
|
|
5
5
|
import { requireAuth } from "./auth.js";
|
|
6
6
|
import { createMcpHandler } from "./mcp.js";
|
|
7
|
+
import { startScheduler } from "./core/scheduler.js";
|
|
7
8
|
const port = parseInt(process.env.PORT ?? "3000");
|
|
8
9
|
const app = new Hono();
|
|
9
10
|
app.use("*", cors({ origin: "*", allowMethods: ["GET", "POST", "OPTIONS"] }));
|
|
10
|
-
app.get("/health", (c) => c.json({ status: "ok", service: "root", version: "0.
|
|
11
|
+
app.get("/health", (c) => c.json({ status: "ok", service: "root", version: "0.3.0" }));
|
|
11
12
|
app.all("/mcp", requireAuth, createMcpHandler());
|
|
12
|
-
serve({ fetch: app.fetch, port }, () => {
|
|
13
|
+
serve({ fetch: app.fetch, port }, async () => {
|
|
13
14
|
const base = process.env.ROOT_URL ?? `http://localhost:${port}`;
|
|
14
15
|
console.log(`Root server running on port ${port}`);
|
|
15
16
|
console.log(` MCP → ${base}/mcp`);
|
|
17
|
+
await startScheduler();
|
|
16
18
|
});
|
|
17
19
|
//# sourceMappingURL=server.js.map
|
package/dist/server.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAC1C,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AACxC,OAAO,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAC1C,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AACxC,OAAO,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAC5C,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAErD,MAAM,IAAI,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI,MAAM,CAAC,CAAC;AAElD,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC;AAEvB,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,EAAE,MAAM,EAAE,GAAG,EAAE,YAAY,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,CAAC,CAAC,CAAC;AAC9E,GAAG,CAAC,GAAG,CAAC,SAAS,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC;AACvF,GAAG,CAAC,GAAG,CAAC,MAAM,EAAE,WAAW,EAAE,gBAAgB,EAAE,CAAC,CAAC;AAEjD,KAAK,CAAC,EAAE,KAAK,EAAE,GAAG,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,KAAK,IAAI,EAAE;IAC3C,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,QAAQ,IAAI,oBAAoB,IAAI,EAAE,CAAC;IAChE,OAAO,CAAC,GAAG,CAAC,+BAA+B,IAAI,EAAE,CAAC,CAAC;IACnD,OAAO,CAAC,GAAG,CAAC,WAAW,IAAI,MAAM,CAAC,CAAC;IACnC,MAAM,cAAc,EAAE,CAAC;AACzB,CAAC,CAAC,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@silverbackbase/root",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Business knowledge primitive for AI agents",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -29,11 +29,13 @@
|
|
|
29
29
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
30
30
|
"hono": "^4.3.0",
|
|
31
31
|
"nanoid": "^5.0.0",
|
|
32
|
+
"node-cron": "^3.0.3",
|
|
32
33
|
"postgres": "^3.4.0",
|
|
33
34
|
"zod": "^3.23.0"
|
|
34
35
|
},
|
|
35
36
|
"devDependencies": {
|
|
36
37
|
"@types/node": "^22.0.0",
|
|
38
|
+
"@types/node-cron": "^3.0.11",
|
|
37
39
|
"typescript": "^5.4.0"
|
|
38
40
|
},
|
|
39
41
|
"engines": {
|