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
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { SDK_API_CONTRACT, SDK_VERSION } from "./version.js";
|
|
2
|
+
|
|
3
|
+
export type SdkCompatibilityStatus =
|
|
4
|
+
| "current"
|
|
5
|
+
| "update_available"
|
|
6
|
+
| "deprecated"
|
|
7
|
+
| "unsupported";
|
|
8
|
+
|
|
9
|
+
export type SdkCompatibilityResponse = {
|
|
10
|
+
ok: boolean;
|
|
11
|
+
status: SdkCompatibilityStatus;
|
|
12
|
+
current: string | null;
|
|
13
|
+
latest: string;
|
|
14
|
+
minimum_supported: string;
|
|
15
|
+
deprecated_below: string;
|
|
16
|
+
api_contract: string;
|
|
17
|
+
update_available: boolean;
|
|
18
|
+
update_required: boolean;
|
|
19
|
+
message: string;
|
|
20
|
+
update_command: string;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const CHECK_TIMEOUT_MS = 2_000;
|
|
24
|
+
|
|
25
|
+
function shouldSkipCompatibilityCheck(): boolean {
|
|
26
|
+
const value = process.env.DEEPLINE_SKIP_SDK_COMPAT_CHECK?.trim().toLowerCase();
|
|
27
|
+
return value === "1" || value === "true" || value === "yes";
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export async function checkSdkCompatibility(baseUrl: string): Promise<{
|
|
31
|
+
response: SdkCompatibilityResponse | null;
|
|
32
|
+
error: Error | null;
|
|
33
|
+
}> {
|
|
34
|
+
if (shouldSkipCompatibilityCheck()) {
|
|
35
|
+
return { response: null, error: null };
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const controller = new AbortController();
|
|
39
|
+
const timeout = setTimeout(() => controller.abort(), CHECK_TIMEOUT_MS);
|
|
40
|
+
try {
|
|
41
|
+
const url = new URL("/api/v2/sdk/compat", baseUrl);
|
|
42
|
+
url.searchParams.set("version", SDK_VERSION);
|
|
43
|
+
const response = await fetch(url, {
|
|
44
|
+
method: "GET",
|
|
45
|
+
headers: {
|
|
46
|
+
"User-Agent": `deepline-ts-sdk/${SDK_VERSION}`,
|
|
47
|
+
"X-Deepline-SDK-Version": SDK_VERSION,
|
|
48
|
+
"X-Deepline-API-Contract": SDK_API_CONTRACT,
|
|
49
|
+
},
|
|
50
|
+
signal: controller.signal,
|
|
51
|
+
});
|
|
52
|
+
const data = (await response.json().catch(() => null)) as
|
|
53
|
+
| SdkCompatibilityResponse
|
|
54
|
+
| null;
|
|
55
|
+
return { response: data, error: null };
|
|
56
|
+
} catch (error) {
|
|
57
|
+
return {
|
|
58
|
+
response: null,
|
|
59
|
+
error: error instanceof Error ? error : new Error(String(error)),
|
|
60
|
+
};
|
|
61
|
+
} finally {
|
|
62
|
+
clearTimeout(timeout);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export async function enforceSdkCompatibility(baseUrl: string): Promise<void> {
|
|
67
|
+
const { response, error } = await checkSdkCompatibility(baseUrl);
|
|
68
|
+
if (error || !response) {
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
if (response.update_required) {
|
|
72
|
+
throw new Error(response.message);
|
|
73
|
+
}
|
|
74
|
+
if (response.status === "deprecated" || response.status === "update_available") {
|
|
75
|
+
process.stderr.write(`${response.message}\n`);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration resolution for the Deepline SDK.
|
|
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.
|
|
6
|
+
*
|
|
7
|
+
* ## Resolution order
|
|
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.worktree`
|
|
14
|
+
* 5. `DEEPLINE_ORIGIN_URL` from the production host auth file
|
|
15
|
+
* 6. Production fallback: `https://code.deepline.com`
|
|
16
|
+
*
|
|
17
|
+
* ### API Key
|
|
18
|
+
* 1. `options.apiKey` (explicit constructor argument)
|
|
19
|
+
* 2. `DEEPLINE_API_KEY` environment variable
|
|
20
|
+
* 3. `DEEPLINE_API_KEY` from `~/.local/deepline/<host-slug>/.env` (SDK CLI config)
|
|
21
|
+
*
|
|
22
|
+
* If no API key is found from any source, a {@link ConfigError} is thrown.
|
|
23
|
+
*
|
|
24
|
+
* ## CLI env file format
|
|
25
|
+
*
|
|
26
|
+
* The CLI stores credentials in simple `KEY=VALUE` files (one per line).
|
|
27
|
+
* These are created by `deepline auth register`:
|
|
28
|
+
*
|
|
29
|
+
* ```
|
|
30
|
+
* ~/.local/deepline/code-deepline-com/.env # production
|
|
31
|
+
* ~/.local/deepline/localhost-3000/.env # local dev
|
|
32
|
+
* ```
|
|
33
|
+
*
|
|
34
|
+
* @module
|
|
35
|
+
*/
|
|
36
|
+
import { readFileSync, existsSync, mkdirSync, writeFileSync } from 'node:fs';
|
|
37
|
+
import { homedir } from 'node:os';
|
|
38
|
+
import { dirname, join, resolve } from 'node:path';
|
|
39
|
+
import type { DeeplineClientOptions, ResolvedConfig } from './types.js';
|
|
40
|
+
import { ConfigError } from './errors.js';
|
|
41
|
+
|
|
42
|
+
/** Production API base URL. */
|
|
43
|
+
const PROD_URL = 'https://code.deepline.com';
|
|
44
|
+
|
|
45
|
+
/** Default request timeout: 60 seconds. */
|
|
46
|
+
const DEFAULT_TIMEOUT = 60_000;
|
|
47
|
+
|
|
48
|
+
/** Default retry count for transient failures. */
|
|
49
|
+
const DEFAULT_MAX_RETRIES = 3;
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Convert a base URL to a filesystem-safe slug for per-host config storage.
|
|
53
|
+
*
|
|
54
|
+
* @param baseUrl - Full URL (e.g. `"http://localhost:3000"`)
|
|
55
|
+
* @returns Slug like `"localhost-3000"` or `"code-deepline-com"`
|
|
56
|
+
*
|
|
57
|
+
* @example
|
|
58
|
+
* ```typescript
|
|
59
|
+
* baseUrlSlug('http://localhost:3000') // "localhost-3000"
|
|
60
|
+
* baseUrlSlug('https://code.deepline.com') // "code-deepline-com"
|
|
61
|
+
* baseUrlSlug('https://example.com:8080') // "example-com-8080"
|
|
62
|
+
* ```
|
|
63
|
+
*/
|
|
64
|
+
function baseUrlSlug(baseUrl: string): string {
|
|
65
|
+
let url: URL;
|
|
66
|
+
try {
|
|
67
|
+
url = new URL(baseUrl);
|
|
68
|
+
} catch {
|
|
69
|
+
return 'unknown';
|
|
70
|
+
}
|
|
71
|
+
const host = url.hostname || 'unknown';
|
|
72
|
+
const port = url.port ? parseInt(url.port, 10) : null;
|
|
73
|
+
let slug = host.replace(/[^a-zA-Z0-9]/g, '-');
|
|
74
|
+
if (port && port !== 80 && port !== 443) {
|
|
75
|
+
slug = `${slug}-${port}`;
|
|
76
|
+
}
|
|
77
|
+
return slug.toLowerCase().replace(/^-+|-+$/g, '');
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Parse a simple `KEY=VALUE` env file. Handles `#` comments and quoted values.
|
|
82
|
+
*
|
|
83
|
+
* @param filePath - Absolute path to the env file
|
|
84
|
+
* @returns Key-value pairs; empty object if file doesn't exist
|
|
85
|
+
*
|
|
86
|
+
* @example
|
|
87
|
+
* ```typescript
|
|
88
|
+
* // File: DEEPLINE_API_KEY="dl_test_abc123"
|
|
89
|
+
* const env = parseEnvFile('/path/to/.env');
|
|
90
|
+
* console.log(env.DEEPLINE_API_KEY); // "dl_test_abc123"
|
|
91
|
+
* ```
|
|
92
|
+
*/
|
|
93
|
+
function parseEnvFile(filePath: string): Record<string, string> {
|
|
94
|
+
if (!existsSync(filePath)) return {};
|
|
95
|
+
const env: Record<string, string> = {};
|
|
96
|
+
const content = readFileSync(filePath, 'utf-8');
|
|
97
|
+
for (const line of content.split(/\r?\n/)) {
|
|
98
|
+
const trimmed = line.trim();
|
|
99
|
+
if (!trimmed || trimmed.startsWith('#')) continue;
|
|
100
|
+
const eqIndex = trimmed.indexOf('=');
|
|
101
|
+
if (eqIndex < 0) continue;
|
|
102
|
+
const key = trimmed.slice(0, eqIndex).trim();
|
|
103
|
+
let value = trimmed.slice(eqIndex + 1).trim();
|
|
104
|
+
// Strip surrounding quotes
|
|
105
|
+
if (
|
|
106
|
+
value.length >= 2 &&
|
|
107
|
+
((value.startsWith('"') && value.endsWith('"')) ||
|
|
108
|
+
(value.startsWith("'") && value.endsWith("'")))
|
|
109
|
+
) {
|
|
110
|
+
value = value.slice(1, -1);
|
|
111
|
+
}
|
|
112
|
+
if (key && value) {
|
|
113
|
+
env[key] = value;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
return env;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function findNearestWorktreeEnv(startDir: string = process.cwd()): Record<string, string> {
|
|
120
|
+
let current = resolve(startDir);
|
|
121
|
+
while (true) {
|
|
122
|
+
const values = parseEnvFile(join(current, '.env.worktree'));
|
|
123
|
+
if (Object.keys(values).length > 0) return values;
|
|
124
|
+
const parent = dirname(current);
|
|
125
|
+
if (parent === current) return {};
|
|
126
|
+
current = parent;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function normalizeWorktreeBaseUrl(baseUrl: string, worktreeEnv = findNearestWorktreeEnv()): string {
|
|
131
|
+
const trimmed = baseUrl.trim().replace(/\/$/, '');
|
|
132
|
+
if (!trimmed) return trimmed;
|
|
133
|
+
try {
|
|
134
|
+
const parsed = new URL(trimmed);
|
|
135
|
+
if (parsed.hostname.endsWith('.localhost') && parsed.port === '1355') {
|
|
136
|
+
const port = worktreeEnv.WORKTREE_APP_PORT || worktreeEnv.PORT;
|
|
137
|
+
if (port) return `${parsed.protocol}//localhost:${port}`;
|
|
138
|
+
}
|
|
139
|
+
} catch {}
|
|
140
|
+
return trimmed;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function resolveWorktreeBaseUrl(): string {
|
|
144
|
+
const worktreeEnv = findNearestWorktreeEnv();
|
|
145
|
+
const declared =
|
|
146
|
+
worktreeEnv.DEEPLINE_API_BASE_URL ||
|
|
147
|
+
worktreeEnv.WORKTREE_PUBLIC_APP_URL ||
|
|
148
|
+
worktreeEnv.APP_URL ||
|
|
149
|
+
'';
|
|
150
|
+
if (declared) return normalizeWorktreeBaseUrl(declared, worktreeEnv);
|
|
151
|
+
const port = worktreeEnv.WORKTREE_APP_PORT || worktreeEnv.PORT || '';
|
|
152
|
+
return port ? `http://localhost:${port}` : '';
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Load the SDK CLI env file for a specific Deepline host.
|
|
157
|
+
*
|
|
158
|
+
* @returns Key-value pairs from `~/.local/deepline/<host-slug>/.env`
|
|
159
|
+
*/
|
|
160
|
+
function sdkCliEnvFilePath(baseUrl: string): string {
|
|
161
|
+
const home = process.env.HOME?.trim() || homedir();
|
|
162
|
+
return join(home, '.local', 'deepline', baseUrlSlug(baseUrl || PROD_URL), '.env');
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function loadCliEnv(baseUrl = PROD_URL): Record<string, string> {
|
|
166
|
+
const envPath = sdkCliEnvFilePath(baseUrl);
|
|
167
|
+
return parseEnvFile(envPath);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
export function hostEnvFilePath(baseUrl: string): string {
|
|
171
|
+
return sdkCliEnvFilePath(baseUrl);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
export function saveHostEnvValues(baseUrl: string, values: Record<string, string>): void {
|
|
175
|
+
const filePath = sdkCliEnvFilePath(baseUrl);
|
|
176
|
+
const dir = dirname(filePath);
|
|
177
|
+
if (!existsSync(dir)) {
|
|
178
|
+
mkdirSync(dir, { recursive: true });
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
const existing = existsSync(filePath) ? parseEnvFile(filePath) : {};
|
|
182
|
+
const merged = { ...existing, ...values };
|
|
183
|
+
const lines = Object.entries(merged)
|
|
184
|
+
.filter(([, value]) => value !== '')
|
|
185
|
+
.map(([key, value]) => `${key}=${value}`);
|
|
186
|
+
writeFileSync(filePath, `${lines.join('\n')}\n`, 'utf-8');
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Load the production SDK CLI env file.
|
|
191
|
+
*
|
|
192
|
+
* This gives `deepline` a stable production default without sharing credentials
|
|
193
|
+
* with local/worktree hosts.
|
|
194
|
+
*/
|
|
195
|
+
function loadGlobalCliEnv(): Record<string, string> {
|
|
196
|
+
return loadCliEnv(PROD_URL);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Check if a URL points to a local development server.
|
|
201
|
+
*
|
|
202
|
+
* @example
|
|
203
|
+
* ```typescript
|
|
204
|
+
* isLocalhost('http://localhost:3000') // true
|
|
205
|
+
* isLocalhost('http://127.0.0.1:3000') // true
|
|
206
|
+
* isLocalhost('https://code.deepline.com') // false
|
|
207
|
+
* ```
|
|
208
|
+
*/
|
|
209
|
+
/**
|
|
210
|
+
* Auto-detect the best base URL when none is explicitly provided.
|
|
211
|
+
*
|
|
212
|
+
* Checks environment variables first, then checkout-local worktree config,
|
|
213
|
+
* then SDK CLI config, before falling back to production.
|
|
214
|
+
*/
|
|
215
|
+
function autoDetectBaseUrl(): string {
|
|
216
|
+
const envOrigin = process.env.DEEPLINE_ORIGIN_URL?.trim();
|
|
217
|
+
if (envOrigin) return normalizeWorktreeBaseUrl(envOrigin);
|
|
218
|
+
|
|
219
|
+
const envBase = process.env.DEEPLINE_API_BASE_URL?.trim();
|
|
220
|
+
if (envBase) return normalizeWorktreeBaseUrl(envBase);
|
|
221
|
+
|
|
222
|
+
const worktreeBaseUrl = resolveWorktreeBaseUrl();
|
|
223
|
+
if (worktreeBaseUrl) return worktreeBaseUrl;
|
|
224
|
+
|
|
225
|
+
const globalEnv = loadGlobalCliEnv();
|
|
226
|
+
const globalOrigin = globalEnv.DEEPLINE_ORIGIN_URL?.trim();
|
|
227
|
+
if (globalOrigin) return normalizeWorktreeBaseUrl(globalOrigin);
|
|
228
|
+
|
|
229
|
+
return PROD_URL;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Resolve SDK configuration from all available sources.
|
|
234
|
+
*
|
|
235
|
+
* Merges explicit options, environment variables, and CLI-managed config files
|
|
236
|
+
* into a fully validated {@link ResolvedConfig}. See the module-level docs for
|
|
237
|
+
* the complete resolution order.
|
|
238
|
+
*
|
|
239
|
+
* @param options - Optional overrides (highest priority)
|
|
240
|
+
* @returns Fully resolved configuration with all fields populated
|
|
241
|
+
* @throws {@link ConfigError} if no API key can be found from any source
|
|
242
|
+
*
|
|
243
|
+
* @example
|
|
244
|
+
* ```typescript
|
|
245
|
+
* import { resolveConfig } from 'deepline';
|
|
246
|
+
*
|
|
247
|
+
* // Auto-resolve everything:
|
|
248
|
+
* const config = resolveConfig();
|
|
249
|
+
* console.log(config.baseUrl); // "http://localhost:3000" or "https://code.deepline.com"
|
|
250
|
+
*
|
|
251
|
+
* // Override specific values:
|
|
252
|
+
* const config2 = resolveConfig({ baseUrl: 'http://localhost:4000', timeout: 10_000 });
|
|
253
|
+
* ```
|
|
254
|
+
*/
|
|
255
|
+
export function resolveConfig(options?: DeeplineClientOptions): ResolvedConfig {
|
|
256
|
+
// Resolve base URL
|
|
257
|
+
const requestedBaseUrl =
|
|
258
|
+
options?.baseUrl?.trim() ||
|
|
259
|
+
autoDetectBaseUrl();
|
|
260
|
+
const baseUrl = normalizeWorktreeBaseUrl(requestedBaseUrl);
|
|
261
|
+
|
|
262
|
+
const cliEnv = loadCliEnv(baseUrl);
|
|
263
|
+
|
|
264
|
+
// Resolve API key: option > env var > SDK CLI env
|
|
265
|
+
const apiKey =
|
|
266
|
+
options?.apiKey?.trim() ||
|
|
267
|
+
process.env.DEEPLINE_API_KEY?.trim() ||
|
|
268
|
+
cliEnv.DEEPLINE_API_KEY ||
|
|
269
|
+
'';
|
|
270
|
+
|
|
271
|
+
if (!apiKey) {
|
|
272
|
+
throw new ConfigError(
|
|
273
|
+
`No API key found. Set DEEPLINE_API_KEY env var, pass apiKey option, or run: deepline auth register`,
|
|
274
|
+
);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
return {
|
|
278
|
+
apiKey,
|
|
279
|
+
baseUrl,
|
|
280
|
+
timeout: options?.timeout ?? DEFAULT_TIMEOUT,
|
|
281
|
+
maxRetries: options?.maxRetries ?? DEFAULT_MAX_RETRIES,
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
export { baseUrlSlug, loadCliEnv, loadGlobalCliEnv, parseEnvFile, autoDetectBaseUrl, PROD_URL };
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Base error class for all Deepline SDK errors.
|
|
3
|
+
*
|
|
4
|
+
* Every error thrown by the SDK extends this class, so you can catch all
|
|
5
|
+
* Deepline-specific errors with a single `catch (e) { if (e instanceof DeeplineError) }`.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```typescript
|
|
9
|
+
* import { DeeplineClient, DeeplineError, AuthError } from 'deepline';
|
|
10
|
+
*
|
|
11
|
+
* const client = new DeeplineClient();
|
|
12
|
+
* try {
|
|
13
|
+
* await client.executeTool('apollo_people_search', { query: 'cto' });
|
|
14
|
+
* } catch (err) {
|
|
15
|
+
* if (err instanceof AuthError) {
|
|
16
|
+
* console.error('Bad API key — run: deepline auth register');
|
|
17
|
+
* } else if (err instanceof DeeplineError) {
|
|
18
|
+
* console.error(`API error ${err.statusCode}: ${err.message}`);
|
|
19
|
+
* }
|
|
20
|
+
* }
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
export class DeeplineError extends Error {
|
|
24
|
+
constructor(
|
|
25
|
+
message: string,
|
|
26
|
+
/** HTTP status code from the API response, if applicable. */
|
|
27
|
+
public statusCode?: number,
|
|
28
|
+
/** Machine-readable error code (e.g. `'AUTH_ERROR'`, `'RATE_LIMIT'`, `'CONFIG_ERROR'`). */
|
|
29
|
+
public code?: string,
|
|
30
|
+
/** Additional context from the API response body. */
|
|
31
|
+
public details?: Record<string, unknown>,
|
|
32
|
+
) {
|
|
33
|
+
super(message);
|
|
34
|
+
this.name = 'DeeplineError';
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Thrown when the API rejects the request due to an invalid or missing API key.
|
|
40
|
+
*
|
|
41
|
+
* This maps to HTTP 401/403 responses. The SDK never retries auth errors —
|
|
42
|
+
* they fail immediately.
|
|
43
|
+
*
|
|
44
|
+
* Fix: run `deepline auth register` to obtain a valid key, or pass one via
|
|
45
|
+
* the `apiKey` option or `DEEPLINE_API_KEY` environment variable.
|
|
46
|
+
*
|
|
47
|
+
* @example
|
|
48
|
+
* ```typescript
|
|
49
|
+
* import { AuthError } from 'deepline';
|
|
50
|
+
*
|
|
51
|
+
* try {
|
|
52
|
+
* await client.listTools();
|
|
53
|
+
* } catch (err) {
|
|
54
|
+
* if (err instanceof AuthError) {
|
|
55
|
+
* // Redirect user to auth flow
|
|
56
|
+
* }
|
|
57
|
+
* }
|
|
58
|
+
* ```
|
|
59
|
+
*/
|
|
60
|
+
export class AuthError extends DeeplineError {
|
|
61
|
+
constructor(message = 'Authentication failed. Check your DEEPLINE_API_KEY.') {
|
|
62
|
+
super(message, 401, 'AUTH_ERROR');
|
|
63
|
+
this.name = 'AuthError';
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Thrown when the API returns HTTP 429 (Too Many Requests).
|
|
69
|
+
*
|
|
70
|
+
* The SDK retries rate-limited requests automatically up to `maxRetries` times
|
|
71
|
+
* with exponential backoff. This error is only thrown when all retries are exhausted.
|
|
72
|
+
*
|
|
73
|
+
* Use {@link RateLimitError.retryAfterMs} to implement your own backoff if needed.
|
|
74
|
+
*
|
|
75
|
+
* @example
|
|
76
|
+
* ```typescript
|
|
77
|
+
* import { RateLimitError } from 'deepline';
|
|
78
|
+
*
|
|
79
|
+
* try {
|
|
80
|
+
* await client.executeTool('apollo_people_search', { query: 'cto' });
|
|
81
|
+
* } catch (err) {
|
|
82
|
+
* if (err instanceof RateLimitError) {
|
|
83
|
+
* console.log(`Retry after ${err.retryAfterMs}ms`);
|
|
84
|
+
* await sleep(err.retryAfterMs);
|
|
85
|
+
* // retry...
|
|
86
|
+
* }
|
|
87
|
+
* }
|
|
88
|
+
* ```
|
|
89
|
+
*/
|
|
90
|
+
export class RateLimitError extends DeeplineError {
|
|
91
|
+
/** Milliseconds to wait before retrying, from the `Retry-After` response header. Defaults to 5000. */
|
|
92
|
+
public retryAfterMs: number;
|
|
93
|
+
|
|
94
|
+
constructor(retryAfterMs = 5000, message?: string) {
|
|
95
|
+
super(message ?? `Rate limited. Retry after ${retryAfterMs}ms.`, 429, 'RATE_LIMIT');
|
|
96
|
+
this.name = 'RateLimitError';
|
|
97
|
+
this.retryAfterMs = retryAfterMs;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Thrown when the SDK cannot resolve a valid configuration.
|
|
103
|
+
*
|
|
104
|
+
* Most commonly: no API key found in any of the resolution sources
|
|
105
|
+
* (explicit option, environment variable, CLI env files).
|
|
106
|
+
*
|
|
107
|
+
* @example
|
|
108
|
+
* ```typescript
|
|
109
|
+
* import { ConfigError } from 'deepline';
|
|
110
|
+
*
|
|
111
|
+
* try {
|
|
112
|
+
* const client = new DeeplineClient();
|
|
113
|
+
* } catch (err) {
|
|
114
|
+
* if (err instanceof ConfigError) {
|
|
115
|
+
* console.error('Run: deepline auth register');
|
|
116
|
+
* }
|
|
117
|
+
* }
|
|
118
|
+
* ```
|
|
119
|
+
*/
|
|
120
|
+
export class ConfigError extends DeeplineError {
|
|
121
|
+
constructor(message: string) {
|
|
122
|
+
super(message, undefined, 'CONFIG_ERROR');
|
|
123
|
+
this.name = 'ConfigError';
|
|
124
|
+
}
|
|
125
|
+
}
|