deepline 0.0.1 → 0.1.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/README.md +324 -0
- package/dist/cli/index.js +6750 -503
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/index.mjs +6735 -512
- package/dist/cli/index.mjs.map +1 -1
- package/dist/index.d.mts +2349 -32
- package/dist/index.d.ts +2349 -32
- package/dist/index.js +1631 -82
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1617 -83
- package/dist/index.mjs.map +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 +14 -12
package/dist/index.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// src/config.ts
|
|
2
|
-
import { readFileSync, existsSync } from "fs";
|
|
2
|
+
import { readFileSync, existsSync, mkdirSync, writeFileSync } from "fs";
|
|
3
3
|
import { homedir } from "os";
|
|
4
|
-
import { join } from "path";
|
|
4
|
+
import { dirname, join, resolve } from "path";
|
|
5
5
|
|
|
6
6
|
// src/errors.ts
|
|
7
7
|
var DeeplineError = class extends Error {
|
|
@@ -23,6 +23,7 @@ var AuthError = class extends DeeplineError {
|
|
|
23
23
|
}
|
|
24
24
|
};
|
|
25
25
|
var RateLimitError = class extends DeeplineError {
|
|
26
|
+
/** Milliseconds to wait before retrying, from the `Retry-After` response header. Defaults to 5000. */
|
|
26
27
|
retryAfterMs;
|
|
27
28
|
constructor(retryAfterMs = 5e3, message) {
|
|
28
29
|
super(message ?? `Rate limited. Retry after ${retryAfterMs}ms.`, 429, "RATE_LIMIT");
|
|
@@ -76,22 +77,62 @@ function parseEnvFile(filePath) {
|
|
|
76
77
|
}
|
|
77
78
|
return env;
|
|
78
79
|
}
|
|
79
|
-
function
|
|
80
|
-
|
|
81
|
-
|
|
80
|
+
function findNearestWorktreeEnv(startDir = process.cwd()) {
|
|
81
|
+
let current = resolve(startDir);
|
|
82
|
+
while (true) {
|
|
83
|
+
const values = parseEnvFile(join(current, ".env.worktree"));
|
|
84
|
+
if (Object.keys(values).length > 0) return values;
|
|
85
|
+
const parent = dirname(current);
|
|
86
|
+
if (parent === current) return {};
|
|
87
|
+
current = parent;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
function normalizeWorktreeBaseUrl(baseUrl, worktreeEnv = findNearestWorktreeEnv()) {
|
|
91
|
+
const trimmed = baseUrl.trim().replace(/\/$/, "");
|
|
92
|
+
if (!trimmed) return trimmed;
|
|
93
|
+
try {
|
|
94
|
+
const parsed = new URL(trimmed);
|
|
95
|
+
if (parsed.hostname.endsWith(".localhost") && parsed.port === "1355") {
|
|
96
|
+
const port = worktreeEnv.WORKTREE_APP_PORT || worktreeEnv.PORT;
|
|
97
|
+
if (port) return `${parsed.protocol}//localhost:${port}`;
|
|
98
|
+
}
|
|
99
|
+
} catch {
|
|
100
|
+
}
|
|
101
|
+
return trimmed;
|
|
102
|
+
}
|
|
103
|
+
function resolveWorktreeBaseUrl() {
|
|
104
|
+
const worktreeEnv = findNearestWorktreeEnv();
|
|
105
|
+
const declared = worktreeEnv.DEEPLINE_API_BASE_URL || worktreeEnv.WORKTREE_PUBLIC_APP_URL || worktreeEnv.APP_URL || "";
|
|
106
|
+
if (declared) return normalizeWorktreeBaseUrl(declared, worktreeEnv);
|
|
107
|
+
const port = worktreeEnv.WORKTREE_APP_PORT || worktreeEnv.PORT || "";
|
|
108
|
+
return port ? `http://localhost:${port}` : "";
|
|
109
|
+
}
|
|
110
|
+
function sdkCliEnvFilePath(baseUrl) {
|
|
111
|
+
const home = process.env.HOME?.trim() || homedir();
|
|
112
|
+
return join(home, ".local", "deepline", baseUrlSlug(baseUrl || PROD_URL), ".env");
|
|
113
|
+
}
|
|
114
|
+
function loadCliEnv(baseUrl = PROD_URL) {
|
|
115
|
+
const envPath = sdkCliEnvFilePath(baseUrl);
|
|
82
116
|
return parseEnvFile(envPath);
|
|
83
117
|
}
|
|
118
|
+
function loadGlobalCliEnv() {
|
|
119
|
+
return loadCliEnv(PROD_URL);
|
|
120
|
+
}
|
|
84
121
|
function autoDetectBaseUrl() {
|
|
122
|
+
const envOrigin = process.env.DEEPLINE_ORIGIN_URL?.trim();
|
|
123
|
+
if (envOrigin) return normalizeWorktreeBaseUrl(envOrigin);
|
|
85
124
|
const envBase = process.env.DEEPLINE_API_BASE_URL?.trim();
|
|
86
|
-
if (envBase) return envBase;
|
|
87
|
-
const
|
|
88
|
-
if (
|
|
89
|
-
|
|
90
|
-
|
|
125
|
+
if (envBase) return normalizeWorktreeBaseUrl(envBase);
|
|
126
|
+
const worktreeBaseUrl = resolveWorktreeBaseUrl();
|
|
127
|
+
if (worktreeBaseUrl) return worktreeBaseUrl;
|
|
128
|
+
const globalEnv = loadGlobalCliEnv();
|
|
129
|
+
const globalOrigin = globalEnv.DEEPLINE_ORIGIN_URL?.trim();
|
|
130
|
+
if (globalOrigin) return normalizeWorktreeBaseUrl(globalOrigin);
|
|
91
131
|
return PROD_URL;
|
|
92
132
|
}
|
|
93
133
|
function resolveConfig(options) {
|
|
94
|
-
const
|
|
134
|
+
const requestedBaseUrl = options?.baseUrl?.trim() || autoDetectBaseUrl();
|
|
135
|
+
const baseUrl = normalizeWorktreeBaseUrl(requestedBaseUrl);
|
|
95
136
|
const cliEnv = loadCliEnv(baseUrl);
|
|
96
137
|
const apiKey = options?.apiKey?.trim() || process.env.DEEPLINE_API_KEY?.trim() || cliEnv.DEEPLINE_API_KEY || "";
|
|
97
138
|
if (!apiKey) {
|
|
@@ -101,94 +142,214 @@ function resolveConfig(options) {
|
|
|
101
142
|
}
|
|
102
143
|
return {
|
|
103
144
|
apiKey,
|
|
104
|
-
baseUrl
|
|
145
|
+
baseUrl,
|
|
105
146
|
timeout: options?.timeout ?? DEFAULT_TIMEOUT,
|
|
106
147
|
maxRetries: options?.maxRetries ?? DEFAULT_MAX_RETRIES
|
|
107
148
|
};
|
|
108
149
|
}
|
|
109
150
|
|
|
151
|
+
// src/version.ts
|
|
152
|
+
var SDK_VERSION = "0.1.1";
|
|
153
|
+
var SDK_API_CONTRACT = "2026-04-plays-v1";
|
|
154
|
+
|
|
155
|
+
// ../shared_libs/play-runtime/coordinator-headers.ts
|
|
156
|
+
var COORDINATOR_URL_OVERRIDE_HEADER = "x-deepline-coordinator-url";
|
|
157
|
+
var WORKER_CALLBACK_URL_OVERRIDE_HEADER = "x-deepline-worker-callback-url";
|
|
158
|
+
|
|
110
159
|
// src/http.ts
|
|
111
|
-
var SDK_VERSION = "0.1.0";
|
|
112
160
|
var HttpClient = class {
|
|
113
161
|
constructor(config) {
|
|
114
162
|
this.config = config;
|
|
115
163
|
}
|
|
116
164
|
config;
|
|
117
|
-
|
|
118
|
-
const url = `${this.config.baseUrl}${path}`;
|
|
119
|
-
const method = options?.method ?? "GET";
|
|
165
|
+
authHeaders(extra) {
|
|
120
166
|
const headers = {
|
|
121
167
|
"Authorization": `Bearer ${this.config.apiKey}`,
|
|
122
168
|
"User-Agent": `deepline-ts-sdk/${SDK_VERSION}`,
|
|
123
|
-
|
|
169
|
+
"X-Deepline-SDK-Version": SDK_VERSION,
|
|
170
|
+
"X-Deepline-API-Contract": SDK_API_CONTRACT,
|
|
171
|
+
...extra
|
|
124
172
|
};
|
|
173
|
+
const bypassToken = typeof process !== "undefined" ? process.env?.VERCEL_PROTECTION_BYPASS_TOKEN : void 0;
|
|
174
|
+
if (bypassToken) {
|
|
175
|
+
headers["x-vercel-protection-bypass"] = bypassToken;
|
|
176
|
+
}
|
|
177
|
+
const playArtifactR2Prefix = typeof process !== "undefined" ? process.env?.DEEPLINE_PLAY_ARTIFACT_R2_PREFIX : void 0;
|
|
178
|
+
if (playArtifactR2Prefix) {
|
|
179
|
+
headers["x-deepline-play-artifact-r2-prefix"] = playArtifactR2Prefix;
|
|
180
|
+
}
|
|
181
|
+
const coordinatorUrl = typeof process !== "undefined" ? process.env?.DEEPLINE_COORDINATOR_URL : void 0;
|
|
182
|
+
if (coordinatorUrl?.trim()) {
|
|
183
|
+
headers[COORDINATOR_URL_OVERRIDE_HEADER] = coordinatorUrl.trim();
|
|
184
|
+
}
|
|
185
|
+
const workerCallbackUrl = typeof process !== "undefined" ? process.env?.DEEPLINE_WORKER_CALLBACK_URL : void 0;
|
|
186
|
+
if (workerCallbackUrl?.trim()) {
|
|
187
|
+
headers[WORKER_CALLBACK_URL_OVERRIDE_HEADER] = workerCallbackUrl.trim();
|
|
188
|
+
}
|
|
189
|
+
return headers;
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Send an HTTP request with automatic retries and error handling.
|
|
193
|
+
*
|
|
194
|
+
* @typeParam T - Expected response body type
|
|
195
|
+
* @param path - API path (e.g. `"/api/v2/tools"`)
|
|
196
|
+
* @param options - HTTP method, body, headers, and timeout
|
|
197
|
+
* @returns Parsed JSON response body
|
|
198
|
+
* @throws {@link AuthError} on HTTP 401/403 (immediate, no retry)
|
|
199
|
+
* @throws {@link RateLimitError} on HTTP 429 after all retries exhausted
|
|
200
|
+
* @throws {@link DeeplineError} on other API errors or connection failures
|
|
201
|
+
*/
|
|
202
|
+
async request(path, options) {
|
|
203
|
+
const baseUrl = this.config.baseUrl;
|
|
204
|
+
const url = `${baseUrl}${path}`;
|
|
205
|
+
const method = options?.method ?? "GET";
|
|
206
|
+
const headers = this.authHeaders(options?.headers);
|
|
125
207
|
if (options?.body !== void 0) {
|
|
126
208
|
headers["Content-Type"] = "application/json";
|
|
127
209
|
}
|
|
128
210
|
let lastError = null;
|
|
211
|
+
const candidateUrls = buildCandidateUrls(url);
|
|
212
|
+
let retryAfterDelayMs = null;
|
|
129
213
|
for (let attempt = 0; attempt <= this.config.maxRetries; attempt++) {
|
|
130
214
|
if (attempt > 0) {
|
|
131
215
|
const backoffMs = Math.min(1e3 * Math.pow(2, attempt - 1), 3e4);
|
|
132
|
-
|
|
216
|
+
const delayMs = retryAfterDelayMs === null ? backoffMs : Math.max(backoffMs, retryAfterDelayMs);
|
|
217
|
+
retryAfterDelayMs = null;
|
|
218
|
+
await sleep(delayMs);
|
|
133
219
|
}
|
|
134
|
-
const
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
220
|
+
for (const candidateUrl of candidateUrls) {
|
|
221
|
+
const controller = new AbortController();
|
|
222
|
+
const timeoutId = setTimeout(
|
|
223
|
+
() => controller.abort(),
|
|
224
|
+
options?.timeout ?? this.config.timeout
|
|
225
|
+
);
|
|
226
|
+
try {
|
|
227
|
+
const response = await fetch(candidateUrl, {
|
|
228
|
+
method,
|
|
229
|
+
headers,
|
|
230
|
+
body: options?.body !== void 0 ? JSON.stringify(options.body) : void 0,
|
|
231
|
+
signal: controller.signal
|
|
232
|
+
});
|
|
233
|
+
clearTimeout(timeoutId);
|
|
234
|
+
if (response.status === 401 || response.status === 403) {
|
|
235
|
+
throw new AuthError();
|
|
236
|
+
}
|
|
237
|
+
if (response.status === 429) {
|
|
238
|
+
const retryAfter = parseRetryAfter(response);
|
|
239
|
+
lastError = new RateLimitError(retryAfter);
|
|
240
|
+
if (attempt < this.config.maxRetries) {
|
|
241
|
+
retryAfterDelayMs = retryAfter;
|
|
242
|
+
break;
|
|
243
|
+
}
|
|
244
|
+
throw lastError;
|
|
245
|
+
}
|
|
246
|
+
const body = await response.text();
|
|
247
|
+
let parsed;
|
|
248
|
+
try {
|
|
249
|
+
parsed = JSON.parse(body);
|
|
250
|
+
} catch {
|
|
251
|
+
parsed = body;
|
|
252
|
+
}
|
|
253
|
+
if (!response.ok) {
|
|
254
|
+
const msg = typeof parsed === "object" && parsed && "error" in parsed ? String(parsed.error) : `HTTP ${response.status}`;
|
|
255
|
+
throw new DeeplineError(msg, response.status, "API_ERROR", {
|
|
256
|
+
response: parsed
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
return parsed;
|
|
260
|
+
} catch (error) {
|
|
261
|
+
clearTimeout(timeoutId);
|
|
262
|
+
if (error instanceof AuthError || error instanceof DeeplineError) {
|
|
263
|
+
throw error;
|
|
264
|
+
}
|
|
265
|
+
if (error instanceof RateLimitError) {
|
|
266
|
+
lastError = error;
|
|
267
|
+
break;
|
|
268
|
+
}
|
|
269
|
+
lastError = error instanceof Error ? error : new Error(String(error));
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
if (attempt < this.config.maxRetries) continue;
|
|
273
|
+
}
|
|
274
|
+
if (lastError instanceof DeeplineError) {
|
|
275
|
+
throw lastError;
|
|
276
|
+
}
|
|
277
|
+
const errorMessage = lastError?.message ? `Unable to connect to ${baseUrl}. ${lastError.message}` : `Unable to connect to ${baseUrl}. Is the computer able to access the url?`;
|
|
278
|
+
throw new DeeplineError(errorMessage);
|
|
279
|
+
}
|
|
280
|
+
/**
|
|
281
|
+
* Send a GET request.
|
|
282
|
+
*
|
|
283
|
+
* @typeParam T - Expected response body type
|
|
284
|
+
* @param path - API path (e.g. `"/api/v2/tools"`)
|
|
285
|
+
*/
|
|
286
|
+
async get(path) {
|
|
287
|
+
return this.request(path, { method: "GET" });
|
|
288
|
+
}
|
|
289
|
+
async *streamSse(path, options) {
|
|
290
|
+
const url = `${this.config.baseUrl}${path}`;
|
|
291
|
+
const method = options?.method ?? "GET";
|
|
292
|
+
const headers = this.authHeaders({
|
|
293
|
+
Accept: "text/event-stream",
|
|
294
|
+
...options?.headers
|
|
295
|
+
});
|
|
296
|
+
if (options?.body !== void 0) {
|
|
297
|
+
headers["Content-Type"] = "application/json";
|
|
298
|
+
}
|
|
299
|
+
let lastError = null;
|
|
300
|
+
for (const candidateUrl of buildCandidateUrls(url)) {
|
|
139
301
|
try {
|
|
140
|
-
const response = await fetch(
|
|
302
|
+
const response = await fetch(candidateUrl, {
|
|
141
303
|
method,
|
|
142
304
|
headers,
|
|
143
305
|
body: options?.body !== void 0 ? JSON.stringify(options.body) : void 0,
|
|
144
|
-
signal:
|
|
306
|
+
signal: options?.signal
|
|
145
307
|
});
|
|
146
|
-
clearTimeout(timeoutId);
|
|
147
308
|
if (response.status === 401 || response.status === 403) {
|
|
148
309
|
throw new AuthError();
|
|
149
310
|
}
|
|
150
|
-
if (response.status === 429) {
|
|
151
|
-
const retryAfter = parseRetryAfter(response);
|
|
152
|
-
lastError = new RateLimitError(retryAfter);
|
|
153
|
-
if (attempt < this.config.maxRetries) continue;
|
|
154
|
-
throw lastError;
|
|
155
|
-
}
|
|
156
|
-
const body = await response.text();
|
|
157
|
-
let parsed;
|
|
158
|
-
try {
|
|
159
|
-
parsed = JSON.parse(body);
|
|
160
|
-
} catch {
|
|
161
|
-
parsed = body;
|
|
162
|
-
}
|
|
163
311
|
if (!response.ok) {
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
response
|
|
167
|
-
|
|
312
|
+
throw new DeeplineError(
|
|
313
|
+
`HTTP ${response.status}`,
|
|
314
|
+
response.status,
|
|
315
|
+
"API_ERROR"
|
|
316
|
+
);
|
|
168
317
|
}
|
|
169
|
-
|
|
318
|
+
if (!response.body) {
|
|
319
|
+
throw new DeeplineError("SSE response did not include a body.");
|
|
320
|
+
}
|
|
321
|
+
yield* decodeSseStream(response.body);
|
|
322
|
+
return;
|
|
170
323
|
} catch (error) {
|
|
171
|
-
clearTimeout(timeoutId);
|
|
172
324
|
if (error instanceof AuthError || error instanceof DeeplineError) {
|
|
173
325
|
throw error;
|
|
174
326
|
}
|
|
175
|
-
if (error instanceof RateLimitError) {
|
|
176
|
-
lastError = error;
|
|
177
|
-
if (attempt < this.config.maxRetries) continue;
|
|
178
|
-
throw error;
|
|
179
|
-
}
|
|
180
327
|
lastError = error instanceof Error ? error : new Error(String(error));
|
|
181
|
-
if (attempt < this.config.maxRetries) continue;
|
|
182
328
|
}
|
|
183
329
|
}
|
|
184
|
-
throw
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
return this.request(path, { method: "GET" });
|
|
330
|
+
throw new DeeplineError(
|
|
331
|
+
lastError?.message ? `Unable to stream from ${this.config.baseUrl}. ${lastError.message}` : `Unable to stream from ${this.config.baseUrl}.`
|
|
332
|
+
);
|
|
188
333
|
}
|
|
334
|
+
/**
|
|
335
|
+
* Send a POST request with a JSON body.
|
|
336
|
+
*
|
|
337
|
+
* @typeParam T - Expected response body type
|
|
338
|
+
* @param path - API path
|
|
339
|
+
* @param body - Request body (will be JSON-serialized)
|
|
340
|
+
*/
|
|
189
341
|
async post(path, body) {
|
|
190
342
|
return this.request(path, { method: "POST", body });
|
|
191
343
|
}
|
|
344
|
+
/**
|
|
345
|
+
* Send a DELETE request.
|
|
346
|
+
*
|
|
347
|
+
* @typeParam T - Expected response body type
|
|
348
|
+
* @param path - API path
|
|
349
|
+
*/
|
|
350
|
+
async delete(path) {
|
|
351
|
+
return this.request(path, { method: "DELETE" });
|
|
352
|
+
}
|
|
192
353
|
};
|
|
193
354
|
function parseRetryAfter(response) {
|
|
194
355
|
const header = response.headers.get("retry-after");
|
|
@@ -200,27 +361,218 @@ function parseRetryAfter(response) {
|
|
|
200
361
|
}
|
|
201
362
|
return 5e3;
|
|
202
363
|
}
|
|
364
|
+
function buildCandidateUrls(url) {
|
|
365
|
+
try {
|
|
366
|
+
const parsed = new URL(url);
|
|
367
|
+
const candidates = [url];
|
|
368
|
+
if (parsed.hostname === "localhost") {
|
|
369
|
+
const loopback = new URL(url);
|
|
370
|
+
loopback.hostname = "127.0.0.1";
|
|
371
|
+
candidates.push(loopback.toString());
|
|
372
|
+
}
|
|
373
|
+
return [...new Set(candidates)];
|
|
374
|
+
} catch {
|
|
375
|
+
return [url];
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
async function* decodeSseStream(body) {
|
|
379
|
+
const reader = body.getReader();
|
|
380
|
+
const decoder = new TextDecoder();
|
|
381
|
+
let buffered = "";
|
|
382
|
+
try {
|
|
383
|
+
while (true) {
|
|
384
|
+
const { value, done } = await reader.read();
|
|
385
|
+
if (done) break;
|
|
386
|
+
buffered += decoder.decode(value, { stream: true });
|
|
387
|
+
const frames = buffered.split(/\r?\n\r?\n/);
|
|
388
|
+
buffered = frames.pop() ?? "";
|
|
389
|
+
for (const frame of frames) {
|
|
390
|
+
const event2 = decodeSseFrame(frame);
|
|
391
|
+
if (event2) {
|
|
392
|
+
yield event2;
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
buffered += decoder.decode();
|
|
397
|
+
const event = decodeSseFrame(buffered);
|
|
398
|
+
if (event) {
|
|
399
|
+
yield event;
|
|
400
|
+
}
|
|
401
|
+
} finally {
|
|
402
|
+
reader.releaseLock();
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
function decodeSseFrame(frame) {
|
|
406
|
+
const data = frame.split(/\r?\n/).filter((line) => line.startsWith("data:")).map((line) => line.slice("data:".length).trimStart()).join("\n").trim();
|
|
407
|
+
if (!data) {
|
|
408
|
+
return null;
|
|
409
|
+
}
|
|
410
|
+
const parsed = JSON.parse(data);
|
|
411
|
+
if (!parsed || typeof parsed !== "object" || typeof parsed.cursor !== "string" || typeof parsed.streamId !== "string" || typeof parsed.scope !== "string" || typeof parsed.type !== "string" || typeof parsed.at !== "string") {
|
|
412
|
+
return null;
|
|
413
|
+
}
|
|
414
|
+
return parsed;
|
|
415
|
+
}
|
|
203
416
|
function sleep(ms) {
|
|
204
|
-
return new Promise((
|
|
417
|
+
return new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
205
418
|
}
|
|
206
419
|
|
|
207
420
|
// src/client.ts
|
|
421
|
+
var TERMINAL_PLAY_STATUSES = /* @__PURE__ */ new Set(["completed", "failed", "cancelled"]);
|
|
422
|
+
function normalizePlayStatus(raw) {
|
|
423
|
+
const status = typeof raw.status === "string" ? raw.status : typeof raw.temporalStatus === "string" ? mapLegacyTemporalStatus(raw.temporalStatus) : "running";
|
|
424
|
+
const runId = typeof raw.runId === "string" ? raw.runId : typeof raw.workflowId === "string" ? raw.workflowId : "";
|
|
425
|
+
return {
|
|
426
|
+
...raw,
|
|
427
|
+
runId,
|
|
428
|
+
status
|
|
429
|
+
};
|
|
430
|
+
}
|
|
431
|
+
function mapLegacyTemporalStatus(status) {
|
|
432
|
+
switch (status.trim().toUpperCase()) {
|
|
433
|
+
case "PENDING":
|
|
434
|
+
return "queued";
|
|
435
|
+
case "COMPLETED":
|
|
436
|
+
return "completed";
|
|
437
|
+
case "FAILED":
|
|
438
|
+
return "failed";
|
|
439
|
+
case "CANCELLED":
|
|
440
|
+
case "TERMINATED":
|
|
441
|
+
case "TIMED_OUT":
|
|
442
|
+
return "cancelled";
|
|
443
|
+
case "RUNNING":
|
|
444
|
+
default:
|
|
445
|
+
return "running";
|
|
446
|
+
}
|
|
447
|
+
}
|
|
208
448
|
var DeeplineClient = class {
|
|
209
449
|
http;
|
|
210
450
|
config;
|
|
451
|
+
/**
|
|
452
|
+
* @param options - Optional overrides for API key, base URL, timeout, and retries.
|
|
453
|
+
* @throws {@link ConfigError} if no API key can be resolved from any source.
|
|
454
|
+
*/
|
|
211
455
|
constructor(options) {
|
|
212
456
|
this.config = resolveConfig(options);
|
|
213
457
|
this.http = new HttpClient(this.config);
|
|
214
458
|
}
|
|
459
|
+
/** The resolved base URL this client is targeting (e.g. `"http://localhost:3000"`). */
|
|
215
460
|
get baseUrl() {
|
|
216
461
|
return this.config.baseUrl;
|
|
217
462
|
}
|
|
218
|
-
|
|
463
|
+
compactSchema(schema) {
|
|
464
|
+
if (!schema) return null;
|
|
465
|
+
const fields = Array.isArray(schema.fields) ? schema.fields.map(
|
|
466
|
+
(field) => field && typeof field === "object" ? {
|
|
467
|
+
name: String(field.name ?? ""),
|
|
468
|
+
type: field.type ?? void 0,
|
|
469
|
+
required: field.required ?? void 0
|
|
470
|
+
} : null
|
|
471
|
+
).filter((field) => Boolean(field?.name)) : [];
|
|
472
|
+
return fields.length > 0 ? { fields } : schema;
|
|
473
|
+
}
|
|
474
|
+
playRunCommand(name) {
|
|
475
|
+
return `deepline plays run ${name} --input '{...}' --watch`;
|
|
476
|
+
}
|
|
477
|
+
summarizePlayListItem(play, options) {
|
|
478
|
+
const aliases = play.aliases?.length ? play.aliases : [play.name];
|
|
479
|
+
const runCommand = this.playRunCommand(play.name);
|
|
480
|
+
return {
|
|
481
|
+
name: play.name,
|
|
482
|
+
...play.reference ? { reference: play.reference } : {},
|
|
483
|
+
...play.displayName ? { displayName: play.displayName } : {},
|
|
484
|
+
origin: play.origin,
|
|
485
|
+
ownerType: play.ownerType,
|
|
486
|
+
canEdit: play.canEdit,
|
|
487
|
+
canClone: play.canClone,
|
|
488
|
+
aliases,
|
|
489
|
+
inputSchema: options?.compact ? this.compactSchema(play.inputSchema) : play.inputSchema ?? null,
|
|
490
|
+
outputSchema: options?.compact ? this.compactSchema(play.outputSchema) : play.outputSchema ?? null,
|
|
491
|
+
runCommand,
|
|
492
|
+
examples: [runCommand],
|
|
493
|
+
currentPublishedVersion: play.currentPublishedVersion ?? null,
|
|
494
|
+
isDraftDirty: play.isDraftDirty
|
|
495
|
+
};
|
|
496
|
+
}
|
|
497
|
+
summarizePlayDetail(detail, options) {
|
|
498
|
+
const play = detail.play;
|
|
499
|
+
return {
|
|
500
|
+
...this.summarizePlayListItem(play, options),
|
|
501
|
+
currentPublishedVersion: play.currentPublishedVersion ?? play.liveRevision?.version ?? null,
|
|
502
|
+
latestRunId: play.latestRunId ?? detail.latestRuns[0]?.workflowId ?? null
|
|
503
|
+
};
|
|
504
|
+
}
|
|
505
|
+
// ——————————————————————————————————————————————————————————
|
|
506
|
+
// Tools
|
|
507
|
+
// ——————————————————————————————————————————————————————————
|
|
508
|
+
/**
|
|
509
|
+
* List all available tools.
|
|
510
|
+
*
|
|
511
|
+
* Returns tool definitions including ID, provider, description, input/output schemas,
|
|
512
|
+
* and list extractor paths for automatic CSV conversion.
|
|
513
|
+
*
|
|
514
|
+
* @returns Array of tool definitions
|
|
515
|
+
*
|
|
516
|
+
* @example
|
|
517
|
+
* ```typescript
|
|
518
|
+
* const tools = await client.listTools();
|
|
519
|
+
* const searchTools = tools.filter(t => t.categories.includes('search'));
|
|
520
|
+
* console.log(`Found ${searchTools.length} search tools`);
|
|
521
|
+
* ```
|
|
522
|
+
*/
|
|
219
523
|
async listTools() {
|
|
220
|
-
const res = await this.http.get(
|
|
524
|
+
const res = await this.http.get(
|
|
525
|
+
"/api/v2/tools"
|
|
526
|
+
);
|
|
221
527
|
return res.tools;
|
|
222
528
|
}
|
|
223
|
-
/**
|
|
529
|
+
/**
|
|
530
|
+
* Get detailed metadata for a single tool.
|
|
531
|
+
*
|
|
532
|
+
* Returns everything from {@link ToolDefinition} plus pricing info, sample
|
|
533
|
+
* inputs/outputs, failure modes, and cost estimates.
|
|
534
|
+
*
|
|
535
|
+
* @param toolId - Tool identifier (e.g. `"apollo_people_search"`)
|
|
536
|
+
* @returns Full tool metadata
|
|
537
|
+
*
|
|
538
|
+
* @example
|
|
539
|
+
* ```typescript
|
|
540
|
+
* const meta = await client.getTool('apollo_people_search');
|
|
541
|
+
* console.log(`Cost: ${meta.estimatedCreditsRange} credits`);
|
|
542
|
+
* console.log(`Input schema:`, meta.inputSchema);
|
|
543
|
+
* ```
|
|
544
|
+
*/
|
|
545
|
+
async getTool(toolId) {
|
|
546
|
+
return this.http.request(
|
|
547
|
+
`/api/v2/integrations/${encodeURIComponent(toolId)}/get`,
|
|
548
|
+
{
|
|
549
|
+
method: "GET",
|
|
550
|
+
headers: {
|
|
551
|
+
"x-deepline-tool-meta-only": "1"
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
);
|
|
555
|
+
}
|
|
556
|
+
/**
|
|
557
|
+
* Execute a tool and return the extracted result.
|
|
558
|
+
*
|
|
559
|
+
* Sends the input payload to the tool and returns the `.result` field from the
|
|
560
|
+
* response. For the full response envelope (including job_id, credits, etc.),
|
|
561
|
+
* use {@link executeToolRaw}.
|
|
562
|
+
*
|
|
563
|
+
* @param toolId - Tool identifier (e.g. `"test_company_search"`)
|
|
564
|
+
* @param input - Tool-specific input parameters
|
|
565
|
+
* @returns The tool's output (shape varies by tool)
|
|
566
|
+
* @throws {@link DeeplineError} if the tool execution fails
|
|
567
|
+
*
|
|
568
|
+
* @example
|
|
569
|
+
* ```typescript
|
|
570
|
+
* const company = await client.executeTool('test_company_search', {
|
|
571
|
+
* domain: 'stripe.com',
|
|
572
|
+
* });
|
|
573
|
+
* console.log(company); // { name: "Stripe", industry: "Financial Services", ... }
|
|
574
|
+
* ```
|
|
575
|
+
*/
|
|
224
576
|
async executeTool(toolId, input) {
|
|
225
577
|
const res = await this.http.post(
|
|
226
578
|
`/api/v2/integrations/${encodeURIComponent(toolId)}/execute`,
|
|
@@ -229,38 +581,602 @@ var DeeplineClient = class {
|
|
|
229
581
|
return res.result ?? res;
|
|
230
582
|
}
|
|
231
583
|
/**
|
|
232
|
-
*
|
|
233
|
-
*
|
|
584
|
+
* Execute a tool and return the full response envelope.
|
|
585
|
+
*
|
|
586
|
+
* Unlike {@link executeTool}, this returns the complete API response including
|
|
587
|
+
* `job_id`, `status`, `credits`, and the raw `result` object.
|
|
588
|
+
*
|
|
589
|
+
* @param toolId - Tool identifier
|
|
590
|
+
* @param input - Tool-specific input parameters
|
|
591
|
+
* @returns Full response with job metadata and result
|
|
592
|
+
*
|
|
593
|
+
* @example
|
|
594
|
+
* ```typescript
|
|
595
|
+
* const raw = await client.executeToolRaw('test_company_search', { domain: 'stripe.com' });
|
|
596
|
+
* console.log(`Job: ${raw.job_id}, Credits: ${raw.credits}`);
|
|
597
|
+
* console.log(`Result:`, raw.result);
|
|
598
|
+
* ```
|
|
599
|
+
*/
|
|
600
|
+
async executeToolRaw(toolId, input) {
|
|
601
|
+
return this.http.post(
|
|
602
|
+
`/api/v2/integrations/${encodeURIComponent(toolId)}/execute`,
|
|
603
|
+
{ payload: input }
|
|
604
|
+
);
|
|
605
|
+
}
|
|
606
|
+
async queryCustomerDb(input) {
|
|
607
|
+
return this.http.post("/api/v2/db/query", {
|
|
608
|
+
sql: input.sql,
|
|
609
|
+
...input.maxRows ? { max_rows: input.maxRows } : {}
|
|
610
|
+
});
|
|
611
|
+
}
|
|
612
|
+
// ——————————————————————————————————————————————————————————
|
|
613
|
+
// Plays — submission and lifecycle
|
|
614
|
+
// ——————————————————————————————————————————————————————————
|
|
615
|
+
/**
|
|
616
|
+
* Start a play run.
|
|
617
|
+
*
|
|
618
|
+
* Internal/advanced primitive. For normal callers, prefer the public
|
|
619
|
+
* entrypoints: the CLI, {@link Deepline.connect}, {@link submitPlay},
|
|
620
|
+
* or {@link runPlay}.
|
|
621
|
+
*
|
|
622
|
+
* Supported invocation surfaces intentionally share this same run contract:
|
|
623
|
+
* `deepline play run`, repo scripts such as `bun run deepline -- play run`,
|
|
624
|
+
* SDK context calls like `Deepline.connect().play(name).run()`, and direct
|
|
625
|
+
* `POST /api/v2/plays/run` calls all return a workflow/run id. The completed
|
|
626
|
+
* output is always retrievable from `getPlayStatus(runId).result` (or from
|
|
627
|
+
* `PlayJob.get()` for SDK context calls). Execution logs live under
|
|
628
|
+
* `progress.logs`; they are not part of the user output object.
|
|
629
|
+
*
|
|
630
|
+
* @param request - Play run configuration (name, code, input, etc.)
|
|
631
|
+
* @returns Workflow metadata including the `workflowId` for status polling
|
|
632
|
+
*
|
|
633
|
+
* @example
|
|
634
|
+
* ```typescript
|
|
635
|
+
* // Run a live play by name:
|
|
636
|
+
* const started = await client.startPlayRun({
|
|
637
|
+
* name: 'email-waterfall',
|
|
638
|
+
* input: { linkedin_url: 'https://linkedin.com/in/jdoe', domain: 'acme.com' },
|
|
639
|
+
* });
|
|
640
|
+
* console.log(`Workflow: ${started.workflowId}`);
|
|
641
|
+
*
|
|
642
|
+
* // Run an ad hoc artifact-backed play:
|
|
643
|
+
* const started2 = await client.startPlayRun({
|
|
644
|
+
* artifactStorageKey: 'plays/v1/orgs/acme/plays/my-play/artifacts/playgraph_abc123.json',
|
|
645
|
+
* });
|
|
646
|
+
* ```
|
|
234
647
|
*/
|
|
235
|
-
async
|
|
648
|
+
async startPlayRun(request) {
|
|
236
649
|
return this.http.post("/api/v2/plays/run", {
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
650
|
+
...request.name ? { name: request.name } : {},
|
|
651
|
+
...request.revisionId ? { revisionId: request.revisionId } : {},
|
|
652
|
+
...request.artifactStorageKey ? { artifactStorageKey: request.artifactStorageKey } : {},
|
|
653
|
+
...request.sourceCode ? { sourceCode: request.sourceCode } : {},
|
|
654
|
+
..."staticPipeline" in request ? { staticPipeline: request.staticPipeline } : {},
|
|
655
|
+
...request.artifactHash ? { artifactHash: request.artifactHash } : {},
|
|
656
|
+
...request.graphHash ? { graphHash: request.graphHash } : {},
|
|
657
|
+
...request.runtimeArtifact ? { runtimeArtifact: request.runtimeArtifact } : {},
|
|
658
|
+
...request.compilerManifest ? { compilerManifest: request.compilerManifest } : {},
|
|
659
|
+
...request.inputFileUpload ? { inputFileUpload: request.inputFileUpload } : {},
|
|
660
|
+
...request.packagedFileUploads?.length ? { packagedFileUploads: request.packagedFileUploads } : {},
|
|
661
|
+
...request.input ? { input: request.input } : {},
|
|
662
|
+
...request.inputFile ? { inputFile: request.inputFile } : {},
|
|
663
|
+
...request.packagedFiles?.length ? { packagedFiles: request.packagedFiles } : {},
|
|
664
|
+
...request.force ? { force: true } : {},
|
|
665
|
+
...typeof request.waitForCompletionMs === "number" ? { waitForCompletionMs: request.waitForCompletionMs } : {},
|
|
666
|
+
// Profile selection is the API's job, not the CLI's. The server
|
|
667
|
+
// hardcodes workers_edge as the default; tests that want a
|
|
668
|
+
// different profile pass `request.profile` explicitly.
|
|
669
|
+
...request.profile ? { profile: request.profile } : {}
|
|
670
|
+
});
|
|
671
|
+
}
|
|
672
|
+
async *startPlayRunStream(request, options) {
|
|
673
|
+
const body = {
|
|
674
|
+
...request.name ? { name: request.name } : {},
|
|
675
|
+
...request.revisionId ? { revisionId: request.revisionId } : {},
|
|
676
|
+
...request.artifactStorageKey ? { artifactStorageKey: request.artifactStorageKey } : {},
|
|
677
|
+
...request.sourceCode ? { sourceCode: request.sourceCode } : {},
|
|
678
|
+
..."staticPipeline" in request ? { staticPipeline: request.staticPipeline } : {},
|
|
679
|
+
...request.artifactHash ? { artifactHash: request.artifactHash } : {},
|
|
680
|
+
...request.graphHash ? { graphHash: request.graphHash } : {},
|
|
681
|
+
...request.runtimeArtifact ? { runtimeArtifact: request.runtimeArtifact } : {},
|
|
682
|
+
...request.compilerManifest ? { compilerManifest: request.compilerManifest } : {},
|
|
683
|
+
...request.inputFileUpload ? { inputFileUpload: request.inputFileUpload } : {},
|
|
684
|
+
...request.packagedFileUploads?.length ? { packagedFileUploads: request.packagedFileUploads } : {},
|
|
685
|
+
...request.input ? { input: request.input } : {},
|
|
686
|
+
...request.inputFile ? { inputFile: request.inputFile } : {},
|
|
687
|
+
...request.packagedFiles?.length ? { packagedFiles: request.packagedFiles } : {},
|
|
688
|
+
...request.force ? { force: true } : {},
|
|
689
|
+
...request.profile ? { profile: request.profile } : {}
|
|
690
|
+
};
|
|
691
|
+
for await (const event of this.http.streamSse(
|
|
692
|
+
"/api/v2/plays/run?stream=true",
|
|
693
|
+
{
|
|
694
|
+
method: "POST",
|
|
695
|
+
body,
|
|
696
|
+
signal: options?.signal
|
|
697
|
+
}
|
|
698
|
+
)) {
|
|
699
|
+
if (event.scope === "play") {
|
|
700
|
+
yield event;
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
/**
|
|
705
|
+
* Register a bundled play artifact.
|
|
706
|
+
*
|
|
707
|
+
* Internal/advanced primitive used by packaging flows. Public callers should
|
|
708
|
+
* prefer the CLI, {@link submitPlay}, or {@link runPlay}.
|
|
709
|
+
*/
|
|
710
|
+
async registerPlayArtifact(input) {
|
|
711
|
+
const compilerManifest = input.compilerManifest ?? await this.compilePlayManifest({
|
|
712
|
+
name: input.name,
|
|
713
|
+
sourceCode: input.sourceCode,
|
|
714
|
+
artifact: input.artifact
|
|
715
|
+
});
|
|
716
|
+
return this.http.post("/api/v2/plays/artifacts", {
|
|
717
|
+
...input,
|
|
718
|
+
compilerManifest
|
|
719
|
+
});
|
|
720
|
+
}
|
|
721
|
+
async registerPlayArtifacts(artifacts) {
|
|
722
|
+
const compiledArtifacts = await Promise.all(
|
|
723
|
+
artifacts.map(async (artifact) => ({
|
|
724
|
+
...artifact,
|
|
725
|
+
compilerManifest: artifact.compilerManifest ?? await this.compilePlayManifest({
|
|
726
|
+
name: artifact.name,
|
|
727
|
+
sourceCode: artifact.sourceCode,
|
|
728
|
+
artifact: artifact.artifact
|
|
729
|
+
})
|
|
730
|
+
}))
|
|
731
|
+
);
|
|
732
|
+
return this.http.post("/api/v2/plays/artifacts", {
|
|
733
|
+
artifacts: compiledArtifacts
|
|
734
|
+
});
|
|
735
|
+
}
|
|
736
|
+
async compilePlayManifest(input) {
|
|
737
|
+
const response = await this.http.post("/api/v2/plays/compile-manifest", input);
|
|
738
|
+
return response.compilerManifest;
|
|
739
|
+
}
|
|
740
|
+
/**
|
|
741
|
+
* Check a bundled play artifact against the server's current play compiler.
|
|
742
|
+
*
|
|
743
|
+
* Unlike {@link registerPlayArtifact}, this does not store the artifact,
|
|
744
|
+
* publish a revision, or start a run. It is the authoritative cloud validation
|
|
745
|
+
* path used by `deepline play check`.
|
|
746
|
+
*/
|
|
747
|
+
async checkPlayArtifact(input) {
|
|
748
|
+
return this.http.post("/api/v2/plays/check", input);
|
|
749
|
+
}
|
|
750
|
+
async startPlayRunFromBundle(input) {
|
|
751
|
+
const compilerManifest = input.compilerManifest ?? await this.compilePlayManifest({
|
|
752
|
+
name: input.name,
|
|
753
|
+
sourceCode: input.sourceCode,
|
|
754
|
+
artifact: input.artifact
|
|
755
|
+
});
|
|
756
|
+
const registeredArtifact = await this.registerPlayArtifact({
|
|
757
|
+
name: input.name,
|
|
758
|
+
sourceCode: input.sourceCode,
|
|
759
|
+
artifact: input.artifact,
|
|
760
|
+
compilerManifest,
|
|
761
|
+
publish: false
|
|
240
762
|
});
|
|
763
|
+
if (!registeredArtifact.artifactStorageKey) {
|
|
764
|
+
throw new Error(
|
|
765
|
+
"registerPlayArtifact did not return an artifactStorageKey."
|
|
766
|
+
);
|
|
767
|
+
}
|
|
768
|
+
return this.startPlayRun({
|
|
769
|
+
name: input.name,
|
|
770
|
+
artifactStorageKey: registeredArtifact.artifactStorageKey,
|
|
771
|
+
compilerManifest,
|
|
772
|
+
...input.input ? { input: input.input } : {},
|
|
773
|
+
...input.inputFile ? { inputFile: input.inputFile } : {},
|
|
774
|
+
...input.packagedFiles?.length ? { packagedFiles: input.packagedFiles } : {},
|
|
775
|
+
...input.force ? { force: true } : {}
|
|
776
|
+
});
|
|
777
|
+
}
|
|
778
|
+
/**
|
|
779
|
+
* Register a bundled play artifact and start a run from the live revision.
|
|
780
|
+
*
|
|
781
|
+
* Convenience wrapper around {@link registerPlayArtifact} plus
|
|
782
|
+
* {@link startPlayRun}. This is the canonical file-backed path used by wrappers.
|
|
783
|
+
* The returned id can be passed to {@link getPlayStatus} to retrieve the same
|
|
784
|
+
* durable `{ result }` object that the CLI prints after `--watch` completes.
|
|
785
|
+
*
|
|
786
|
+
* @param code - Source string fallback; the bundled artifact should be passed in `options.artifact`
|
|
787
|
+
* @param csvPath - Path to input CSV file, or `null`
|
|
788
|
+
* @param name - Play name (extracted from source if omitted)
|
|
789
|
+
* @param options - Additional submission options
|
|
790
|
+
* @returns Workflow metadata with `workflowId`
|
|
791
|
+
*
|
|
792
|
+
* @example
|
|
793
|
+
* ```typescript
|
|
794
|
+
* const started = await client.submitPlay(
|
|
795
|
+
* originalSource,
|
|
796
|
+
* './leads.csv',
|
|
797
|
+
* 'bulk-enrich',
|
|
798
|
+
* { artifact: bundledArtifact, input: { limit: 100 } },
|
|
799
|
+
* );
|
|
800
|
+
* ```
|
|
801
|
+
*/
|
|
802
|
+
async submitPlay(code, csvPath, name, options) {
|
|
803
|
+
const runtimeInput = options?.input ? { ...options.input } : {};
|
|
804
|
+
if (csvPath) {
|
|
805
|
+
runtimeInput.file = csvPath;
|
|
806
|
+
}
|
|
807
|
+
const sourceCode = options?.sourceCode ?? code;
|
|
808
|
+
const artifact = options?.artifact;
|
|
809
|
+
if (!name?.trim()) {
|
|
810
|
+
throw new Error("submitPlay requires a play name.");
|
|
811
|
+
}
|
|
812
|
+
if (!artifact) {
|
|
813
|
+
throw new Error("submitPlay requires a bundled play artifact.");
|
|
814
|
+
}
|
|
815
|
+
const compilerManifest = options?.compilerManifest ?? await this.compilePlayManifest({
|
|
816
|
+
name,
|
|
817
|
+
sourceCode,
|
|
818
|
+
artifact
|
|
819
|
+
});
|
|
820
|
+
const registeredArtifact = await this.registerPlayArtifact({
|
|
821
|
+
name,
|
|
822
|
+
sourceCode,
|
|
823
|
+
artifact,
|
|
824
|
+
compilerManifest,
|
|
825
|
+
publish: false
|
|
826
|
+
});
|
|
827
|
+
if (!registeredArtifact.artifactStorageKey) {
|
|
828
|
+
throw new Error(
|
|
829
|
+
"registerPlayArtifact did not return an artifactStorageKey."
|
|
830
|
+
);
|
|
831
|
+
}
|
|
832
|
+
return this.startPlayRun({
|
|
833
|
+
name,
|
|
834
|
+
artifactStorageKey: registeredArtifact.artifactStorageKey,
|
|
835
|
+
sourceCode,
|
|
836
|
+
staticPipeline: registeredArtifact.staticPipeline ?? null,
|
|
837
|
+
artifactHash: typeof artifact.artifactHash === "string" ? artifact.artifactHash : void 0,
|
|
838
|
+
graphHash: typeof artifact.graphHash === "string" ? artifact.graphHash : void 0,
|
|
839
|
+
runtimeArtifact: artifact,
|
|
840
|
+
compilerManifest,
|
|
841
|
+
...Object.keys(runtimeInput).length > 0 ? { input: runtimeInput } : {},
|
|
842
|
+
...options?.inputFile ? { inputFile: options.inputFile } : {},
|
|
843
|
+
...options?.packagedFiles?.length ? { packagedFiles: options.packagedFiles } : {},
|
|
844
|
+
...options?.force ? { force: true } : {}
|
|
845
|
+
});
|
|
846
|
+
}
|
|
847
|
+
/**
|
|
848
|
+
* Upload files to the staging area for use in play runs.
|
|
849
|
+
*
|
|
850
|
+
* Internal/advanced primitive used by packaging flows. Public callers should
|
|
851
|
+
* prefer the CLI, {@link submitPlay}, or {@link runPlay}.
|
|
852
|
+
*
|
|
853
|
+
* Staged files are referenced by their returned {@link PlayStagedFileRef}
|
|
854
|
+
* in subsequent {@link startPlayRun} calls via `inputFile` or `packagedFiles`.
|
|
855
|
+
*
|
|
856
|
+
* @param files - Array of files to stage (base64-encoded content)
|
|
857
|
+
* @returns Array of staged file references
|
|
858
|
+
*
|
|
859
|
+
* @example
|
|
860
|
+
* ```typescript
|
|
861
|
+
* const staged = await client.stagePlayFiles([{
|
|
862
|
+
* logicalPath: 'data/leads.csv',
|
|
863
|
+
* contentBase64: Buffer.from(csvContent).toString('base64'),
|
|
864
|
+
* contentHash: sha256(csvContent),
|
|
865
|
+
* contentType: 'text/csv',
|
|
866
|
+
* bytes: csvContent.length,
|
|
867
|
+
* }]);
|
|
868
|
+
* // Use staged[0] as inputFile in startPlayRun
|
|
869
|
+
* ```
|
|
870
|
+
*/
|
|
871
|
+
async stagePlayFiles(files) {
|
|
872
|
+
const response = await this.http.post(
|
|
873
|
+
"/api/v2/plays/files/stage",
|
|
874
|
+
{ files }
|
|
875
|
+
);
|
|
876
|
+
return response.files;
|
|
877
|
+
}
|
|
878
|
+
async resolveStagedPlayFiles(files) {
|
|
879
|
+
return this.http.post("/api/v2/plays/files/stage", { files });
|
|
241
880
|
}
|
|
242
|
-
|
|
881
|
+
// ——————————————————————————————————————————————————————————
|
|
882
|
+
// Plays — status and monitoring
|
|
883
|
+
// ——————————————————————————————————————————————————————————
|
|
884
|
+
/**
|
|
885
|
+
* Get the current status of a play execution.
|
|
886
|
+
*
|
|
887
|
+
* Internal/advanced primitive. Public callers should usually prefer
|
|
888
|
+
* {@link runPlay}, {@link PlayJob.get}, or `deepline play run --watch`.
|
|
889
|
+
*
|
|
890
|
+
* Poll this method until `status` reaches a terminal state:
|
|
891
|
+
* `'completed'`, `'failed'`, or `'cancelled'`.
|
|
892
|
+
*
|
|
893
|
+
* @param workflowId - Play-run id from {@link startPlayRun}
|
|
894
|
+
* @returns Current status with progress logs and partial results
|
|
895
|
+
*
|
|
896
|
+
* @example
|
|
897
|
+
* ```typescript
|
|
898
|
+
* const status = await client.getPlayStatus('play-abc123');
|
|
899
|
+
* console.log(`Status: ${status.status}`);
|
|
900
|
+
* console.log(`Logs: ${status.progress?.logs.length ?? 0} lines`);
|
|
901
|
+
* ```
|
|
902
|
+
*/
|
|
243
903
|
async getPlayStatus(workflowId) {
|
|
244
|
-
|
|
904
|
+
const response = await this.http.get(
|
|
905
|
+
`/api/v2/plays/run/${encodeURIComponent(workflowId)}`
|
|
906
|
+
);
|
|
907
|
+
return normalizePlayStatus(response);
|
|
245
908
|
}
|
|
246
|
-
/**
|
|
909
|
+
/**
|
|
910
|
+
* Get the lightweight tail-polling status for a play execution.
|
|
911
|
+
*
|
|
912
|
+
* This is intentionally smaller than {@link getPlayStatus}: it returns the
|
|
913
|
+
* fields needed for CLI log tailing while the run is in flight, without
|
|
914
|
+
* forcing the API to rebuild final result views on every poll. Call
|
|
915
|
+
* {@link getPlayStatus} once after a terminal state for the full result.
|
|
916
|
+
*/
|
|
917
|
+
async getPlayTailStatus(workflowId, options) {
|
|
918
|
+
const params = new URLSearchParams({ mode: "tail" });
|
|
919
|
+
if (typeof options?.afterLogIndex === "number") {
|
|
920
|
+
params.set("afterLogIndex", String(options.afterLogIndex));
|
|
921
|
+
}
|
|
922
|
+
if (typeof options?.waitMs === "number") {
|
|
923
|
+
params.set("waitMs", String(options.waitMs));
|
|
924
|
+
}
|
|
925
|
+
if (options?.terminalOnly) {
|
|
926
|
+
params.set("terminalOnly", "true");
|
|
927
|
+
}
|
|
928
|
+
const response = await this.http.get(
|
|
929
|
+
`/api/v2/plays/run/${encodeURIComponent(workflowId)}?${params.toString()}`
|
|
930
|
+
);
|
|
931
|
+
return normalizePlayStatus(response);
|
|
932
|
+
}
|
|
933
|
+
/**
|
|
934
|
+
* Stream semantic play-run events using the same SSE feed as the dashboard.
|
|
935
|
+
*
|
|
936
|
+
* Consumers should still keep a polling fallback: SSE is the fast live-update
|
|
937
|
+
* transport, while the status endpoints remain the authoritative recovery path.
|
|
938
|
+
*/
|
|
939
|
+
async *streamPlayRunEvents(workflowId, options) {
|
|
940
|
+
const headers = options?.lastEventId && options.lastEventId.trim() ? { "Last-Event-ID": options.lastEventId.trim() } : void 0;
|
|
941
|
+
const params = new URLSearchParams({ stream: "true" });
|
|
942
|
+
params.set("mode", options?.mode ?? "cli");
|
|
943
|
+
for await (const event of this.http.streamSse(
|
|
944
|
+
`/api/v2/plays/run/${encodeURIComponent(workflowId)}?${params.toString()}`,
|
|
945
|
+
{ signal: options?.signal, headers }
|
|
946
|
+
)) {
|
|
947
|
+
if (event.scope === "play") {
|
|
948
|
+
yield event;
|
|
949
|
+
}
|
|
950
|
+
}
|
|
951
|
+
}
|
|
952
|
+
/**
|
|
953
|
+
* Cancel a running play execution.
|
|
954
|
+
*
|
|
955
|
+
* Sends a stop request for the run.
|
|
956
|
+
*
|
|
957
|
+
* @param workflowId - Temporal workflow ID to cancel
|
|
958
|
+
*
|
|
959
|
+
* @example
|
|
960
|
+
* ```typescript
|
|
961
|
+
* await client.cancelPlay('play-abc123');
|
|
962
|
+
* ```
|
|
963
|
+
*/
|
|
247
964
|
async cancelPlay(workflowId) {
|
|
248
|
-
await this.http.request(
|
|
965
|
+
await this.http.request(
|
|
966
|
+
`/api/v2/plays/run/${encodeURIComponent(workflowId)}/stop`,
|
|
967
|
+
{ method: "POST" }
|
|
968
|
+
);
|
|
249
969
|
}
|
|
250
970
|
/**
|
|
251
|
-
*
|
|
252
|
-
*
|
|
971
|
+
* Stop a running play execution, including open HITL waits.
|
|
972
|
+
*
|
|
973
|
+
* @param workflowId - Temporal workflow ID to stop
|
|
974
|
+
* @param options.reason - Optional audit/debug reason
|
|
975
|
+
*/
|
|
976
|
+
async stopPlay(workflowId, options) {
|
|
977
|
+
return this.http.post(
|
|
978
|
+
`/api/v2/plays/run/${encodeURIComponent(workflowId)}/stop`,
|
|
979
|
+
options?.reason ? { reason: options.reason } : {}
|
|
980
|
+
);
|
|
981
|
+
}
|
|
982
|
+
/**
|
|
983
|
+
* List recent runs for a named play.
|
|
984
|
+
*
|
|
985
|
+
* Returns runs sorted by start time (newest first), including workflow IDs,
|
|
986
|
+
* status, timestamps, and metadata.
|
|
987
|
+
*
|
|
988
|
+
* @param playName - The play name to query
|
|
989
|
+
* @returns Array of run summaries (empty array if no runs exist)
|
|
990
|
+
*
|
|
991
|
+
* @example
|
|
992
|
+
* ```typescript
|
|
993
|
+
* const runs = await client.listPlayRuns('email-waterfall');
|
|
994
|
+
* for (const run of runs) {
|
|
995
|
+
* console.log(`${run.workflowId}: ${run.status} (${run.executionTime})`);
|
|
996
|
+
* }
|
|
997
|
+
* ```
|
|
998
|
+
*/
|
|
999
|
+
async listPlayRuns(playName) {
|
|
1000
|
+
const encodedName = encodeURIComponent(playName);
|
|
1001
|
+
const response = await this.http.get(
|
|
1002
|
+
`/api/v2/plays/${encodedName}/runs`
|
|
1003
|
+
);
|
|
1004
|
+
return response.runs ?? [];
|
|
1005
|
+
}
|
|
1006
|
+
async listPlays() {
|
|
1007
|
+
const response = await this.http.get(
|
|
1008
|
+
"/api/v2/plays"
|
|
1009
|
+
);
|
|
1010
|
+
return response.plays ?? [];
|
|
1011
|
+
}
|
|
1012
|
+
async searchPlays(options) {
|
|
1013
|
+
const query = options.query.trim().toLowerCase();
|
|
1014
|
+
const terms = query.split(/\s+/).filter(Boolean);
|
|
1015
|
+
const plays = await this.listPlays();
|
|
1016
|
+
return plays.filter((play) => {
|
|
1017
|
+
if (options.origin && (play.origin ?? "owned") !== options.origin) {
|
|
1018
|
+
return false;
|
|
1019
|
+
}
|
|
1020
|
+
const haystack = [
|
|
1021
|
+
play.name,
|
|
1022
|
+
play.reference,
|
|
1023
|
+
play.displayName,
|
|
1024
|
+
play.origin,
|
|
1025
|
+
...play.aliases ?? [],
|
|
1026
|
+
play.inputSchema ? JSON.stringify(play.inputSchema) : ""
|
|
1027
|
+
].filter(Boolean).join(" ").toLowerCase();
|
|
1028
|
+
return terms.every((term) => haystack.includes(term));
|
|
1029
|
+
}).map((play) => this.summarizePlayListItem(play, options));
|
|
1030
|
+
}
|
|
1031
|
+
/**
|
|
1032
|
+
* Get the full definition and state of a named play.
|
|
1033
|
+
*
|
|
1034
|
+
* Returns the play's revision state (draft, live), recent runs,
|
|
1035
|
+
* sheet processing summary, and database URL.
|
|
1036
|
+
*
|
|
1037
|
+
* @param name - Play name
|
|
1038
|
+
* @returns Complete play detail
|
|
1039
|
+
*
|
|
1040
|
+
* @example
|
|
1041
|
+
* ```typescript
|
|
1042
|
+
* const detail = await client.getPlay('email-waterfall');
|
|
1043
|
+
* console.log(`Live: v${detail.play.currentPublishedVersion}`);
|
|
1044
|
+
* console.log(`Draft dirty: ${detail.play.isDraftDirty}`);
|
|
1045
|
+
* console.log(`Total runs: ${detail.play.runCount}`);
|
|
1046
|
+
* ```
|
|
1047
|
+
*/
|
|
1048
|
+
async getPlay(name) {
|
|
1049
|
+
const encodedName = encodeURIComponent(name);
|
|
1050
|
+
return this.http.get(`/api/v2/plays/${encodedName}`);
|
|
1051
|
+
}
|
|
1052
|
+
async describePlay(name, options) {
|
|
1053
|
+
const detail = await this.getPlay(name);
|
|
1054
|
+
return this.summarizePlayDetail(detail, options);
|
|
1055
|
+
}
|
|
1056
|
+
/**
|
|
1057
|
+
* Clear run history and durable sheet/result data for a play without deleting
|
|
1058
|
+
* the play definition or revisions.
|
|
1059
|
+
*/
|
|
1060
|
+
async clearPlayHistory(name, request = {}) {
|
|
1061
|
+
const encodedName = encodeURIComponent(name);
|
|
1062
|
+
return this.http.post(
|
|
1063
|
+
`/api/v2/plays/${encodedName}/history/clear`,
|
|
1064
|
+
request
|
|
1065
|
+
);
|
|
1066
|
+
}
|
|
1067
|
+
/**
|
|
1068
|
+
* List saved versions for a named play.
|
|
1069
|
+
*
|
|
1070
|
+
* Returns immutable revision snapshots newest-first, including the revision
|
|
1071
|
+
* id needed for exact-version runs and live-version switching.
|
|
1072
|
+
*
|
|
1073
|
+
* @param name - Play name
|
|
1074
|
+
* @returns Version list (newest first)
|
|
1075
|
+
*/
|
|
1076
|
+
async listPlayVersions(name) {
|
|
1077
|
+
const encodedName = encodeURIComponent(name);
|
|
1078
|
+
const response = await this.http.get(
|
|
1079
|
+
`/api/v2/plays/${encodedName}/versions`
|
|
1080
|
+
);
|
|
1081
|
+
return response.versions ?? [];
|
|
1082
|
+
}
|
|
1083
|
+
/**
|
|
1084
|
+
* Make a play revision live.
|
|
1085
|
+
*
|
|
1086
|
+
* When `revisionId` is omitted, the current working revision becomes live.
|
|
1087
|
+
* The live version is what executes when the play is run by name without
|
|
1088
|
+
* specifying an explicit revision.
|
|
1089
|
+
*
|
|
1090
|
+
* @param name - Play name
|
|
1091
|
+
* @param request - Optional explicit revision to make live
|
|
1092
|
+
* @returns Result with the new live version number
|
|
1093
|
+
*
|
|
1094
|
+
* @example
|
|
1095
|
+
* ```typescript
|
|
1096
|
+
* const result = await client.publishPlayVersion('email-waterfall');
|
|
1097
|
+
* if (result.success) {
|
|
1098
|
+
* console.log(`Live v${result.liveVersion}`);
|
|
1099
|
+
* }
|
|
1100
|
+
* ```
|
|
1101
|
+
*/
|
|
1102
|
+
async publishPlayVersion(name, request = {}) {
|
|
1103
|
+
const encodedName = encodeURIComponent(name);
|
|
1104
|
+
return this.http.post(
|
|
1105
|
+
`/api/v2/plays/${encodedName}/live`,
|
|
1106
|
+
request
|
|
1107
|
+
);
|
|
1108
|
+
}
|
|
1109
|
+
/**
|
|
1110
|
+
* Delete an org-owned play definition, including its revisions, trigger
|
|
1111
|
+
* bindings, and local run records. Deepline prebuilt plays are read-only.
|
|
1112
|
+
*/
|
|
1113
|
+
async deletePlay(name) {
|
|
1114
|
+
const encodedName = encodeURIComponent(name);
|
|
1115
|
+
return this.http.delete(`/api/v2/plays/${encodedName}`);
|
|
1116
|
+
}
|
|
1117
|
+
// ——————————————————————————————————————————————————————————
|
|
1118
|
+
// Plays — high-level orchestration
|
|
1119
|
+
// ——————————————————————————————————————————————————————————
|
|
1120
|
+
/**
|
|
1121
|
+
* Run a play end-to-end: submit, poll until terminal, return result.
|
|
1122
|
+
*
|
|
1123
|
+
* This is the highest-level play execution method. It submits the play,
|
|
1124
|
+
* polls for status updates, and returns a structured result with logs
|
|
1125
|
+
* and timing. Supports cancellation via `AbortSignal`.
|
|
1126
|
+
*
|
|
1127
|
+
* @param code - Source string fallback; pass the bundled artifact in `options.artifact`
|
|
1128
|
+
* @param csvPath - Input CSV path, or `null`
|
|
1129
|
+
* @param name - Play name
|
|
1130
|
+
* @param options - Execution options
|
|
1131
|
+
* @returns Final execution result with success/failure, output, logs, and duration
|
|
1132
|
+
*
|
|
1133
|
+
* @example
|
|
1134
|
+
* ```typescript
|
|
1135
|
+
* const result = await client.runPlay(bundledCode, null, 'my-play', {
|
|
1136
|
+
* input: { domain: 'stripe.com' },
|
|
1137
|
+
* onProgress: (status) => {
|
|
1138
|
+
* const logs = status.progress?.logs ?? [];
|
|
1139
|
+
* console.log(`[${status.status}] ${logs.length} log lines`);
|
|
1140
|
+
* },
|
|
1141
|
+
* pollIntervalMs: 1000,
|
|
1142
|
+
* });
|
|
1143
|
+
*
|
|
1144
|
+
* if (result.success) {
|
|
1145
|
+
* console.log('Output:', result.result);
|
|
1146
|
+
* } else {
|
|
1147
|
+
* console.error(`Failed after ${result.durationMs}ms:`, result.error);
|
|
1148
|
+
* }
|
|
1149
|
+
* ```
|
|
1150
|
+
*
|
|
1151
|
+
* @example Cancellation
|
|
1152
|
+
* ```typescript
|
|
1153
|
+
* const controller = new AbortController();
|
|
1154
|
+
* setTimeout(() => controller.abort(), 30_000); // 30s timeout
|
|
1155
|
+
*
|
|
1156
|
+
* const result = await client.runPlay(code, null, 'slow-play', {
|
|
1157
|
+
* signal: controller.signal,
|
|
1158
|
+
* });
|
|
1159
|
+
* // result.success === false, result.error === 'Cancelled by user'
|
|
1160
|
+
* ```
|
|
253
1161
|
*/
|
|
254
1162
|
async runPlay(code, csvPath, name, options) {
|
|
255
|
-
const { workflowId } = await this.submitPlay(code, csvPath, name
|
|
256
|
-
|
|
1163
|
+
const { workflowId } = await this.submitPlay(code, csvPath, name, {
|
|
1164
|
+
input: options?.input,
|
|
1165
|
+
sourceCode: options?.sourceCode,
|
|
1166
|
+
artifact: options?.artifact,
|
|
1167
|
+
compilerManifest: options?.compilerManifest,
|
|
1168
|
+
inputFile: options?.inputFile,
|
|
1169
|
+
packagedFiles: options?.packagedFiles,
|
|
1170
|
+
force: options?.force
|
|
1171
|
+
});
|
|
1172
|
+
const pollInterval = options?.pollIntervalMs ?? 500;
|
|
257
1173
|
const start = Date.now();
|
|
258
1174
|
while (true) {
|
|
259
1175
|
if (options?.signal?.aborted) {
|
|
260
1176
|
await this.cancelPlay(workflowId);
|
|
261
1177
|
return {
|
|
262
1178
|
success: false,
|
|
263
|
-
workflowId,
|
|
1179
|
+
runId: workflowId,
|
|
264
1180
|
logs: [],
|
|
265
1181
|
durationMs: Date.now() - start,
|
|
266
1182
|
error: "Cancelled by user"
|
|
@@ -268,32 +1184,650 @@ var DeeplineClient = class {
|
|
|
268
1184
|
}
|
|
269
1185
|
const status = await this.getPlayStatus(workflowId);
|
|
270
1186
|
options?.onProgress?.(status);
|
|
271
|
-
|
|
272
|
-
if (terminal.includes(status.temporalStatus)) {
|
|
1187
|
+
if (TERMINAL_PLAY_STATUSES.has(status.status)) {
|
|
273
1188
|
return {
|
|
274
|
-
success: status.
|
|
275
|
-
workflowId,
|
|
1189
|
+
success: status.status === "completed",
|
|
1190
|
+
runId: status.runId || workflowId,
|
|
276
1191
|
result: status.result,
|
|
277
1192
|
logs: status.progress?.logs ?? [],
|
|
278
1193
|
durationMs: Date.now() - start,
|
|
279
|
-
error: status.progress?.error ?? (status.
|
|
1194
|
+
error: status.progress?.error ?? (status.status !== "completed" ? status.status : void 0)
|
|
280
1195
|
};
|
|
281
1196
|
}
|
|
282
|
-
await new Promise((
|
|
1197
|
+
await new Promise((resolve2) => setTimeout(resolve2, pollInterval));
|
|
283
1198
|
}
|
|
284
1199
|
}
|
|
285
|
-
|
|
1200
|
+
// ——————————————————————————————————————————————————————————
|
|
1201
|
+
// Health
|
|
1202
|
+
// ——————————————————————————————————————————————————————————
|
|
1203
|
+
/**
|
|
1204
|
+
* Check API connectivity and server health.
|
|
1205
|
+
*
|
|
1206
|
+
* @returns Health status with API version
|
|
1207
|
+
*
|
|
1208
|
+
* @example
|
|
1209
|
+
* ```typescript
|
|
1210
|
+
* const health = await client.health();
|
|
1211
|
+
* console.log(`API: ${health.status} (${health.version})`);
|
|
1212
|
+
* // { status: "ok", version: "v2" }
|
|
1213
|
+
* ```
|
|
1214
|
+
*/
|
|
286
1215
|
async health() {
|
|
287
|
-
return this.http.get(
|
|
1216
|
+
return this.http.get(
|
|
1217
|
+
"/api/v2/health"
|
|
1218
|
+
);
|
|
1219
|
+
}
|
|
1220
|
+
};
|
|
1221
|
+
|
|
1222
|
+
// src/tool-output.ts
|
|
1223
|
+
import { mkdirSync as mkdirSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
1224
|
+
import { homedir as homedir2 } from "os";
|
|
1225
|
+
import { join as join2 } from "path";
|
|
1226
|
+
function isPlainObject(value) {
|
|
1227
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
1228
|
+
}
|
|
1229
|
+
var EMAIL_PATTERN = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
1230
|
+
var PHONE_KEY_PATTERN = /(^|[_-])(phone|mobile|cell|telephone|tel)([_-]|$)|phone|mobile|telephone/i;
|
|
1231
|
+
function normalizeScalarString(value) {
|
|
1232
|
+
if (typeof value === "string") {
|
|
1233
|
+
const trimmed = value.trim();
|
|
1234
|
+
return trimmed.length > 0 ? trimmed : null;
|
|
1235
|
+
}
|
|
1236
|
+
if (typeof value === "number" && Number.isFinite(value)) {
|
|
1237
|
+
return String(value);
|
|
1238
|
+
}
|
|
1239
|
+
return null;
|
|
1240
|
+
}
|
|
1241
|
+
function looksLikeEmail(value) {
|
|
1242
|
+
const candidate = normalizeScalarString(value);
|
|
1243
|
+
if (!candidate || !EMAIL_PATTERN.test(candidate)) return null;
|
|
1244
|
+
return candidate;
|
|
1245
|
+
}
|
|
1246
|
+
function looksLikePhone(value) {
|
|
1247
|
+
const candidate = normalizeScalarString(value);
|
|
1248
|
+
if (!candidate) return null;
|
|
1249
|
+
const digits = candidate.replace(/\D/g, "");
|
|
1250
|
+
if (digits.length < 7 || digits.length > 16) return null;
|
|
1251
|
+
return candidate;
|
|
1252
|
+
}
|
|
1253
|
+
function findEmail(value, depth = 0) {
|
|
1254
|
+
if (depth > 6) return null;
|
|
1255
|
+
const direct = looksLikeEmail(value);
|
|
1256
|
+
if (direct) return direct;
|
|
1257
|
+
if (Array.isArray(value)) {
|
|
1258
|
+
for (const entry of value) {
|
|
1259
|
+
const nested = findEmail(entry, depth + 1);
|
|
1260
|
+
if (nested) return nested;
|
|
1261
|
+
}
|
|
1262
|
+
return null;
|
|
1263
|
+
}
|
|
1264
|
+
if (!isPlainObject(value)) return null;
|
|
1265
|
+
for (const [key, child] of Object.entries(value)) {
|
|
1266
|
+
if (/email/i.test(key)) {
|
|
1267
|
+
const keyed = looksLikeEmail(child);
|
|
1268
|
+
if (keyed) return keyed;
|
|
1269
|
+
}
|
|
1270
|
+
}
|
|
1271
|
+
for (const child of Object.values(value)) {
|
|
1272
|
+
const nested = findEmail(child, depth + 1);
|
|
1273
|
+
if (nested) return nested;
|
|
1274
|
+
}
|
|
1275
|
+
return null;
|
|
1276
|
+
}
|
|
1277
|
+
function findPhone(value, depth = 0) {
|
|
1278
|
+
if (depth > 6) return null;
|
|
1279
|
+
if (Array.isArray(value)) {
|
|
1280
|
+
for (const entry of value) {
|
|
1281
|
+
const nested = findPhone(entry, depth + 1);
|
|
1282
|
+
if (nested) return nested;
|
|
1283
|
+
}
|
|
1284
|
+
return null;
|
|
1285
|
+
}
|
|
1286
|
+
if (!isPlainObject(value)) return null;
|
|
1287
|
+
for (const [key, child] of Object.entries(value)) {
|
|
1288
|
+
if (PHONE_KEY_PATTERN.test(key)) {
|
|
1289
|
+
const keyed = looksLikePhone(child);
|
|
1290
|
+
if (keyed) return keyed;
|
|
1291
|
+
}
|
|
1292
|
+
}
|
|
1293
|
+
for (const child of Object.values(value)) {
|
|
1294
|
+
const nested = findPhone(child, depth + 1);
|
|
1295
|
+
if (nested) return nested;
|
|
1296
|
+
}
|
|
1297
|
+
return null;
|
|
1298
|
+
}
|
|
1299
|
+
var DeeplineToolCallResult = class {
|
|
1300
|
+
constructor(value) {
|
|
1301
|
+
this.value = value;
|
|
1302
|
+
}
|
|
1303
|
+
value;
|
|
1304
|
+
getEmail() {
|
|
1305
|
+
return findEmail(this.value);
|
|
1306
|
+
}
|
|
1307
|
+
getPhone() {
|
|
1308
|
+
return findPhone(this.value);
|
|
1309
|
+
}
|
|
1310
|
+
tryList(options) {
|
|
1311
|
+
return tryConvertToList(this.value, options)?.rows ?? null;
|
|
288
1312
|
}
|
|
289
1313
|
};
|
|
1314
|
+
function createToolCallResult(value) {
|
|
1315
|
+
return new DeeplineToolCallResult(value);
|
|
1316
|
+
}
|
|
1317
|
+
function getByDottedPath(root, dottedPath) {
|
|
1318
|
+
let current = root;
|
|
1319
|
+
for (const segment of String(dottedPath || "").split(".").filter(Boolean)) {
|
|
1320
|
+
if (!isPlainObject(current) || !(segment in current)) {
|
|
1321
|
+
return null;
|
|
1322
|
+
}
|
|
1323
|
+
current = current[segment];
|
|
1324
|
+
}
|
|
1325
|
+
return current;
|
|
1326
|
+
}
|
|
1327
|
+
function normalizeRows(value) {
|
|
1328
|
+
if (!Array.isArray(value)) return null;
|
|
1329
|
+
return value.map((entry) => {
|
|
1330
|
+
if (isPlainObject(entry)) return entry;
|
|
1331
|
+
return { value: entry };
|
|
1332
|
+
});
|
|
1333
|
+
}
|
|
1334
|
+
function candidateRoots(payload) {
|
|
1335
|
+
const roots = [{ path: null, value: payload }];
|
|
1336
|
+
if (isPlainObject(payload) && isPlainObject(payload.result)) {
|
|
1337
|
+
roots.push({ path: "result", value: payload.result });
|
|
1338
|
+
if (isPlainObject(payload.result.data)) {
|
|
1339
|
+
roots.push({ path: "result.data", value: payload.result.data });
|
|
1340
|
+
}
|
|
1341
|
+
}
|
|
1342
|
+
return roots;
|
|
1343
|
+
}
|
|
1344
|
+
function findBestArrayCandidate(value, pathPrefix = "", depth = 0) {
|
|
1345
|
+
if (depth > 5) return null;
|
|
1346
|
+
const directRows = normalizeRows(value);
|
|
1347
|
+
const hasObjectRow = directRows?.some((row) => Object.keys(row).some((key) => key !== "value")) ?? false;
|
|
1348
|
+
let best = directRows && directRows.length > 0 && hasObjectRow ? { path: pathPrefix, rows: directRows } : null;
|
|
1349
|
+
if (!isPlainObject(value)) {
|
|
1350
|
+
return best;
|
|
1351
|
+
}
|
|
1352
|
+
for (const [key, child] of Object.entries(value)) {
|
|
1353
|
+
const childPath = pathPrefix ? `${pathPrefix}.${key}` : key;
|
|
1354
|
+
const candidate = findBestArrayCandidate(child, childPath, depth + 1);
|
|
1355
|
+
if (!candidate) continue;
|
|
1356
|
+
if (!best || candidate.rows.length > best.rows.length) {
|
|
1357
|
+
best = candidate;
|
|
1358
|
+
}
|
|
1359
|
+
}
|
|
1360
|
+
return best;
|
|
1361
|
+
}
|
|
1362
|
+
function tryConvertToList(payload, options) {
|
|
1363
|
+
const listExtractorPaths = Array.isArray(options?.listExtractorPaths) ? options?.listExtractorPaths.filter((entry) => typeof entry === "string" && entry.trim().length > 0) : [];
|
|
1364
|
+
if (listExtractorPaths.length > 0) {
|
|
1365
|
+
for (const root of candidateRoots(payload)) {
|
|
1366
|
+
for (const extractorPath of listExtractorPaths) {
|
|
1367
|
+
const resolved = getByDottedPath(root.value, extractorPath);
|
|
1368
|
+
const rows = normalizeRows(resolved);
|
|
1369
|
+
if (rows && rows.length > 0) {
|
|
1370
|
+
const sourcePath = root.path ? `${root.path}.${extractorPath}` : extractorPath;
|
|
1371
|
+
return { rows, strategy: "configured_paths", sourcePath };
|
|
1372
|
+
}
|
|
1373
|
+
}
|
|
1374
|
+
}
|
|
1375
|
+
}
|
|
1376
|
+
for (const root of candidateRoots(payload)) {
|
|
1377
|
+
const candidate = findBestArrayCandidate(root.value, root.path ?? "");
|
|
1378
|
+
if (!candidate || candidate.rows.length === 0) continue;
|
|
1379
|
+
return {
|
|
1380
|
+
rows: candidate.rows,
|
|
1381
|
+
strategy: "auto_detected",
|
|
1382
|
+
sourcePath: candidate.path || root.path
|
|
1383
|
+
};
|
|
1384
|
+
}
|
|
1385
|
+
return null;
|
|
1386
|
+
}
|
|
1387
|
+
function ensureOutputDir() {
|
|
1388
|
+
const outputDir = join2(homedir2(), ".local", "share", "deepline", "data");
|
|
1389
|
+
mkdirSync2(outputDir, { recursive: true });
|
|
1390
|
+
return outputDir;
|
|
1391
|
+
}
|
|
1392
|
+
function writeJsonOutputFile(payload, stem) {
|
|
1393
|
+
const outputDir = ensureOutputDir();
|
|
1394
|
+
const outputPath = join2(outputDir, `${stem}_${Date.now()}.json`);
|
|
1395
|
+
writeFileSync2(outputPath, JSON.stringify(payload, null, 2), "utf-8");
|
|
1396
|
+
return outputPath;
|
|
1397
|
+
}
|
|
1398
|
+
function writeCsvOutputFile(rows, stem) {
|
|
1399
|
+
const outputDir = ensureOutputDir();
|
|
1400
|
+
const outputPath = join2(outputDir, `${stem}_${Date.now()}.csv`);
|
|
1401
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1402
|
+
const columns = [];
|
|
1403
|
+
for (const row of rows) {
|
|
1404
|
+
for (const key of Object.keys(row)) {
|
|
1405
|
+
if (!seen.has(key)) {
|
|
1406
|
+
seen.add(key);
|
|
1407
|
+
columns.push(key);
|
|
1408
|
+
}
|
|
1409
|
+
}
|
|
1410
|
+
}
|
|
1411
|
+
const escapeCell = (value) => {
|
|
1412
|
+
const normalized = value == null ? "" : typeof value === "string" || typeof value === "number" || typeof value === "boolean" ? String(value) : JSON.stringify(value);
|
|
1413
|
+
if (/[",\n]/.test(normalized)) {
|
|
1414
|
+
return `"${normalized.replace(/"/g, '""')}"`;
|
|
1415
|
+
}
|
|
1416
|
+
return normalized;
|
|
1417
|
+
};
|
|
1418
|
+
const lines = [];
|
|
1419
|
+
lines.push(columns.map(escapeCell).join(","));
|
|
1420
|
+
for (const row of rows) {
|
|
1421
|
+
lines.push(columns.map((column) => escapeCell(row[column])).join(","));
|
|
1422
|
+
}
|
|
1423
|
+
writeFileSync2(outputPath, `${lines.join("\n")}
|
|
1424
|
+
`, "utf-8");
|
|
1425
|
+
const previewRows = rows.slice(0, 5);
|
|
1426
|
+
const previewColumns = columns.slice(0, 5);
|
|
1427
|
+
const preview = [
|
|
1428
|
+
previewColumns.join(","),
|
|
1429
|
+
...previewRows.map((row) => previewColumns.map((column) => escapeCell(row[column])).join(","))
|
|
1430
|
+
].join("\n");
|
|
1431
|
+
return {
|
|
1432
|
+
path: outputPath,
|
|
1433
|
+
rowCount: rows.length,
|
|
1434
|
+
columns,
|
|
1435
|
+
preview
|
|
1436
|
+
};
|
|
1437
|
+
}
|
|
1438
|
+
function extractSummaryFields(payload) {
|
|
1439
|
+
const candidates = candidateRoots(payload);
|
|
1440
|
+
for (const candidate of candidates) {
|
|
1441
|
+
if (!isPlainObject(candidate.value)) continue;
|
|
1442
|
+
const summaryEntries = Object.entries(candidate.value).filter(([, value]) => {
|
|
1443
|
+
return value == null || typeof value === "string" || typeof value === "number" || typeof value === "boolean";
|
|
1444
|
+
});
|
|
1445
|
+
if (summaryEntries.length === 0) continue;
|
|
1446
|
+
return Object.fromEntries(summaryEntries);
|
|
1447
|
+
}
|
|
1448
|
+
return {};
|
|
1449
|
+
}
|
|
1450
|
+
|
|
1451
|
+
// src/play.ts
|
|
1452
|
+
var DeeplineConditionalStepResolver = class _DeeplineConditionalStepResolver {
|
|
1453
|
+
constructor(when2, run, elseValue) {
|
|
1454
|
+
this.when = when2;
|
|
1455
|
+
this.run = run;
|
|
1456
|
+
this.elseValue = elseValue;
|
|
1457
|
+
}
|
|
1458
|
+
when;
|
|
1459
|
+
run;
|
|
1460
|
+
elseValue;
|
|
1461
|
+
kind = "conditional";
|
|
1462
|
+
else(value) {
|
|
1463
|
+
return new _DeeplineConditionalStepResolver(this.when, this.run, value);
|
|
1464
|
+
}
|
|
1465
|
+
};
|
|
1466
|
+
var DeeplineStepProgram = class _DeeplineStepProgram {
|
|
1467
|
+
constructor(steps2, returnResolver) {
|
|
1468
|
+
this.steps = steps2;
|
|
1469
|
+
this.returnResolver = returnResolver;
|
|
1470
|
+
}
|
|
1471
|
+
steps;
|
|
1472
|
+
returnResolver;
|
|
1473
|
+
kind = "steps";
|
|
1474
|
+
step(name, resolver) {
|
|
1475
|
+
if (!name.trim()) {
|
|
1476
|
+
throw new Error(
|
|
1477
|
+
"steps().step(name, ...) requires a non-empty step name."
|
|
1478
|
+
);
|
|
1479
|
+
}
|
|
1480
|
+
return new _DeeplineStepProgram(
|
|
1481
|
+
[
|
|
1482
|
+
...this.steps,
|
|
1483
|
+
{
|
|
1484
|
+
name,
|
|
1485
|
+
resolver
|
|
1486
|
+
}
|
|
1487
|
+
],
|
|
1488
|
+
this.returnResolver
|
|
1489
|
+
);
|
|
1490
|
+
}
|
|
1491
|
+
return(resolver) {
|
|
1492
|
+
return new _DeeplineStepProgram(this.steps, resolver);
|
|
1493
|
+
}
|
|
1494
|
+
};
|
|
1495
|
+
function steps() {
|
|
1496
|
+
return new DeeplineStepProgram([]);
|
|
1497
|
+
}
|
|
1498
|
+
function when(predicate, resolver) {
|
|
1499
|
+
return new DeeplineConditionalStepResolver(predicate, resolver, null);
|
|
1500
|
+
}
|
|
1501
|
+
var PLAY_METADATA_SYMBOL = /* @__PURE__ */ Symbol.for("deepline.play.metadata");
|
|
1502
|
+
var DeeplinePlayJobImpl = class {
|
|
1503
|
+
constructor(client, runId) {
|
|
1504
|
+
this.client = client;
|
|
1505
|
+
this.id = runId;
|
|
1506
|
+
}
|
|
1507
|
+
client;
|
|
1508
|
+
id;
|
|
1509
|
+
async status() {
|
|
1510
|
+
return this.client.getPlayStatus(this.id);
|
|
1511
|
+
}
|
|
1512
|
+
async tail(options) {
|
|
1513
|
+
const intervalMs = options?.intervalMs ?? 500;
|
|
1514
|
+
const onLog = options?.onLog ?? ((line) => console.log(line));
|
|
1515
|
+
const terminalStates = /* @__PURE__ */ new Set(["completed", "failed", "cancelled"]);
|
|
1516
|
+
let lastLogIndex = 0;
|
|
1517
|
+
while (true) {
|
|
1518
|
+
const status = await this.status();
|
|
1519
|
+
const logs = status.progress?.logs ?? [];
|
|
1520
|
+
for (let index = lastLogIndex; index < logs.length; index += 1) {
|
|
1521
|
+
onLog(logs[index]);
|
|
1522
|
+
}
|
|
1523
|
+
lastLogIndex = logs.length;
|
|
1524
|
+
if (terminalStates.has(status.status)) {
|
|
1525
|
+
return status;
|
|
1526
|
+
}
|
|
1527
|
+
await new Promise((resolve2) => setTimeout(resolve2, intervalMs));
|
|
1528
|
+
}
|
|
1529
|
+
}
|
|
1530
|
+
async get(options) {
|
|
1531
|
+
const intervalMs = options?.intervalMs ?? 500;
|
|
1532
|
+
const terminalStates = /* @__PURE__ */ new Set(["completed", "failed", "cancelled"]);
|
|
1533
|
+
while (true) {
|
|
1534
|
+
const status = await this.status();
|
|
1535
|
+
if (terminalStates.has(status.status)) {
|
|
1536
|
+
if (status.status !== "completed") {
|
|
1537
|
+
throw new DeeplineError(
|
|
1538
|
+
status.progress?.error || `Play run ${this.id} ended with ${status.status}.`
|
|
1539
|
+
);
|
|
1540
|
+
}
|
|
1541
|
+
const payload = status.result;
|
|
1542
|
+
return (payload && "output" in payload ? payload.output : status.result) ?? null;
|
|
1543
|
+
}
|
|
1544
|
+
await new Promise((resolve2) => setTimeout(resolve2, intervalMs));
|
|
1545
|
+
}
|
|
1546
|
+
}
|
|
1547
|
+
async cancel() {
|
|
1548
|
+
await this.client.cancelPlay(this.id);
|
|
1549
|
+
}
|
|
1550
|
+
async stop(options) {
|
|
1551
|
+
return this.client.stopPlay(this.id, options);
|
|
1552
|
+
}
|
|
1553
|
+
};
|
|
1554
|
+
function createNamedPlayHandle(clientFactory, name) {
|
|
1555
|
+
return {
|
|
1556
|
+
name,
|
|
1557
|
+
get: () => clientFactory().getPlay(name),
|
|
1558
|
+
runs: () => clientFactory().listPlayRuns(name),
|
|
1559
|
+
versions: () => clientFactory().listPlayVersions(name),
|
|
1560
|
+
publish: (options) => clientFactory().publishPlayVersion(name, options),
|
|
1561
|
+
clearHistory: (options) => clientFactory().clearPlayHistory(name, options),
|
|
1562
|
+
async run(input, options) {
|
|
1563
|
+
const client = clientFactory();
|
|
1564
|
+
const started = await client.startPlayRun({
|
|
1565
|
+
name,
|
|
1566
|
+
...options?.revisionId ? { revisionId: options.revisionId } : {},
|
|
1567
|
+
input
|
|
1568
|
+
});
|
|
1569
|
+
return new DeeplinePlayJobImpl(client, started.workflowId);
|
|
1570
|
+
},
|
|
1571
|
+
async runSync(input, options) {
|
|
1572
|
+
const job = await this.run(input, options);
|
|
1573
|
+
return job.get();
|
|
1574
|
+
}
|
|
1575
|
+
};
|
|
1576
|
+
}
|
|
1577
|
+
var DeeplineContext = class {
|
|
1578
|
+
client;
|
|
1579
|
+
constructor(options) {
|
|
1580
|
+
this.client = new DeeplineClient(options);
|
|
1581
|
+
}
|
|
1582
|
+
/**
|
|
1583
|
+
* Tool operations namespace.
|
|
1584
|
+
*
|
|
1585
|
+
* @example
|
|
1586
|
+
* ```typescript
|
|
1587
|
+
* const tools = await ctx.tools.list();
|
|
1588
|
+
* const meta = await ctx.tools.get('apollo_people_search');
|
|
1589
|
+
* const result = await ctx.tools.execute({
|
|
1590
|
+
* tool: 'test_company_search',
|
|
1591
|
+
* input: { domain: 'stripe.com' },
|
|
1592
|
+
* });
|
|
1593
|
+
* const rows = result.tryList({ listExtractorPaths: ['people'] });
|
|
1594
|
+
* const email = result.getEmail();
|
|
1595
|
+
* ```
|
|
1596
|
+
*/
|
|
1597
|
+
get tools() {
|
|
1598
|
+
return {
|
|
1599
|
+
/** List all available tools. */
|
|
1600
|
+
list: () => this.client.listTools(),
|
|
1601
|
+
/** Get detailed metadata for a tool. */
|
|
1602
|
+
get: (toolId) => this.client.getTool(toolId),
|
|
1603
|
+
/** Execute a tool and return an ergonomic result wrapper. */
|
|
1604
|
+
execute: async (request) => createToolCallResult(
|
|
1605
|
+
await this.client.executeTool(request.tool, request.input)
|
|
1606
|
+
)
|
|
1607
|
+
};
|
|
1608
|
+
}
|
|
1609
|
+
get plays() {
|
|
1610
|
+
return {
|
|
1611
|
+
list: () => this.client.listPlays(),
|
|
1612
|
+
get: (name) => this.play(name)
|
|
1613
|
+
};
|
|
1614
|
+
}
|
|
1615
|
+
get prebuilt() {
|
|
1616
|
+
const explicit = {
|
|
1617
|
+
companyToContact: {
|
|
1618
|
+
playName: "prebuilt/company-to-contact",
|
|
1619
|
+
name: "prebuilt/company-to-contact"
|
|
1620
|
+
},
|
|
1621
|
+
personToPhone: {
|
|
1622
|
+
playName: "prebuilt/person-to-phone",
|
|
1623
|
+
name: "prebuilt/person-to-phone"
|
|
1624
|
+
},
|
|
1625
|
+
personToEmail: {
|
|
1626
|
+
playName: "prebuilt/person-to-email",
|
|
1627
|
+
name: "prebuilt/person-to-email"
|
|
1628
|
+
},
|
|
1629
|
+
personLinkedinToEmail: {
|
|
1630
|
+
playName: "prebuilt/person-linkedin-to-email",
|
|
1631
|
+
name: "prebuilt/person-linkedin-to-email"
|
|
1632
|
+
}
|
|
1633
|
+
};
|
|
1634
|
+
return new Proxy(
|
|
1635
|
+
{},
|
|
1636
|
+
{
|
|
1637
|
+
get: (_target, prop) => {
|
|
1638
|
+
if (typeof prop !== "string") return void 0;
|
|
1639
|
+
if (prop in explicit) {
|
|
1640
|
+
return explicit[prop];
|
|
1641
|
+
}
|
|
1642
|
+
const playName = prop.startsWith("prebuilt/") ? prop : `prebuilt/${prop}`;
|
|
1643
|
+
return {
|
|
1644
|
+
playName,
|
|
1645
|
+
name: playName
|
|
1646
|
+
};
|
|
1647
|
+
}
|
|
1648
|
+
}
|
|
1649
|
+
);
|
|
1650
|
+
}
|
|
1651
|
+
/**
|
|
1652
|
+
* Get a named play handle for remote lifecycle operations.
|
|
1653
|
+
*
|
|
1654
|
+
* @typeParam TInput - Expected input type
|
|
1655
|
+
* @typeParam TOutput - Expected output type
|
|
1656
|
+
* @param name - Play name (as registered on the server)
|
|
1657
|
+
* @returns Named play handle with run, versions, get, publish, etc.
|
|
1658
|
+
*
|
|
1659
|
+
* @example
|
|
1660
|
+
* ```typescript
|
|
1661
|
+
* const play = ctx.play<{ domain: string }>('email-waterfall');
|
|
1662
|
+
* const job = await play.run({ domain: 'stripe.com' });
|
|
1663
|
+
* const result = await job.get();
|
|
1664
|
+
* ```
|
|
1665
|
+
*/
|
|
1666
|
+
play(name) {
|
|
1667
|
+
return createNamedPlayHandle(() => this.client, name);
|
|
1668
|
+
}
|
|
1669
|
+
async runPlay(playOrRef, input) {
|
|
1670
|
+
const name = typeof playOrRef === "string" ? playOrRef : playOrRef.playName ?? playOrRef.name ?? "";
|
|
1671
|
+
return await this.play(name).runSync(input);
|
|
1672
|
+
}
|
|
1673
|
+
};
|
|
1674
|
+
var Deepline = class {
|
|
1675
|
+
/**
|
|
1676
|
+
* Create a connected SDK context.
|
|
1677
|
+
*
|
|
1678
|
+
* Resolves configuration from options, environment variables, and CLI config
|
|
1679
|
+
* files. See {@link resolveConfig} for the resolution order.
|
|
1680
|
+
*
|
|
1681
|
+
* @param options - Optional overrides for API key, base URL, etc.
|
|
1682
|
+
* @returns Ready-to-use SDK context
|
|
1683
|
+
* @throws {@link ConfigError} if no API key can be resolved
|
|
1684
|
+
*
|
|
1685
|
+
* @example
|
|
1686
|
+
* ```typescript
|
|
1687
|
+
* // Auto-config (uses env vars / CLI auth):
|
|
1688
|
+
* const ctx = await Deepline.connect();
|
|
1689
|
+
*
|
|
1690
|
+
* // Explicit config:
|
|
1691
|
+
* const ctx2 = await Deepline.connect({
|
|
1692
|
+
* apiKey: 'dl_test_...',
|
|
1693
|
+
* baseUrl: 'http://localhost:3000',
|
|
1694
|
+
* });
|
|
1695
|
+
* ```
|
|
1696
|
+
*/
|
|
1697
|
+
static async connect(options) {
|
|
1698
|
+
return new DeeplineContext(options);
|
|
1699
|
+
}
|
|
1700
|
+
};
|
|
1701
|
+
function defineInput(schema) {
|
|
1702
|
+
if (!schema || typeof schema !== "object" || Array.isArray(schema)) {
|
|
1703
|
+
throw new Error(
|
|
1704
|
+
"defineInput<T>(schema) requires a JSON-schema-like object."
|
|
1705
|
+
);
|
|
1706
|
+
}
|
|
1707
|
+
return { schema };
|
|
1708
|
+
}
|
|
1709
|
+
function definePlay(nameOrConfig, maybeFn, maybeBindings) {
|
|
1710
|
+
const config = typeof nameOrConfig === "string" ? {
|
|
1711
|
+
name: nameOrConfig,
|
|
1712
|
+
fn: maybeFn,
|
|
1713
|
+
bindings: maybeBindings,
|
|
1714
|
+
inputSchema: void 0,
|
|
1715
|
+
billing: maybeBindings?.billing
|
|
1716
|
+
} : {
|
|
1717
|
+
name: nameOrConfig.id,
|
|
1718
|
+
fn: nameOrConfig.run,
|
|
1719
|
+
bindings: nameOrConfig.bindings,
|
|
1720
|
+
inputSchema: nameOrConfig.input.schema,
|
|
1721
|
+
billing: nameOrConfig.billing
|
|
1722
|
+
};
|
|
1723
|
+
const name = config.name;
|
|
1724
|
+
const fn = config.fn;
|
|
1725
|
+
const bindings = config.bindings;
|
|
1726
|
+
const billing = config.billing;
|
|
1727
|
+
const inputSchema = config.inputSchema;
|
|
1728
|
+
if (typeof fn !== "function") {
|
|
1729
|
+
throw new Error("definePlay(...) requires an async run function.");
|
|
1730
|
+
}
|
|
1731
|
+
if (name.includes("/")) {
|
|
1732
|
+
throw new Error(
|
|
1733
|
+
'definePlay(name, ...) play names cannot contain "/". Slash is reserved for qualified references like "prebuilt/example" or "self/example".'
|
|
1734
|
+
);
|
|
1735
|
+
}
|
|
1736
|
+
const normalizedName = name.trim().replace(/[^a-z0-9]+/gi, "_").replace(/_+/g, "_").replace(/^_+|_+$/g, "").toLowerCase();
|
|
1737
|
+
if (!normalizedName) {
|
|
1738
|
+
throw new Error(
|
|
1739
|
+
"definePlay(name, ...) requires a play name with at least one letter or number. Use only letters, numbers, underscores, or hyphens."
|
|
1740
|
+
);
|
|
1741
|
+
}
|
|
1742
|
+
if (normalizedName.length > 63) {
|
|
1743
|
+
throw new Error(
|
|
1744
|
+
`definePlay("${name}", ...) is too long after normalization (${normalizedName.length}/63). Shorten the play name to 63 characters or fewer. Normalized value: "${normalizedName}".`
|
|
1745
|
+
);
|
|
1746
|
+
}
|
|
1747
|
+
const metadata = {
|
|
1748
|
+
name,
|
|
1749
|
+
...bindings ? { bindings } : {},
|
|
1750
|
+
...inputSchema ? { inputSchema } : {},
|
|
1751
|
+
...billing ? { billing } : {}
|
|
1752
|
+
};
|
|
1753
|
+
const play = fn;
|
|
1754
|
+
Object.defineProperty(play, PLAY_METADATA_SYMBOL, {
|
|
1755
|
+
value: metadata,
|
|
1756
|
+
enumerable: false,
|
|
1757
|
+
configurable: false,
|
|
1758
|
+
writable: false
|
|
1759
|
+
});
|
|
1760
|
+
Object.defineProperty(play, "playName", {
|
|
1761
|
+
value: name,
|
|
1762
|
+
enumerable: true,
|
|
1763
|
+
configurable: false,
|
|
1764
|
+
writable: false
|
|
1765
|
+
});
|
|
1766
|
+
Object.defineProperty(play, "bindings", {
|
|
1767
|
+
value: bindings,
|
|
1768
|
+
enumerable: true,
|
|
1769
|
+
configurable: false,
|
|
1770
|
+
writable: false
|
|
1771
|
+
});
|
|
1772
|
+
const handle = createNamedPlayHandle(
|
|
1773
|
+
() => new DeeplineClient(),
|
|
1774
|
+
name
|
|
1775
|
+
);
|
|
1776
|
+
for (const key of [
|
|
1777
|
+
"name",
|
|
1778
|
+
"get",
|
|
1779
|
+
"runs",
|
|
1780
|
+
"versions",
|
|
1781
|
+
"publish",
|
|
1782
|
+
"run",
|
|
1783
|
+
"runSync"
|
|
1784
|
+
]) {
|
|
1785
|
+
Object.defineProperty(play, key, {
|
|
1786
|
+
value: handle[key],
|
|
1787
|
+
enumerable: false,
|
|
1788
|
+
configurable: false,
|
|
1789
|
+
writable: false
|
|
1790
|
+
});
|
|
1791
|
+
}
|
|
1792
|
+
return play;
|
|
1793
|
+
}
|
|
1794
|
+
var defineWorkflow = definePlay;
|
|
1795
|
+
function getDefinedPlayMetadata(value) {
|
|
1796
|
+
if (typeof value !== "function") {
|
|
1797
|
+
return null;
|
|
1798
|
+
}
|
|
1799
|
+
const metadata = value[PLAY_METADATA_SYMBOL];
|
|
1800
|
+
if (!metadata || typeof metadata !== "object") {
|
|
1801
|
+
return null;
|
|
1802
|
+
}
|
|
1803
|
+
const candidate = metadata;
|
|
1804
|
+
if (!candidate.name || typeof candidate.name !== "string") {
|
|
1805
|
+
return null;
|
|
1806
|
+
}
|
|
1807
|
+
return candidate;
|
|
1808
|
+
}
|
|
290
1809
|
export {
|
|
291
1810
|
AuthError,
|
|
292
1811
|
ConfigError,
|
|
1812
|
+
Deepline,
|
|
293
1813
|
DeeplineClient,
|
|
1814
|
+
DeeplineContext,
|
|
294
1815
|
DeeplineError,
|
|
295
1816
|
PROD_URL,
|
|
296
1817
|
RateLimitError,
|
|
297
|
-
|
|
1818
|
+
SDK_API_CONTRACT,
|
|
1819
|
+
SDK_VERSION,
|
|
1820
|
+
createToolCallResult,
|
|
1821
|
+
defineInput,
|
|
1822
|
+
definePlay,
|
|
1823
|
+
defineWorkflow,
|
|
1824
|
+
extractSummaryFields,
|
|
1825
|
+
getDefinedPlayMetadata,
|
|
1826
|
+
resolveConfig,
|
|
1827
|
+
steps,
|
|
1828
|
+
tryConvertToList,
|
|
1829
|
+
when,
|
|
1830
|
+
writeCsvOutputFile,
|
|
1831
|
+
writeJsonOutputFile
|
|
298
1832
|
};
|
|
299
1833
|
//# sourceMappingURL=index.mjs.map
|