@telemai/sdk 0.2.4 → 0.2.6
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 +18 -0
- package/dist/{chunk-RYY4S5LP.js → chunk-DKTEG7EY.js} +4 -3
- package/dist/index.cjs +114 -19
- package/dist/index.d.cts +78 -1
- package/dist/index.d.ts +78 -1
- package/dist/index.js +110 -17
- package/dist/vercel/index.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -198,6 +198,24 @@ console.log(summary.query, summary.goal, summary.context)
|
|
|
198
198
|
`results` returns the query, the goal and the context of the session, plus the
|
|
199
199
|
results of each search step.
|
|
200
200
|
|
|
201
|
+
## Monitors
|
|
202
|
+
|
|
203
|
+
A monitor runs a query on a schedule. Poll it to get the results that are new since
|
|
204
|
+
your last poll. `poll` returns a `SearchResponse`:
|
|
205
|
+
|
|
206
|
+
```ts
|
|
207
|
+
const monitor = await telem.monitors.create("Nvidia files a new 8-K with the SEC", {
|
|
208
|
+
providers: ["exa", "parallel"],
|
|
209
|
+
frequency: "1h",
|
|
210
|
+
})
|
|
211
|
+
const response = await telem.monitors.poll(monitor.id)
|
|
212
|
+
await telem.monitors.update(monitor.id, { status: "paused" })
|
|
213
|
+
await telem.monitors.delete(monitor.id)
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
`telem.monitors` also has `get` and `list`. See [Monitors](https://docs.telem.ai/resources/monitors/)
|
|
217
|
+
for the fields, the costs and the errors.
|
|
218
|
+
|
|
201
219
|
## Vercel AI SDK
|
|
202
220
|
|
|
203
221
|
If your agent runs on the Vercel AI SDK, `@telemai/sdk/vercel` gives it
|
|
@@ -107,7 +107,7 @@ function retryDelay(retryAfter, attempt) {
|
|
|
107
107
|
}
|
|
108
108
|
return backoff(attempt);
|
|
109
109
|
}
|
|
110
|
-
async function request(config, method, path, body, abort) {
|
|
110
|
+
async function request(config, method, path, body, abort, retry = "all") {
|
|
111
111
|
const url = `${config.baseUrl}/${path.replace(/^\/+/, "")}`;
|
|
112
112
|
const headers = { "User-Agent": config.userAgent };
|
|
113
113
|
if (config.apiKey) headers.Authorization = `Bearer ${config.apiKey}`;
|
|
@@ -172,14 +172,15 @@ async function request(config, method, path, body, abort) {
|
|
|
172
172
|
if (signal?.aborted) {
|
|
173
173
|
throw new TimeoutError(`request to ${url} timed out after ${config.timeoutMs}ms`, cause);
|
|
174
174
|
}
|
|
175
|
-
if (attempt >= config.maxRetries) throw connectionError(url, cause);
|
|
175
|
+
if (retry === "rate_limit" || attempt >= config.maxRetries) throw connectionError(url, cause);
|
|
176
176
|
await pause(backoff(attempt));
|
|
177
177
|
continue;
|
|
178
178
|
} finally {
|
|
179
179
|
signal?.removeEventListener("abort", onAbort2);
|
|
180
180
|
}
|
|
181
181
|
if (received.status >= 200 && received.status < 300) return received.body;
|
|
182
|
-
|
|
182
|
+
const retryable = retry === "rate_limit" ? received.status === 429 : isRetryable(received.status);
|
|
183
|
+
if (retryable && attempt < config.maxRetries) {
|
|
183
184
|
await pause(retryDelay(received.retryAfter, attempt));
|
|
184
185
|
continue;
|
|
185
186
|
}
|
package/dist/index.cjs
CHANGED
|
@@ -33,6 +33,7 @@ __export(index_exports, {
|
|
|
33
33
|
sessionKey: () => sessionKey,
|
|
34
34
|
snapshotNodeKey: () => snapshotNodeKey,
|
|
35
35
|
toolMarker: () => toolMarker,
|
|
36
|
+
verifyWebhook: () => verifyWebhook,
|
|
36
37
|
windowAnchor: () => windowAnchor,
|
|
37
38
|
withTrajectory: () => withTrajectory
|
|
38
39
|
});
|
|
@@ -59,7 +60,7 @@ var TimeoutError = class extends TelemError {
|
|
|
59
60
|
};
|
|
60
61
|
|
|
61
62
|
// src/version.ts
|
|
62
|
-
var VERSION = "0.2.
|
|
63
|
+
var VERSION = "0.2.6";
|
|
63
64
|
|
|
64
65
|
// src/config.ts
|
|
65
66
|
var DEFAULT_BASE_URL = "https://router.telem.ai";
|
|
@@ -250,7 +251,7 @@ function retryDelay(retryAfter, attempt) {
|
|
|
250
251
|
}
|
|
251
252
|
return backoff(attempt);
|
|
252
253
|
}
|
|
253
|
-
async function request(config, method, path, body, abort) {
|
|
254
|
+
async function request(config, method, path, body, abort, retry = "all") {
|
|
254
255
|
const url = `${config.baseUrl}/${path.replace(/^\/+/, "")}`;
|
|
255
256
|
const headers = { "User-Agent": config.userAgent };
|
|
256
257
|
if (config.apiKey) headers.Authorization = `Bearer ${config.apiKey}`;
|
|
@@ -315,14 +316,15 @@ async function request(config, method, path, body, abort) {
|
|
|
315
316
|
if (signal?.aborted) {
|
|
316
317
|
throw new TimeoutError(`request to ${url} timed out after ${config.timeoutMs}ms`, cause);
|
|
317
318
|
}
|
|
318
|
-
if (attempt >= config.maxRetries) throw connectionError(url, cause);
|
|
319
|
+
if (retry === "rate_limit" || attempt >= config.maxRetries) throw connectionError(url, cause);
|
|
319
320
|
await pause(backoff(attempt));
|
|
320
321
|
continue;
|
|
321
322
|
} finally {
|
|
322
323
|
signal?.removeEventListener("abort", onAbort2);
|
|
323
324
|
}
|
|
324
325
|
if (received.status >= 200 && received.status < 300) return received.body;
|
|
325
|
-
|
|
326
|
+
const retryable = retry === "rate_limit" ? received.status === 429 : isRetryable(received.status);
|
|
327
|
+
if (retryable && attempt < config.maxRetries) {
|
|
326
328
|
await pause(retryDelay(received.retryAfter, attempt));
|
|
327
329
|
continue;
|
|
328
330
|
}
|
|
@@ -547,21 +549,6 @@ async function fetchUrls(config, url, options) {
|
|
|
547
549
|
return buildFetchResponse(await request(config, "POST", "/v1/fetch", body, options.signal));
|
|
548
550
|
}
|
|
549
551
|
|
|
550
|
-
// src/methods/providers.ts
|
|
551
|
-
async function providers(config) {
|
|
552
|
-
const body = await request(config, "GET", "/v1/preprocessors");
|
|
553
|
-
return asArray(asObject(body).preprocessors).filter(isObject).map((entry) => ({
|
|
554
|
-
name: entry.name ?? "",
|
|
555
|
-
type: entry.type ?? "",
|
|
556
|
-
activeByDefault: entry.active_by_default ?? false,
|
|
557
|
-
description: entry.description ?? null,
|
|
558
|
-
method: entry.method ?? null,
|
|
559
|
-
url: entry.url ?? null,
|
|
560
|
-
normalized: entry.normalized ?? false,
|
|
561
|
-
tiers: asArray(entry.tiers)
|
|
562
|
-
}));
|
|
563
|
-
}
|
|
564
|
-
|
|
565
552
|
// src/methods/search.ts
|
|
566
553
|
var MIN_SCHEMA_VERSION = 2;
|
|
567
554
|
function searchBlock(defaults, options) {
|
|
@@ -709,6 +696,110 @@ async function search(config, query, options) {
|
|
|
709
696
|
return buildSearchResponse(data);
|
|
710
697
|
}
|
|
711
698
|
|
|
699
|
+
// src/methods/monitors.ts
|
|
700
|
+
function monitorFrom(body) {
|
|
701
|
+
const data = asObject(body);
|
|
702
|
+
return {
|
|
703
|
+
id: String(data.id ?? ""),
|
|
704
|
+
query: data.query ?? "",
|
|
705
|
+
frequency: data.frequency ?? "",
|
|
706
|
+
providers: asArray(data.providers).filter(isObject).map((p) => ({ name: String(p.name ?? ""), status: String(p.status ?? "") })),
|
|
707
|
+
sessionId: String(data.session_id ?? ""),
|
|
708
|
+
createdAt: data.created_at ?? "",
|
|
709
|
+
lastPolledAt: data.last_polled_at ?? null,
|
|
710
|
+
webhookUrl: data.webhook_url ?? null,
|
|
711
|
+
webhookSecret: data.webhook_secret ?? null,
|
|
712
|
+
raw: data
|
|
713
|
+
};
|
|
714
|
+
}
|
|
715
|
+
function createMonitors(config) {
|
|
716
|
+
const path = (monitorId, suffix = "") => {
|
|
717
|
+
if (!monitorId || /^\.+$/.test(monitorId)) throw invalidRequest("a monitor id is required");
|
|
718
|
+
return `/v1/monitors/${encodeURIComponent(monitorId)}${suffix}`;
|
|
719
|
+
};
|
|
720
|
+
return {
|
|
721
|
+
/** Create a monitor. Each provider runs `query` on the schedule. */
|
|
722
|
+
async create(query, options) {
|
|
723
|
+
const body = {
|
|
724
|
+
query,
|
|
725
|
+
options: { providers: { include: options.providers }, frequency: options.frequency }
|
|
726
|
+
};
|
|
727
|
+
if (options.providerOverrides !== void 0) {
|
|
728
|
+
body.options.provider_overrides = options.providerOverrides;
|
|
729
|
+
}
|
|
730
|
+
if (options.metadata !== void 0) body.metadata = options.metadata;
|
|
731
|
+
if (options.webhookUrl !== void 0) body.webhook_url = options.webhookUrl;
|
|
732
|
+
const data = await request(config, "POST", "/v1/monitors", body, options.signal, "rate_limit");
|
|
733
|
+
return monitorFrom(data);
|
|
734
|
+
},
|
|
735
|
+
/** List your monitors, newest first (server default 50, maximum 200). */
|
|
736
|
+
async list(options = {}) {
|
|
737
|
+
const query = options.limit === void 0 ? "" : `?limit=${options.limit}`;
|
|
738
|
+
const body = await request(config, "GET", `/v1/monitors${query}`, void 0, options.signal);
|
|
739
|
+
return asArray(asObject(body).monitors).map(monitorFrom);
|
|
740
|
+
},
|
|
741
|
+
/** Get one monitor. */
|
|
742
|
+
async get(monitorId, options = {}) {
|
|
743
|
+
return monitorFrom(await request(config, "GET", path(monitorId), void 0, options.signal));
|
|
744
|
+
},
|
|
745
|
+
/** Pause, resume or change a monitor. Only the fields given are sent. */
|
|
746
|
+
async update(monitorId, options) {
|
|
747
|
+
const body = {};
|
|
748
|
+
if (options.status !== void 0) body.status = options.status;
|
|
749
|
+
if (options.frequency !== void 0) body.frequency = options.frequency;
|
|
750
|
+
if (options.providerOverrides !== void 0) body.provider_overrides = options.providerOverrides;
|
|
751
|
+
if (options.webhookUrl !== void 0) body.webhook_url = options.webhookUrl;
|
|
752
|
+
const route = path(monitorId);
|
|
753
|
+
return monitorFrom(await request(config, "PATCH", route, body, options.signal));
|
|
754
|
+
},
|
|
755
|
+
/** Get the results that are new since the last poll, one entry per provider. */
|
|
756
|
+
async poll(monitorId, options = {}) {
|
|
757
|
+
const body = {};
|
|
758
|
+
if (options.session !== void 0) body.session_id = options.session;
|
|
759
|
+
if (options.metadata !== void 0) body.metadata = options.metadata;
|
|
760
|
+
const route = path(monitorId, "/poll");
|
|
761
|
+
const data = await request(config, "POST", route, body, options.signal, "rate_limit");
|
|
762
|
+
return buildSearchResponse(data);
|
|
763
|
+
},
|
|
764
|
+
/** Delete a monitor. A repeat deletes what a failed delete left. */
|
|
765
|
+
async delete(monitorId, options = {}) {
|
|
766
|
+
await request(config, "DELETE", path(monitorId), void 0, options.signal);
|
|
767
|
+
}
|
|
768
|
+
};
|
|
769
|
+
}
|
|
770
|
+
async function verifyWebhook(body, header, secret, toleranceSeconds = 300) {
|
|
771
|
+
const parts = Object.fromEntries(
|
|
772
|
+
header.split(",").map((item) => [item.slice(0, item.indexOf("=")), item.slice(item.indexOf("=") + 1)])
|
|
773
|
+
);
|
|
774
|
+
const t = parts.t ?? "";
|
|
775
|
+
const v1 = parts.v1 ?? "";
|
|
776
|
+
if (!/^\d+$/.test(t) || Math.abs(Date.now() / 1e3 - Number(t)) > toleranceSeconds) return false;
|
|
777
|
+
if (!/^([0-9a-f]{2})+$/.test(v1)) return false;
|
|
778
|
+
const encoder = new TextEncoder();
|
|
779
|
+
const raw = typeof body === "string" ? encoder.encode(body) : body;
|
|
780
|
+
const signed = new Uint8Array([...encoder.encode(`${t}.`), ...raw]);
|
|
781
|
+
const key = await crypto.subtle.importKey("raw", encoder.encode(secret), { name: "HMAC", hash: "SHA-256" }, false, [
|
|
782
|
+
"verify"
|
|
783
|
+
]);
|
|
784
|
+
const signature = new Uint8Array(v1.match(/../g).map((byte) => parseInt(byte, 16)));
|
|
785
|
+
return crypto.subtle.verify("HMAC", key, signature, signed);
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
// src/methods/providers.ts
|
|
789
|
+
async function providers(config) {
|
|
790
|
+
const body = await request(config, "GET", "/v1/preprocessors");
|
|
791
|
+
return asArray(asObject(body).preprocessors).filter(isObject).map((entry) => ({
|
|
792
|
+
name: entry.name ?? "",
|
|
793
|
+
type: entry.type ?? "",
|
|
794
|
+
activeByDefault: entry.active_by_default ?? false,
|
|
795
|
+
description: entry.description ?? null,
|
|
796
|
+
method: entry.method ?? null,
|
|
797
|
+
url: entry.url ?? null,
|
|
798
|
+
normalized: entry.normalized ?? false,
|
|
799
|
+
tiers: asArray(entry.tiers)
|
|
800
|
+
}));
|
|
801
|
+
}
|
|
802
|
+
|
|
712
803
|
// src/methods/sessions.ts
|
|
713
804
|
function sessionsFrom(body) {
|
|
714
805
|
return asArray(asObject(body).sessions).filter(isObject).map((entry) => ({
|
|
@@ -774,6 +865,8 @@ var Telem = class {
|
|
|
774
865
|
maxRetries;
|
|
775
866
|
/** The session read surface: `list` and `results(id)`. */
|
|
776
867
|
sessions;
|
|
868
|
+
/** Monitors: `create`, `list`, `get`, `update`, `poll`, `delete`. */
|
|
869
|
+
monitors;
|
|
777
870
|
#config;
|
|
778
871
|
/**
|
|
779
872
|
* @param options - An API key string, or an options object. Anything left unset
|
|
@@ -788,6 +881,7 @@ var Telem = class {
|
|
|
788
881
|
this.timeoutMs = config.timeoutMs;
|
|
789
882
|
this.maxRetries = config.maxRetries;
|
|
790
883
|
this.sessions = createSessions(config);
|
|
884
|
+
this.monitors = createMonitors(config);
|
|
791
885
|
}
|
|
792
886
|
/**
|
|
793
887
|
* Run a web search.
|
|
@@ -834,6 +928,7 @@ var index_default = Telem;
|
|
|
834
928
|
sessionKey,
|
|
835
929
|
snapshotNodeKey,
|
|
836
930
|
toolMarker,
|
|
931
|
+
verifyWebhook,
|
|
837
932
|
windowAnchor,
|
|
838
933
|
withTrajectory
|
|
839
934
|
});
|
package/dist/index.d.cts
CHANGED
|
@@ -222,6 +222,75 @@ type SessionsResource = {
|
|
|
222
222
|
list(): Promise<SessionSummary[]>;
|
|
223
223
|
results(sessionId: string): Promise<SessionResults>;
|
|
224
224
|
};
|
|
225
|
+
/** Options for `monitors.create`. */
|
|
226
|
+
type MonitorCreateOptions = {
|
|
227
|
+
/** One or more provider names, for example `["exa", "parallel"]`. */
|
|
228
|
+
providers: string[];
|
|
229
|
+
/** `"1h"`, `"6h"`, `"1d"` or `"7d"`. */
|
|
230
|
+
frequency: string;
|
|
231
|
+
/** Per-provider create fields, keyed by provider name. */
|
|
232
|
+
providerOverrides?: Record<string, Json>;
|
|
233
|
+
/** Request metadata (query lineage keys), sent as given. */
|
|
234
|
+
metadata?: Json;
|
|
235
|
+
/** An `https` URL that receives the new results. */
|
|
236
|
+
webhookUrl?: string;
|
|
237
|
+
signal?: AbortSignal;
|
|
238
|
+
};
|
|
239
|
+
/** Options for `monitors.update`. Send at least one field. */
|
|
240
|
+
type MonitorUpdateOptions = {
|
|
241
|
+
/** `"paused"` or `"active"`. */
|
|
242
|
+
status?: string;
|
|
243
|
+
frequency?: string;
|
|
244
|
+
/** New overrides for the providers named. They replace the ones sent before. */
|
|
245
|
+
providerOverrides?: Record<string, Json>;
|
|
246
|
+
/** A new webhook URL, with a new `webhookSecret` in the response. `null` removes it. */
|
|
247
|
+
webhookUrl?: string | null;
|
|
248
|
+
signal?: AbortSignal;
|
|
249
|
+
};
|
|
250
|
+
/** Options for `monitors.poll`. */
|
|
251
|
+
type MonitorPollOptions = {
|
|
252
|
+
/** Record this poll in another session (default: the monitor's own). */
|
|
253
|
+
session?: string;
|
|
254
|
+
/** Request metadata (query lineage keys), sent as given. */
|
|
255
|
+
metadata?: Json;
|
|
256
|
+
signal?: AbortSignal;
|
|
257
|
+
};
|
|
258
|
+
/** One provider of a monitor. `status` is `active`, `paused` (you paused it) or
|
|
259
|
+
* `idle` (paused because nobody polled; the next poll resumes it). */
|
|
260
|
+
type MonitorProvider = {
|
|
261
|
+
name: string;
|
|
262
|
+
status: string;
|
|
263
|
+
};
|
|
264
|
+
/** A monitor: a query that providers run on a schedule. */
|
|
265
|
+
type Monitor = {
|
|
266
|
+
id: string;
|
|
267
|
+
query: string;
|
|
268
|
+
frequency: string;
|
|
269
|
+
providers: MonitorProvider[];
|
|
270
|
+
sessionId: string;
|
|
271
|
+
createdAt: string;
|
|
272
|
+
lastPolledAt: string | null;
|
|
273
|
+
webhookUrl: string | null;
|
|
274
|
+
/** Signs the webhook deliveries. Only in the response that set `webhookUrl`. */
|
|
275
|
+
webhookSecret: string | null;
|
|
276
|
+
raw: Json;
|
|
277
|
+
};
|
|
278
|
+
/** The `telem.monitors` namespace. */
|
|
279
|
+
type MonitorsResource = {
|
|
280
|
+
create(query: string, options: MonitorCreateOptions): Promise<Monitor>;
|
|
281
|
+
list(options?: {
|
|
282
|
+
limit?: number;
|
|
283
|
+
signal?: AbortSignal;
|
|
284
|
+
}): Promise<Monitor[]>;
|
|
285
|
+
get(monitorId: string, options?: {
|
|
286
|
+
signal?: AbortSignal;
|
|
287
|
+
}): Promise<Monitor>;
|
|
288
|
+
update(monitorId: string, options: MonitorUpdateOptions): Promise<Monitor>;
|
|
289
|
+
poll(monitorId: string, options?: MonitorPollOptions): Promise<SearchResponse>;
|
|
290
|
+
delete(monitorId: string, options?: {
|
|
291
|
+
signal?: AbortSignal;
|
|
292
|
+
}): Promise<void>;
|
|
293
|
+
};
|
|
225
294
|
|
|
226
295
|
/**
|
|
227
296
|
* The CLOSED set of codes the client itself assigns, for failures that never
|
|
@@ -261,6 +330,12 @@ declare class TimeoutError extends TelemError {
|
|
|
261
330
|
constructor(message: string, cause?: unknown);
|
|
262
331
|
}
|
|
263
332
|
|
|
333
|
+
/**
|
|
334
|
+
* Check a monitor webhook: the `Telem-Signature` header against the raw body.
|
|
335
|
+
* True when the signature is correct and not older than `toleranceSeconds`.
|
|
336
|
+
*/
|
|
337
|
+
declare function verifyWebhook(body: string | Uint8Array, header: string, secret: string, toleranceSeconds?: number): Promise<boolean>;
|
|
338
|
+
|
|
264
339
|
/** Metadata keys the trajectory payload owns. A caller-supplied value for any of
|
|
265
340
|
* these is dropped rather than allowed to forge identity. */
|
|
266
341
|
declare const RESERVED_METADATA: ReadonlySet<string>;
|
|
@@ -358,6 +433,8 @@ declare class Telem {
|
|
|
358
433
|
readonly maxRetries: number;
|
|
359
434
|
/** The session read surface: `list` and `results(id)`. */
|
|
360
435
|
readonly sessions: SessionsResource;
|
|
436
|
+
/** Monitors: `create`, `list`, `get`, `update`, `poll`, `delete`. */
|
|
437
|
+
readonly monitors: MonitorsResource;
|
|
361
438
|
/**
|
|
362
439
|
* @param options - An API key string, or an options object. Anything left unset
|
|
363
440
|
* resolves from `TELEM_*` env vars, then `~/.telem/credentials.json` (credentials
|
|
@@ -389,4 +466,4 @@ declare class Telem {
|
|
|
389
466
|
providers(): Promise<ProviderInfo[]>;
|
|
390
467
|
}
|
|
391
468
|
|
|
392
|
-
export { type FetchOptions, type FetchResponse, type FetchResult, type Json, type ProviderInfo, type ProviderRun, RESERVED_METADATA, type SearchDefaults, type SearchOptions, type SearchResponse, type SearchResult, type SessionResults, type SessionSummary, type SessionsResource, type SnapshotArgs, Telem, type TelemClientErrorCode, TelemError, type TelemErrorOptions, type TelemOptions, TimeoutError, type TrajectoryArgs, type TrajectoryEnvelope, type TrajectorySnapshot, buildMetadata, buildSnapshot, contentAnchor, Telem as default, eventNodeKey, fingerprint, sessionKey, snapshotNodeKey, toolMarker, windowAnchor, withTrajectory };
|
|
469
|
+
export { type FetchOptions, type FetchResponse, type FetchResult, type Json, type Monitor, type MonitorCreateOptions, type MonitorPollOptions, type MonitorProvider, type MonitorUpdateOptions, type MonitorsResource, type ProviderInfo, type ProviderRun, RESERVED_METADATA, type SearchDefaults, type SearchOptions, type SearchResponse, type SearchResult, type SessionResults, type SessionSummary, type SessionsResource, type SnapshotArgs, Telem, type TelemClientErrorCode, TelemError, type TelemErrorOptions, type TelemOptions, TimeoutError, type TrajectoryArgs, type TrajectoryEnvelope, type TrajectorySnapshot, buildMetadata, buildSnapshot, contentAnchor, Telem as default, eventNodeKey, fingerprint, sessionKey, snapshotNodeKey, toolMarker, verifyWebhook, windowAnchor, withTrajectory };
|
package/dist/index.d.ts
CHANGED
|
@@ -222,6 +222,75 @@ type SessionsResource = {
|
|
|
222
222
|
list(): Promise<SessionSummary[]>;
|
|
223
223
|
results(sessionId: string): Promise<SessionResults>;
|
|
224
224
|
};
|
|
225
|
+
/** Options for `monitors.create`. */
|
|
226
|
+
type MonitorCreateOptions = {
|
|
227
|
+
/** One or more provider names, for example `["exa", "parallel"]`. */
|
|
228
|
+
providers: string[];
|
|
229
|
+
/** `"1h"`, `"6h"`, `"1d"` or `"7d"`. */
|
|
230
|
+
frequency: string;
|
|
231
|
+
/** Per-provider create fields, keyed by provider name. */
|
|
232
|
+
providerOverrides?: Record<string, Json>;
|
|
233
|
+
/** Request metadata (query lineage keys), sent as given. */
|
|
234
|
+
metadata?: Json;
|
|
235
|
+
/** An `https` URL that receives the new results. */
|
|
236
|
+
webhookUrl?: string;
|
|
237
|
+
signal?: AbortSignal;
|
|
238
|
+
};
|
|
239
|
+
/** Options for `monitors.update`. Send at least one field. */
|
|
240
|
+
type MonitorUpdateOptions = {
|
|
241
|
+
/** `"paused"` or `"active"`. */
|
|
242
|
+
status?: string;
|
|
243
|
+
frequency?: string;
|
|
244
|
+
/** New overrides for the providers named. They replace the ones sent before. */
|
|
245
|
+
providerOverrides?: Record<string, Json>;
|
|
246
|
+
/** A new webhook URL, with a new `webhookSecret` in the response. `null` removes it. */
|
|
247
|
+
webhookUrl?: string | null;
|
|
248
|
+
signal?: AbortSignal;
|
|
249
|
+
};
|
|
250
|
+
/** Options for `monitors.poll`. */
|
|
251
|
+
type MonitorPollOptions = {
|
|
252
|
+
/** Record this poll in another session (default: the monitor's own). */
|
|
253
|
+
session?: string;
|
|
254
|
+
/** Request metadata (query lineage keys), sent as given. */
|
|
255
|
+
metadata?: Json;
|
|
256
|
+
signal?: AbortSignal;
|
|
257
|
+
};
|
|
258
|
+
/** One provider of a monitor. `status` is `active`, `paused` (you paused it) or
|
|
259
|
+
* `idle` (paused because nobody polled; the next poll resumes it). */
|
|
260
|
+
type MonitorProvider = {
|
|
261
|
+
name: string;
|
|
262
|
+
status: string;
|
|
263
|
+
};
|
|
264
|
+
/** A monitor: a query that providers run on a schedule. */
|
|
265
|
+
type Monitor = {
|
|
266
|
+
id: string;
|
|
267
|
+
query: string;
|
|
268
|
+
frequency: string;
|
|
269
|
+
providers: MonitorProvider[];
|
|
270
|
+
sessionId: string;
|
|
271
|
+
createdAt: string;
|
|
272
|
+
lastPolledAt: string | null;
|
|
273
|
+
webhookUrl: string | null;
|
|
274
|
+
/** Signs the webhook deliveries. Only in the response that set `webhookUrl`. */
|
|
275
|
+
webhookSecret: string | null;
|
|
276
|
+
raw: Json;
|
|
277
|
+
};
|
|
278
|
+
/** The `telem.monitors` namespace. */
|
|
279
|
+
type MonitorsResource = {
|
|
280
|
+
create(query: string, options: MonitorCreateOptions): Promise<Monitor>;
|
|
281
|
+
list(options?: {
|
|
282
|
+
limit?: number;
|
|
283
|
+
signal?: AbortSignal;
|
|
284
|
+
}): Promise<Monitor[]>;
|
|
285
|
+
get(monitorId: string, options?: {
|
|
286
|
+
signal?: AbortSignal;
|
|
287
|
+
}): Promise<Monitor>;
|
|
288
|
+
update(monitorId: string, options: MonitorUpdateOptions): Promise<Monitor>;
|
|
289
|
+
poll(monitorId: string, options?: MonitorPollOptions): Promise<SearchResponse>;
|
|
290
|
+
delete(monitorId: string, options?: {
|
|
291
|
+
signal?: AbortSignal;
|
|
292
|
+
}): Promise<void>;
|
|
293
|
+
};
|
|
225
294
|
|
|
226
295
|
/**
|
|
227
296
|
* The CLOSED set of codes the client itself assigns, for failures that never
|
|
@@ -261,6 +330,12 @@ declare class TimeoutError extends TelemError {
|
|
|
261
330
|
constructor(message: string, cause?: unknown);
|
|
262
331
|
}
|
|
263
332
|
|
|
333
|
+
/**
|
|
334
|
+
* Check a monitor webhook: the `Telem-Signature` header against the raw body.
|
|
335
|
+
* True when the signature is correct and not older than `toleranceSeconds`.
|
|
336
|
+
*/
|
|
337
|
+
declare function verifyWebhook(body: string | Uint8Array, header: string, secret: string, toleranceSeconds?: number): Promise<boolean>;
|
|
338
|
+
|
|
264
339
|
/** Metadata keys the trajectory payload owns. A caller-supplied value for any of
|
|
265
340
|
* these is dropped rather than allowed to forge identity. */
|
|
266
341
|
declare const RESERVED_METADATA: ReadonlySet<string>;
|
|
@@ -358,6 +433,8 @@ declare class Telem {
|
|
|
358
433
|
readonly maxRetries: number;
|
|
359
434
|
/** The session read surface: `list` and `results(id)`. */
|
|
360
435
|
readonly sessions: SessionsResource;
|
|
436
|
+
/** Monitors: `create`, `list`, `get`, `update`, `poll`, `delete`. */
|
|
437
|
+
readonly monitors: MonitorsResource;
|
|
361
438
|
/**
|
|
362
439
|
* @param options - An API key string, or an options object. Anything left unset
|
|
363
440
|
* resolves from `TELEM_*` env vars, then `~/.telem/credentials.json` (credentials
|
|
@@ -389,4 +466,4 @@ declare class Telem {
|
|
|
389
466
|
providers(): Promise<ProviderInfo[]>;
|
|
390
467
|
}
|
|
391
468
|
|
|
392
|
-
export { type FetchOptions, type FetchResponse, type FetchResult, type Json, type ProviderInfo, type ProviderRun, RESERVED_METADATA, type SearchDefaults, type SearchOptions, type SearchResponse, type SearchResult, type SessionResults, type SessionSummary, type SessionsResource, type SnapshotArgs, Telem, type TelemClientErrorCode, TelemError, type TelemErrorOptions, type TelemOptions, TimeoutError, type TrajectoryArgs, type TrajectoryEnvelope, type TrajectorySnapshot, buildMetadata, buildSnapshot, contentAnchor, Telem as default, eventNodeKey, fingerprint, sessionKey, snapshotNodeKey, toolMarker, windowAnchor, withTrajectory };
|
|
469
|
+
export { type FetchOptions, type FetchResponse, type FetchResult, type Json, type Monitor, type MonitorCreateOptions, type MonitorPollOptions, type MonitorProvider, type MonitorUpdateOptions, type MonitorsResource, type ProviderInfo, type ProviderRun, RESERVED_METADATA, type SearchDefaults, type SearchOptions, type SearchResponse, type SearchResult, type SessionResults, type SessionSummary, type SessionsResource, type SnapshotArgs, Telem, type TelemClientErrorCode, TelemError, type TelemErrorOptions, type TelemOptions, TimeoutError, type TrajectoryArgs, type TrajectoryEnvelope, type TrajectorySnapshot, buildMetadata, buildSnapshot, contentAnchor, Telem as default, eventNodeKey, fingerprint, sessionKey, snapshotNodeKey, toolMarker, verifyWebhook, windowAnchor, withTrajectory };
|
package/dist/index.js
CHANGED
|
@@ -19,10 +19,10 @@ import {
|
|
|
19
19
|
windowAnchor,
|
|
20
20
|
wireMetadata,
|
|
21
21
|
withTrajectory
|
|
22
|
-
} from "./chunk-
|
|
22
|
+
} from "./chunk-DKTEG7EY.js";
|
|
23
23
|
|
|
24
24
|
// src/version.ts
|
|
25
|
-
var VERSION = "0.2.
|
|
25
|
+
var VERSION = "0.2.6";
|
|
26
26
|
|
|
27
27
|
// src/config.ts
|
|
28
28
|
var DEFAULT_BASE_URL = "https://router.telem.ai";
|
|
@@ -182,21 +182,6 @@ async function fetchUrls(config, url, options) {
|
|
|
182
182
|
return buildFetchResponse(await request(config, "POST", "/v1/fetch", body, options.signal));
|
|
183
183
|
}
|
|
184
184
|
|
|
185
|
-
// src/methods/providers.ts
|
|
186
|
-
async function providers(config) {
|
|
187
|
-
const body = await request(config, "GET", "/v1/preprocessors");
|
|
188
|
-
return asArray(asObject(body).preprocessors).filter(isObject).map((entry) => ({
|
|
189
|
-
name: entry.name ?? "",
|
|
190
|
-
type: entry.type ?? "",
|
|
191
|
-
activeByDefault: entry.active_by_default ?? false,
|
|
192
|
-
description: entry.description ?? null,
|
|
193
|
-
method: entry.method ?? null,
|
|
194
|
-
url: entry.url ?? null,
|
|
195
|
-
normalized: entry.normalized ?? false,
|
|
196
|
-
tiers: asArray(entry.tiers)
|
|
197
|
-
}));
|
|
198
|
-
}
|
|
199
|
-
|
|
200
185
|
// src/methods/search.ts
|
|
201
186
|
var MIN_SCHEMA_VERSION = 2;
|
|
202
187
|
function searchBlock(defaults, options) {
|
|
@@ -344,6 +329,110 @@ async function search(config, query, options) {
|
|
|
344
329
|
return buildSearchResponse(data);
|
|
345
330
|
}
|
|
346
331
|
|
|
332
|
+
// src/methods/monitors.ts
|
|
333
|
+
function monitorFrom(body) {
|
|
334
|
+
const data = asObject(body);
|
|
335
|
+
return {
|
|
336
|
+
id: String(data.id ?? ""),
|
|
337
|
+
query: data.query ?? "",
|
|
338
|
+
frequency: data.frequency ?? "",
|
|
339
|
+
providers: asArray(data.providers).filter(isObject).map((p) => ({ name: String(p.name ?? ""), status: String(p.status ?? "") })),
|
|
340
|
+
sessionId: String(data.session_id ?? ""),
|
|
341
|
+
createdAt: data.created_at ?? "",
|
|
342
|
+
lastPolledAt: data.last_polled_at ?? null,
|
|
343
|
+
webhookUrl: data.webhook_url ?? null,
|
|
344
|
+
webhookSecret: data.webhook_secret ?? null,
|
|
345
|
+
raw: data
|
|
346
|
+
};
|
|
347
|
+
}
|
|
348
|
+
function createMonitors(config) {
|
|
349
|
+
const path = (monitorId, suffix = "") => {
|
|
350
|
+
if (!monitorId || /^\.+$/.test(monitorId)) throw invalidRequest("a monitor id is required");
|
|
351
|
+
return `/v1/monitors/${encodeURIComponent(monitorId)}${suffix}`;
|
|
352
|
+
};
|
|
353
|
+
return {
|
|
354
|
+
/** Create a monitor. Each provider runs `query` on the schedule. */
|
|
355
|
+
async create(query, options) {
|
|
356
|
+
const body = {
|
|
357
|
+
query,
|
|
358
|
+
options: { providers: { include: options.providers }, frequency: options.frequency }
|
|
359
|
+
};
|
|
360
|
+
if (options.providerOverrides !== void 0) {
|
|
361
|
+
body.options.provider_overrides = options.providerOverrides;
|
|
362
|
+
}
|
|
363
|
+
if (options.metadata !== void 0) body.metadata = options.metadata;
|
|
364
|
+
if (options.webhookUrl !== void 0) body.webhook_url = options.webhookUrl;
|
|
365
|
+
const data = await request(config, "POST", "/v1/monitors", body, options.signal, "rate_limit");
|
|
366
|
+
return monitorFrom(data);
|
|
367
|
+
},
|
|
368
|
+
/** List your monitors, newest first (server default 50, maximum 200). */
|
|
369
|
+
async list(options = {}) {
|
|
370
|
+
const query = options.limit === void 0 ? "" : `?limit=${options.limit}`;
|
|
371
|
+
const body = await request(config, "GET", `/v1/monitors${query}`, void 0, options.signal);
|
|
372
|
+
return asArray(asObject(body).monitors).map(monitorFrom);
|
|
373
|
+
},
|
|
374
|
+
/** Get one monitor. */
|
|
375
|
+
async get(monitorId, options = {}) {
|
|
376
|
+
return monitorFrom(await request(config, "GET", path(monitorId), void 0, options.signal));
|
|
377
|
+
},
|
|
378
|
+
/** Pause, resume or change a monitor. Only the fields given are sent. */
|
|
379
|
+
async update(monitorId, options) {
|
|
380
|
+
const body = {};
|
|
381
|
+
if (options.status !== void 0) body.status = options.status;
|
|
382
|
+
if (options.frequency !== void 0) body.frequency = options.frequency;
|
|
383
|
+
if (options.providerOverrides !== void 0) body.provider_overrides = options.providerOverrides;
|
|
384
|
+
if (options.webhookUrl !== void 0) body.webhook_url = options.webhookUrl;
|
|
385
|
+
const route = path(monitorId);
|
|
386
|
+
return monitorFrom(await request(config, "PATCH", route, body, options.signal));
|
|
387
|
+
},
|
|
388
|
+
/** Get the results that are new since the last poll, one entry per provider. */
|
|
389
|
+
async poll(monitorId, options = {}) {
|
|
390
|
+
const body = {};
|
|
391
|
+
if (options.session !== void 0) body.session_id = options.session;
|
|
392
|
+
if (options.metadata !== void 0) body.metadata = options.metadata;
|
|
393
|
+
const route = path(monitorId, "/poll");
|
|
394
|
+
const data = await request(config, "POST", route, body, options.signal, "rate_limit");
|
|
395
|
+
return buildSearchResponse(data);
|
|
396
|
+
},
|
|
397
|
+
/** Delete a monitor. A repeat deletes what a failed delete left. */
|
|
398
|
+
async delete(monitorId, options = {}) {
|
|
399
|
+
await request(config, "DELETE", path(monitorId), void 0, options.signal);
|
|
400
|
+
}
|
|
401
|
+
};
|
|
402
|
+
}
|
|
403
|
+
async function verifyWebhook(body, header, secret, toleranceSeconds = 300) {
|
|
404
|
+
const parts = Object.fromEntries(
|
|
405
|
+
header.split(",").map((item) => [item.slice(0, item.indexOf("=")), item.slice(item.indexOf("=") + 1)])
|
|
406
|
+
);
|
|
407
|
+
const t = parts.t ?? "";
|
|
408
|
+
const v1 = parts.v1 ?? "";
|
|
409
|
+
if (!/^\d+$/.test(t) || Math.abs(Date.now() / 1e3 - Number(t)) > toleranceSeconds) return false;
|
|
410
|
+
if (!/^([0-9a-f]{2})+$/.test(v1)) return false;
|
|
411
|
+
const encoder = new TextEncoder();
|
|
412
|
+
const raw = typeof body === "string" ? encoder.encode(body) : body;
|
|
413
|
+
const signed = new Uint8Array([...encoder.encode(`${t}.`), ...raw]);
|
|
414
|
+
const key = await crypto.subtle.importKey("raw", encoder.encode(secret), { name: "HMAC", hash: "SHA-256" }, false, [
|
|
415
|
+
"verify"
|
|
416
|
+
]);
|
|
417
|
+
const signature = new Uint8Array(v1.match(/../g).map((byte) => parseInt(byte, 16)));
|
|
418
|
+
return crypto.subtle.verify("HMAC", key, signature, signed);
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
// src/methods/providers.ts
|
|
422
|
+
async function providers(config) {
|
|
423
|
+
const body = await request(config, "GET", "/v1/preprocessors");
|
|
424
|
+
return asArray(asObject(body).preprocessors).filter(isObject).map((entry) => ({
|
|
425
|
+
name: entry.name ?? "",
|
|
426
|
+
type: entry.type ?? "",
|
|
427
|
+
activeByDefault: entry.active_by_default ?? false,
|
|
428
|
+
description: entry.description ?? null,
|
|
429
|
+
method: entry.method ?? null,
|
|
430
|
+
url: entry.url ?? null,
|
|
431
|
+
normalized: entry.normalized ?? false,
|
|
432
|
+
tiers: asArray(entry.tiers)
|
|
433
|
+
}));
|
|
434
|
+
}
|
|
435
|
+
|
|
347
436
|
// src/methods/sessions.ts
|
|
348
437
|
function sessionsFrom(body) {
|
|
349
438
|
return asArray(asObject(body).sessions).filter(isObject).map((entry) => ({
|
|
@@ -409,6 +498,8 @@ var Telem = class {
|
|
|
409
498
|
maxRetries;
|
|
410
499
|
/** The session read surface: `list` and `results(id)`. */
|
|
411
500
|
sessions;
|
|
501
|
+
/** Monitors: `create`, `list`, `get`, `update`, `poll`, `delete`. */
|
|
502
|
+
monitors;
|
|
412
503
|
#config;
|
|
413
504
|
/**
|
|
414
505
|
* @param options - An API key string, or an options object. Anything left unset
|
|
@@ -423,6 +514,7 @@ var Telem = class {
|
|
|
423
514
|
this.timeoutMs = config.timeoutMs;
|
|
424
515
|
this.maxRetries = config.maxRetries;
|
|
425
516
|
this.sessions = createSessions(config);
|
|
517
|
+
this.monitors = createMonitors(config);
|
|
426
518
|
}
|
|
427
519
|
/**
|
|
428
520
|
* Run a web search.
|
|
@@ -469,6 +561,7 @@ export {
|
|
|
469
561
|
sessionKey,
|
|
470
562
|
snapshotNodeKey,
|
|
471
563
|
toolMarker,
|
|
564
|
+
verifyWebhook,
|
|
472
565
|
windowAnchor,
|
|
473
566
|
withTrajectory
|
|
474
567
|
};
|
package/dist/vercel/index.js
CHANGED