@diffdelta/client 0.1.0 → 0.1.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/README.md +152 -0
- package/dist/index.d.mts +143 -15
- package/dist/index.d.ts +143 -15
- package/dist/index.js +109 -24
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +103 -23
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
ADDED
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
# @diffdelta/client
|
|
2
|
+
|
|
3
|
+
TypeScript/JavaScript client for [DiffDelta](https://diffdelta.io) — agent-ready intelligence feeds for security advisories, changelogs, status pages, and more.
|
|
4
|
+
|
|
5
|
+
**One import. 46 sources. Structured signals. Zero scraping.**
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install @diffdelta/client
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Quick Start
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
import { DiffDelta } from "@diffdelta/client";
|
|
17
|
+
|
|
18
|
+
const dd = new DiffDelta();
|
|
19
|
+
|
|
20
|
+
// Poll for changes — checks head.json first (~200 bytes),
|
|
21
|
+
// only fetches the full feed if something changed.
|
|
22
|
+
const items = await dd.poll();
|
|
23
|
+
|
|
24
|
+
for (const item of items) {
|
|
25
|
+
console.log(`${item.source}: ${item.headline}`);
|
|
26
|
+
|
|
27
|
+
// Structured signals — no parsing needed
|
|
28
|
+
if (item.signals.severity) {
|
|
29
|
+
console.log(` Severity: ${item.signals.severity.level} (CVSS ${item.signals.severity.cvss})`);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Action codes — the bot knows what to do
|
|
33
|
+
if (item.suggestedAction === "PATCH_IMMEDIATELY") {
|
|
34
|
+
triggerEmergencyPatch(item);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Stack Discovery
|
|
40
|
+
|
|
41
|
+
Tell DiffDelta what you use, get back exactly which sources to watch:
|
|
42
|
+
|
|
43
|
+
```ts
|
|
44
|
+
const sources = await dd.discoverSources(["openai", "langchain", "pinecone"]);
|
|
45
|
+
// → ["openai_sdk_releases", "openai_api_changelog", "langchain_releases", "pinecone_status"]
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Health Check
|
|
49
|
+
|
|
50
|
+
Check if the DiffDelta pipeline is alive before trusting the data:
|
|
51
|
+
|
|
52
|
+
```ts
|
|
53
|
+
const health = await dd.checkHealth();
|
|
54
|
+
if (!health.ok) {
|
|
55
|
+
console.warn(`Pipeline degraded: ${health.sourcesOk}/${health.sourcesChecked} sources healthy`);
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Verified Silence
|
|
60
|
+
|
|
61
|
+
When nothing changed, DiffDelta proves it checked:
|
|
62
|
+
|
|
63
|
+
```ts
|
|
64
|
+
const head = await dd.head();
|
|
65
|
+
if (!head.changed && head.allClear) {
|
|
66
|
+
console.log(`All clear: ${head.sourcesChecked} sources verified, confidence ${head.allClearConfidence}`);
|
|
67
|
+
// A non-DiffDelta bot can never make this claim.
|
|
68
|
+
}
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Continuous Monitoring
|
|
72
|
+
|
|
73
|
+
```ts
|
|
74
|
+
const ac = new AbortController();
|
|
75
|
+
|
|
76
|
+
dd.watch(
|
|
77
|
+
(item) => {
|
|
78
|
+
if (item.suggestedAction === "PATCH_IMMEDIATELY") {
|
|
79
|
+
alertOncall(item);
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
{ tags: ["security"], signal: ac.signal }
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
// Stop after 1 hour
|
|
86
|
+
setTimeout(() => ac.abort(), 3_600_000);
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Per-Source Polling
|
|
90
|
+
|
|
91
|
+
More efficient if you only care about one source:
|
|
92
|
+
|
|
93
|
+
```ts
|
|
94
|
+
const items = await dd.pollSource("cisa_kev");
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## Cursor Persistence
|
|
98
|
+
|
|
99
|
+
Cursors are saved to `~/.diffdelta/cursors.json` by default so your bot survives restarts. Disable with:
|
|
100
|
+
|
|
101
|
+
```ts
|
|
102
|
+
const dd = new DiffDelta({ cursorPath: "memory" }); // in-memory only
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## Signal Types
|
|
106
|
+
|
|
107
|
+
Every item can carry structured signals — pre-extracted, no parsing needed:
|
|
108
|
+
|
|
109
|
+
| Signal | Fields | Example |
|
|
110
|
+
|--------|--------|---------|
|
|
111
|
+
| `severity` | level, cvss, cwes, packages, exploited | `{ level: "critical", cvss: 9.8 }` |
|
|
112
|
+
| `release` | version, prerelease, security_patch | `{ version: "4.2.1", security_patch: true }` |
|
|
113
|
+
| `incident` | status, impact | `{ status: "investigating", impact: "major" }` |
|
|
114
|
+
| `deprecation` | type, affects, confidence | `{ type: "breaking_change", affects: ["gpt-4-turbo"] }` |
|
|
115
|
+
| `suggested_action` | action code | `"PATCH_IMMEDIATELY"` |
|
|
116
|
+
|
|
117
|
+
## Action Codes
|
|
118
|
+
|
|
119
|
+
| Code | Meaning |
|
|
120
|
+
|------|---------|
|
|
121
|
+
| `PATCH_IMMEDIATELY` | Active exploitation or critical severity. Patch now. |
|
|
122
|
+
| `PATCH_SOON` | High severity. Schedule a patch. |
|
|
123
|
+
| `VERSION_PIN` | Breaking change coming. Pin your current version. |
|
|
124
|
+
| `REVIEW_CHANGELOG` | New release with notable changes. |
|
|
125
|
+
| `MONITOR_STATUS` | Incident in progress. Watch for updates. |
|
|
126
|
+
| `ACKNOWLEDGE` | Low-risk change. Log it. |
|
|
127
|
+
| `NO_ACTION` | Informational only. |
|
|
128
|
+
|
|
129
|
+
## Options
|
|
130
|
+
|
|
131
|
+
```ts
|
|
132
|
+
const dd = new DiffDelta({
|
|
133
|
+
baseUrl: "https://diffdelta.io", // default
|
|
134
|
+
apiKey: "dd_live_...", // Pro tier (optional)
|
|
135
|
+
cursorPath: null, // null = in-memory, string = file path
|
|
136
|
+
timeout: 15_000, // HTTP timeout in ms
|
|
137
|
+
});
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
## Protocol
|
|
141
|
+
|
|
142
|
+
DiffDelta uses a three-layer polling protocol (ddv1):
|
|
143
|
+
|
|
144
|
+
1. **head.json** (~200 bytes) — cursor + counts. Poll this first.
|
|
145
|
+
2. **digest.json** (~500 bytes) — narrative + alert count. Fetch if cursor changed.
|
|
146
|
+
3. **latest.json** (~5-40KB) — full items with signals. Fetch if alerts > 0.
|
|
147
|
+
|
|
148
|
+
The SDK handles this automatically. You never fetch more than you need.
|
|
149
|
+
|
|
150
|
+
## License
|
|
151
|
+
|
|
152
|
+
MIT
|
package/dist/index.d.mts
CHANGED
|
@@ -1,6 +1,55 @@
|
|
|
1
|
+
/** Severity signal extracted from security advisories. */
|
|
2
|
+
interface SeveritySignal {
|
|
3
|
+
level: "critical" | "high" | "medium" | "low" | string;
|
|
4
|
+
source: string;
|
|
5
|
+
cvss?: number;
|
|
6
|
+
cwes?: string[];
|
|
7
|
+
packages?: string[];
|
|
8
|
+
exploited?: boolean;
|
|
9
|
+
provenance?: SignalProvenance;
|
|
10
|
+
}
|
|
11
|
+
/** Release signal extracted from changelogs/GitHub releases. */
|
|
12
|
+
interface ReleaseSignal {
|
|
13
|
+
version: string;
|
|
14
|
+
prerelease?: boolean;
|
|
15
|
+
security_patch?: boolean;
|
|
16
|
+
provenance?: SignalProvenance;
|
|
17
|
+
}
|
|
18
|
+
/** Incident signal extracted from status pages. */
|
|
19
|
+
interface IncidentSignal {
|
|
20
|
+
status: "investigating" | "identified" | "monitoring" | "resolved" | string;
|
|
21
|
+
impact?: "minor" | "major" | "critical" | string;
|
|
22
|
+
provenance?: SignalProvenance;
|
|
23
|
+
}
|
|
24
|
+
/** Deprecation signal extracted from changelogs/advisories. */
|
|
25
|
+
interface DeprecationSignal {
|
|
26
|
+
type: "breaking_change" | "end_of_life" | "removal" | "deprecated" | string;
|
|
27
|
+
affects?: string[];
|
|
28
|
+
confidence: "high" | "medium" | "low" | string;
|
|
29
|
+
source: string;
|
|
30
|
+
provenance?: SignalProvenance;
|
|
31
|
+
}
|
|
32
|
+
/** Provenance chain for a signal — traces it to an authoritative source. */
|
|
33
|
+
interface SignalProvenance {
|
|
34
|
+
method: string;
|
|
35
|
+
authority: string;
|
|
36
|
+
authority_url: string;
|
|
37
|
+
evidence_url: string;
|
|
38
|
+
}
|
|
39
|
+
/** Action codes telling a bot exactly what to do. */
|
|
40
|
+
type SuggestedAction = "PATCH_IMMEDIATELY" | "PATCH_SOON" | "VERSION_PIN" | "REVIEW_CHANGELOG" | "MONITOR_STATUS" | "ACKNOWLEDGE" | "NO_ACTION";
|
|
41
|
+
/** All structured signals on an item. */
|
|
42
|
+
interface Signals {
|
|
43
|
+
severity?: SeveritySignal;
|
|
44
|
+
release?: ReleaseSignal;
|
|
45
|
+
incident?: IncidentSignal;
|
|
46
|
+
deprecation?: DeprecationSignal;
|
|
47
|
+
suggested_action?: SuggestedAction;
|
|
48
|
+
[key: string]: unknown;
|
|
49
|
+
}
|
|
1
50
|
/** A single item from a DiffDelta feed. */
|
|
2
51
|
interface FeedItem {
|
|
3
|
-
/** Source identifier (e.g. "cisa_kev", "
|
|
52
|
+
/** Source identifier (e.g. "cisa_kev", "github_advisories"). */
|
|
4
53
|
source: string;
|
|
5
54
|
/** Unique item ID within the source. */
|
|
6
55
|
id: string;
|
|
@@ -14,11 +63,15 @@ interface FeedItem {
|
|
|
14
63
|
publishedAt: string | null;
|
|
15
64
|
/** When the item was last updated. */
|
|
16
65
|
updatedAt: string | null;
|
|
17
|
-
/** Which change bucket: "new", "updated", or "
|
|
18
|
-
bucket: "new" | "updated" | "removed";
|
|
19
|
-
/**
|
|
66
|
+
/** Which change bucket: "new", "updated", "removed", or "flagged". */
|
|
67
|
+
bucket: "new" | "updated" | "removed" | "flagged";
|
|
68
|
+
/** Structured signals: severity, release, incident, deprecation, suggested_action. */
|
|
69
|
+
signals: Signals;
|
|
70
|
+
/** Shortcut: the suggested_action code, if any. */
|
|
71
|
+
suggestedAction: SuggestedAction | null;
|
|
72
|
+
/** Risk score 0.0–1.0, or null if not scored. */
|
|
20
73
|
riskScore: number | null;
|
|
21
|
-
/**
|
|
74
|
+
/** Item-level provenance (fetched_at, evidence_urls, content_hash). */
|
|
22
75
|
provenance: Record<string, unknown>;
|
|
23
76
|
/** The full raw item object from the feed. */
|
|
24
77
|
raw: Record<string, unknown>;
|
|
@@ -27,14 +80,40 @@ interface FeedItem {
|
|
|
27
80
|
interface Head {
|
|
28
81
|
/** Opaque cursor string for change detection. */
|
|
29
82
|
cursor: string;
|
|
30
|
-
/** Content hash of the latest feed. */
|
|
31
|
-
hash: string;
|
|
32
83
|
/** Whether content has changed since last generation. */
|
|
33
84
|
changed: boolean;
|
|
34
85
|
/** When this head was generated. */
|
|
35
86
|
generatedAt: string;
|
|
36
87
|
/** Recommended polling interval in seconds. */
|
|
37
88
|
ttlSec: number;
|
|
89
|
+
/** URL to the full latest.json feed. */
|
|
90
|
+
latestUrl: string;
|
|
91
|
+
/** URL to the digest (global head only). */
|
|
92
|
+
digestUrl: string | null;
|
|
93
|
+
/** Item counts: new, updated, removed, flagged. */
|
|
94
|
+
counts: {
|
|
95
|
+
new: number;
|
|
96
|
+
updated: number;
|
|
97
|
+
removed: number;
|
|
98
|
+
flagged: number;
|
|
99
|
+
};
|
|
100
|
+
/** Number of sources checked this cycle (Verified Silence). */
|
|
101
|
+
sourcesChecked: number;
|
|
102
|
+
/** Number of sources healthy this cycle. */
|
|
103
|
+
sourcesOk: number;
|
|
104
|
+
/** True if nothing changed AND all sources are healthy. */
|
|
105
|
+
allClear: boolean;
|
|
106
|
+
/** 0.0–1.0 confidence that allClear is trustworthy. */
|
|
107
|
+
allClearConfidence: number | null;
|
|
108
|
+
/** Pipeline freshness: oldest data age, stale count. */
|
|
109
|
+
freshness: {
|
|
110
|
+
oldest_data_age_sec: number;
|
|
111
|
+
mean_data_age_sec: number;
|
|
112
|
+
stale_count: number;
|
|
113
|
+
all_fresh: boolean;
|
|
114
|
+
} | null;
|
|
115
|
+
/** The full raw head.json object. */
|
|
116
|
+
raw: Record<string, unknown>;
|
|
38
117
|
}
|
|
39
118
|
/** A full DiffDelta feed response. */
|
|
40
119
|
interface Feed {
|
|
@@ -54,6 +133,8 @@ interface Feed {
|
|
|
54
133
|
updated: FeedItem[];
|
|
55
134
|
/** Items in the "removed" bucket. */
|
|
56
135
|
removed: FeedItem[];
|
|
136
|
+
/** Items in the "flagged" bucket. */
|
|
137
|
+
flagged: FeedItem[];
|
|
57
138
|
/** Human-readable summary of what changed. */
|
|
58
139
|
narrative: string;
|
|
59
140
|
/** The full raw feed object. */
|
|
@@ -80,6 +161,25 @@ interface SourceInfo {
|
|
|
80
161
|
/** Path to the source's latest.json. */
|
|
81
162
|
latestUrl: string;
|
|
82
163
|
}
|
|
164
|
+
/** Health check response from /healthz.json. */
|
|
165
|
+
interface HealthCheck {
|
|
166
|
+
ok: boolean;
|
|
167
|
+
service: string;
|
|
168
|
+
time: string;
|
|
169
|
+
sourcesChecked: number;
|
|
170
|
+
sourcesOk: number;
|
|
171
|
+
engineVersion: string;
|
|
172
|
+
}
|
|
173
|
+
/** Parse a raw feed item into a typed FeedItem. */
|
|
174
|
+
declare function parseFeedItem(data: Record<string, unknown>, bucket?: "new" | "updated" | "removed" | "flagged"): FeedItem;
|
|
175
|
+
/** Parse a raw head.json response into a typed Head. */
|
|
176
|
+
declare function parseHead(data: Record<string, unknown>): Head;
|
|
177
|
+
/** Parse a raw latest.json response into a typed Feed. */
|
|
178
|
+
declare function parseFeed(data: Record<string, unknown>): Feed;
|
|
179
|
+
/** Parse a raw source object into a typed SourceInfo. */
|
|
180
|
+
declare function parseSourceInfo(data: Record<string, unknown>): SourceInfo;
|
|
181
|
+
/** Parse a raw healthz.json response into a typed HealthCheck. */
|
|
182
|
+
declare function parseHealthCheck(data: Record<string, unknown>): HealthCheck;
|
|
83
183
|
|
|
84
184
|
/**
|
|
85
185
|
* DiffDelta TypeScript client — agent-ready intelligence feeds.
|
|
@@ -90,6 +190,10 @@ interface SourceInfo {
|
|
|
90
190
|
*
|
|
91
191
|
* const dd = new DiffDelta();
|
|
92
192
|
*
|
|
193
|
+
* // Quick health check — is the pipeline alive?
|
|
194
|
+
* const health = await dd.checkHealth();
|
|
195
|
+
* console.log(`Pipeline: ${health.ok ? "healthy" : "degraded"}, last run: ${health.time}`);
|
|
196
|
+
*
|
|
93
197
|
* // Poll for new items across all sources
|
|
94
198
|
* const items = await dd.poll();
|
|
95
199
|
* items.forEach(i => console.log(`${i.source}: ${i.headline}`));
|
|
@@ -97,6 +201,10 @@ interface SourceInfo {
|
|
|
97
201
|
* // Poll only security sources
|
|
98
202
|
* const sec = await dd.poll({ tags: ["security"] });
|
|
99
203
|
*
|
|
204
|
+
* // Check what's relevant to your stack
|
|
205
|
+
* const sources = await dd.discoverSources(["openai", "langchain", "pinecone"]);
|
|
206
|
+
* console.log("Watch these:", sources);
|
|
207
|
+
*
|
|
100
208
|
* // Continuous monitoring
|
|
101
209
|
* dd.watch(item => console.log("🚨", item.headline), { tags: ["security"] });
|
|
102
210
|
* ```
|
|
@@ -119,17 +227,17 @@ interface DiffDeltaOptions {
|
|
|
119
227
|
interface PollOptions {
|
|
120
228
|
/** Filter items to these tags (e.g. ["security"]). */
|
|
121
229
|
tags?: string[];
|
|
122
|
-
/** Filter items to these source IDs (e.g. ["cisa_kev", "
|
|
230
|
+
/** Filter items to these source IDs (e.g. ["cisa_kev", "github_advisories"]). */
|
|
123
231
|
sources?: string[];
|
|
124
232
|
/**
|
|
125
233
|
* Which buckets to return.
|
|
126
|
-
* Defaults to ["new", "updated"].
|
|
127
|
-
* Use ["new", "updated", "removed"] to include removals.
|
|
234
|
+
* Defaults to ["new", "updated", "flagged"].
|
|
235
|
+
* Use ["new", "updated", "removed", "flagged"] to include removals.
|
|
128
236
|
*/
|
|
129
|
-
buckets?: Array<"new" | "updated" | "removed">;
|
|
237
|
+
buckets?: Array<"new" | "updated" | "removed" | "flagged">;
|
|
130
238
|
}
|
|
131
239
|
interface WatchOptions extends PollOptions {
|
|
132
|
-
/** Seconds between polls. Defaults to feed TTL (usually
|
|
240
|
+
/** Seconds between polls. Defaults to feed TTL (usually 60s). */
|
|
133
241
|
interval?: number;
|
|
134
242
|
/** If provided, an AbortSignal to stop watching. */
|
|
135
243
|
signal?: AbortSignal;
|
|
@@ -144,7 +252,7 @@ declare class DiffDelta {
|
|
|
144
252
|
/**
|
|
145
253
|
* Poll the global feed for new items since last poll.
|
|
146
254
|
*
|
|
147
|
-
* Checks head.json first (~
|
|
255
|
+
* Checks head.json first (~200 bytes). Only fetches the full feed
|
|
148
256
|
* if the cursor has changed. Automatically saves the new cursor.
|
|
149
257
|
*/
|
|
150
258
|
poll(options?: PollOptions): Promise<FeedItem[]>;
|
|
@@ -156,7 +264,7 @@ declare class DiffDelta {
|
|
|
156
264
|
*/
|
|
157
265
|
pollSource(sourceId: string, options?: Omit<PollOptions, "sources">): Promise<FeedItem[]>;
|
|
158
266
|
/**
|
|
159
|
-
* Fetch a head.json pointer.
|
|
267
|
+
* Fetch a head.json pointer. Cheapest call (~200 bytes).
|
|
160
268
|
* @param url Full URL to head.json. Defaults to global head.
|
|
161
269
|
*/
|
|
162
270
|
head(url?: string): Promise<Head>;
|
|
@@ -167,6 +275,23 @@ declare class DiffDelta {
|
|
|
167
275
|
fetchFeed(url?: string): Promise<Feed>;
|
|
168
276
|
/** List all available DiffDelta sources. */
|
|
169
277
|
sources(): Promise<SourceInfo[]>;
|
|
278
|
+
/**
|
|
279
|
+
* Check pipeline health. Returns when the engine last ran and whether
|
|
280
|
+
* all sources are healthy. A stale timestamp means the pipeline is down.
|
|
281
|
+
*/
|
|
282
|
+
checkHealth(): Promise<HealthCheck>;
|
|
283
|
+
/**
|
|
284
|
+
* Given a list of dependency names your bot uses, returns the source IDs
|
|
285
|
+
* you should monitor. Uses the static stacks.json mapping — no API call,
|
|
286
|
+
* pure local lookup after one fetch.
|
|
287
|
+
*
|
|
288
|
+
* @example
|
|
289
|
+
* ```ts
|
|
290
|
+
* const sources = await dd.discoverSources(["openai", "langchain", "pinecone"]);
|
|
291
|
+
* // → ["openai_sdk_releases", "openai_api_changelog", "langchain_releases", "pinecone_status"]
|
|
292
|
+
* ```
|
|
293
|
+
*/
|
|
294
|
+
discoverSources(dependencies: string[]): Promise<string[]>;
|
|
170
295
|
/**
|
|
171
296
|
* Continuously poll and call a function for each new item.
|
|
172
297
|
*
|
|
@@ -177,6 +302,9 @@ declare class DiffDelta {
|
|
|
177
302
|
* ```ts
|
|
178
303
|
* dd.watch(item => {
|
|
179
304
|
* console.log(`🚨 ${item.source}: ${item.headline}`);
|
|
305
|
+
* if (item.suggestedAction === "PATCH_IMMEDIATELY") {
|
|
306
|
+
* triggerAlert(item);
|
|
307
|
+
* }
|
|
180
308
|
* }, { tags: ["security"] });
|
|
181
309
|
* ```
|
|
182
310
|
*
|
|
@@ -227,4 +355,4 @@ declare class MemoryCursorStore {
|
|
|
227
355
|
clear(key?: string): void;
|
|
228
356
|
}
|
|
229
357
|
|
|
230
|
-
export { CursorStore, DiffDelta, type DiffDeltaOptions, type Feed, type FeedItem, type Head, MemoryCursorStore, type PollOptions, type SourceInfo, type WatchOptions };
|
|
358
|
+
export { CursorStore, type DeprecationSignal, DiffDelta, type DiffDeltaOptions, type Feed, type FeedItem, type Head, type HealthCheck, type IncidentSignal, MemoryCursorStore, type PollOptions, type ReleaseSignal, type SeveritySignal, type SignalProvenance, type Signals, type SourceInfo, type SuggestedAction, type WatchOptions, parseFeed, parseFeedItem, parseHead, parseHealthCheck, parseSourceInfo };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,55 @@
|
|
|
1
|
+
/** Severity signal extracted from security advisories. */
|
|
2
|
+
interface SeveritySignal {
|
|
3
|
+
level: "critical" | "high" | "medium" | "low" | string;
|
|
4
|
+
source: string;
|
|
5
|
+
cvss?: number;
|
|
6
|
+
cwes?: string[];
|
|
7
|
+
packages?: string[];
|
|
8
|
+
exploited?: boolean;
|
|
9
|
+
provenance?: SignalProvenance;
|
|
10
|
+
}
|
|
11
|
+
/** Release signal extracted from changelogs/GitHub releases. */
|
|
12
|
+
interface ReleaseSignal {
|
|
13
|
+
version: string;
|
|
14
|
+
prerelease?: boolean;
|
|
15
|
+
security_patch?: boolean;
|
|
16
|
+
provenance?: SignalProvenance;
|
|
17
|
+
}
|
|
18
|
+
/** Incident signal extracted from status pages. */
|
|
19
|
+
interface IncidentSignal {
|
|
20
|
+
status: "investigating" | "identified" | "monitoring" | "resolved" | string;
|
|
21
|
+
impact?: "minor" | "major" | "critical" | string;
|
|
22
|
+
provenance?: SignalProvenance;
|
|
23
|
+
}
|
|
24
|
+
/** Deprecation signal extracted from changelogs/advisories. */
|
|
25
|
+
interface DeprecationSignal {
|
|
26
|
+
type: "breaking_change" | "end_of_life" | "removal" | "deprecated" | string;
|
|
27
|
+
affects?: string[];
|
|
28
|
+
confidence: "high" | "medium" | "low" | string;
|
|
29
|
+
source: string;
|
|
30
|
+
provenance?: SignalProvenance;
|
|
31
|
+
}
|
|
32
|
+
/** Provenance chain for a signal — traces it to an authoritative source. */
|
|
33
|
+
interface SignalProvenance {
|
|
34
|
+
method: string;
|
|
35
|
+
authority: string;
|
|
36
|
+
authority_url: string;
|
|
37
|
+
evidence_url: string;
|
|
38
|
+
}
|
|
39
|
+
/** Action codes telling a bot exactly what to do. */
|
|
40
|
+
type SuggestedAction = "PATCH_IMMEDIATELY" | "PATCH_SOON" | "VERSION_PIN" | "REVIEW_CHANGELOG" | "MONITOR_STATUS" | "ACKNOWLEDGE" | "NO_ACTION";
|
|
41
|
+
/** All structured signals on an item. */
|
|
42
|
+
interface Signals {
|
|
43
|
+
severity?: SeveritySignal;
|
|
44
|
+
release?: ReleaseSignal;
|
|
45
|
+
incident?: IncidentSignal;
|
|
46
|
+
deprecation?: DeprecationSignal;
|
|
47
|
+
suggested_action?: SuggestedAction;
|
|
48
|
+
[key: string]: unknown;
|
|
49
|
+
}
|
|
1
50
|
/** A single item from a DiffDelta feed. */
|
|
2
51
|
interface FeedItem {
|
|
3
|
-
/** Source identifier (e.g. "cisa_kev", "
|
|
52
|
+
/** Source identifier (e.g. "cisa_kev", "github_advisories"). */
|
|
4
53
|
source: string;
|
|
5
54
|
/** Unique item ID within the source. */
|
|
6
55
|
id: string;
|
|
@@ -14,11 +63,15 @@ interface FeedItem {
|
|
|
14
63
|
publishedAt: string | null;
|
|
15
64
|
/** When the item was last updated. */
|
|
16
65
|
updatedAt: string | null;
|
|
17
|
-
/** Which change bucket: "new", "updated", or "
|
|
18
|
-
bucket: "new" | "updated" | "removed";
|
|
19
|
-
/**
|
|
66
|
+
/** Which change bucket: "new", "updated", "removed", or "flagged". */
|
|
67
|
+
bucket: "new" | "updated" | "removed" | "flagged";
|
|
68
|
+
/** Structured signals: severity, release, incident, deprecation, suggested_action. */
|
|
69
|
+
signals: Signals;
|
|
70
|
+
/** Shortcut: the suggested_action code, if any. */
|
|
71
|
+
suggestedAction: SuggestedAction | null;
|
|
72
|
+
/** Risk score 0.0–1.0, or null if not scored. */
|
|
20
73
|
riskScore: number | null;
|
|
21
|
-
/**
|
|
74
|
+
/** Item-level provenance (fetched_at, evidence_urls, content_hash). */
|
|
22
75
|
provenance: Record<string, unknown>;
|
|
23
76
|
/** The full raw item object from the feed. */
|
|
24
77
|
raw: Record<string, unknown>;
|
|
@@ -27,14 +80,40 @@ interface FeedItem {
|
|
|
27
80
|
interface Head {
|
|
28
81
|
/** Opaque cursor string for change detection. */
|
|
29
82
|
cursor: string;
|
|
30
|
-
/** Content hash of the latest feed. */
|
|
31
|
-
hash: string;
|
|
32
83
|
/** Whether content has changed since last generation. */
|
|
33
84
|
changed: boolean;
|
|
34
85
|
/** When this head was generated. */
|
|
35
86
|
generatedAt: string;
|
|
36
87
|
/** Recommended polling interval in seconds. */
|
|
37
88
|
ttlSec: number;
|
|
89
|
+
/** URL to the full latest.json feed. */
|
|
90
|
+
latestUrl: string;
|
|
91
|
+
/** URL to the digest (global head only). */
|
|
92
|
+
digestUrl: string | null;
|
|
93
|
+
/** Item counts: new, updated, removed, flagged. */
|
|
94
|
+
counts: {
|
|
95
|
+
new: number;
|
|
96
|
+
updated: number;
|
|
97
|
+
removed: number;
|
|
98
|
+
flagged: number;
|
|
99
|
+
};
|
|
100
|
+
/** Number of sources checked this cycle (Verified Silence). */
|
|
101
|
+
sourcesChecked: number;
|
|
102
|
+
/** Number of sources healthy this cycle. */
|
|
103
|
+
sourcesOk: number;
|
|
104
|
+
/** True if nothing changed AND all sources are healthy. */
|
|
105
|
+
allClear: boolean;
|
|
106
|
+
/** 0.0–1.0 confidence that allClear is trustworthy. */
|
|
107
|
+
allClearConfidence: number | null;
|
|
108
|
+
/** Pipeline freshness: oldest data age, stale count. */
|
|
109
|
+
freshness: {
|
|
110
|
+
oldest_data_age_sec: number;
|
|
111
|
+
mean_data_age_sec: number;
|
|
112
|
+
stale_count: number;
|
|
113
|
+
all_fresh: boolean;
|
|
114
|
+
} | null;
|
|
115
|
+
/** The full raw head.json object. */
|
|
116
|
+
raw: Record<string, unknown>;
|
|
38
117
|
}
|
|
39
118
|
/** A full DiffDelta feed response. */
|
|
40
119
|
interface Feed {
|
|
@@ -54,6 +133,8 @@ interface Feed {
|
|
|
54
133
|
updated: FeedItem[];
|
|
55
134
|
/** Items in the "removed" bucket. */
|
|
56
135
|
removed: FeedItem[];
|
|
136
|
+
/** Items in the "flagged" bucket. */
|
|
137
|
+
flagged: FeedItem[];
|
|
57
138
|
/** Human-readable summary of what changed. */
|
|
58
139
|
narrative: string;
|
|
59
140
|
/** The full raw feed object. */
|
|
@@ -80,6 +161,25 @@ interface SourceInfo {
|
|
|
80
161
|
/** Path to the source's latest.json. */
|
|
81
162
|
latestUrl: string;
|
|
82
163
|
}
|
|
164
|
+
/** Health check response from /healthz.json. */
|
|
165
|
+
interface HealthCheck {
|
|
166
|
+
ok: boolean;
|
|
167
|
+
service: string;
|
|
168
|
+
time: string;
|
|
169
|
+
sourcesChecked: number;
|
|
170
|
+
sourcesOk: number;
|
|
171
|
+
engineVersion: string;
|
|
172
|
+
}
|
|
173
|
+
/** Parse a raw feed item into a typed FeedItem. */
|
|
174
|
+
declare function parseFeedItem(data: Record<string, unknown>, bucket?: "new" | "updated" | "removed" | "flagged"): FeedItem;
|
|
175
|
+
/** Parse a raw head.json response into a typed Head. */
|
|
176
|
+
declare function parseHead(data: Record<string, unknown>): Head;
|
|
177
|
+
/** Parse a raw latest.json response into a typed Feed. */
|
|
178
|
+
declare function parseFeed(data: Record<string, unknown>): Feed;
|
|
179
|
+
/** Parse a raw source object into a typed SourceInfo. */
|
|
180
|
+
declare function parseSourceInfo(data: Record<string, unknown>): SourceInfo;
|
|
181
|
+
/** Parse a raw healthz.json response into a typed HealthCheck. */
|
|
182
|
+
declare function parseHealthCheck(data: Record<string, unknown>): HealthCheck;
|
|
83
183
|
|
|
84
184
|
/**
|
|
85
185
|
* DiffDelta TypeScript client — agent-ready intelligence feeds.
|
|
@@ -90,6 +190,10 @@ interface SourceInfo {
|
|
|
90
190
|
*
|
|
91
191
|
* const dd = new DiffDelta();
|
|
92
192
|
*
|
|
193
|
+
* // Quick health check — is the pipeline alive?
|
|
194
|
+
* const health = await dd.checkHealth();
|
|
195
|
+
* console.log(`Pipeline: ${health.ok ? "healthy" : "degraded"}, last run: ${health.time}`);
|
|
196
|
+
*
|
|
93
197
|
* // Poll for new items across all sources
|
|
94
198
|
* const items = await dd.poll();
|
|
95
199
|
* items.forEach(i => console.log(`${i.source}: ${i.headline}`));
|
|
@@ -97,6 +201,10 @@ interface SourceInfo {
|
|
|
97
201
|
* // Poll only security sources
|
|
98
202
|
* const sec = await dd.poll({ tags: ["security"] });
|
|
99
203
|
*
|
|
204
|
+
* // Check what's relevant to your stack
|
|
205
|
+
* const sources = await dd.discoverSources(["openai", "langchain", "pinecone"]);
|
|
206
|
+
* console.log("Watch these:", sources);
|
|
207
|
+
*
|
|
100
208
|
* // Continuous monitoring
|
|
101
209
|
* dd.watch(item => console.log("🚨", item.headline), { tags: ["security"] });
|
|
102
210
|
* ```
|
|
@@ -119,17 +227,17 @@ interface DiffDeltaOptions {
|
|
|
119
227
|
interface PollOptions {
|
|
120
228
|
/** Filter items to these tags (e.g. ["security"]). */
|
|
121
229
|
tags?: string[];
|
|
122
|
-
/** Filter items to these source IDs (e.g. ["cisa_kev", "
|
|
230
|
+
/** Filter items to these source IDs (e.g. ["cisa_kev", "github_advisories"]). */
|
|
123
231
|
sources?: string[];
|
|
124
232
|
/**
|
|
125
233
|
* Which buckets to return.
|
|
126
|
-
* Defaults to ["new", "updated"].
|
|
127
|
-
* Use ["new", "updated", "removed"] to include removals.
|
|
234
|
+
* Defaults to ["new", "updated", "flagged"].
|
|
235
|
+
* Use ["new", "updated", "removed", "flagged"] to include removals.
|
|
128
236
|
*/
|
|
129
|
-
buckets?: Array<"new" | "updated" | "removed">;
|
|
237
|
+
buckets?: Array<"new" | "updated" | "removed" | "flagged">;
|
|
130
238
|
}
|
|
131
239
|
interface WatchOptions extends PollOptions {
|
|
132
|
-
/** Seconds between polls. Defaults to feed TTL (usually
|
|
240
|
+
/** Seconds between polls. Defaults to feed TTL (usually 60s). */
|
|
133
241
|
interval?: number;
|
|
134
242
|
/** If provided, an AbortSignal to stop watching. */
|
|
135
243
|
signal?: AbortSignal;
|
|
@@ -144,7 +252,7 @@ declare class DiffDelta {
|
|
|
144
252
|
/**
|
|
145
253
|
* Poll the global feed for new items since last poll.
|
|
146
254
|
*
|
|
147
|
-
* Checks head.json first (~
|
|
255
|
+
* Checks head.json first (~200 bytes). Only fetches the full feed
|
|
148
256
|
* if the cursor has changed. Automatically saves the new cursor.
|
|
149
257
|
*/
|
|
150
258
|
poll(options?: PollOptions): Promise<FeedItem[]>;
|
|
@@ -156,7 +264,7 @@ declare class DiffDelta {
|
|
|
156
264
|
*/
|
|
157
265
|
pollSource(sourceId: string, options?: Omit<PollOptions, "sources">): Promise<FeedItem[]>;
|
|
158
266
|
/**
|
|
159
|
-
* Fetch a head.json pointer.
|
|
267
|
+
* Fetch a head.json pointer. Cheapest call (~200 bytes).
|
|
160
268
|
* @param url Full URL to head.json. Defaults to global head.
|
|
161
269
|
*/
|
|
162
270
|
head(url?: string): Promise<Head>;
|
|
@@ -167,6 +275,23 @@ declare class DiffDelta {
|
|
|
167
275
|
fetchFeed(url?: string): Promise<Feed>;
|
|
168
276
|
/** List all available DiffDelta sources. */
|
|
169
277
|
sources(): Promise<SourceInfo[]>;
|
|
278
|
+
/**
|
|
279
|
+
* Check pipeline health. Returns when the engine last ran and whether
|
|
280
|
+
* all sources are healthy. A stale timestamp means the pipeline is down.
|
|
281
|
+
*/
|
|
282
|
+
checkHealth(): Promise<HealthCheck>;
|
|
283
|
+
/**
|
|
284
|
+
* Given a list of dependency names your bot uses, returns the source IDs
|
|
285
|
+
* you should monitor. Uses the static stacks.json mapping — no API call,
|
|
286
|
+
* pure local lookup after one fetch.
|
|
287
|
+
*
|
|
288
|
+
* @example
|
|
289
|
+
* ```ts
|
|
290
|
+
* const sources = await dd.discoverSources(["openai", "langchain", "pinecone"]);
|
|
291
|
+
* // → ["openai_sdk_releases", "openai_api_changelog", "langchain_releases", "pinecone_status"]
|
|
292
|
+
* ```
|
|
293
|
+
*/
|
|
294
|
+
discoverSources(dependencies: string[]): Promise<string[]>;
|
|
170
295
|
/**
|
|
171
296
|
* Continuously poll and call a function for each new item.
|
|
172
297
|
*
|
|
@@ -177,6 +302,9 @@ declare class DiffDelta {
|
|
|
177
302
|
* ```ts
|
|
178
303
|
* dd.watch(item => {
|
|
179
304
|
* console.log(`🚨 ${item.source}: ${item.headline}`);
|
|
305
|
+
* if (item.suggestedAction === "PATCH_IMMEDIATELY") {
|
|
306
|
+
* triggerAlert(item);
|
|
307
|
+
* }
|
|
180
308
|
* }, { tags: ["security"] });
|
|
181
309
|
* ```
|
|
182
310
|
*
|
|
@@ -227,4 +355,4 @@ declare class MemoryCursorStore {
|
|
|
227
355
|
clear(key?: string): void;
|
|
228
356
|
}
|
|
229
357
|
|
|
230
|
-
export { CursorStore, DiffDelta, type DiffDeltaOptions, type Feed, type FeedItem, type Head, MemoryCursorStore, type PollOptions, type SourceInfo, type WatchOptions };
|
|
358
|
+
export { CursorStore, type DeprecationSignal, DiffDelta, type DiffDeltaOptions, type Feed, type FeedItem, type Head, type HealthCheck, type IncidentSignal, MemoryCursorStore, type PollOptions, type ReleaseSignal, type SeveritySignal, type SignalProvenance, type Signals, type SourceInfo, type SuggestedAction, type WatchOptions, parseFeed, parseFeedItem, parseHead, parseHealthCheck, parseSourceInfo };
|