deepline 0.1.0 → 0.1.2
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/dist/cli/index.js +212 -54
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/index.mjs +198 -40
- package/dist/cli/index.mjs.map +1 -1
- package/dist/index.d.mts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/index.mjs +1 -1
- package/dist/repo/apps/play-runner-workers/src/coordinator-entry.ts +3256 -0
- package/dist/repo/apps/play-runner-workers/src/dedup-do.ts +710 -0
- package/dist/repo/apps/play-runner-workers/src/entry.ts +5070 -0
- package/dist/repo/apps/play-runner-workers/src/runtime/README.md +21 -0
- package/dist/repo/apps/play-runner-workers/src/runtime/batching.ts +177 -0
- package/dist/repo/apps/play-runner-workers/src/runtime/execution-plan.ts +52 -0
- package/dist/repo/apps/play-runner-workers/src/runtime/tool-batch.ts +100 -0
- package/dist/repo/apps/play-runner-workers/src/runtime/tool-result.ts +184 -0
- package/dist/repo/sdk/src/cli/commands/auth.ts +482 -0
- package/dist/repo/sdk/src/cli/commands/billing.ts +188 -0
- package/dist/repo/sdk/src/cli/commands/csv.ts +123 -0
- package/dist/repo/sdk/src/cli/commands/db.ts +119 -0
- package/dist/repo/sdk/src/cli/commands/feedback.ts +40 -0
- package/dist/repo/sdk/src/cli/commands/org.ts +117 -0
- package/dist/repo/sdk/src/cli/commands/play.ts +3200 -0
- package/dist/repo/sdk/src/cli/commands/tools.ts +687 -0
- package/dist/repo/sdk/src/cli/dataset-stats.ts +341 -0
- package/dist/repo/sdk/src/cli/index.ts +138 -0
- package/dist/repo/sdk/src/cli/progress.ts +135 -0
- package/dist/repo/sdk/src/cli/trace.ts +61 -0
- package/dist/repo/sdk/src/cli/utils.ts +145 -0
- package/dist/repo/sdk/src/client.ts +1188 -0
- package/dist/repo/sdk/src/compat.ts +77 -0
- package/dist/repo/sdk/src/config.ts +285 -0
- package/dist/repo/sdk/src/errors.ts +125 -0
- package/dist/repo/sdk/src/http.ts +391 -0
- package/dist/repo/sdk/src/index.ts +139 -0
- package/dist/repo/sdk/src/play.ts +1330 -0
- package/dist/repo/sdk/src/plays/bundle-play-file.ts +133 -0
- package/dist/repo/sdk/src/plays/harness-stub.ts +210 -0
- package/dist/repo/sdk/src/plays/local-file-discovery.ts +326 -0
- package/dist/repo/sdk/src/tool-output.ts +489 -0
- package/dist/repo/sdk/src/types.ts +669 -0
- package/dist/repo/sdk/src/version.ts +2 -0
- package/dist/repo/sdk/src/worker-play-entry.ts +286 -0
- package/dist/repo/shared_libs/observability/node-tracing.ts +129 -0
- package/dist/repo/shared_libs/observability/tracing.ts +98 -0
- package/dist/repo/shared_libs/play-runtime/backend.ts +139 -0
- package/dist/repo/shared_libs/play-runtime/batch-runtime.ts +182 -0
- package/dist/repo/shared_libs/play-runtime/batching-types.ts +91 -0
- package/dist/repo/shared_libs/play-runtime/context.ts +3999 -0
- package/dist/repo/shared_libs/play-runtime/coordinator-headers.ts +78 -0
- package/dist/repo/shared_libs/play-runtime/ctx-contract.ts +250 -0
- package/dist/repo/shared_libs/play-runtime/ctx-types.ts +713 -0
- package/dist/repo/shared_libs/play-runtime/dataset-id.ts +10 -0
- package/dist/repo/shared_libs/play-runtime/db-session-crypto.ts +304 -0
- package/dist/repo/shared_libs/play-runtime/db-session.ts +462 -0
- package/dist/repo/shared_libs/play-runtime/dedup-backend.ts +0 -0
- package/dist/repo/shared_libs/play-runtime/default-batch-strategies.ts +124 -0
- package/dist/repo/shared_libs/play-runtime/execution-plan.ts +262 -0
- package/dist/repo/shared_libs/play-runtime/live-events.ts +214 -0
- package/dist/repo/shared_libs/play-runtime/live-state-contract.ts +50 -0
- package/dist/repo/shared_libs/play-runtime/map-execution-frame.ts +114 -0
- package/dist/repo/shared_libs/play-runtime/map-row-identity.ts +158 -0
- package/dist/repo/shared_libs/play-runtime/profiles.ts +90 -0
- package/dist/repo/shared_libs/play-runtime/progress-emitter.ts +172 -0
- package/dist/repo/shared_libs/play-runtime/protocol.ts +121 -0
- package/dist/repo/shared_libs/play-runtime/public-play-contract.ts +42 -0
- package/dist/repo/shared_libs/play-runtime/result-normalization.ts +33 -0
- package/dist/repo/shared_libs/play-runtime/runtime-actions.ts +208 -0
- package/dist/repo/shared_libs/play-runtime/runtime-api.ts +1873 -0
- package/dist/repo/shared_libs/play-runtime/runtime-constraints.ts +2 -0
- package/dist/repo/shared_libs/play-runtime/runtime-pg-driver-neon-serverless.ts +201 -0
- package/dist/repo/shared_libs/play-runtime/runtime-pg-driver-pg.ts +48 -0
- package/dist/repo/shared_libs/play-runtime/runtime-pg-driver.ts +84 -0
- package/dist/repo/shared_libs/play-runtime/scheduler-backend.ts +174 -0
- package/dist/repo/shared_libs/play-runtime/static-pipeline-types.ts +147 -0
- package/dist/repo/shared_libs/play-runtime/suspension.ts +68 -0
- package/dist/repo/shared_libs/play-runtime/tool-batch-executor.ts +146 -0
- package/dist/repo/shared_libs/play-runtime/tool-result.ts +387 -0
- package/dist/repo/shared_libs/play-runtime/tracing.ts +31 -0
- package/dist/repo/shared_libs/play-runtime/waterfall-replay.ts +75 -0
- package/dist/repo/shared_libs/play-runtime/worker-api-types.ts +140 -0
- package/dist/repo/shared_libs/plays/artifact-transport.ts +14 -0
- package/dist/repo/shared_libs/plays/artifact-types.ts +49 -0
- package/dist/repo/shared_libs/plays/bundling/index.ts +1346 -0
- package/dist/repo/shared_libs/plays/compiler-manifest.ts +186 -0
- package/dist/repo/shared_libs/plays/contracts.ts +51 -0
- package/dist/repo/shared_libs/plays/dataset.ts +308 -0
- package/dist/repo/shared_libs/plays/definition.ts +264 -0
- package/dist/repo/shared_libs/plays/file-refs.ts +11 -0
- package/dist/repo/shared_libs/plays/rate-limit-scheduler.ts +206 -0
- package/dist/repo/shared_libs/plays/resolve-static-pipeline.ts +164 -0
- package/dist/repo/shared_libs/plays/row-identity.ts +302 -0
- package/dist/repo/shared_libs/plays/runtime-validation.ts +415 -0
- package/dist/repo/shared_libs/plays/static-pipeline.ts +560 -0
- package/dist/repo/shared_libs/temporal/constants.ts +39 -0
- package/dist/repo/shared_libs/temporal/preview-config.ts +153 -0
- package/package.json +4 -4
|
@@ -0,0 +1,391 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HTTP transport layer for the Deepline SDK.
|
|
3
|
+
*
|
|
4
|
+
* Handles authentication, retries with exponential backoff, localhost failover
|
|
5
|
+
* (tries both `localhost` and `127.0.0.1`), and structured error mapping.
|
|
6
|
+
*
|
|
7
|
+
* ## Retry behavior
|
|
8
|
+
*
|
|
9
|
+
* - **Max attempts**: `1 + maxRetries` (default: 4 total attempts)
|
|
10
|
+
* - **Backoff**: exponential — 1s, 2s, 4s, 8s, ... capped at 30s
|
|
11
|
+
* - **Retryable**: network errors, timeouts, HTTP 429 (rate limit)
|
|
12
|
+
* - **Not retryable**: HTTP 401/403 (auth errors), other 4xx/5xx API errors
|
|
13
|
+
*
|
|
14
|
+
* ## Localhost failover
|
|
15
|
+
*
|
|
16
|
+
* Local development hosts try loopback variants. This handles DNS resolution
|
|
17
|
+
* differences across platforms.
|
|
18
|
+
*
|
|
19
|
+
* @module
|
|
20
|
+
*/
|
|
21
|
+
import type { ResolvedConfig } from './types.js';
|
|
22
|
+
import { AuthError, DeeplineError, RateLimitError } from './errors.js';
|
|
23
|
+
import { SDK_API_CONTRACT, SDK_VERSION } from './version.js';
|
|
24
|
+
import type { LiveEventEnvelope } from './types.js';
|
|
25
|
+
import {
|
|
26
|
+
COORDINATOR_URL_OVERRIDE_HEADER,
|
|
27
|
+
WORKER_CALLBACK_URL_OVERRIDE_HEADER,
|
|
28
|
+
} from '../../shared_libs/play-runtime/coordinator-headers.js';
|
|
29
|
+
|
|
30
|
+
interface RequestOptions {
|
|
31
|
+
method?: 'GET' | 'POST' | 'PUT' | 'DELETE';
|
|
32
|
+
body?: unknown;
|
|
33
|
+
headers?: Record<string, string>;
|
|
34
|
+
/** Per-request timeout override in milliseconds. */
|
|
35
|
+
timeout?: number;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
interface StreamOptions {
|
|
39
|
+
method?: 'GET' | 'POST';
|
|
40
|
+
body?: unknown;
|
|
41
|
+
headers?: Record<string, string>;
|
|
42
|
+
signal?: AbortSignal;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Low-level HTTP client used internally by {@link DeeplineClient}.
|
|
47
|
+
*
|
|
48
|
+
* You typically don't construct this directly — it's created automatically
|
|
49
|
+
* when you instantiate `DeeplineClient` or call `Deepline.connect()`.
|
|
50
|
+
*
|
|
51
|
+
* @example
|
|
52
|
+
* ```typescript
|
|
53
|
+
* // Internal usage pattern:
|
|
54
|
+
* const http = new HttpClient(resolvedConfig);
|
|
55
|
+
* const tools = await http.get<{ tools: ToolDefinition[] }>('/api/v2/tools');
|
|
56
|
+
* const result = await http.post('/api/v2/integrations/apollo/execute', { payload: input });
|
|
57
|
+
* ```
|
|
58
|
+
*/
|
|
59
|
+
export class HttpClient {
|
|
60
|
+
constructor(private config: ResolvedConfig) {}
|
|
61
|
+
|
|
62
|
+
private authHeaders(extra?: Record<string, string>): Record<string, string> {
|
|
63
|
+
const headers: Record<string, string> = {
|
|
64
|
+
'Authorization': `Bearer ${this.config.apiKey}`,
|
|
65
|
+
'User-Agent': `deepline-ts-sdk/${SDK_VERSION}`,
|
|
66
|
+
'X-Deepline-SDK-Version': SDK_VERSION,
|
|
67
|
+
'X-Deepline-API-Contract': SDK_API_CONTRACT,
|
|
68
|
+
...extra,
|
|
69
|
+
};
|
|
70
|
+
const bypassToken = typeof process !== 'undefined'
|
|
71
|
+
? process.env?.VERCEL_PROTECTION_BYPASS_TOKEN
|
|
72
|
+
: undefined;
|
|
73
|
+
if (bypassToken) {
|
|
74
|
+
headers['x-vercel-protection-bypass'] = bypassToken;
|
|
75
|
+
}
|
|
76
|
+
const playArtifactR2Prefix = typeof process !== 'undefined'
|
|
77
|
+
? process.env?.DEEPLINE_PLAY_ARTIFACT_R2_PREFIX
|
|
78
|
+
: undefined;
|
|
79
|
+
if (playArtifactR2Prefix) {
|
|
80
|
+
headers['x-deepline-play-artifact-r2-prefix'] = playArtifactR2Prefix;
|
|
81
|
+
}
|
|
82
|
+
const coordinatorUrl = typeof process !== 'undefined'
|
|
83
|
+
? process.env?.DEEPLINE_COORDINATOR_URL
|
|
84
|
+
: undefined;
|
|
85
|
+
if (coordinatorUrl?.trim()) {
|
|
86
|
+
headers[COORDINATOR_URL_OVERRIDE_HEADER] = coordinatorUrl.trim();
|
|
87
|
+
}
|
|
88
|
+
const workerCallbackUrl = typeof process !== 'undefined'
|
|
89
|
+
? process.env?.DEEPLINE_WORKER_CALLBACK_URL
|
|
90
|
+
: undefined;
|
|
91
|
+
if (workerCallbackUrl?.trim()) {
|
|
92
|
+
headers[WORKER_CALLBACK_URL_OVERRIDE_HEADER] = workerCallbackUrl.trim();
|
|
93
|
+
}
|
|
94
|
+
return headers;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Send an HTTP request with automatic retries and error handling.
|
|
99
|
+
*
|
|
100
|
+
* @typeParam T - Expected response body type
|
|
101
|
+
* @param path - API path (e.g. `"/api/v2/tools"`)
|
|
102
|
+
* @param options - HTTP method, body, headers, and timeout
|
|
103
|
+
* @returns Parsed JSON response body
|
|
104
|
+
* @throws {@link AuthError} on HTTP 401/403 (immediate, no retry)
|
|
105
|
+
* @throws {@link RateLimitError} on HTTP 429 after all retries exhausted
|
|
106
|
+
* @throws {@link DeeplineError} on other API errors or connection failures
|
|
107
|
+
*/
|
|
108
|
+
async request<T = unknown>(path: string, options?: RequestOptions): Promise<T> {
|
|
109
|
+
const baseUrl = this.config.baseUrl;
|
|
110
|
+
const url = `${baseUrl}${path}`;
|
|
111
|
+
const method = options?.method ?? 'GET';
|
|
112
|
+
|
|
113
|
+
const headers = this.authHeaders(options?.headers);
|
|
114
|
+
|
|
115
|
+
if (options?.body !== undefined) {
|
|
116
|
+
headers['Content-Type'] = 'application/json';
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
let lastError: Error | null = null;
|
|
120
|
+
const candidateUrls = buildCandidateUrls(url);
|
|
121
|
+
let retryAfterDelayMs: number | null = null;
|
|
122
|
+
|
|
123
|
+
for (let attempt = 0; attempt <= this.config.maxRetries; attempt++) {
|
|
124
|
+
if (attempt > 0) {
|
|
125
|
+
const backoffMs = Math.min(1000 * Math.pow(2, attempt - 1), 30_000);
|
|
126
|
+
const delayMs =
|
|
127
|
+
retryAfterDelayMs === null
|
|
128
|
+
? backoffMs
|
|
129
|
+
: Math.max(backoffMs, retryAfterDelayMs);
|
|
130
|
+
retryAfterDelayMs = null;
|
|
131
|
+
await sleep(delayMs);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
for (const candidateUrl of candidateUrls) {
|
|
135
|
+
const controller = new AbortController();
|
|
136
|
+
const timeoutId = setTimeout(
|
|
137
|
+
() => controller.abort(),
|
|
138
|
+
options?.timeout ?? this.config.timeout,
|
|
139
|
+
);
|
|
140
|
+
|
|
141
|
+
try {
|
|
142
|
+
const response = await fetch(candidateUrl, {
|
|
143
|
+
method,
|
|
144
|
+
headers,
|
|
145
|
+
body: options?.body !== undefined ? JSON.stringify(options.body) : undefined,
|
|
146
|
+
signal: controller.signal,
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
clearTimeout(timeoutId);
|
|
150
|
+
|
|
151
|
+
if (response.status === 401 || response.status === 403) {
|
|
152
|
+
throw new AuthError();
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
if (response.status === 429) {
|
|
156
|
+
const retryAfter = parseRetryAfter(response);
|
|
157
|
+
lastError = new RateLimitError(retryAfter);
|
|
158
|
+
if (attempt < this.config.maxRetries) {
|
|
159
|
+
retryAfterDelayMs = retryAfter;
|
|
160
|
+
break;
|
|
161
|
+
}
|
|
162
|
+
throw lastError;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const body = await response.text();
|
|
166
|
+
let parsed: unknown;
|
|
167
|
+
try {
|
|
168
|
+
parsed = JSON.parse(body);
|
|
169
|
+
} catch {
|
|
170
|
+
parsed = body;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
if (!response.ok) {
|
|
174
|
+
const msg =
|
|
175
|
+
typeof parsed === 'object' && parsed && 'error' in parsed
|
|
176
|
+
? String((parsed as Record<string, unknown>).error)
|
|
177
|
+
: `HTTP ${response.status}`;
|
|
178
|
+
throw new DeeplineError(msg, response.status, 'API_ERROR', {
|
|
179
|
+
response: parsed,
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
return parsed as T;
|
|
184
|
+
} catch (error) {
|
|
185
|
+
clearTimeout(timeoutId);
|
|
186
|
+
if (error instanceof AuthError || error instanceof DeeplineError) {
|
|
187
|
+
throw error;
|
|
188
|
+
}
|
|
189
|
+
if (error instanceof RateLimitError) {
|
|
190
|
+
lastError = error;
|
|
191
|
+
break;
|
|
192
|
+
}
|
|
193
|
+
lastError = error instanceof Error ? error : new Error(String(error));
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
if (attempt < this.config.maxRetries) continue;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
if (lastError instanceof DeeplineError) {
|
|
201
|
+
throw lastError;
|
|
202
|
+
}
|
|
203
|
+
const errorMessage = lastError?.message
|
|
204
|
+
? `Unable to connect to ${baseUrl}. ${lastError.message}`
|
|
205
|
+
: `Unable to connect to ${baseUrl}. Is the computer able to access the url?`;
|
|
206
|
+
throw new DeeplineError(errorMessage);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Send a GET request.
|
|
211
|
+
*
|
|
212
|
+
* @typeParam T - Expected response body type
|
|
213
|
+
* @param path - API path (e.g. `"/api/v2/tools"`)
|
|
214
|
+
*/
|
|
215
|
+
async get<T = unknown>(path: string): Promise<T> {
|
|
216
|
+
return this.request<T>(path, { method: 'GET' });
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
async *streamSse<TEvent extends LiveEventEnvelope = LiveEventEnvelope>(
|
|
220
|
+
path: string,
|
|
221
|
+
options?: StreamOptions,
|
|
222
|
+
): AsyncGenerator<TEvent> {
|
|
223
|
+
const url = `${this.config.baseUrl}${path}`;
|
|
224
|
+
const method = options?.method ?? 'GET';
|
|
225
|
+
const headers = this.authHeaders({
|
|
226
|
+
Accept: 'text/event-stream',
|
|
227
|
+
...options?.headers,
|
|
228
|
+
});
|
|
229
|
+
if (options?.body !== undefined) {
|
|
230
|
+
headers['Content-Type'] = 'application/json';
|
|
231
|
+
}
|
|
232
|
+
let lastError: Error | null = null;
|
|
233
|
+
|
|
234
|
+
for (const candidateUrl of buildCandidateUrls(url)) {
|
|
235
|
+
try {
|
|
236
|
+
const response = await fetch(candidateUrl, {
|
|
237
|
+
method,
|
|
238
|
+
headers,
|
|
239
|
+
body:
|
|
240
|
+
options?.body !== undefined
|
|
241
|
+
? JSON.stringify(options.body)
|
|
242
|
+
: undefined,
|
|
243
|
+
signal: options?.signal,
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
if (response.status === 401 || response.status === 403) {
|
|
247
|
+
throw new AuthError();
|
|
248
|
+
}
|
|
249
|
+
if (!response.ok) {
|
|
250
|
+
throw new DeeplineError(
|
|
251
|
+
`HTTP ${response.status}`,
|
|
252
|
+
response.status,
|
|
253
|
+
'API_ERROR',
|
|
254
|
+
);
|
|
255
|
+
}
|
|
256
|
+
if (!response.body) {
|
|
257
|
+
throw new DeeplineError('SSE response did not include a body.');
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
yield* decodeSseStream<TEvent>(response.body);
|
|
261
|
+
return;
|
|
262
|
+
} catch (error) {
|
|
263
|
+
if (error instanceof AuthError || error instanceof DeeplineError) {
|
|
264
|
+
throw error;
|
|
265
|
+
}
|
|
266
|
+
lastError = error instanceof Error ? error : new Error(String(error));
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
throw new DeeplineError(
|
|
271
|
+
lastError?.message
|
|
272
|
+
? `Unable to stream from ${this.config.baseUrl}. ${lastError.message}`
|
|
273
|
+
: `Unable to stream from ${this.config.baseUrl}.`,
|
|
274
|
+
);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* Send a POST request with a JSON body.
|
|
279
|
+
*
|
|
280
|
+
* @typeParam T - Expected response body type
|
|
281
|
+
* @param path - API path
|
|
282
|
+
* @param body - Request body (will be JSON-serialized)
|
|
283
|
+
*/
|
|
284
|
+
async post<T = unknown>(path: string, body: unknown): Promise<T> {
|
|
285
|
+
return this.request<T>(path, { method: 'POST', body });
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* Send a DELETE request.
|
|
290
|
+
*
|
|
291
|
+
* @typeParam T - Expected response body type
|
|
292
|
+
* @param path - API path
|
|
293
|
+
*/
|
|
294
|
+
async delete<T = unknown>(path: string): Promise<T> {
|
|
295
|
+
return this.request<T>(path, { method: 'DELETE' });
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
/** Parse the `Retry-After` header as milliseconds. Falls back to 5000ms. */
|
|
300
|
+
function parseRetryAfter(response: Response): number {
|
|
301
|
+
const header = response.headers.get('retry-after');
|
|
302
|
+
if (header) {
|
|
303
|
+
const seconds = Number(header);
|
|
304
|
+
if (Number.isFinite(seconds) && seconds > 0) {
|
|
305
|
+
return seconds * 1000;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
return 5000;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
/**
|
|
312
|
+
* For local development URLs, generate loopback variants to handle platform DNS
|
|
313
|
+
* differences.
|
|
314
|
+
*/
|
|
315
|
+
function buildCandidateUrls(url: string): string[] {
|
|
316
|
+
try {
|
|
317
|
+
const parsed = new URL(url);
|
|
318
|
+
const candidates = [url];
|
|
319
|
+
if (parsed.hostname === 'localhost') {
|
|
320
|
+
const loopback = new URL(url);
|
|
321
|
+
loopback.hostname = '127.0.0.1';
|
|
322
|
+
candidates.push(loopback.toString());
|
|
323
|
+
}
|
|
324
|
+
return [...new Set(candidates)];
|
|
325
|
+
} catch {
|
|
326
|
+
return [url];
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
async function* decodeSseStream<TEvent extends LiveEventEnvelope>(
|
|
331
|
+
body: ReadableStream<Uint8Array>,
|
|
332
|
+
): AsyncGenerator<TEvent> {
|
|
333
|
+
const reader = body.getReader();
|
|
334
|
+
const decoder = new TextDecoder();
|
|
335
|
+
let buffered = '';
|
|
336
|
+
|
|
337
|
+
try {
|
|
338
|
+
while (true) {
|
|
339
|
+
const { value, done } = await reader.read();
|
|
340
|
+
if (done) break;
|
|
341
|
+
buffered += decoder.decode(value, { stream: true });
|
|
342
|
+
const frames = buffered.split(/\r?\n\r?\n/);
|
|
343
|
+
buffered = frames.pop() ?? '';
|
|
344
|
+
for (const frame of frames) {
|
|
345
|
+
const event = decodeSseFrame<TEvent>(frame);
|
|
346
|
+
if (event) {
|
|
347
|
+
yield event;
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
buffered += decoder.decode();
|
|
353
|
+
const event = decodeSseFrame<TEvent>(buffered);
|
|
354
|
+
if (event) {
|
|
355
|
+
yield event;
|
|
356
|
+
}
|
|
357
|
+
} finally {
|
|
358
|
+
reader.releaseLock();
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
function decodeSseFrame<TEvent extends LiveEventEnvelope>(
|
|
363
|
+
frame: string,
|
|
364
|
+
): TEvent | null {
|
|
365
|
+
const data = frame
|
|
366
|
+
.split(/\r?\n/)
|
|
367
|
+
.filter((line) => line.startsWith('data:'))
|
|
368
|
+
.map((line) => line.slice('data:'.length).trimStart())
|
|
369
|
+
.join('\n')
|
|
370
|
+
.trim();
|
|
371
|
+
if (!data) {
|
|
372
|
+
return null;
|
|
373
|
+
}
|
|
374
|
+
const parsed = JSON.parse(data) as Partial<LiveEventEnvelope>;
|
|
375
|
+
if (
|
|
376
|
+
!parsed ||
|
|
377
|
+
typeof parsed !== 'object' ||
|
|
378
|
+
typeof parsed.cursor !== 'string' ||
|
|
379
|
+
typeof parsed.streamId !== 'string' ||
|
|
380
|
+
typeof parsed.scope !== 'string' ||
|
|
381
|
+
typeof parsed.type !== 'string' ||
|
|
382
|
+
typeof parsed.at !== 'string'
|
|
383
|
+
) {
|
|
384
|
+
return null;
|
|
385
|
+
}
|
|
386
|
+
return parsed as TEvent;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
function sleep(ms: number): Promise<void> {
|
|
390
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
391
|
+
}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deepline SDK — TypeScript client for the Deepline enrichment platform.
|
|
3
|
+
*
|
|
4
|
+
* ## Quick start
|
|
5
|
+
*
|
|
6
|
+
* ```typescript
|
|
7
|
+
* import { Deepline, definePlay } from 'deepline';
|
|
8
|
+
*
|
|
9
|
+
* // Connect (auto-resolves API key from env / CLI config)
|
|
10
|
+
* const ctx = await Deepline.connect();
|
|
11
|
+
*
|
|
12
|
+
* // Execute a tool
|
|
13
|
+
* const result = await ctx.tools.execute({
|
|
14
|
+
* tool: 'test_company_search',
|
|
15
|
+
* input: { domain: 'stripe.com' },
|
|
16
|
+
* });
|
|
17
|
+
* const company = result.value;
|
|
18
|
+
*
|
|
19
|
+
* // Run a named play
|
|
20
|
+
* const job = await ctx.play('email-waterfall').run({ domain: 'stripe.com' });
|
|
21
|
+
* const result = await job.get();
|
|
22
|
+
* ```
|
|
23
|
+
*
|
|
24
|
+
* ## Define a play
|
|
25
|
+
*
|
|
26
|
+
* ```typescript
|
|
27
|
+
* import { definePlay } from 'deepline';
|
|
28
|
+
*
|
|
29
|
+
* export default definePlay('my-play', async (ctx, input: { domain: string }) => {
|
|
30
|
+
* ctx.log(`Looking up ${input.domain}`);
|
|
31
|
+
* const company = await ctx.tools.execute({
|
|
32
|
+
* id: 'company_search',
|
|
33
|
+
* tool: 'test_company_search',
|
|
34
|
+
* input: { domain: input.domain },
|
|
35
|
+
* description: 'Look up company details by domain.',
|
|
36
|
+
* });
|
|
37
|
+
* return company;
|
|
38
|
+
* });
|
|
39
|
+
* ```
|
|
40
|
+
*
|
|
41
|
+
* Then run: `deepline play run --file my-play.play.ts --watch`
|
|
42
|
+
*
|
|
43
|
+
* ## Architecture
|
|
44
|
+
*
|
|
45
|
+
* - {@link DeeplineClient} — Low-level REST client (1:1 with API endpoints)
|
|
46
|
+
* - {@link DeeplineContext} / {@link Deepline} — High-level SDK with tool shortcuts and play handles
|
|
47
|
+
* - {@link definePlay} — Define plays as typed async functions with durable execution
|
|
48
|
+
* - {@link PlayJob} — Handle to a running play (status, logs, wait, cancel)
|
|
49
|
+
*
|
|
50
|
+
* ## Configuration
|
|
51
|
+
*
|
|
52
|
+
* The SDK auto-resolves configuration from multiple sources. See {@link resolveConfig}
|
|
53
|
+
* for the full resolution order. For most setups, just run `deepline auth register`
|
|
54
|
+
* once and the SDK works zero-config.
|
|
55
|
+
*
|
|
56
|
+
* @packageDocumentation
|
|
57
|
+
*/
|
|
58
|
+
|
|
59
|
+
// ——— Client ———
|
|
60
|
+
export { DeeplineClient } from './client.js';
|
|
61
|
+
export type { PlayStatus } from './client.js';
|
|
62
|
+
export { SDK_API_CONTRACT, SDK_VERSION } from './version.js';
|
|
63
|
+
|
|
64
|
+
// ——— Errors ———
|
|
65
|
+
export {
|
|
66
|
+
DeeplineError,
|
|
67
|
+
AuthError,
|
|
68
|
+
RateLimitError,
|
|
69
|
+
ConfigError,
|
|
70
|
+
} from './errors.js';
|
|
71
|
+
|
|
72
|
+
// ——— Config ———
|
|
73
|
+
export { resolveConfig, PROD_URL } from './config.js';
|
|
74
|
+
|
|
75
|
+
// ——— Play framework ———
|
|
76
|
+
export {
|
|
77
|
+
Deepline,
|
|
78
|
+
DeeplineContext,
|
|
79
|
+
defineInput,
|
|
80
|
+
definePlay,
|
|
81
|
+
defineWorkflow,
|
|
82
|
+
getDefinedPlayMetadata,
|
|
83
|
+
steps,
|
|
84
|
+
when,
|
|
85
|
+
} from './play.js';
|
|
86
|
+
|
|
87
|
+
// ——— Tool output processing ———
|
|
88
|
+
export {
|
|
89
|
+
createToolCallResult,
|
|
90
|
+
tryConvertToList,
|
|
91
|
+
writeCsvOutputFile,
|
|
92
|
+
writeJsonOutputFile,
|
|
93
|
+
extractSummaryFields,
|
|
94
|
+
} from './tool-output.js';
|
|
95
|
+
|
|
96
|
+
export type { ToolCallResult } from './tool-output.js';
|
|
97
|
+
|
|
98
|
+
// ——— Types (re-exported for consumers) ———
|
|
99
|
+
export type {
|
|
100
|
+
DeeplineClientOptions,
|
|
101
|
+
ResolvedConfig,
|
|
102
|
+
ToolDefinition,
|
|
103
|
+
ToolMetadata,
|
|
104
|
+
LiveEventEnvelope,
|
|
105
|
+
PlayLiveEvent,
|
|
106
|
+
PlayRunResult,
|
|
107
|
+
PlayRunStart,
|
|
108
|
+
ClearPlayHistoryRequest,
|
|
109
|
+
ClearPlayHistoryResult,
|
|
110
|
+
PlayRevisionSummary,
|
|
111
|
+
PublishPlayVersionRequest,
|
|
112
|
+
PublishPlayVersionResult,
|
|
113
|
+
StartPlayRunRequest,
|
|
114
|
+
PlayListItem,
|
|
115
|
+
} from './types.js';
|
|
116
|
+
|
|
117
|
+
export type {
|
|
118
|
+
DefinePlayConfig,
|
|
119
|
+
DeeplineNamedPlay,
|
|
120
|
+
DeeplinePlayRuntimeContext,
|
|
121
|
+
DefinedPlay,
|
|
122
|
+
PlayBindings,
|
|
123
|
+
PlayInputContract,
|
|
124
|
+
PlayJob,
|
|
125
|
+
ConditionalStepResolver,
|
|
126
|
+
MapDefinitionOptions,
|
|
127
|
+
MapRowKey,
|
|
128
|
+
MapRunOptions,
|
|
129
|
+
MapStepBuilder,
|
|
130
|
+
MapStepResolver,
|
|
131
|
+
PlayDataset,
|
|
132
|
+
PlayDatasetInput,
|
|
133
|
+
PlayStepProgramStep,
|
|
134
|
+
PrebuiltPlayRef,
|
|
135
|
+
StepProgram,
|
|
136
|
+
StepProgramResolver,
|
|
137
|
+
StepResolver,
|
|
138
|
+
ToolExecuteResult,
|
|
139
|
+
} from './play.js';
|