@justram/pie 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.
Files changed (83) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/LICENSE +21 -0
  3. package/README.md +236 -0
  4. package/bin/pie +14 -0
  5. package/dist/cache/index.d.ts +4 -0
  6. package/dist/cache/index.js +3 -0
  7. package/dist/cache/warm.d.ts +3 -0
  8. package/dist/cache/warm.js +23 -0
  9. package/dist/cli/args.d.ts +30 -0
  10. package/dist/cli/args.js +185 -0
  11. package/dist/cli/attachments.d.ts +7 -0
  12. package/dist/cli/attachments.js +29 -0
  13. package/dist/cli/config.d.ts +22 -0
  14. package/dist/cli/config.js +20 -0
  15. package/dist/cli/image.d.ts +17 -0
  16. package/dist/cli/image.js +73 -0
  17. package/dist/cli/index.d.ts +2 -0
  18. package/dist/cli/index.js +1 -0
  19. package/dist/cli/oauth.d.ts +14 -0
  20. package/dist/cli/oauth.js +178 -0
  21. package/dist/cli/stream.d.ts +7 -0
  22. package/dist/cli/stream.js +73 -0
  23. package/dist/cli.d.ts +2 -0
  24. package/dist/cli.js +15 -0
  25. package/dist/core/cache/file.d.ts +4 -0
  26. package/dist/core/cache/file.js +44 -0
  27. package/dist/core/cache/key.d.ts +2 -0
  28. package/dist/core/cache/key.js +12 -0
  29. package/dist/core/cache/memory.d.ts +4 -0
  30. package/dist/core/cache/memory.js +33 -0
  31. package/dist/core/cache/types.d.ts +19 -0
  32. package/dist/core/cache/types.js +1 -0
  33. package/dist/core/errors.d.ts +39 -0
  34. package/dist/core/errors.js +50 -0
  35. package/dist/core/events.d.ts +87 -0
  36. package/dist/core/events.js +1 -0
  37. package/dist/core/extract.d.ts +4 -0
  38. package/dist/core/extract.js +384 -0
  39. package/dist/core/frontmatter.d.ts +5 -0
  40. package/dist/core/frontmatter.js +58 -0
  41. package/dist/core/helpers.d.ts +5 -0
  42. package/dist/core/helpers.js +80 -0
  43. package/dist/core/schema/normalize.d.ts +7 -0
  44. package/dist/core/schema/normalize.js +187 -0
  45. package/dist/core/setup.d.ts +13 -0
  46. package/dist/core/setup.js +174 -0
  47. package/dist/core/types.d.ts +143 -0
  48. package/dist/core/types.js +1 -0
  49. package/dist/core/validators/assert.d.ts +1 -0
  50. package/dist/core/validators/assert.js +18 -0
  51. package/dist/core/validators/command.d.ts +1 -0
  52. package/dist/core/validators/command.js +10 -0
  53. package/dist/core/validators/http.d.ts +1 -0
  54. package/dist/core/validators/http.js +28 -0
  55. package/dist/core/validators/index.d.ts +22 -0
  56. package/dist/core/validators/index.js +55 -0
  57. package/dist/core/validators/shell.d.ts +9 -0
  58. package/dist/core/validators/shell.js +24 -0
  59. package/dist/errors.d.ts +1 -0
  60. package/dist/errors.js +1 -0
  61. package/dist/events.d.ts +1 -0
  62. package/dist/events.js +1 -0
  63. package/dist/extract.d.ts +4 -0
  64. package/dist/extract.js +18 -0
  65. package/dist/index.d.ts +13 -0
  66. package/dist/index.js +8 -0
  67. package/dist/main.d.ts +9 -0
  68. package/dist/main.js +571 -0
  69. package/dist/models.d.ts +21 -0
  70. package/dist/models.js +21 -0
  71. package/dist/recipes/index.d.ts +34 -0
  72. package/dist/recipes/index.js +185 -0
  73. package/dist/runtime/node.d.ts +2 -0
  74. package/dist/runtime/node.js +71 -0
  75. package/dist/runtime/types.d.ts +32 -0
  76. package/dist/runtime/types.js +1 -0
  77. package/dist/setup.d.ts +2 -0
  78. package/dist/setup.js +1 -0
  79. package/dist/types.d.ts +1 -0
  80. package/dist/types.js +1 -0
  81. package/dist/utils/helpers.d.ts +5 -0
  82. package/dist/utils/helpers.js +80 -0
  83. package/package.json +71 -0
