@allisall/sdk 0.0.0-stage → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +202 -0
- package/NOTICE +2 -0
- package/README.md +89 -2
- package/dist/client.d.ts +77 -0
- package/dist/client.js +130 -0
- package/dist/decimal.d.ts +20 -0
- package/dist/decimal.js +23 -0
- package/dist/errors.d.ts +79 -0
- package/dist/errors.js +98 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +4 -0
- package/dist/ingest.d.ts +28 -0
- package/dist/ingest.js +247 -0
- package/dist/retry.d.ts +18 -0
- package/dist/retry.js +105 -0
- package/dist/runtime.d.ts +17 -0
- package/dist/runtime.js +60 -0
- package/dist/transport.d.ts +53 -0
- package/dist/transport.js +220 -0
- package/dist/types.d.ts +135 -0
- package/dist/types.js +80 -0
- package/dist/uuid.d.ts +6 -0
- package/dist/uuid.js +20 -0
- package/dist/version.d.ts +2 -0
- package/dist/version.js +2 -0
- package/package.json +39 -4
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
// One HTTP attempt (headers, timeout, reading the response) and the loop
|
|
2
|
+
// that repeats it under the retry rules in retry.ts.
|
|
3
|
+
import { AllisallError, ApiError, ConnectionError } from "./errors.js";
|
|
4
|
+
import { backoffMs, isRetryableStatus, MAX_RETRY_AFTER_MS, parseRetryAfter, sleep, } from "./retry.js";
|
|
5
|
+
import { uuidv4 } from "./uuid.js";
|
|
6
|
+
/** The message of the error refusing an API key in a browser. */
|
|
7
|
+
export const BROWSER_KEY_MESSAGE = "allisall: refusing to use an API key in a browser, where it is readable by anyone who loads the page. " +
|
|
8
|
+
"Call the API from your server instead, or pass dangerouslyAllowBrowser: true if you accept that risk.";
|
|
9
|
+
/** Reports whether a bearer token is an API key, which starts `aia_`. */
|
|
10
|
+
export function isApiKey(token) {
|
|
11
|
+
return token.startsWith("aia_");
|
|
12
|
+
}
|
|
13
|
+
/** Joins the base URL and an operation's path with exactly one `/`. */
|
|
14
|
+
export function joinURL(base, path) {
|
|
15
|
+
const url = new URL(base.href);
|
|
16
|
+
url.pathname = url.pathname.replace(/\/+$/, "") + path;
|
|
17
|
+
return url.href;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Reports whether every character of a credential, already trimmed, is
|
|
21
|
+
* printable ASCII (U+0021 to U+007E), as a bearer credential is. Anything
|
|
22
|
+
* else, a control character, an inner space or a non-ASCII character,
|
|
23
|
+
* cannot travel in the header. The check never quotes the credential, so
|
|
24
|
+
* no error built from it can leak the secret.
|
|
25
|
+
*/
|
|
26
|
+
export function isPrintableCredential(credential) {
|
|
27
|
+
for (let i = 0; i < credential.length; i++) {
|
|
28
|
+
const c = credential.charCodeAt(i);
|
|
29
|
+
if (c < 0x21 || c > 0x7e)
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
return true;
|
|
33
|
+
}
|
|
34
|
+
async function bearerToken(config) {
|
|
35
|
+
const { credential } = config;
|
|
36
|
+
if (credential.kind === "apiKey")
|
|
37
|
+
return credential.apiKey;
|
|
38
|
+
const raw = await credential.provider();
|
|
39
|
+
const token = typeof raw === "string" ? raw.trim() : raw;
|
|
40
|
+
if (token === "") {
|
|
41
|
+
throw new AllisallError("allisall: the token provider returned an empty token");
|
|
42
|
+
}
|
|
43
|
+
if (typeof token === "string" && !isPrintableCredential(token)) {
|
|
44
|
+
throw new AllisallError("allisall: the token provider returned a token holding a character other than printable ASCII, which cannot be sent");
|
|
45
|
+
}
|
|
46
|
+
if (config.refuseApiKeys && typeof token === "string" && isApiKey(token)) {
|
|
47
|
+
throw new AllisallError(BROWSER_KEY_MESSAGE);
|
|
48
|
+
}
|
|
49
|
+
return token;
|
|
50
|
+
}
|
|
51
|
+
function isObject(value) {
|
|
52
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
53
|
+
}
|
|
54
|
+
function parseJSON(text) {
|
|
55
|
+
try {
|
|
56
|
+
const value = JSON.parse(text);
|
|
57
|
+
return value;
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
return undefined;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
const OPTIONAL_LOCATORS = [
|
|
64
|
+
"pointer",
|
|
65
|
+
"parameter",
|
|
66
|
+
"header",
|
|
67
|
+
"part",
|
|
68
|
+
"column",
|
|
69
|
+
];
|
|
70
|
+
function readFieldError(value) {
|
|
71
|
+
if (!isObject(value))
|
|
72
|
+
return undefined;
|
|
73
|
+
const { code, detail } = value;
|
|
74
|
+
if (typeof code !== "string" || typeof detail !== "string")
|
|
75
|
+
return undefined;
|
|
76
|
+
const out = { code, detail };
|
|
77
|
+
for (const name of OPTIONAL_LOCATORS) {
|
|
78
|
+
const locator = value[name];
|
|
79
|
+
if (typeof locator === "string")
|
|
80
|
+
out[name] = locator;
|
|
81
|
+
}
|
|
82
|
+
return out;
|
|
83
|
+
}
|
|
84
|
+
const PROBLEM_STRINGS = [
|
|
85
|
+
"type",
|
|
86
|
+
"title",
|
|
87
|
+
"detail",
|
|
88
|
+
"code",
|
|
89
|
+
"request_id",
|
|
90
|
+
];
|
|
91
|
+
/**
|
|
92
|
+
* Reads an `application/problem+json` body as a problem document. Returns
|
|
93
|
+
* `undefined` for any other body, or one that is not a JSON object. A
|
|
94
|
+
* member of the wrong type is left out on its own, as is a malformed
|
|
95
|
+
* entry of `errors`; the rest of the problem is kept. Members the SDK does
|
|
96
|
+
* not know are ignored.
|
|
97
|
+
*/
|
|
98
|
+
export function readProblem(contentType, text) {
|
|
99
|
+
const mediaType = contentType?.split(";")[0]?.trim().toLowerCase();
|
|
100
|
+
if (mediaType !== "application/problem+json")
|
|
101
|
+
return undefined;
|
|
102
|
+
const body = parseJSON(text);
|
|
103
|
+
if (!isObject(body))
|
|
104
|
+
return undefined;
|
|
105
|
+
const problem = {};
|
|
106
|
+
for (const name of PROBLEM_STRINGS) {
|
|
107
|
+
const value = body[name];
|
|
108
|
+
if (typeof value === "string")
|
|
109
|
+
problem[name] = value;
|
|
110
|
+
}
|
|
111
|
+
if (typeof body.status === "number")
|
|
112
|
+
problem.status = body.status;
|
|
113
|
+
if (Array.isArray(body.errors)) {
|
|
114
|
+
problem.errors = body.errors
|
|
115
|
+
.map(readFieldError)
|
|
116
|
+
.filter((e) => e !== undefined);
|
|
117
|
+
}
|
|
118
|
+
return problem;
|
|
119
|
+
}
|
|
120
|
+
function headerOrUndefined(headers, name) {
|
|
121
|
+
return headers.get(name) ?? undefined;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Sends one attempt. Resolves with the parsed `2xx` body; throws an
|
|
125
|
+
* `ApiError` for any other status or an unreadable `2xx` body, a
|
|
126
|
+
* `ConnectionError` when no response arrived, or the caller's abort reason.
|
|
127
|
+
*/
|
|
128
|
+
async function attempt(config, url, body, read, token, requestId, signal) {
|
|
129
|
+
const headers = {
|
|
130
|
+
Authorization: `Bearer ${token}`,
|
|
131
|
+
"Content-Type": "application/json",
|
|
132
|
+
"X-Request-Id": requestId,
|
|
133
|
+
};
|
|
134
|
+
if (config.userAgent !== undefined)
|
|
135
|
+
headers["User-Agent"] = config.userAgent;
|
|
136
|
+
const timeout = AbortSignal.timeout(config.timeout);
|
|
137
|
+
const combined = signal === undefined ? timeout : AbortSignal.any([signal, timeout]);
|
|
138
|
+
const doFetch = config.fetch ?? globalThis.fetch;
|
|
139
|
+
let response;
|
|
140
|
+
let text;
|
|
141
|
+
try {
|
|
142
|
+
response = await doFetch(url, {
|
|
143
|
+
method: "POST",
|
|
144
|
+
headers,
|
|
145
|
+
body,
|
|
146
|
+
signal: combined,
|
|
147
|
+
// A redirect is never followed, so Authorization never travels to
|
|
148
|
+
// another location.
|
|
149
|
+
redirect: "manual",
|
|
150
|
+
});
|
|
151
|
+
text = await response.text();
|
|
152
|
+
}
|
|
153
|
+
catch (err) {
|
|
154
|
+
if (signal?.aborted)
|
|
155
|
+
throw signal.reason;
|
|
156
|
+
throw new ConnectionError(requestId, err);
|
|
157
|
+
}
|
|
158
|
+
const status = response.status;
|
|
159
|
+
const responseId = headerOrUndefined(response.headers, "X-Request-Id");
|
|
160
|
+
// A 3xx, or a browser's opaque redirect, whose status is 0.
|
|
161
|
+
if ((status >= 300 && status < 400) || response.type === "opaqueredirect") {
|
|
162
|
+
throw new ApiError({
|
|
163
|
+
status,
|
|
164
|
+
headers: response.headers,
|
|
165
|
+
requestId: responseId,
|
|
166
|
+
message: `allisall: the API answered ${status} with a redirect, which the SDK does not follow`,
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
if (status >= 200 && status < 300) {
|
|
170
|
+
const parsed = parseJSON(text);
|
|
171
|
+
const value = parsed === undefined ? undefined : read(parsed);
|
|
172
|
+
if (value !== undefined)
|
|
173
|
+
return value;
|
|
174
|
+
throw new ApiError({
|
|
175
|
+
status,
|
|
176
|
+
headers: response.headers,
|
|
177
|
+
requestId: responseId,
|
|
178
|
+
message: `allisall: the API answered ${status} with a body the SDK could not read`,
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
throw new ApiError({
|
|
182
|
+
status,
|
|
183
|
+
problem: readProblem(response.headers.get("Content-Type"), text),
|
|
184
|
+
requestId: responseId,
|
|
185
|
+
retryAfter: parseRetryAfter(response.headers.get("Retry-After"), Date.now()),
|
|
186
|
+
headers: response.headers,
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Posts a JSON body, retrying a failure worth repeating. The body is the
|
|
191
|
+
* same bytes on every attempt. Each attempt reads a fresh token and sends
|
|
192
|
+
* a fresh `X-Request-Id`.
|
|
193
|
+
*/
|
|
194
|
+
export async function post(config, path, body, read, signal) {
|
|
195
|
+
const url = joinURL(config.baseURL, path);
|
|
196
|
+
for (let retry = 0;; retry++) {
|
|
197
|
+
// An error from the token provider ends the call unwrapped and unretried.
|
|
198
|
+
const token = await bearerToken(config);
|
|
199
|
+
try {
|
|
200
|
+
return await attempt(config, url, body, read, token, uuidv4(), signal);
|
|
201
|
+
}
|
|
202
|
+
catch (err) {
|
|
203
|
+
if (!(err instanceof ApiError || err instanceof ConnectionError))
|
|
204
|
+
throw err;
|
|
205
|
+
const retryable = err instanceof ConnectionError || isRetryableStatus(err.status);
|
|
206
|
+
if (!retryable || retry >= config.maxRetries)
|
|
207
|
+
throw err;
|
|
208
|
+
let wait;
|
|
209
|
+
if (err instanceof ApiError && err.retryAfter !== undefined) {
|
|
210
|
+
if (err.retryAfter > MAX_RETRY_AFTER_MS)
|
|
211
|
+
throw err;
|
|
212
|
+
wait = err.retryAfter;
|
|
213
|
+
}
|
|
214
|
+
else {
|
|
215
|
+
wait = backoffMs(retry + 1);
|
|
216
|
+
}
|
|
217
|
+
await sleep(wait, signal);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import type { Decimal } from "./decimal.js";
|
|
2
|
+
/** The known request-level error codes, in the API's catalogue order. */
|
|
3
|
+
export declare const ERROR_CODES: readonly ["invalid_json", "validation_failed", "unauthenticated", "insufficient_scope", "session_required", "account_not_provisioned", "not_found", "no_active_value_model", "method_not_allowed", "idempotency_key_in_use", "client_key_in_use", "agent_key_in_use", "client_has_events", "client_has_calculations", "agent_has_events", "client_key_has_events", "value_model_exists", "value_model_version_stale", "calculation_transition_invalid", "calculation_not_draft", "calculation_not_published", "report_shared", "report_not_shared", "api_key_revoked", "payload_too_large", "uri_too_long", "unsupported_media_type", "idempotency_key_reused", "rate_limited", "internal_error"];
|
|
4
|
+
/** The known field-level error codes, in the API's catalogue order. */
|
|
5
|
+
export declare const FIELD_ERROR_CODES: readonly ["required", "unknown_field", "immutable", "invalid_type", "invalid_format", "invalid_value", "too_long", "too_many", "out_of_range", "too_precise", "duplicate", "too_large", "mutually_exclusive", "unknown_reference", "client_key_required", "currency_mismatch"];
|
|
6
|
+
/** The event categories. */
|
|
7
|
+
export declare const EVENT_CATEGORIES: readonly ["session", "task", "model", "outcome", "exception"];
|
|
8
|
+
/** The members of `EventInput`, in the API's order. */
|
|
9
|
+
export declare const EVENT_INPUT_MEMBERS: readonly ["event_id", "timestamp", "agent_key", "client_key", "category", "event_type", "outcome_type", "quantity", "cost", "tokens_in", "tokens_out", "model", "currency", "metadata"];
|
|
10
|
+
/**
|
|
11
|
+
* A request-level error code. New codes are additive, so any other string
|
|
12
|
+
* is possible too: branch on the codes you handle, and fall back on the
|
|
13
|
+
* HTTP status for the rest.
|
|
14
|
+
*/
|
|
15
|
+
export type ErrorCode = (typeof ERROR_CODES)[number] | (string & {});
|
|
16
|
+
/**
|
|
17
|
+
* A field-level error code, as in a rejected ingest row. New codes are
|
|
18
|
+
* additive, so any other string is possible too.
|
|
19
|
+
*/
|
|
20
|
+
export type FieldErrorCode = (typeof FIELD_ERROR_CODES)[number] | (string & {});
|
|
21
|
+
/** An event category. */
|
|
22
|
+
export type EventCategory = (typeof EVENT_CATEGORIES)[number];
|
|
23
|
+
/**
|
|
24
|
+
* One event to ingest. Members keep the API's snake_case names. The SDK
|
|
25
|
+
* sends every member the object holds, so a misspelt member reaches the
|
|
26
|
+
* API and is reported as a rejected row rather than dropped.
|
|
27
|
+
*/
|
|
28
|
+
export interface EventInput {
|
|
29
|
+
/**
|
|
30
|
+
* A sender-chosen id; a repeat within the workspace is a duplicate. When
|
|
31
|
+
* it is absent, `null` or `""`, the SDK generates a UUID. Set your own
|
|
32
|
+
* when the same event may be sent again in a later call, such as after a
|
|
33
|
+
* queue redelivers a message.
|
|
34
|
+
*/
|
|
35
|
+
event_id?: string | null;
|
|
36
|
+
/** When the event happened: a `Date`, or an RFC 3339 instant with an offset. */
|
|
37
|
+
timestamp: Date | string;
|
|
38
|
+
/** The agent that acted. */
|
|
39
|
+
agent_key: string;
|
|
40
|
+
/** The client the event belongs to. */
|
|
41
|
+
client_key?: string | null;
|
|
42
|
+
/** The event's category. */
|
|
43
|
+
category: EventCategory;
|
|
44
|
+
/** What the agent did. */
|
|
45
|
+
event_type: string;
|
|
46
|
+
/** Required when `category` is `outcome`. */
|
|
47
|
+
outcome_type?: string | null;
|
|
48
|
+
/** Absent, `null` or `0` means 1. */
|
|
49
|
+
quantity?: number | null;
|
|
50
|
+
/** In `currency`, or the workspace currency: a `Decimal`, or a finite number. */
|
|
51
|
+
cost?: Decimal | number | null;
|
|
52
|
+
/** Tokens in. */
|
|
53
|
+
tokens_in?: number | null;
|
|
54
|
+
/** Tokens out. */
|
|
55
|
+
tokens_out?: number | null;
|
|
56
|
+
/** The model the agent invoked. */
|
|
57
|
+
model?: string | null;
|
|
58
|
+
/** The currency of `cost`. Absent means the workspace currency. */
|
|
59
|
+
currency?: string | null;
|
|
60
|
+
/** Free-form members, at most 8 KB serialised. */
|
|
61
|
+
metadata?: Record<string, unknown> | null;
|
|
62
|
+
}
|
|
63
|
+
/** One problem with a rejected ingest row. */
|
|
64
|
+
export interface IngestRowError {
|
|
65
|
+
/** The zero-based position of the row in the list passed to `ingest`. */
|
|
66
|
+
index: number;
|
|
67
|
+
/** The field-level code. */
|
|
68
|
+
code: FieldErrorCode;
|
|
69
|
+
/** An RFC 6901 JSON Pointer to the problem, starting `/events/<index>`. */
|
|
70
|
+
pointer: string;
|
|
71
|
+
/** Human text. Its wording may change at any time. */
|
|
72
|
+
detail: string;
|
|
73
|
+
}
|
|
74
|
+
/** The outcome of one ingest batch, as the API sends it. */
|
|
75
|
+
export interface IngestResult {
|
|
76
|
+
accepted: number;
|
|
77
|
+
duplicates: number;
|
|
78
|
+
rejected: number;
|
|
79
|
+
errors: IngestRowError[];
|
|
80
|
+
errors_truncated: boolean;
|
|
81
|
+
}
|
|
82
|
+
/** The combined outcome of one `ingest` call, over the whole list. */
|
|
83
|
+
export interface IngestSummary {
|
|
84
|
+
/** Events stored. */
|
|
85
|
+
accepted: number;
|
|
86
|
+
/** Events already stored, by `event_id` or content hash. */
|
|
87
|
+
duplicates: number;
|
|
88
|
+
/** Events refused, counted once each, including any too large to send. */
|
|
89
|
+
rejected: number;
|
|
90
|
+
/** One entry per problem, in the order of the list passed to `ingest`. */
|
|
91
|
+
errors: IngestRowError[];
|
|
92
|
+
/** Whether any rejected row had more problems than `errors` holds. */
|
|
93
|
+
errors_truncated: boolean;
|
|
94
|
+
/** The number of batches sent. */
|
|
95
|
+
batches: number;
|
|
96
|
+
}
|
|
97
|
+
/** One problem in a request, from a problem document's `errors`. */
|
|
98
|
+
export interface FieldError {
|
|
99
|
+
/** The field-level code. */
|
|
100
|
+
code: FieldErrorCode;
|
|
101
|
+
/** An RFC 6901 JSON Pointer into the request body. */
|
|
102
|
+
pointer?: string;
|
|
103
|
+
/** A query parameter, by name. */
|
|
104
|
+
parameter?: string;
|
|
105
|
+
/** A request header, by name. */
|
|
106
|
+
header?: string;
|
|
107
|
+
/** A multipart part, by its form field name. */
|
|
108
|
+
part?: string;
|
|
109
|
+
/** A column of a CSV part. */
|
|
110
|
+
column?: string;
|
|
111
|
+
/** Human text. Its wording may change at any time. */
|
|
112
|
+
detail: string;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* An RFC 9457 problem document, as the API sends it with an error status.
|
|
116
|
+
* The API sends every member but `errors`; each is optional here because a
|
|
117
|
+
* member of the wrong type, such as one from a proxy, is left out rather
|
|
118
|
+
* than failing the whole document.
|
|
119
|
+
*/
|
|
120
|
+
export interface Problem {
|
|
121
|
+
/** A URI identifying the problem; it maps one to one onto `code`. */
|
|
122
|
+
type?: string;
|
|
123
|
+
/** The fixed summary for `code`. */
|
|
124
|
+
title?: string;
|
|
125
|
+
/** The response status. */
|
|
126
|
+
status?: number;
|
|
127
|
+
/** Human text about this occurrence. Its wording may change at any time. */
|
|
128
|
+
detail?: string;
|
|
129
|
+
/** The error code. Branch on it. */
|
|
130
|
+
code?: ErrorCode;
|
|
131
|
+
/** The request's id, to quote when reporting the problem. */
|
|
132
|
+
request_id?: string;
|
|
133
|
+
/** Every problem found. Present only with `validation_failed`. */
|
|
134
|
+
errors?: FieldError[];
|
|
135
|
+
}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
// The wire types the SDK uses, written by hand from the API's OpenAPI
|
|
2
|
+
// document. A test reads that document and holds every list below to it,
|
|
3
|
+
// until types generated from the document replace this file.
|
|
4
|
+
/** The known request-level error codes, in the API's catalogue order. */
|
|
5
|
+
export const ERROR_CODES = [
|
|
6
|
+
"invalid_json",
|
|
7
|
+
"validation_failed",
|
|
8
|
+
"unauthenticated",
|
|
9
|
+
"insufficient_scope",
|
|
10
|
+
"session_required",
|
|
11
|
+
"account_not_provisioned",
|
|
12
|
+
"not_found",
|
|
13
|
+
"no_active_value_model",
|
|
14
|
+
"method_not_allowed",
|
|
15
|
+
"idempotency_key_in_use",
|
|
16
|
+
"client_key_in_use",
|
|
17
|
+
"agent_key_in_use",
|
|
18
|
+
"client_has_events",
|
|
19
|
+
"client_has_calculations",
|
|
20
|
+
"agent_has_events",
|
|
21
|
+
"client_key_has_events",
|
|
22
|
+
"value_model_exists",
|
|
23
|
+
"value_model_version_stale",
|
|
24
|
+
"calculation_transition_invalid",
|
|
25
|
+
"calculation_not_draft",
|
|
26
|
+
"calculation_not_published",
|
|
27
|
+
"report_shared",
|
|
28
|
+
"report_not_shared",
|
|
29
|
+
"api_key_revoked",
|
|
30
|
+
"payload_too_large",
|
|
31
|
+
"uri_too_long",
|
|
32
|
+
"unsupported_media_type",
|
|
33
|
+
"idempotency_key_reused",
|
|
34
|
+
"rate_limited",
|
|
35
|
+
"internal_error",
|
|
36
|
+
];
|
|
37
|
+
/** The known field-level error codes, in the API's catalogue order. */
|
|
38
|
+
export const FIELD_ERROR_CODES = [
|
|
39
|
+
"required",
|
|
40
|
+
"unknown_field",
|
|
41
|
+
"immutable",
|
|
42
|
+
"invalid_type",
|
|
43
|
+
"invalid_format",
|
|
44
|
+
"invalid_value",
|
|
45
|
+
"too_long",
|
|
46
|
+
"too_many",
|
|
47
|
+
"out_of_range",
|
|
48
|
+
"too_precise",
|
|
49
|
+
"duplicate",
|
|
50
|
+
"too_large",
|
|
51
|
+
"mutually_exclusive",
|
|
52
|
+
"unknown_reference",
|
|
53
|
+
"client_key_required",
|
|
54
|
+
"currency_mismatch",
|
|
55
|
+
];
|
|
56
|
+
/** The event categories. */
|
|
57
|
+
export const EVENT_CATEGORIES = [
|
|
58
|
+
"session",
|
|
59
|
+
"task",
|
|
60
|
+
"model",
|
|
61
|
+
"outcome",
|
|
62
|
+
"exception",
|
|
63
|
+
];
|
|
64
|
+
/** The members of `EventInput`, in the API's order. */
|
|
65
|
+
export const EVENT_INPUT_MEMBERS = [
|
|
66
|
+
"event_id",
|
|
67
|
+
"timestamp",
|
|
68
|
+
"agent_key",
|
|
69
|
+
"client_key",
|
|
70
|
+
"category",
|
|
71
|
+
"event_type",
|
|
72
|
+
"outcome_type",
|
|
73
|
+
"quantity",
|
|
74
|
+
"cost",
|
|
75
|
+
"tokens_in",
|
|
76
|
+
"tokens_out",
|
|
77
|
+
"model",
|
|
78
|
+
"currency",
|
|
79
|
+
"metadata",
|
|
80
|
+
];
|
package/dist/uuid.d.ts
ADDED
package/dist/uuid.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
const HEX = Array.from({ length: 256 }, (_, i) => i.toString(16).padStart(2, "0"));
|
|
2
|
+
/**
|
|
3
|
+
* Returns a new UUID version 4 (RFC 9562 §5.4), lowercase, in the
|
|
4
|
+
* 8-4-4-4-12 form. It uses `crypto.getRandomValues`, which, unlike
|
|
5
|
+
* `crypto.randomUUID`, every browser offers outside secure contexts too.
|
|
6
|
+
*/
|
|
7
|
+
export function uuidv4() {
|
|
8
|
+
const bytes = new Uint8Array(16);
|
|
9
|
+
globalThis.crypto.getRandomValues(bytes);
|
|
10
|
+
// Version 4 in the high nibble of byte 6; variant 10 in the top bits of byte 8.
|
|
11
|
+
bytes[6] = ((bytes[6] ?? 0) & 0x0f) | 0x40;
|
|
12
|
+
bytes[8] = ((bytes[8] ?? 0) & 0x3f) | 0x80;
|
|
13
|
+
let out = "";
|
|
14
|
+
for (let i = 0; i < 16; i++) {
|
|
15
|
+
if (i === 4 || i === 6 || i === 8 || i === 10)
|
|
16
|
+
out += "-";
|
|
17
|
+
out += HEX[bytes[i] ?? 0];
|
|
18
|
+
}
|
|
19
|
+
return out;
|
|
20
|
+
}
|
package/dist/version.js
ADDED
package/package.json
CHANGED
|
@@ -1,6 +1,41 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@allisall/sdk",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "The allisall SDK for TypeScript and JavaScript: send events to the allisall API.",
|
|
5
|
+
"license": "Apache-2.0",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/All-is-AI/allisall-typescript.git"
|
|
9
|
+
},
|
|
10
|
+
"type": "module",
|
|
11
|
+
"sideEffects": false,
|
|
12
|
+
"exports": {
|
|
13
|
+
".": {
|
|
14
|
+
"types": "./dist/index.d.ts",
|
|
15
|
+
"default": "./dist/index.js"
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
"types": "./dist/index.d.ts",
|
|
19
|
+
"files": [
|
|
20
|
+
"dist",
|
|
21
|
+
"LICENSE",
|
|
22
|
+
"NOTICE"
|
|
23
|
+
],
|
|
24
|
+
"engines": {
|
|
25
|
+
"node": ">=22"
|
|
26
|
+
},
|
|
27
|
+
"scripts": {
|
|
28
|
+
"build": "tsc -p tsconfig.build.json",
|
|
29
|
+
"lint": "biome check . && tsc --noEmit -p tsconfig.json",
|
|
30
|
+
"test": "vitest run && npm run test:consumer",
|
|
31
|
+
"test:consumer": "tsc -p tsconfig.build.json && node test/consumer/extract-readme.mjs && node node_modules/typescript-5.0/bin/tsc -p test/consumer/tsconfig.bundler.json && node node_modules/typescript-5.0/bin/tsc -p test/consumer/tsconfig.node16.json && node node_modules/typescript-5.0/bin/tsc -p test/consumer/tsconfig.node-types.json"
|
|
32
|
+
},
|
|
33
|
+
"devDependencies": {
|
|
34
|
+
"@biomejs/biome": "2.5.4",
|
|
35
|
+
"@types/node": "^22.20.5",
|
|
36
|
+
"typescript": "~5.9.3",
|
|
37
|
+
"typescript-5.0": "npm:typescript@5.0.4",
|
|
38
|
+
"vitest": "^4.1.10",
|
|
39
|
+
"yaml": "2.9.0"
|
|
40
|
+
}
|
|
41
|
+
}
|