deepline 0.1.31 → 0.1.33

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.
@@ -1,47 +1,43 @@
1
1
  /**
2
- * Configuration resolution for the Deepline SDK.
2
+ * Configuration resolution for the Deepline SDK and SDK CLI.
3
3
  *
4
- * The SDK resolves configuration from multiple sources in a strict priority order,
5
- * making it zero-config for common setups while allowing full override control.
4
+ * The public SDK CLI env contract is deliberately small:
6
5
  *
7
- * ## Resolution order
6
+ * - `DEEPLINE_HOST_URL`: Deepline API/app host, for example `https://code.deepline.com`
7
+ * - `DEEPLINE_API_KEY`: API key for that host and workspace
8
8
  *
9
- * ### Base URL
10
- * 1. `options.baseUrl` (explicit constructor argument)
11
- * 2. `DEEPLINE_ORIGIN_URL` environment variable
12
- * 3. `DEEPLINE_API_BASE_URL` environment variable
13
- * 4. Nearest checkout-local `.env.deepline`
14
- * 5. Nearest checkout-local profile file (`.env.deepline.prod`, etc.)
15
- * 6. Nearest checkout-local app env file (`.env.prod`, `.env.staging`, `.env.local`, `.env`)
16
- * 7. Nearest checkout-local `.env.worktree`
17
- * 8. `DEEPLINE_ORIGIN_URL` from the production host auth file
18
- * 9. Production fallback: `https://code.deepline.com`
9
+ * The CLI also stores the same two keys in per-host files under
10
+ * `~/.local/deepline/<host-slug>/.env`, created by `deepline auth register`.
19
11
  *
20
- * ### API Key
21
- * 1. `options.apiKey` (explicit constructor argument)
22
- * 2. `DEEPLINE_API_KEY` environment variable
23
- * 3. `DEEPLINE_API_KEY` from `~/.local/deepline/<host-slug>/.env` (SDK CLI config)
12
+ * Resolution order:
24
13
  *
25
- * If no API key is found from any source, a {@link ConfigError} is thrown.
14
+ * Base URL:
15
+ * 1. `options.baseUrl`
16
+ * 2. `DEEPLINE_HOST_URL`
17
+ * 3. nearest project `.env.deepline`
18
+ * 4. production host auth file
19
+ * 5. production fallback: `https://code.deepline.com`
26
20
  *
27
- * ## CLI env file format
21
+ * API key:
22
+ * 1. `options.apiKey`
23
+ * 2. `DEEPLINE_API_KEY`
24
+ * 3. nearest project `.env.deepline`
25
+ * 4. host auth file for the resolved base URL
28
26
  *
29
- * The CLI stores credentials in simple `KEY=VALUE` files (one per line).
30
- * These are created by `deepline auth register`:
31
- *
32
- * ```
33
- * ~/.local/deepline/code-deepline-com/.env # production
34
- * ~/.local/deepline/localhost-3000/.env # local dev
35
- * ```
27
+ * App/runtime env files such as `.env`, `.env.local`, and `.env.worktree` do
28
+ * not route the SDK CLI. Put CLI routing in `.env.deepline`.
36
29
  *
37
30
  * @module
38
31
  */
39
- import { readFileSync, existsSync, mkdirSync, writeFileSync } from 'node:fs';
32
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
40
33
  import { homedir } from 'node:os';
41
34
  import { dirname, join, resolve } from 'node:path';
42
35
  import type { DeeplineClientOptions, ResolvedConfig } from './types.js';
43
36
  import { ConfigError } from './errors.js';
44
37
 
38
+ export const HOST_URL_ENV = 'DEEPLINE_HOST_URL';
39
+ export const API_KEY_ENV = 'DEEPLINE_API_KEY';
40
+
45
41
  /** Production API base URL. */
46
42
  const PROD_URL = 'https://code.deepline.com';
47
43
 
@@ -51,26 +47,13 @@ const DEFAULT_TIMEOUT = 60_000;
51
47
  /** Default retry count for transient failures. */
52
48
  const DEFAULT_MAX_RETRIES = 3;
53
49
 
54
- const ACTIVE_DEEPLINE_ENV_FILE = '.env.deepline';
55
-
56
- function isProdBaseUrl(baseUrl: string): boolean {
57
- return baseUrl.trim().replace(/\/$/, '') === PROD_URL;
58
- }
50
+ const PROJECT_DEEPLINE_ENV_FILE = '.env.deepline';
59
51
 
