@jxburros/ai-nugget 0.3.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/LICENSE +21 -0
- package/README.md +316 -0
- package/dist/adapters/engines/anthropic.d.ts +8 -0
- package/dist/adapters/engines/anthropic.js +278 -0
- package/dist/adapters/engines/base.d.ts +23 -0
- package/dist/adapters/engines/base.js +93 -0
- package/dist/adapters/engines/google.d.ts +8 -0
- package/dist/adapters/engines/google.js +217 -0
- package/dist/adapters/engines/ollama.d.ts +10 -0
- package/dist/adapters/engines/ollama.js +160 -0
- package/dist/adapters/engines/openaiChat.d.ts +15 -0
- package/dist/adapters/engines/openaiChat.js +228 -0
- package/dist/adapters/index.d.ts +5 -0
- package/dist/adapters/index.js +17 -0
- package/dist/adapters/profiles.d.ts +42 -0
- package/dist/adapters/profiles.js +111 -0
- package/dist/agent/index.d.ts +3 -0
- package/dist/agent/index.js +3 -0
- package/dist/agent/loop.d.ts +70 -0
- package/dist/agent/loop.js +263 -0
- package/dist/agent/tools.d.ts +29 -0
- package/dist/agent/tools.js +48 -0
- package/dist/errors.d.ts +22 -0
- package/dist/errors.js +80 -0
- package/dist/handler.d.ts +48 -0
- package/dist/handler.js +371 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +11 -0
- package/dist/json.d.ts +7 -0
- package/dist/json.js +114 -0
- package/dist/keys.d.ts +7 -0
- package/dist/keys.js +78 -0
- package/dist/policy.d.ts +6 -0
- package/dist/policy.js +37 -0
- package/dist/redact.d.ts +8 -0
- package/dist/redact.js +46 -0
- package/dist/tokens.d.ts +5 -0
- package/dist/tokens.js +18 -0
- package/dist/transport.d.ts +28 -0
- package/dist/transport.js +145 -0
- package/dist/types.d.ts +191 -0
- package/dist/types.js +4 -0
- package/dist/util.d.ts +8 -0
- package/dist/util.js +51 -0
- package/nugget/VERSION.txt +2 -0
- package/nugget/src/adapters/engines/anthropic.ts +260 -0
- package/nugget/src/adapters/engines/base.ts +90 -0
- package/nugget/src/adapters/engines/google.ts +209 -0
- package/nugget/src/adapters/engines/ollama.ts +153 -0
- package/nugget/src/adapters/engines/openaiChat.ts +223 -0
- package/nugget/src/adapters/index.ts +17 -0
- package/nugget/src/adapters/profiles.ts +149 -0
- package/nugget/src/agent/index.ts +2 -0
- package/nugget/src/agent/loop.ts +314 -0
- package/nugget/src/agent/tools.ts +55 -0
- package/nugget/src/errors.ts +80 -0
- package/nugget/src/handler.ts +408 -0
- package/nugget/src/index.ts +10 -0
- package/nugget/src/json.ts +108 -0
- package/nugget/src/keys.ts +70 -0
- package/nugget/src/policy.ts +39 -0
- package/nugget/src/redact.ts +48 -0
- package/nugget/src/tokens.ts +21 -0
- package/nugget/src/transport.ts +148 -0
- package/nugget/src/types.ts +175 -0
- package/nugget/src/util.ts +51 -0
- package/package.json +69 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { Usage } from './types.js';
|
|
2
|
+
|
|
3
|
+
export function estimateTokens(text: string): number {
|
|
4
|
+
return Math.ceil(text.length / 4);
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export function estimatedUsage(inputText: string, outputText: string): Usage {
|
|
8
|
+
return {
|
|
9
|
+
inputTokens: estimateTokens(inputText),
|
|
10
|
+
outputTokens: estimateTokens(outputText),
|
|
11
|
+
estimated: true,
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function mergeUsage(a: Usage, b: Usage): Usage {
|
|
16
|
+
return {
|
|
17
|
+
inputTokens: (a.inputTokens ?? 0) + (b.inputTokens ?? 0),
|
|
18
|
+
outputTokens: (a.outputTokens ?? 0) + (b.outputTokens ?? 0),
|
|
19
|
+
estimated: a.estimated || b.estimated,
|
|
20
|
+
};
|
|
21
|
+
}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import { AIError, classify, fromUnknown } from './errors.js';
|
|
2
|
+
|
|
3
|
+
export function withTimeout(ms: number, external?: AbortSignal): {
|
|
4
|
+
signal: AbortSignal;
|
|
5
|
+
done(): void;
|
|
6
|
+
timedOut(): boolean;
|
|
7
|
+
} {
|
|
8
|
+
const controller = new AbortController();
|
|
9
|
+
let didTimeOut = false;
|
|
10
|
+
const timer = setTimeout(() => {
|
|
11
|
+
didTimeOut = true;
|
|
12
|
+
controller.abort(new AIError(`Request timed out after ${ms}ms`, { kind: 'timeout' }));
|
|
13
|
+
}, ms);
|
|
14
|
+
const onAbort = () => controller.abort(external?.reason);
|
|
15
|
+
if (external) {
|
|
16
|
+
if (external.aborted) onAbort();
|
|
17
|
+
else external.addEventListener('abort', onAbort, { once: true });
|
|
18
|
+
}
|
|
19
|
+
return {
|
|
20
|
+
signal: controller.signal,
|
|
21
|
+
done() {
|
|
22
|
+
clearTimeout(timer);
|
|
23
|
+
external?.removeEventListener('abort', onAbort);
|
|
24
|
+
},
|
|
25
|
+
timedOut() {
|
|
26
|
+
return didTimeOut;
|
|
27
|
+
},
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export async function fetchJson(url: string, init: RequestInit & { timeoutMs: number; provider?: string }): Promise<{
|
|
32
|
+
data: unknown;
|
|
33
|
+
status: number;
|
|
34
|
+
totalMs: number;
|
|
35
|
+
}> {
|
|
36
|
+
const timeout = withTimeout(init.timeoutMs, init.signal ?? undefined);
|
|
37
|
+
const started = Date.now();
|
|
38
|
+
try {
|
|
39
|
+
const res = await fetch(url, { ...init, signal: timeout.signal });
|
|
40
|
+
const raw = await res.text().catch(() => '');
|
|
41
|
+
if (!res.ok) throw classify(res.status, raw, init.provider, res.headers);
|
|
42
|
+
return { data: raw.trim() ? tolerantJson(raw) : null, status: res.status, totalMs: Date.now() - started };
|
|
43
|
+
} catch (error) {
|
|
44
|
+
if (timeout.timedOut()) throw new AIError(`Request timed out after ${init.timeoutMs}ms`, { kind: 'timeout', provider: init.provider });
|
|
45
|
+
throw fromUnknown(error, init.provider);
|
|
46
|
+
} finally {
|
|
47
|
+
timeout.done();
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* POST a JSON body and return the raw {@link Response} for streaming.
|
|
53
|
+
*
|
|
54
|
+
* The caller owns the abort/timeout `signal` and is responsible for keeping it
|
|
55
|
+
* alive until the response body has been fully consumed — passing a
|
|
56
|
+
* `withTimeout()` signal here and cleaning it up only after the stream ends is
|
|
57
|
+
* what lets external cancellation and the timeout apply for the *whole* stream,
|
|
58
|
+
* not merely the connection handshake. Non-2xx responses are classified and
|
|
59
|
+
* thrown before the body is handed back.
|
|
60
|
+
*/
|
|
61
|
+
export async function postResponse(
|
|
62
|
+
url: string,
|
|
63
|
+
body: unknown,
|
|
64
|
+
headers: Record<string, string>,
|
|
65
|
+
signal: AbortSignal,
|
|
66
|
+
provider: string,
|
|
67
|
+
): Promise<Response> {
|
|
68
|
+
let res: Response;
|
|
69
|
+
try {
|
|
70
|
+
res = await fetch(url, {
|
|
71
|
+
method: 'POST',
|
|
72
|
+
headers: { 'content-type': 'application/json', ...headers },
|
|
73
|
+
body: JSON.stringify(body),
|
|
74
|
+
signal,
|
|
75
|
+
});
|
|
76
|
+
} catch (error) {
|
|
77
|
+
throw fromUnknown(error, provider);
|
|
78
|
+
}
|
|
79
|
+
if (!res.ok) {
|
|
80
|
+
const raw = await res.text().catch(() => '');
|
|
81
|
+
throw classify(res.status, raw, provider, res.headers);
|
|
82
|
+
}
|
|
83
|
+
return res;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export async function* sseLines(res: Response): AsyncIterable<string> {
|
|
87
|
+
for await (const line of textLines(res)) {
|
|
88
|
+
const trimmed = line.trim();
|
|
89
|
+
if (!trimmed || trimmed.startsWith(':')) continue;
|
|
90
|
+
if (!trimmed.startsWith('data:')) continue;
|
|
91
|
+
const data = trimmed.slice(5).trim();
|
|
92
|
+
if (!data || data === '[DONE]') continue;
|
|
93
|
+
yield data;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export async function* ndjsonLines(res: Response): AsyncIterable<unknown> {
|
|
98
|
+
for await (const line of textLines(res)) {
|
|
99
|
+
const trimmed = line.trim();
|
|
100
|
+
if (!trimmed) continue;
|
|
101
|
+
try {
|
|
102
|
+
yield JSON.parse(trimmed) as unknown;
|
|
103
|
+
} catch {
|
|
104
|
+
throw new AIError('Provider returned malformed NDJSON', { kind: 'invalid_response' });
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export async function* textLines(res: Response): AsyncIterable<string> {
|
|
110
|
+
if (!res.body) {
|
|
111
|
+
const raw = await res.text().catch(() => '');
|
|
112
|
+
for (const line of raw.split(/\r?\n/)) yield line;
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
const reader = res.body.getReader();
|
|
116
|
+
const decoder = new TextDecoder();
|
|
117
|
+
let buffer = '';
|
|
118
|
+
let completed = false;
|
|
119
|
+
try {
|
|
120
|
+
for (;;) {
|
|
121
|
+
const { value, done } = await reader.read();
|
|
122
|
+
if (done) {
|
|
123
|
+
completed = true;
|
|
124
|
+
break;
|
|
125
|
+
}
|
|
126
|
+
buffer += decoder.decode(value, { stream: true });
|
|
127
|
+
let newlineIndex: number;
|
|
128
|
+
while ((newlineIndex = buffer.indexOf('\n')) >= 0) {
|
|
129
|
+
const line = buffer.slice(0, newlineIndex);
|
|
130
|
+
buffer = buffer.slice(newlineIndex + 1);
|
|
131
|
+
yield line;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
buffer += decoder.decode();
|
|
135
|
+
if (buffer.trim()) yield buffer;
|
|
136
|
+
} finally {
|
|
137
|
+
if (!completed) await reader.cancel().catch(() => undefined);
|
|
138
|
+
reader.releaseLock();
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function tolerantJson(raw: string): unknown {
|
|
143
|
+
try {
|
|
144
|
+
return JSON.parse(raw) as unknown;
|
|
145
|
+
} catch {
|
|
146
|
+
return { text: raw };
|
|
147
|
+
}
|
|
148
|
+
}
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
export type Role = 'system' | 'user' | 'assistant' | 'tool';
|
|
2
|
+
|
|
3
|
+
export interface ContentPart {
|
|
4
|
+
type: 'text' | 'image';
|
|
5
|
+
text?: string;
|
|
6
|
+
imageBase64?: string;
|
|
7
|
+
mimeType?: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface ChatMessage {
|
|
11
|
+
role: Role;
|
|
12
|
+
content: string | ContentPart[];
|
|
13
|
+
toolCalls?: ToolCall[]; // role: 'assistant'
|
|
14
|
+
toolCallId?: string; // role: 'tool' — answers a specific call
|
|
15
|
+
name?: string; // role: 'tool' — the tool's name (Gemini keys
|
|
16
|
+
// function responses by name, not id)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface ToolCall {
|
|
20
|
+
id: string;
|
|
21
|
+
name: string;
|
|
22
|
+
arguments: unknown;
|
|
23
|
+
raw?: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface ToolSchema {
|
|
27
|
+
name: string;
|
|
28
|
+
description: string;
|
|
29
|
+
parameters: object;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface ChatRequest {
|
|
33
|
+
model: string;
|
|
34
|
+
messages: ChatMessage[];
|
|
35
|
+
temperature?: number;
|
|
36
|
+
maxTokens?: number;
|
|
37
|
+
topP?: number;
|
|
38
|
+
responseFormat?: { type: 'text' } | { type: 'json'; schema?: object };
|
|
39
|
+
tools?: ToolSchema[];
|
|
40
|
+
toolChoice?: 'auto' | 'none' | { name: string };
|
|
41
|
+
stopSequences?: string[];
|
|
42
|
+
signal?: AbortSignal;
|
|
43
|
+
metadata?: Record<string, unknown>;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export interface Connection {
|
|
47
|
+
id: string;
|
|
48
|
+
provider: string;
|
|
49
|
+
baseUrl?: string;
|
|
50
|
+
keyRef?: KeyRef;
|
|
51
|
+
timeoutMs?: number;
|
|
52
|
+
headers?: Record<string, string>;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export type KeyRef =
|
|
56
|
+
| { kind: 'none' }
|
|
57
|
+
| { kind: 'env'; name: string }
|
|
58
|
+
| { kind: 'literal'; value: string }
|
|
59
|
+
| { kind: 'stored'; ref: string }
|
|
60
|
+
| { kind: 'brokered'; ref: string };
|
|
61
|
+
|
|
62
|
+
export interface KeySource {
|
|
63
|
+
resolve(ref: KeyRef): Promise<
|
|
64
|
+
| { ok: true; apiKey: string | null }
|
|
65
|
+
| { ok: false; reason: 'missing' | 'locked' | 'denied' }
|
|
66
|
+
>;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export interface ResolvedConnection extends Connection {
|
|
70
|
+
baseUrl: string;
|
|
71
|
+
apiKey: string | null;
|
|
72
|
+
headers: Record<string, string>;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export interface Usage {
|
|
76
|
+
inputTokens?: number;
|
|
77
|
+
outputTokens?: number;
|
|
78
|
+
estimated: boolean;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export interface ChatResult {
|
|
82
|
+
text: string;
|
|
83
|
+
toolCalls?: ToolCall[];
|
|
84
|
+
finishReason: 'stop' | 'length' | 'tool' | 'content_filter' | 'error' | 'canceled';
|
|
85
|
+
usage: Usage;
|
|
86
|
+
timing: { firstTokenMs: number | null; totalMs: number };
|
|
87
|
+
model: string;
|
|
88
|
+
source: ModelSource;
|
|
89
|
+
raw?: unknown;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export interface ModelSource {
|
|
93
|
+
provider: string;
|
|
94
|
+
connectionId: string;
|
|
95
|
+
baseUrl?: string;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export function modelRef(source: ModelSource, model: string): string {
|
|
99
|
+
return `${source.provider}/${model}`;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export type AIErrorKind =
|
|
103
|
+
| 'auth'
|
|
104
|
+
| 'rate_limit'
|
|
105
|
+
| 'timeout'
|
|
106
|
+
| 'network'
|
|
107
|
+
| 'server'
|
|
108
|
+
| 'invalid_request'
|
|
109
|
+
| 'invalid_response'
|
|
110
|
+
| 'context_length'
|
|
111
|
+
| 'canceled'
|
|
112
|
+
| 'policy_blocked'
|
|
113
|
+
| 'key_unavailable'
|
|
114
|
+
| 'tool_error'
|
|
115
|
+
| 'budget_exceeded';
|
|
116
|
+
|
|
117
|
+
export type StreamEvent =
|
|
118
|
+
| { type: 'start'; callId: string; provider: string; model: string }
|
|
119
|
+
| { type: 'delta'; text: string }
|
|
120
|
+
| { type: 'tool_call'; call: ToolCall }
|
|
121
|
+
| { type: 'context'; kind: string; data: unknown }
|
|
122
|
+
| { type: 'retry'; attempt: number; reason: AIErrorKind; delayMs: number }
|
|
123
|
+
| { type: 'done'; result: ChatResult }
|
|
124
|
+
| { type: 'error'; error: import('./errors.js').AIError };
|
|
125
|
+
|
|
126
|
+
export interface ProviderAdapter {
|
|
127
|
+
readonly provider: string;
|
|
128
|
+
chat(conn: ResolvedConnection, req: ChatRequest): Promise<ChatResult>;
|
|
129
|
+
stream(conn: ResolvedConnection, req: ChatRequest): AsyncIterable<StreamEvent>;
|
|
130
|
+
listModels?(conn: ResolvedConnection): Promise<ModelInfo[]>;
|
|
131
|
+
health?(conn: ResolvedConnection): Promise<{ ok: boolean; detail?: string }>;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
export interface ModelInfo {
|
|
135
|
+
id: string;
|
|
136
|
+
source: ModelSource;
|
|
137
|
+
contextWindow?: number;
|
|
138
|
+
capabilities?: string[];
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export interface GovernancePolicy {
|
|
142
|
+
checkModel(provider: string, model: string): { allowed: true } | { allowed: false; reason: string };
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
export interface Redactor {
|
|
146
|
+
redact(text: string): string;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
export interface TelemetrySink {
|
|
150
|
+
record(r: CallRecord): void | Promise<void>;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
export interface CallInfo {
|
|
154
|
+
callId: string;
|
|
155
|
+
connection: Connection;
|
|
156
|
+
provider: string;
|
|
157
|
+
model: string;
|
|
158
|
+
metadata?: Record<string, unknown>;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
export interface CallRecord {
|
|
162
|
+
callId: string;
|
|
163
|
+
connectionId: string;
|
|
164
|
+
provider: string;
|
|
165
|
+
model: string;
|
|
166
|
+
startedAt: number;
|
|
167
|
+
timing: ChatResult['timing'];
|
|
168
|
+
usage: Usage;
|
|
169
|
+
finishReason: ChatResult['finishReason'];
|
|
170
|
+
error?: { kind: AIErrorKind; status?: number; message: string };
|
|
171
|
+
attempts: number;
|
|
172
|
+
metadata?: Record<string, unknown>;
|
|
173
|
+
promptChars: number;
|
|
174
|
+
responseChars: number;
|
|
175
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { ChatMessage } from './types.js';
|
|
2
|
+
|
|
3
|
+
export function asRecord(value: unknown): Record<string, unknown> | undefined {
|
|
4
|
+
return value && typeof value === 'object' && !Array.isArray(value) ? value as Record<string, unknown> : undefined;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export function asString(value: unknown): string | undefined {
|
|
8
|
+
return typeof value === 'string' ? value : undefined;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function asNumber(value: unknown): number | undefined {
|
|
12
|
+
return typeof value === 'number' && Number.isFinite(value) ? value : undefined;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function promptChars(messages: ChatMessage[]): number {
|
|
16
|
+
return messages.reduce((sum, message) => {
|
|
17
|
+
if (typeof message.content === 'string') return sum + message.content.length;
|
|
18
|
+
return sum + message.content.reduce((partSum, part) => partSum + (part.text?.length ?? 0) + (part.imageBase64?.length ?? 0), 0);
|
|
19
|
+
}, 0);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function textFromMessages(messages: ChatMessage[]): string {
|
|
23
|
+
return messages.map((message) => {
|
|
24
|
+
if (typeof message.content === 'string') return message.content;
|
|
25
|
+
return message.content.map((part) => part.text ?? '').join('');
|
|
26
|
+
}).join('\n');
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function sleep(ms: number, signal?: AbortSignal): Promise<void> {
|
|
30
|
+
if (ms <= 0) return Promise.resolve();
|
|
31
|
+
return new Promise((resolve, reject) => {
|
|
32
|
+
let settled = false;
|
|
33
|
+
const cleanup = () => signal?.removeEventListener('abort', onAbort);
|
|
34
|
+
const timer = setTimeout(() => {
|
|
35
|
+
settled = true;
|
|
36
|
+
cleanup();
|
|
37
|
+
resolve();
|
|
38
|
+
}, ms);
|
|
39
|
+
const onAbort = () => {
|
|
40
|
+
if (settled) return;
|
|
41
|
+
settled = true;
|
|
42
|
+
clearTimeout(timer);
|
|
43
|
+
cleanup();
|
|
44
|
+
reject(new DOMException('Sleep aborted', 'AbortError'));
|
|
45
|
+
};
|
|
46
|
+
if (signal) {
|
|
47
|
+
if (signal.aborted) onAbort();
|
|
48
|
+
else signal.addEventListener('abort', onAbort, { once: true });
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@jxburros/ai-nugget",
|
|
3
|
+
"version": "0.3.1",
|
|
4
|
+
"private": false,
|
|
5
|
+
"type": "module",
|
|
6
|
+
"description": "Small isomorphic AI model nugget for provider calls, policy, redaction, telemetry, and agent loops.",
|
|
7
|
+
"license": "MIT",
|
|
8
|
+
"author": "jxburros",
|
|
9
|
+
"keywords": [
|
|
10
|
+
"ai",
|
|
11
|
+
"llm",
|
|
12
|
+
"openai",
|
|
13
|
+
"anthropic",
|
|
14
|
+
"gemini",
|
|
15
|
+
"ollama",
|
|
16
|
+
"typescript",
|
|
17
|
+
"telemetry"
|
|
18
|
+
],
|
|
19
|
+
"engines": {
|
|
20
|
+
"node": ">=20"
|
|
21
|
+
},
|
|
22
|
+
"main": "./dist/index.js",
|
|
23
|
+
"types": "./dist/index.d.ts",
|
|
24
|
+
"sideEffects": false,
|
|
25
|
+
"repository": {
|
|
26
|
+
"type": "git",
|
|
27
|
+
"url": "git+https://github.com/jxburros/AI-Nugget.git"
|
|
28
|
+
},
|
|
29
|
+
"bugs": {
|
|
30
|
+
"url": "https://github.com/jxburros/AI-Nugget/issues"
|
|
31
|
+
},
|
|
32
|
+
"homepage": "https://github.com/jxburros/AI-Nugget#readme",
|
|
33
|
+
"exports": {
|
|
34
|
+
".": {
|
|
35
|
+
"types": "./dist/index.d.ts",
|
|
36
|
+
"import": "./dist/index.js",
|
|
37
|
+
"default": "./dist/index.js"
|
|
38
|
+
},
|
|
39
|
+
"./agent": {
|
|
40
|
+
"types": "./dist/agent/index.d.ts",
|
|
41
|
+
"import": "./dist/agent/index.js",
|
|
42
|
+
"default": "./dist/agent/index.js"
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
"files": [
|
|
46
|
+
"dist/**/*.js",
|
|
47
|
+
"dist/**/*.d.ts",
|
|
48
|
+
"nugget",
|
|
49
|
+
"README.md",
|
|
50
|
+
"LICENSE"
|
|
51
|
+
],
|
|
52
|
+
"scripts": {
|
|
53
|
+
"build": "tsc -p tsconfig.json",
|
|
54
|
+
"test": "vitest run",
|
|
55
|
+
"test:browser": "vitest run --config vitest.browser.config.ts",
|
|
56
|
+
"test:live": "vitest run tests/live-smoke.test.ts",
|
|
57
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
58
|
+
"build:nugget": "node scripts/build-nugget.mjs",
|
|
59
|
+
"prepublishOnly": "npm run build && npm run build:nugget"
|
|
60
|
+
},
|
|
61
|
+
"devDependencies": {
|
|
62
|
+
"@types/node": "^22.15.21",
|
|
63
|
+
"@vitest/browser": "^4.1.9",
|
|
64
|
+
"@vitest/browser-playwright": "^4.1.9",
|
|
65
|
+
"playwright": "^1.61.1",
|
|
66
|
+
"typescript": "^5.8.3",
|
|
67
|
+
"vitest": "^4.1.5"
|
|
68
|
+
}
|
|
69
|
+
}
|