@@ -0,0 +1,73 @@
1
+ import * as photon from "@silvia-odwyer/photon-node";
2
+ const DEFAULT_OPTIONS = {
3
+ maxWidth: 2000,
4
+ maxHeight: 2000,
5
+ jpegQuality: 85,
6
+ };
7
+ export function resizeImage(image, options = {}) {
8
+ const opts = { ...DEFAULT_OPTIONS, ...options };
9
+ const buffer = Buffer.from(image.data, "base64");
10
+ let decoded;
11
+ try {
12
+ decoded = photon.PhotonImage.new_from_byteslice(new Uint8Array(buffer));
13
+ const originalWidth = decoded.get_width();
14
+ const originalHeight = decoded.get_height();
15
+ if (originalWidth <= opts.maxWidth && originalHeight <= opts.maxHeight) {
16
+ return {
17
+ image,
18
+ originalWidth,
19
+ originalHeight,
20
+ width: originalWidth,
21
+ height: originalHeight,
22
+ wasResized: false,
23
+ };
24
+ }
25
+ const { width, height } = clampDimensions(originalWidth, originalHeight, opts.maxWidth, opts.maxHeight);
26
+ const resized = photon.resize(decoded, width, height, photon.SamplingFilter.Lanczos3);
27
+ try {
28
+ const mimeType = image.mimeType === "image/jpeg" ? "image/jpeg" : "image/png";
29
+ const bytes = mimeType === "image/jpeg" ? resized.get_bytes_jpeg(opts.jpegQuality) : resized.get_bytes();
30
+ const data = Buffer.from(bytes).toString("base64");
31
+ return {
32
+ image: { type: "image", mimeType, data },
33
+ originalWidth,
34
+ originalHeight,
35
+ width,
36
+ height,
37
+ wasResized: true,
38
+ };
39
+ }
40
+ finally {
41
+ resized.free();
42
+ }
43
+ }
44
+ finally {
45
+ decoded?.free();
46
+ }
47
+ }
48
+ export function isImagePath(path) {
49
+ return /\.(png|jpe?g|gif|webp)$/i.test(path);
50
+ }
51
+ export function mimeTypeForImage(path) {
52
+ const lower = path.toLowerCase();
53
+ if (lower.endsWith(".jpg") || lower.endsWith(".jpeg"))
54
+ return "image/jpeg";
55
+ if (lower.endsWith(".gif"))
56
+ return "image/gif";
57
+ if (lower.endsWith(".webp"))
58
+ return "image/webp";
59
+ return "image/png";
60
+ }
61
+ function clampDimensions(width, height, maxWidth, maxHeight) {
62
+ let nextWidth = width;
63
+ let nextHeight = height;
64
+ if (nextWidth > maxWidth) {
65
+ nextHeight = Math.round((nextHeight * maxWidth) / nextWidth);
66
+ nextWidth = maxWidth;
67
+ }
68
+ if (nextHeight > maxHeight) {
69
+ nextWidth = Math.round((nextWidth * maxHeight) / nextHeight);
70
+ nextHeight = maxHeight;
71
+ }
72
+ return { width: Math.max(1, nextWidth), height: Math.max(1, nextHeight) };
73
+ }
@@ -0,0 +1,2 @@
1
+ export type { CliDeps } from "../main.js";
2
+ export { main as runCli } from "../main.js";
@@ -0,0 +1 @@
1
+ export { main as runCli } from "../main.js";
@@ -0,0 +1,14 @@
1
+ import type { Writable } from "node:stream";
2
+ import { type OAuthCredentials } from "@mariozechner/pi-ai";
3
+ export type ApiKeyCredential = {
4
+ type: "api_key";
5
+ key: string;
6
+ };
7
+ export type OAuthCredential = {
8
+ type: "oauth";
9
+ } & OAuthCredentials;
10
+ export type AuthCredential = ApiKeyCredential | OAuthCredential;
11
+ export type AuthMap = Record<string, AuthCredential | undefined>;
12
+ export declare function resolveApiKeyForProvider(provider: string, stderr: Writable): Promise<string | undefined>;
13
+ export declare function getSupportedOAuthProviders(): string[];
14
+ export declare function loginWithOAuthProvider(provider: string, stderr: Writable): Promise<string>;
@@ -0,0 +1,178 @@
1
+ import { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
2
+ import { homedir } from "node:os";
3
+ import { dirname, join } from "node:path";
4
+ import { createInterface } from "node:readline/promises";
5
+ import { getEnvApiKey, getOAuthApiKey, getOAuthProviders, loginAnthropic, loginAntigravity, loginGeminiCli, loginGitHubCopilot, loginOpenAICodex, } from "@mariozechner/pi-ai";
6
+ const AUTH_PATH = join(homedir(), ".pi", "agent", "auth.json");
7
+ const OAUTH_PROVIDERS = new Set(getOAuthProviders().map((provider) => provider.id));
8
+ export async function resolveApiKeyForProvider(provider, stderr) {
9
+ const auth = loadAuth();
10
+ const authKey = await getKeyFromAuth(provider, auth);
11
+ if (authKey) {
12
+ return authKey;
13
+ }
14
+ const envKey = getEnvApiKey(provider);
15
+ if (envKey) {
16
+ return envKey;
17
+ }
18
+ if (!isOAuthProvider(provider)) {
19
+ return undefined;
20
+ }
21
+ return await loginAndResolve(provider, auth, stderr);
22
+ }
23
+ export function getSupportedOAuthProviders() {
24
+ return getOAuthProviders()
25
+ .map((provider) => provider.id)
26
+ .sort();
27
+ }
28
+ export async function loginWithOAuthProvider(provider, stderr) {
29
+ if (!isOAuthProvider(provider)) {
30
+ const supported = getSupportedOAuthProviders().join(", ");
31
+ throw new Error(`Unsupported OAuth provider: ${provider}. Supported: ${supported}`);
32
+ }
33
+ const auth = loadAuth();
34
+ return await loginAndResolve(provider, auth, stderr);
35
+ }
36
+ function loadAuth() {
37
+ try {
38
+ const raw = readFileSync(AUTH_PATH, "utf-8");
39
+ const parsed = JSON.parse(raw);
40
+ return parsed && typeof parsed === "object" ? parsed : {};
41
+ }
42
+ catch {
43
+ return {};
44
+ }
45
+ }
46
+ function saveAuth(auth) {
47
+ const dir = dirname(AUTH_PATH);
48
+ if (!existsSync(dir)) {
49
+ mkdirSync(dir, { recursive: true, mode: 0o700 });
50
+ }
51
+ writeFileSync(AUTH_PATH, JSON.stringify(auth, null, 2), "utf-8");
52
+ chmodSync(AUTH_PATH, 0o600);
53
+ }
54
+ async function getKeyFromAuth(provider, auth) {
55
+ const entry = auth[provider];
56
+ if (!entry) {
57
+ return undefined;
58
+ }
59
+ if (entry.type === "api_key") {
60
+ return entry.key;
61
+ }
62
+ if (!isOAuthProvider(provider)) {
63
+ return undefined;
64
+ }
65
+ const result = await getOAuthApiKey(provider, oauthCredentials(auth));
66
+ if (!result) {
67
+ return undefined;
68
+ }
69
+ auth[provider] = { type: "oauth", ...result.newCredentials };
70
+ saveAuth(auth);
71
+ return result.apiKey;
72
+ }
73
+ async function loginAndResolve(provider, auth, stderr) {
74
+ const result = await getOAuthApiKey(provider, oauthCredentials(auth));
75
+ if (result) {
76
+ auth[provider] = { type: "oauth", ...result.newCredentials };
77
+ saveAuth(auth);
78
+ return result.apiKey;
79
+ }
80
+ writeLine(stderr, `[oauth:${provider}] No credentials found in ${AUTH_PATH}. Starting login...`);
81
+ const credentials = await login(provider, stderr);
82
+ auth[provider] = { type: "oauth", ...credentials };
83
+ saveAuth(auth);
84
+ const refreshed = await getOAuthApiKey(provider, oauthCredentials(auth));
85
+ if (!refreshed) {
86
+ throw new Error(`OAuth login completed, but no credentials were returned for ${provider}`);
87
+ }
88
+ auth[provider] = { type: "oauth", ...refreshed.newCredentials };
89
+ saveAuth(auth);
90
+ return refreshed.apiKey;
91
+ }
92
+ function oauthCredentials(auth) {
93
+ const result = {};
94
+ for (const [key, value] of Object.entries(auth)) {
95
+ if (value?.type === "oauth") {
96
+ result[key] = value;
97
+ }
98
+ }
99
+ return result;
100
+ }
101
+ function isOAuthProvider(provider) {
102
+ return OAUTH_PROVIDERS.has(provider);
103
+ }
104
+ async function login(provider, stderr) {
105
+ switch (provider) {
106
+ case "anthropic":
107
+ return await loginAnthropic((url) => {
108
+ writeLine(stderr, `[oauth:${provider}] Open this URL to authenticate:`);
109
+ writeLine(stderr, url);
110
+ }, async () => {
111
+ return await promptOnce("Paste the authorization code:", stderr);
112
+ });
113
+ case "github-copilot":
114
+ return await loginGitHubCopilot({
115
+ onAuth: (url, instructions) => {
116
+ writeLine(stderr, `[oauth:${provider}] Open this URL to authenticate:`);
117
+ writeLine(stderr, url);
118
+ if (instructions)
119
+ writeLine(stderr, instructions);
120
+ },
121
+ onPrompt: async ({ message }) => {
122
+ return await promptOnce(message, stderr);
123
+ },
124
+ onProgress: (message) => {
125
+ writeLine(stderr, `[oauth:${provider}] ${message}`);
126
+ },
127
+ });
128
+ case "google-gemini-cli":
129
+ return await loginGeminiCli(({ url, instructions }) => {
130
+ writeLine(stderr, `[oauth:${provider}] Open this URL to authenticate:`);
131
+ writeLine(stderr, url);
132
+ if (instructions)
133
+ writeLine(stderr, instructions);
134
+ }, (message) => {
135
+ writeLine(stderr, `[oauth:${provider}] ${message}`);
136
+ }, async () => {
137
+ return await promptOnce("Paste the full redirect URL (or press Enter to wait for browser callback):", stderr);
138
+ });
139
+ case "google-antigravity":
140
+ return await loginAntigravity(({ url, instructions }) => {
141
+ writeLine(stderr, `[oauth:${provider}] Open this URL to authenticate:`);
142
+ writeLine(stderr, url);
143
+ if (instructions)
144
+ writeLine(stderr, instructions);
145
+ }, (message) => {
146
+ writeLine(stderr, `[oauth:${provider}] ${message}`);
147
+ }, async () => {
148
+ return await promptOnce("Paste the full redirect URL (or press Enter to wait for browser callback):", stderr);
149
+ });
150
+ case "openai-codex":
151
+ return await loginOpenAICodex({
152
+ onAuth: ({ url, instructions }) => {
153
+ writeLine(stderr, `[oauth:${provider}] Open this URL to authenticate:`);
154
+ writeLine(stderr, url);
155
+ if (instructions)
156
+ writeLine(stderr, instructions);
157
+ },
158
+ onPrompt: async ({ message }) => {
159
+ return await promptOnce(message, stderr);
160
+ },
161
+ onProgress: (message) => {
162
+ writeLine(stderr, `[oauth:${provider}] ${message}`);
163
+ },
164
+ onManualCodeInput: async () => {
165
+ return await promptOnce("Paste the authorization code:", stderr);
166
+ },
167
+ });
168
+ default:
169
+ throw new Error(`OAuth login flow not implemented for provider: ${provider}.`);
170
+ }
171
+ }
172
+ async function promptOnce(prompt, stderr) {
173
+ const rl = createInterface({ input: process.stdin, output: stderr });
174
+ return rl.question(`${prompt} `).finally(() => rl.close());
175
+ }
176
+ function writeLine(stream, message) {
177
+ stream.write(`${message}\n`);
178
+ }
@@ -0,0 +1,7 @@
1
+ export interface JsonStreamer {
2
+ handleDelta(delta: string): void;
3
+ handleToolCall(args: unknown): void;
4
+ reset(): void;
5
+ }
6
+ export declare function createJsonStreamer(writeLine: (line: string) => void): JsonStreamer;
7
+ export declare function parsePartialJson(text: string): unknown | null;
@@ -0,0 +1,73 @@
1
+ import { parseStreamingJson } from "@mariozechner/pi-ai";
2
+ export function createJsonStreamer(writeLine) {
3
+ let buffer = "";
4
+ let lastSerialized = null;
5
+ const emit = (value) => {
6
+ const serialized = JSON.stringify(value);
7
+ if (serialized === lastSerialized) {
8
+ return;
9
+ }
10
+ lastSerialized = serialized;
11
+ writeLine(`${JSON.stringify({ partial: value })}\n`);
12
+ };
13
+ return {
14
+ handleDelta: (delta) => {
15
+ buffer += delta;
16
+ const parsed = parsePartialJson(buffer);
17
+ if (parsed !== null) {
18
+ emit(parsed);
19
+ }
20
+ },
21
+ handleToolCall: (args) => {
22
+ emit(args);
23
+ },
24
+ reset: () => {
25
+ buffer = "";
26
+ lastSerialized = null;
27
+ },
28
+ };
29
+ }
30
+ export function parsePartialJson(text) {
31
+ const start = findJsonStart(text);
32
+ if (start === -1) {
33
+ return null;
34
+ }
35
+ const slice = text.slice(start).trim();
36
+ if (!slice) {
37
+ return null;
38
+ }
39
+ const parsed = parseStreamingJson(slice);
40
+ return shouldEmitParsed(parsed, slice) ? parsed : null;
41
+ }
42
+ function shouldEmitParsed(parsed, source) {
43
+ if (parsed === null || parsed === undefined) {
44
+ return false;
45
+ }
46
+ const trimmed = source.trim();
47
+ if (trimmed === "{}" || trimmed === "[]") {
48
+ return true;
49
+ }
50
+ if (Array.isArray(parsed)) {
51
+ if (parsed.length > 0) {
52
+ return true;
53
+ }
54
+ return trimmed.endsWith("]");
55
+ }
56
+ if (typeof parsed === "object") {
57
+ const keys = Object.keys(parsed);
58
+ if (keys.length > 0) {
59
+ return true;
60
+ }
61
+ return trimmed.endsWith("}");
62
+ }
63
+ return true;
64
+ }
65
+ function findJsonStart(text) {
66
+ const objStart = text.indexOf("{");
67
+ const arrStart = text.indexOf("[");
68
+ if (objStart === -1)
69
+ return arrStart;
70
+ if (arrStart === -1)
71
+ return objStart;
72
+ return Math.min(objStart, arrStart);
73
+ }
package/dist/cli.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export { type CliDeps, runCli } from "./cli/index.js";
package/dist/cli.js ADDED
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env node
2
+ import { pathToFileURL } from "node:url";
3
+ import { main } from "./main.js";
4
+ export { runCli } from "./cli/index.js";
5
+ async function run() {
6
+ const exitCode = await main(process.argv.slice(2));
7
+ process.exitCode = exitCode;
8
+ }
9
+ if (import.meta.url === pathToFileURL(process.argv[1]).href) {
10
+ void run().catch((error) => {
11
+ const message = error instanceof Error ? (error.stack ?? error.message) : String(error);
12
+ console.error(message);
13
+ process.exitCode = 1;
14
+ });
15
+ }
@@ -0,0 +1,4 @@
1
+ import type { CacheStore } from "./types.js";
2
+ export declare function createFileCache(options: {
3
+ directory: string;
4
+ }): CacheStore;
@@ -0,0 +1,44 @@
1
+ import { mkdir, readFile, rm, stat, writeFile } from "node:fs/promises";
2
+ function cachePath(directory, key) {
3
+ const prefix = key.slice(0, 2);
4
+ return `${directory}/${prefix}/${key}.json`;
5
+ }
6
+ function cacheDir(directory, key) {
7
+ return `${directory}/${key.slice(0, 2)}`;
8
+ }
9
+ async function exists(path) {
10
+ try {
11
+ await stat(path);
12
+ return true;
13
+ }
14
+ catch {
15
+ return false;
16
+ }
17
+ }
18
+ export function createFileCache(options) {
19
+ const directory = options.directory.replaceAll(/\/+$/g, "");
20
+ return {
21
+ get: async (key) => {
22
+ const path = cachePath(directory, key);
23
+ if (!(await exists(path))) {
24
+ return undefined;
25
+ }
26
+ const text = await readFile(path, { encoding: "utf8" });
27
+ return JSON.parse(text);
28
+ },
29
+ set: async (key, entry) => {
30
+ const dir = cacheDir(directory, key);
31
+ await mkdir(dir, { recursive: true });
32
+ const path = cachePath(directory, key);
33
+ await writeFile(path, JSON.stringify(entry), { encoding: "utf8" });
34
+ },
35
+ delete: async (key) => {
36
+ const path = cachePath(directory, key);
37
+ await rm(path, { force: true });
38
+ },
39
+ clear: async () => {
40
+ await rm(directory, { recursive: true, force: true });
41
+ await mkdir(directory, { recursive: true });
42
+ },
43
+ };
44
+ }
@@ -0,0 +1,2 @@
1
+ import type { ExtractOptions } from "../types.js";
2
+ export declare function computeCacheKey(input: string, options: ExtractOptions<unknown>): string;
@@ -0,0 +1,12 @@
1
+ import { createHash } from "node:crypto";
2
+ export function computeCacheKey(input, options) {
3
+ const keyData = {
4
+ input,
5
+ schema: JSON.stringify(options.schema),
6
+ prompt: options.prompt,
7
+ model: options.model?.name ?? "unknown",
8
+ validateCommand: options.validateCommand ?? null,
9
+ validateUrl: options.validateUrl ?? null,
10
+ };
11
+ return createHash("sha256").update(JSON.stringify(keyData)).digest("hex");
12
+ }
@@ -0,0 +1,4 @@
1
+ import type { CacheStore } from "./types.js";
2
+ export declare function createMemoryCache(options?: {
3
+ maxSize?: number;
4
+ }): CacheStore;
@@ -0,0 +1,33 @@
1
+ export function createMemoryCache(options) {
2
+ const maxSize = options?.maxSize ?? 1000;
3
+ const map = new Map();
4
+ return {
5
+ get: async (key) => {
6
+ const entry = map.get(key);
7
+ if (!entry) {
8
+ return undefined;
9
+ }
10
+ // Refresh LRU
11
+ map.delete(key);
12
+ map.set(key, entry);
13
+ return entry;
14
+ },
15
+ set: async (key, entry) => {
16
+ map.delete(key);
17
+ map.set(key, entry);
18
+ while (map.size > maxSize) {
19
+ const oldest = map.keys().next().value;
20
+ if (!oldest) {
21
+ break;
22
+ }
23
+ map.delete(oldest);
24
+ }
25
+ },
26
+ delete: async (key) => {
27
+ map.delete(key);
28
+ },
29
+ clear: async () => {
30
+ map.clear();
31
+ },
32
+ };
33
+ }
@@ -0,0 +1,19 @@
1
+ import type { ExtractOptions, Usage } from "../types.js";
2
+ export interface CacheEntry<T> {
3
+ data: T;
4
+ timestamp: number;
5
+ turns: number;
6
+ usage: Usage;
7
+ }
8
+ export interface CacheStore {
9
+ get<T>(key: string): Promise<CacheEntry<T> | undefined>;
10
+ set<T>(key: string, entry: CacheEntry<T>, ttl?: number): Promise<void>;
11
+ delete(key: string): Promise<void>;
12
+ clear(): Promise<void>;
13
+ }
14
+ export interface CacheOptions {
15
+ ttl?: number;
16
+ key?: (input: string, options: ExtractOptions<unknown>) => string;
17
+ store?: CacheStore;
18
+ revalidate?: boolean;
19
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,39 @@
1
+ export declare class ExtractError extends Error {
2
+ constructor(message: string, options?: {
3
+ cause?: Error;
4
+ });
5
+ }
6
+ export interface ValidationErrorDetail {
7
+ path: string;
8
+ message: string;
9
+ }
10
+ export declare class SchemaValidationError extends ExtractError {
11
+ readonly errors: ValidationErrorDetail[];
12
+ constructor(message: string, errors: ValidationErrorDetail[], options?: {
13
+ cause?: Error;
14
+ });
15
+ }
16
+ export declare class CommandValidationError extends ExtractError {
17
+ readonly command: string;
18
+ readonly exitCode: number;
19
+ constructor(message: string, command: string, exitCode: number, options?: {
20
+ cause?: Error;
21
+ });
22
+ }
23
+ export declare class HttpValidationError extends ExtractError {
24
+ readonly url: string;
25
+ readonly statusCode: number;
26
+ constructor(message: string, url: string, statusCode: number, options?: {
27
+ cause?: Error;
28
+ });
29
+ }
30
+ export declare class MaxTurnsError extends ExtractError {
31
+ readonly turns: number;
32
+ readonly lastError?: Error;
33
+ constructor(message: string, turns: number, lastError?: Error, options?: {
34
+ cause?: Error;
35
+ });
36
+ }
37
+ export declare class AbortError extends ExtractError {
38
+ constructor(message?: string);
39
+ }
@@ -0,0 +1,50 @@
1
+ export class ExtractError extends Error {
2
+ constructor(message, options) {
3
+ super(message, options);
4
+ this.name = "ExtractError";
5
+ }
6
+ }
7
+ export class SchemaValidationError extends ExtractError {
8
+ errors;
9
+ constructor(message, errors, options) {
10
+ super(message, options);
11
+ this.name = "SchemaValidationError";
12
+ this.errors = errors;
13
+ }
14
+ }
15
+ export class CommandValidationError extends ExtractError {
16
+ command;
17
+ exitCode;
18
+ constructor(message, command, exitCode, options) {
19
+ super(message, options);
20
+ this.name = "CommandValidationError";
21
+ this.command = command;
22
+ this.exitCode = exitCode;
23
+ }
24
+ }
25
+ export class HttpValidationError extends ExtractError {
26
+ url;
27
+ statusCode;
28
+ constructor(message, url, statusCode, options) {
29
+ super(message, options);
30
+ this.name = "HttpValidationError";
31
+ this.url = url;
32
+ this.statusCode = statusCode;
33
+ }
34
+ }
35
+ export class MaxTurnsError extends ExtractError {
36
+ turns;
37
+ lastError;
38
+ constructor(message, turns, lastError, options) {
39
+ super(message, options);
40
+ this.name = "MaxTurnsError";
41
+ this.turns = turns;
42
+ this.lastError = lastError;
43
+ }
44
+ }
45
+ export class AbortError extends ExtractError {
46
+ constructor(message = "Extraction aborted") {
47
+ super(message);
48
+ this.name = "AbortError";
49
+ }
50
+ }
@@ -0,0 +1,87 @@
1
+ import type { Api, AssistantMessage, Model, ToolCall } from "@mariozechner/pi-ai";
2
+ import type { Usage } from "./types.js";
3
+ export type ValidatorLayer = "schema" | "sync" | "async" | "command" | "http";
4
+ export type ExtractEvent<T> = StartEvent | CompleteEvent<T> | ErrorEvent | WarningEvent | CacheHitEvent | CacheMissEvent | CacheSetEvent | TurnStartEvent | TurnEndEvent | LlmStartEvent | LlmSelectedEvent | LlmDeltaEvent | LlmEndEvent | ThinkingEvent | ToolCallEvent | JsonExtractedEvent | ValidationStartEvent | ValidationPassEvent | ValidationErrorEvent;
5
+ export interface StartEvent {
6
+ type: "start";
7
+ maxTurns: number;
8
+ }
9
+ export interface CompleteEvent<T> {
10
+ type: "complete";
11
+ result: T;
12
+ turns: number;
13
+ usage: Usage;
14
+ }
15
+ export interface ErrorEvent {
16
+ type: "error";
17
+ error: Error;
18
+ turns: number;
19
+ }
20
+ export interface WarningEvent {
21
+ type: "warning";
22
+ code: "thinking_unsupported";
23
+ message: string;
24
+ }
25
+ export interface CacheHitEvent {
26
+ type: "cache_hit";
27
+ key: string;
28
+ age: number;
29
+ }
30
+ export interface CacheMissEvent {
31
+ type: "cache_miss";
32
+ key: string;
33
+ }
34
+ export interface CacheSetEvent {
35
+ type: "cache_set";
36
+ key: string;
37
+ }
38
+ export interface TurnStartEvent {
39
+ type: "turn_start";
40
+ turn: number;
41
+ }
42
+ export interface TurnEndEvent {
43
+ type: "turn_end";
44
+ turn: number;
45
+ hasResult: boolean;
46
+ }
47
+ export interface LlmStartEvent {
48
+ type: "llm_start";
49
+ }
50
+ export interface LlmSelectedEvent {
51
+ type: "llm_selected";
52
+ model: Model<Api>;
53
+ }
54
+ export interface LlmDeltaEvent {
55
+ type: "llm_delta";
56
+ delta: string;
57
+ }
58
+ export interface LlmEndEvent {
59
+ type: "llm_end";
60
+ message: AssistantMessage;
61
+ usage: Usage;
62
+ }
63
+ export interface ThinkingEvent {
64
+ type: "thinking";
65
+ text: string;
66
+ }
67
+ export interface ToolCallEvent {
68
+ type: "tool_call";
69
+ toolCall: ToolCall;
70
+ }
71
+ export interface JsonExtractedEvent {
72
+ type: "json_extracted";
73
+ source: "tool_call" | "text";
74
+ }
75
+ export interface ValidationStartEvent {
76
+ type: "validation_start";
77
+ layer: ValidatorLayer;
78
+ }
79
+ export interface ValidationPassEvent {
80
+ type: "validation_pass";
81
+ layer: ValidatorLayer;
82
+ }
83
+ export interface ValidationErrorEvent {
84
+ type: "validation_error";
85
+ layer: ValidatorLayer;
86
+ error: string;
87
+ }