60
- function profileNameForBaseUrl(baseUrl: string): 'prod' | 'dev' {
61
- return isProdBaseUrl(baseUrl) ? 'prod' : 'dev';
62
- }
63
-
64
- function projectEnvStartDir(): string {
65
- return process.env.DEEPLINE_PROJECT_ENV_DIR?.trim() || process.cwd();
66
- }
52
+ type EnvValues = Record<string, string>;
67
53
 
68
54
  /**
69
55
  * Convert a base URL to a filesystem-safe slug for per-host config storage.
70
56
  *
71
- * @param baseUrl - Full URL (e.g. `"http://localhost:3000"`)
72
- * @returns Slug like `"localhost-3000"` or `"code-deepline-com"`
73
- *
74
57
  * @example
75
58
  * ```typescript
76
59
  * baseUrlSlug('http://localhost:3000') // "localhost-3000"
@@ -86,7 +69,7 @@ function baseUrlSlug(baseUrl: string): string {
86
69
  return 'unknown';
87
70
  }
88
71
  const host = url.hostname || 'unknown';
89
- const port = url.port ? parseInt(url.port, 10) : null;
72
+ const port = url.port ? Number.parseInt(url.port, 10) : null;
90
73
  let slug = host.replace(/[^a-zA-Z0-9]/g, '-');
91
74
  if (port && port !== 80 && port !== 443) {
92
75
  slug = `${slug}-${port}`;
@@ -96,20 +79,10 @@ function baseUrlSlug(baseUrl: string): string {
96
79
 
97
80
  /**
98
81
  * Parse a simple `KEY=VALUE` env file. Handles `#` comments and quoted values.
99
- *
100
- * @param filePath - Absolute path to the env file
101
- * @returns Key-value pairs; empty object if file doesn't exist
102
- *
103
- * @example
104
- * ```typescript
105
- * // File: DEEPLINE_API_KEY="dl_test_abc123"
106
- * const env = parseEnvFile('/path/to/.env');
107
- * console.log(env.DEEPLINE_API_KEY); // "dl_test_abc123"
108
- * ```
109
82
  */
110
- function parseEnvFile(filePath: string): Record<string, string> {
83
+ function parseEnvFile(filePath: string): EnvValues {
111
84
  if (!existsSync(filePath)) return {};
112
- const env: Record<string, string> = {};
85
+ const env: EnvValues = {};
113
86
  const content = readFileSync(filePath, 'utf-8');
114
87
  for (const line of content.split(/\r?\n/)) {
115
88
  const trimmed = line.trim();
@@ -118,7 +91,6 @@ function parseEnvFile(filePath: string): Record<string, string> {
118
91
  if (eqIndex < 0) continue;
119
92
  const key = trimmed.slice(0, eqIndex).trim();
120
93
  let value = trimmed.slice(eqIndex + 1).trim();
121
- // Strip surrounding quotes
122
94
  if (
123
95
  value.length >= 2 &&
124
96
  ((value.startsWith('"') && value.endsWith('"')) ||
@@ -134,223 +106,136 @@ function parseEnvFile(filePath: string): Record<string, string> {
134
106
  }
135
107
 
136
108
  function findNearestEnvFile(
137
- names: string[],
109
+ name: string,
138
110
  startDir: string = process.cwd(),
139
111
  ): string | null {
140
112
  let current = resolve(startDir);
141
113
  while (true) {
142
- for (const name of names) {
143
- const filePath = join(current, name);
144
- if (existsSync(filePath)) return filePath;
145
- }
114
+ const filePath = join(current, name);
115
+ if (existsSync(filePath)) return filePath;
146
116
  const parent = dirname(current);
147
117
  if (parent === current) return null;
148
118
  current = parent;
149
119
  }
150
120
  }
151
121
 
152
- function findNearestEnv(names: string[], startDir: string = process.cwd()): Record<string, string> {
153
- const filePath = findNearestEnvFile(names, startDir);
122
+ function loadProjectDeeplineEnv(startDir = process.cwd()): EnvValues {
123
+ const filePath = findNearestEnvFile(PROJECT_DEEPLINE_ENV_FILE, startDir);
154
124
  return filePath ? parseEnvFile(filePath) : {};
155
125
  }
156
126
 
157
- function findNearestWorktreeEnv(startDir: string = process.cwd()): Record<string, string> {
158
- return findNearestEnv(['.env.worktree'], startDir);
159
- }
160
-
161
- function resolveProfileEnvFileNames(): string[] {
162
- const explicitProfile =
163
- process.env.DEEPLINE_ENV_PROFILE?.trim() ||
164
- process.env.DEEPLINE_PROFILE?.trim() ||
165
- '';
166
- const names: string[] = [];
167
- if (explicitProfile) names.push(`.env.deepline.${explicitProfile}`);
168
- const nodeEnv = process.env.NODE_ENV?.trim();
169
- if (nodeEnv === 'production') names.push('.env.deepline.prod');
170
- else if (nodeEnv === 'staging') names.push('.env.deepline.staging');
171
- names.push(ACTIVE_DEEPLINE_ENV_FILE);
172
- return names;
173
- }
174
-
175
- function resolveProjectAppEnvFileNames(): string[] {
176
- const nodeEnv = process.env.NODE_ENV?.trim();
177
- const names: string[] = [];
178
- if (nodeEnv === 'production') names.push('.env.prod');
179
- if (nodeEnv === 'staging') names.push('.env.staging');
180
- names.push('.env.local', '.env');
181
- return names;
182
- }
183
-
184
- function resolveBaseUrlFromEnvValues(env: Record<string, string>): string {
185
- return (
186
- env.DEEPLINE_ORIGIN_URL?.trim() ||
187
- env.DEEPLINE_API_BASE_URL?.trim() ||
188
- ''
189
- );
190
- }
191
-
192
- function loadProjectDeeplineEnv(): Record<string, string> {
193
- return findNearestEnv(resolveProfileEnvFileNames(), projectEnvStartDir());
194
- }
195
-
196
- function loadProjectAppEnv(): Record<string, string> {
197
- return findNearestEnv(resolveProjectAppEnvFileNames(), projectEnvStartDir());
198
- }
199
-
200
- function normalizeWorktreeBaseUrl(baseUrl: string, worktreeEnv = findNearestWorktreeEnv()): string {
201
- const trimmed = baseUrl.trim().replace(/\/$/, '');
202
- if (!trimmed) return trimmed;
127
+ function normalizeBaseUrl(baseUrl: string): string {
128
+ const trimmed = baseUrl.trim().replace(/\/+$/, '');
129
+ if (!trimmed) return '';
203
130
  try {
204
131
  const parsed = new URL(trimmed);
205
- if (parsed.hostname.endsWith('.localhost') && parsed.port === '1355') {
206
- const port = worktreeEnv.WORKTREE_APP_PORT || worktreeEnv.PORT;
207
- if (port) return `${parsed.protocol}//localhost:${port}`;
132
+ if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') {
133
+ return '';
208
134
  }
209
- } catch {}
210
- return trimmed;
135
+ return parsed.toString().replace(/\/+$/, '');
136
+ } catch {
137
+ return '';
138
+ }
211
139
  }
212
140
 
213
- function resolveWorktreeBaseUrl(): string {
214
- const worktreeEnv = findNearestWorktreeEnv();
215
- const declared =
216
- worktreeEnv.DEEPLINE_API_BASE_URL ||
217
- worktreeEnv.WORKTREE_PUBLIC_APP_URL ||
218
- worktreeEnv.APP_URL ||
219
- '';
220
- if (declared) return normalizeWorktreeBaseUrl(declared, worktreeEnv);
221
- const port = worktreeEnv.WORKTREE_APP_PORT || worktreeEnv.PORT || '';
222
- return port ? `http://localhost:${port}` : '';
141
+ function firstNonEmpty(...values: Array<string | undefined | null>): string {
142
+ for (const value of values) {
143
+ const trimmed = value?.trim();
144
+ if (trimmed) return trimmed;
145
+ }
146
+ return '';
223
147
  }
224
148
 
225
- /**
226
- * Load the SDK CLI env file for a specific Deepline host.
227
- *
228
- * @returns Key-value pairs from `~/.local/deepline/<host-slug>/.env`
229
- */
230
- function sdkCliEnvFilePath(baseUrl: string): string {
149
+ function sdkCliConfigDir(baseUrl: string): string {
231
150
  const home = process.env.HOME?.trim() || homedir();
232
- return join(home, '.local', 'deepline', baseUrlSlug(baseUrl || PROD_URL), '.env');
151
+ return join(home, '.local', 'deepline', baseUrlSlug(baseUrl || PROD_URL));
152
+ }
153
+
154
+ function sdkCliEnvFilePath(baseUrl: string): string {
155
+ return join(sdkCliConfigDir(baseUrl), '.env');
233
156
  }
234
157
 
235
- function loadCliEnv(baseUrl = PROD_URL): Record<string, string> {
236
- const envPath = sdkCliEnvFilePath(baseUrl);
237
- return parseEnvFile(envPath);
158
+ function loadCliEnv(baseUrl = PROD_URL): EnvValues {
159
+ return parseEnvFile(sdkCliEnvFilePath(baseUrl));
160
+ }
161
+
162
+ export function hostConfigDirPath(baseUrl: string): string {
163
+ return sdkCliConfigDir(baseUrl);
238
164
  }
239
165
 
240
166
  export function hostEnvFilePath(baseUrl: string): string {
241
167
  return sdkCliEnvFilePath(baseUrl);
242
168
  }
243
169
 
244
- export function saveHostEnvValues(baseUrl: string, values: Record<string, string>): void {
170
+ export function saveHostEnvValues(baseUrl: string, values: EnvValues): void {
245
171
  const filePath = sdkCliEnvFilePath(baseUrl);
246
172
  const dir = dirname(filePath);
247
173
  if (!existsSync(dir)) {
248
174
  mkdirSync(dir, { recursive: true });
249
175
  }
250
176
 
251
- const existing = existsSync(filePath) ? parseEnvFile(filePath) : {};
177
+ const existing = parseEnvFile(filePath);
252
178
  const merged = { ...existing, ...values };
179
+ const allowedKeys = new Set([HOST_URL_ENV, API_KEY_ENV]);
253
180
  const lines = Object.entries(merged)
254
- .filter(([, value]) => value !== '')
181
+ .filter(([key, value]) => allowedKeys.has(key) && value !== '')
255
182
  .map(([key, value]) => `${key}=${value}`);
256
183
  writeFileSync(filePath, `${lines.join('\n')}\n`, 'utf-8');
257
184
  }
258
185
 
259
- /**
260
- * Load the production SDK CLI env file.
261
- *
262
- * This gives `deepline` a stable production default without sharing credentials
263
- * with local/worktree hosts.
264
- */
265
- function loadGlobalCliEnv(): Record<string, string> {
186
+ function loadGlobalCliEnv(): EnvValues {
266
187
  return loadCliEnv(PROD_URL);
267
188
  }
268
189
 
269
- /**
270
- * Check if a URL points to a local development server.
271
- *
272
- * @example
273
- * ```typescript
274
- * isLocalhost('http://localhost:3000') // true
275
- * isLocalhost('http://127.0.0.1:3000') // true
276
- * isLocalhost('https://code.deepline.com') // false
277
- * ```
278
- */
279
190
  /**
280
191
  * Auto-detect the best base URL when none is explicitly provided.
281
- *
282
- * Checks environment variables first, then checkout-local worktree config,
283
- * then SDK CLI config, before falling back to production.
284
192
  */
285
193
  function autoDetectBaseUrl(): string {
286
- const envOrigin = process.env.DEEPLINE_ORIGIN_URL?.trim();
287
- if (envOrigin) return normalizeWorktreeBaseUrl(envOrigin);
288
-
289
- const envBase = process.env.DEEPLINE_API_BASE_URL?.trim();
290
- if (envBase) return normalizeWorktreeBaseUrl(envBase);
291
-
292
- const projectDeeplineBaseUrl = resolveBaseUrlFromEnvValues(loadProjectDeeplineEnv());
293
- if (projectDeeplineBaseUrl) return normalizeWorktreeBaseUrl(projectDeeplineBaseUrl);
294
-
295
- const projectAppBaseUrl = resolveBaseUrlFromEnvValues(loadProjectAppEnv());
296
- if (projectAppBaseUrl) return normalizeWorktreeBaseUrl(projectAppBaseUrl);
297
-
298
- const worktreeBaseUrl = resolveWorktreeBaseUrl();
299
- if (worktreeBaseUrl) return worktreeBaseUrl;
300
-
194
+ const projectEnv = loadProjectDeeplineEnv();
301
195
  const globalEnv = loadGlobalCliEnv();
302
- const globalOrigin = globalEnv.DEEPLINE_ORIGIN_URL?.trim();
303
- if (globalOrigin) return normalizeWorktreeBaseUrl(globalOrigin);
196
+ return (
197
+ normalizeBaseUrl(process.env[HOST_URL_ENV] ?? '') ||
198
+ normalizeBaseUrl(projectEnv[HOST_URL_ENV] ?? '') ||
199
+ normalizeBaseUrl(globalEnv[HOST_URL_ENV] ?? '') ||
200
+ PROD_URL
201
+ );
202
+ }
304
203
 
305
- return PROD_URL;
204
+ export function resolveApiKeyForBaseUrl(
205
+ baseUrl: string,
206
+ explicitApiKey?: string | null,
207
+ ): string {
208
+ const normalizedBaseUrl = normalizeBaseUrl(baseUrl);
209
+ const projectEnv = loadProjectDeeplineEnv();
210
+ const cliEnv = loadCliEnv(normalizedBaseUrl || baseUrl);
211
+ const projectBaseUrl = normalizeBaseUrl(projectEnv[HOST_URL_ENV] ?? '');
212
+ const projectKeyApplies = projectBaseUrl === normalizedBaseUrl;
213
+ return firstNonEmpty(
214
+ explicitApiKey,
215
+ process.env[API_KEY_ENV],
216
+ projectKeyApplies ? projectEnv[API_KEY_ENV] : '',
217
+ cliEnv[API_KEY_ENV],
218
+ );
306
219
  }
307
220
 
308
221
  /**
309
- * Resolve SDK configuration from all available sources.
310
- *
311
- * Merges explicit options, environment variables, and CLI-managed config files
312
- * into a fully validated {@link ResolvedConfig}. See the module-level docs for
313
- * the complete resolution order.
314
- *
315
- * @param options - Optional overrides (highest priority)
316
- * @returns Fully resolved configuration with all fields populated
317
- * @throws {@link ConfigError} if no API key can be found from any source
318
- *
319
- * @example
320
- * ```typescript
321
- * import { resolveConfig } from 'deepline';
322
- *
323
- * // Auto-resolve everything:
324
- * const config = resolveConfig();
325
- * console.log(config.baseUrl); // "http://localhost:3000" or "https://code.deepline.com"
326
- *
327
- * // Override specific values:
328
- * const config2 = resolveConfig({ baseUrl: 'http://localhost:4000', timeout: 10_000 });
329
- * ```
222
+ * Resolve SDK configuration from the public SDK CLI env contract.
330
223
  */
331
224
  export function resolveConfig(options?: DeeplineClientOptions): ResolvedConfig {
332
- // Resolve base URL
333
- const requestedBaseUrl =
334
- options?.baseUrl?.trim() ||
335
- autoDetectBaseUrl();
336
- const baseUrl = normalizeWorktreeBaseUrl(requestedBaseUrl);
337
-
338
- const cliEnv = loadCliEnv(baseUrl);
339
- const projectDeeplineEnv = loadProjectDeeplineEnv();
340
- const projectAppEnv = loadProjectAppEnv();
225
+ const baseUrl = normalizeBaseUrl(
226
+ options?.baseUrl?.trim() || autoDetectBaseUrl(),
227
+ );
228
+ if (!baseUrl) {
229
+ throw new ConfigError(
230
+ `Invalid ${HOST_URL_ENV}. Expected an http(s) URL such as https://code.deepline.com.`,
231
+ );
232
+ }
341
233
 
342
- // Resolve API key: option > env var > SDK CLI env
343
- const apiKey =
344
- options?.apiKey?.trim() ||
345
- process.env.DEEPLINE_API_KEY?.trim() ||
346
- projectDeeplineEnv.DEEPLINE_API_KEY ||
347
- projectAppEnv.DEEPLINE_API_KEY ||
348
- cliEnv.DEEPLINE_API_KEY ||
349
- '';
234
+ const apiKey = resolveApiKeyForBaseUrl(baseUrl, options?.apiKey);
350
235
 
351
236
  if (!apiKey) {
352
237
  throw new ConfigError(
353
- `No API key found. Set DEEPLINE_API_KEY env var, pass apiKey option, or run: deepline auth register`,
238
+ `No API key found. Set ${API_KEY_ENV}, add it to .env.deepline, or run: deepline auth register`,
354
239
  );
355
240
  }
356
241
 
@@ -362,34 +247,25 @@ export function resolveConfig(options?: DeeplineClientOptions): ResolvedConfig {
362
247
  };
363
248
  }
364
249
 
365
- function mergeEnvFile(filePath: string, values: Record<string, string>): void {
366
- const existing = existsSync(filePath) ? parseEnvFile(filePath) : {};
250
+ function mergeProjectEnvFile(filePath: string, values: EnvValues): void {
251
+ const existing = parseEnvFile(filePath);
367
252
  const merged = { ...existing, ...values };
368
253
  const dir = dirname(filePath);
369
254
  if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
255
+ const allowedKeys = new Set([HOST_URL_ENV, API_KEY_ENV]);
370
256
  const lines = Object.entries(merged)
371
- .filter(([, value]) => value !== '')
257
+ .filter(([key, value]) => allowedKeys.has(key) && value !== '')
372
258
  .map(([key, value]) => `${key}=${value}`);
373
259
  writeFileSync(filePath, `${lines.join('\n')}\n`, 'utf-8');
374
260
  }
375
261
 
376
262
  export function saveProjectDeeplineEnvValues(
377
- baseUrl: string,
378
- values: Record<string, string>,
379
- startDir: string = projectEnvStartDir(),
263
+ values: EnvValues,
264
+ startDir: string = process.cwd(),
380
265
  ): string[] {
381
- const root = resolve(startDir);
382
- const profile = profileNameForBaseUrl(baseUrl);
383
- const files = [
384
- join(root, ACTIVE_DEEPLINE_ENV_FILE),
385
- join(root, `.env.deepline.${profile}`),
386
- ];
387
- if (profile === 'dev') files.push(join(root, '.env'));
388
-
389
- for (const filePath of files) {
390
- mergeEnvFile(filePath, values);
391
- }
392
- return files;
266
+ const filePath = join(resolve(startDir), PROJECT_DEEPLINE_ENV_FILE);
267
+ mergeProjectEnvFile(filePath, values);
268
+ return [filePath];
393
269
  }
394
270
 
395
271
  export {
@@ -30,7 +30,7 @@ import type { PlayCompilerManifest } from '../../shared_libs/plays/compiler-mani
30
30
  export interface DeeplineClientOptions {
31
31
  /** API key. Overrides `DEEPLINE_API_KEY` env var and CLI-stored keys. */
32
32
  apiKey?: string;
33
- /** Base URL of the Deepline API. Overrides `DEEPLINE_ORIGIN_URL` / `DEEPLINE_API_BASE_URL`. */
33
+ /** Base URL of the Deepline API. Overrides `DEEPLINE_HOST_URL`. */
34
34
  baseUrl?: string;
35
35
  /** Per-request timeout in milliseconds. Default: `60_000` (60 seconds). */
36
36
  timeout?: number;
@@ -251,6 +251,8 @@ export interface PlayStatus {
251
251
  name?: string;
252
252
  /** Alias for `name` used by run/result APIs. */
253
253
  playName?: string;
254
+ /** Dashboard URL for inspecting the play and its run output in the app. */
255
+ dashboardUrl?: string;
254
256
  /** Product-level play-run state. */
255
257
  status:
256
258
  | 'queued'
@@ -1,2 +1,2 @@
1
- export const SDK_VERSION = "0.1.31";
2
- export const SDK_API_CONTRACT = "2026-05-runs-export-v3";
1
+ export const SDK_VERSION = "0.1.33";
2
+ export const SDK_API_CONTRACT = "2026-05-host-env-generic-play-input-flags";
@@ -116,7 +116,7 @@ export type PlaySchedulerRunHandle = {
116
116
  * Stream live progress events. Implementations may use SSE, polling, etc.
117
117
  * The contract: yields events in order until terminal status.
118
118
  */
119
- observe(): AsyncIterable<PlaySchedulerProgressEvent>;
119
+ observe(options?: { signal?: AbortSignal }): AsyncIterable<PlaySchedulerProgressEvent>;
120
120
  /** Cooperatively cancel the run. */
121
121
  cancel(): Promise<void>;
122
122
  /** Inject an external event (HITL, webhook). */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "deepline",
3
- "version": "0.1.31",
3
+ "version": "0.1.33",
4
4
  "description": "Deepline SDK + CLI — B2B data enrichment powered by durable cloud execution",
5
5
  "license": "MIT",
6
6
  "repository": {