deepline 0.1.12 → 0.1.19
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 +14 -6
- package/dist/cli/index.js +1298 -711
- package/dist/cli/index.mjs +1294 -707
- package/dist/index.d.mts +199 -23
- package/dist/index.d.ts +199 -23
- package/dist/index.js +219 -13
- package/dist/index.mjs +219 -13
- package/dist/repo/apps/play-runner-workers/src/coordinator-entry.ts +68 -12
- package/dist/repo/apps/play-runner-workers/src/entry.ts +241 -51
- package/dist/repo/sdk/src/client.ts +237 -0
- package/dist/repo/sdk/src/config.ts +125 -8
- package/dist/repo/sdk/src/http.ts +10 -2
- package/dist/repo/sdk/src/play.ts +19 -36
- package/dist/repo/sdk/src/plays/bundle-play-file.ts +22 -8
- package/dist/repo/sdk/src/plays/local-file-discovery.ts +207 -160
- package/dist/repo/sdk/src/types.ts +25 -0
- package/dist/repo/sdk/src/version.ts +2 -2
- package/dist/repo/shared_libs/play-runtime/tool-result.ts +237 -145
- package/dist/repo/shared_libs/plays/bundling/index.ts +206 -229
- package/dist/repo/shared_libs/plays/dataset.ts +28 -0
- package/package.json +5 -4
- package/dist/cli/index.js.map +0 -1
- package/dist/cli/index.mjs.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/index.mjs.map +0 -1
- package/dist/repo/apps/play-runner-workers/src/runtime/README.md +0 -21
- package/dist/repo/apps/play-runner-workers/src/runtime/batching.ts +0 -177
- package/dist/repo/apps/play-runner-workers/src/runtime/execution-plan.ts +0 -52
- package/dist/repo/apps/play-runner-workers/src/runtime/tool-batch.ts +0 -100
- package/dist/repo/sdk/src/cli/commands/auth.ts +0 -500
- package/dist/repo/sdk/src/cli/commands/billing.ts +0 -188
- package/dist/repo/sdk/src/cli/commands/csv.ts +0 -123
- package/dist/repo/sdk/src/cli/commands/db.ts +0 -119
- package/dist/repo/sdk/src/cli/commands/feedback.ts +0 -40
- package/dist/repo/sdk/src/cli/commands/org.ts +0 -117
- package/dist/repo/sdk/src/cli/commands/play.ts +0 -3441
- package/dist/repo/sdk/src/cli/commands/tools.ts +0 -687
- package/dist/repo/sdk/src/cli/dataset-stats.ts +0 -415
- package/dist/repo/sdk/src/cli/index.ts +0 -148
- package/dist/repo/sdk/src/cli/progress.ts +0 -149
- package/dist/repo/sdk/src/cli/skills-sync.ts +0 -141
- package/dist/repo/sdk/src/cli/trace.ts +0 -61
- package/dist/repo/sdk/src/cli/utils.ts +0 -145
- package/dist/repo/sdk/src/compat.ts +0 -77
- package/dist/repo/shared_libs/observability/node-tracing.ts +0 -129
- package/dist/repo/shared_libs/observability/tracing.ts +0 -98
- package/dist/repo/shared_libs/play-runtime/context.ts +0 -4242
- package/dist/repo/shared_libs/play-runtime/ctx-contract.ts +0 -250
- package/dist/repo/shared_libs/play-runtime/ctx-types.ts +0 -725
- package/dist/repo/shared_libs/play-runtime/dataset-id.ts +0 -10
- package/dist/repo/shared_libs/play-runtime/db-session-crypto.ts +0 -304
- package/dist/repo/shared_libs/play-runtime/db-session.ts +0 -462
- package/dist/repo/shared_libs/play-runtime/live-events.ts +0 -214
- package/dist/repo/shared_libs/play-runtime/live-state-contract.ts +0 -50
- package/dist/repo/shared_libs/play-runtime/map-execution-frame.ts +0 -114
- package/dist/repo/shared_libs/play-runtime/map-row-identity.ts +0 -158
- package/dist/repo/shared_libs/play-runtime/progress-emitter.ts +0 -172
- package/dist/repo/shared_libs/play-runtime/protocol.ts +0 -121
- package/dist/repo/shared_libs/play-runtime/public-play-contract.ts +0 -42
- package/dist/repo/shared_libs/play-runtime/result-normalization.ts +0 -33
- package/dist/repo/shared_libs/play-runtime/runtime-api.ts +0 -1873
- package/dist/repo/shared_libs/play-runtime/runtime-constraints.ts +0 -2
- package/dist/repo/shared_libs/play-runtime/runtime-pg-driver-neon-serverless.ts +0 -201
- package/dist/repo/shared_libs/play-runtime/runtime-pg-driver-pg.ts +0 -48
- package/dist/repo/shared_libs/play-runtime/runtime-pg-driver.ts +0 -84
- package/dist/repo/shared_libs/play-runtime/static-pipeline-types.ts +0 -147
- package/dist/repo/shared_libs/play-runtime/suspension.ts +0 -68
- package/dist/repo/shared_libs/play-runtime/tracing.ts +0 -31
- package/dist/repo/shared_libs/play-runtime/waterfall-replay.ts +0 -75
- package/dist/repo/shared_libs/play-runtime/worker-api-types.ts +0 -140
- package/dist/repo/shared_libs/plays/artifact-transport.ts +0 -14
- package/dist/repo/shared_libs/plays/artifact-types.ts +0 -49
- package/dist/repo/shared_libs/plays/compiler-manifest.ts +0 -186
- package/dist/repo/shared_libs/plays/definition.ts +0 -264
- package/dist/repo/shared_libs/plays/file-refs.ts +0 -11
- package/dist/repo/shared_libs/plays/rate-limit-scheduler.ts +0 -206
- package/dist/repo/shared_libs/plays/resolve-static-pipeline.ts +0 -164
- package/dist/repo/shared_libs/plays/runtime-validation.ts +0 -395
- package/dist/repo/shared_libs/temporal/constants.ts +0 -39
- package/dist/repo/shared_libs/temporal/preview-config.ts +0 -153
|
@@ -1,141 +0,0 @@
|
|
|
1
|
-
import { spawn } from 'node:child_process';
|
|
2
|
-
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
3
|
-
import { homedir } from 'node:os';
|
|
4
|
-
import { dirname, join } from 'node:path';
|
|
5
|
-
import { getActiveCliProgress } from './progress.js';
|
|
6
|
-
import { baseUrlSlug } from '../config.js';
|
|
7
|
-
|
|
8
|
-
const CHECK_TIMEOUT_MS = 3_000;
|
|
9
|
-
const SDK_SKILL_NAME = 'deepline-sdk';
|
|
10
|
-
const SKILL_AGENTS = ['codex', 'claude-code', 'cursor'] as const;
|
|
11
|
-
|
|
12
|
-
type UpdateCheckResponse = {
|
|
13
|
-
skills?: {
|
|
14
|
-
needs_update?: unknown;
|
|
15
|
-
remote?: {
|
|
16
|
-
version?: unknown;
|
|
17
|
-
};
|
|
18
|
-
};
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
let attemptedSync = false;
|
|
22
|
-
|
|
23
|
-
function shouldSkipSkillsSync(): boolean {
|
|
24
|
-
const value = process.env.DEEPLINE_SKIP_SKILLS_SYNC?.trim().toLowerCase();
|
|
25
|
-
return value === '1' || value === 'true' || value === 'yes' || value === 'on';
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
function sdkSkillsVersionPath(baseUrl: string): string {
|
|
29
|
-
const home = process.env.HOME?.trim() || homedir();
|
|
30
|
-
return join(home, '.local', 'deepline', baseUrlSlug(baseUrl), 'sdk-skills', '.version');
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
function readLocalSkillsVersion(baseUrl: string): string {
|
|
34
|
-
const path = sdkSkillsVersionPath(baseUrl);
|
|
35
|
-
if (!existsSync(path)) return '';
|
|
36
|
-
try {
|
|
37
|
-
return readFileSync(path, 'utf-8').trim();
|
|
38
|
-
} catch {
|
|
39
|
-
return '';
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
function writeLocalSkillsVersion(baseUrl: string, version: string): void {
|
|
44
|
-
const path = sdkSkillsVersionPath(baseUrl);
|
|
45
|
-
mkdirSync(dirname(path), { recursive: true });
|
|
46
|
-
writeFileSync(path, `${version}\n`, 'utf-8');
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
async function fetchSkillsUpdate(baseUrl: string, localVersion: string): Promise<{
|
|
50
|
-
needsUpdate: boolean;
|
|
51
|
-
remoteVersion: string;
|
|
52
|
-
} | null> {
|
|
53
|
-
const controller = new AbortController();
|
|
54
|
-
const timeout = setTimeout(() => controller.abort(), CHECK_TIMEOUT_MS);
|
|
55
|
-
try {
|
|
56
|
-
const response = await fetch(new URL('/api/v2/cli/update-check', baseUrl), {
|
|
57
|
-
method: 'POST',
|
|
58
|
-
headers: { 'Content-Type': 'application/json' },
|
|
59
|
-
body: JSON.stringify({
|
|
60
|
-
skills: {
|
|
61
|
-
version: localVersion,
|
|
62
|
-
},
|
|
63
|
-
}),
|
|
64
|
-
signal: controller.signal,
|
|
65
|
-
});
|
|
66
|
-
if (!response.ok) return null;
|
|
67
|
-
const data = (await response.json().catch(() => null)) as UpdateCheckResponse | null;
|
|
68
|
-
const skills = data?.skills;
|
|
69
|
-
if (!skills) return null;
|
|
70
|
-
return {
|
|
71
|
-
needsUpdate: skills.needs_update === true,
|
|
72
|
-
remoteVersion: typeof skills.remote?.version === 'string' ? skills.remote.version.trim() : '',
|
|
73
|
-
};
|
|
74
|
-
} catch {
|
|
75
|
-
return null;
|
|
76
|
-
} finally {
|
|
77
|
-
clearTimeout(timeout);
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
function runSkillsInstall(baseUrl: string): Promise<boolean> {
|
|
82
|
-
const packageUrl = new URL('/.well-known/skills/index.json', baseUrl).toString();
|
|
83
|
-
const args = [
|
|
84
|
-
'skills',
|
|
85
|
-
'add',
|
|
86
|
-
packageUrl,
|
|
87
|
-
'--agents',
|
|
88
|
-
...SKILL_AGENTS,
|
|
89
|
-
'--global',
|
|
90
|
-
'--yes',
|
|
91
|
-
'--skill',
|
|
92
|
-
SDK_SKILL_NAME,
|
|
93
|
-
'--full-depth',
|
|
94
|
-
];
|
|
95
|
-
|
|
96
|
-
return new Promise((resolve) => {
|
|
97
|
-
const child = spawn('npx', args, {
|
|
98
|
-
stdio: ['ignore', 'ignore', 'pipe'],
|
|
99
|
-
env: process.env,
|
|
100
|
-
});
|
|
101
|
-
let stderr = '';
|
|
102
|
-
child.stderr.on('data', (chunk: Buffer) => {
|
|
103
|
-
stderr += chunk.toString('utf-8');
|
|
104
|
-
});
|
|
105
|
-
child.on('error', (error) => {
|
|
106
|
-
process.stderr.write(`SDK skills sync failed to start: ${error.message}\n`);
|
|
107
|
-
resolve(false);
|
|
108
|
-
});
|
|
109
|
-
child.on('close', (code) => {
|
|
110
|
-
if (code === 0) {
|
|
111
|
-
resolve(true);
|
|
112
|
-
return;
|
|
113
|
-
}
|
|
114
|
-
const detail = stderr.trim();
|
|
115
|
-
process.stderr.write(
|
|
116
|
-
`SDK skills sync failed${detail ? `: ${detail}` : ''}\n` +
|
|
117
|
-
`Run manually: npx ${args.map((arg) => (arg.includes(' ') ? JSON.stringify(arg) : arg)).join(' ')}\n`,
|
|
118
|
-
);
|
|
119
|
-
resolve(false);
|
|
120
|
-
});
|
|
121
|
-
});
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
export async function syncSdkSkillsIfNeeded(baseUrl: string): Promise<void> {
|
|
125
|
-
if (attemptedSync || shouldSkipSkillsSync()) return;
|
|
126
|
-
attemptedSync = true;
|
|
127
|
-
|
|
128
|
-
const localVersion = readLocalSkillsVersion(baseUrl);
|
|
129
|
-
const update = await fetchSkillsUpdate(baseUrl, localVersion);
|
|
130
|
-
if (!update?.needsUpdate || !update.remoteVersion) return;
|
|
131
|
-
|
|
132
|
-
const progress = getActiveCliProgress();
|
|
133
|
-
progress?.writeLine('SDK skills changed; syncing deepline-sdk skill...') ??
|
|
134
|
-
process.stderr.write('SDK skills changed; syncing deepline-sdk skill...\n');
|
|
135
|
-
const installed = await runSkillsInstall(baseUrl);
|
|
136
|
-
if (!installed) return;
|
|
137
|
-
|
|
138
|
-
writeLocalSkillsVersion(baseUrl, update.remoteVersion);
|
|
139
|
-
progress?.writeLine('SDK skills are up to date.') ??
|
|
140
|
-
process.stderr.write('SDK skills are up to date.\n');
|
|
141
|
-
}
|
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
type CliTraceFields = Record<string, unknown>;
|
|
2
|
-
|
|
3
|
-
type CliTraceEvent = CliTraceFields & {
|
|
4
|
-
phase: string;
|
|
5
|
-
ms?: number;
|
|
6
|
-
ok?: boolean;
|
|
7
|
-
};
|
|
8
|
-
|
|
9
|
-
const cliTraceStartedAt = Date.now();
|
|
10
|
-
|
|
11
|
-
function isTruthyEnv(value: string | undefined): boolean {
|
|
12
|
-
return value === '1' || value === 'true' || value === 'yes';
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export function isCliTraceEnabled(): boolean {
|
|
16
|
-
return isTruthyEnv(process.env.DEEPLINE_CLI_TRACE);
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export function recordCliTrace(event: CliTraceEvent): void {
|
|
20
|
-
if (!isCliTraceEnabled()) {
|
|
21
|
-
return;
|
|
22
|
-
}
|
|
23
|
-
const now = Date.now();
|
|
24
|
-
const payload = {
|
|
25
|
-
ts: now,
|
|
26
|
-
source: 'cli',
|
|
27
|
-
sinceStartMs: now - cliTraceStartedAt,
|
|
28
|
-
...event,
|
|
29
|
-
};
|
|
30
|
-
process.stderr.write(`[cli-trace] ${JSON.stringify(payload)}\n`);
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
export async function traceCliSpan<T>(
|
|
34
|
-
phase: string,
|
|
35
|
-
fields: CliTraceFields,
|
|
36
|
-
run: () => Promise<T>,
|
|
37
|
-
): Promise<T> {
|
|
38
|
-
if (!isCliTraceEnabled()) {
|
|
39
|
-
return run();
|
|
40
|
-
}
|
|
41
|
-
const startedAt = Date.now();
|
|
42
|
-
try {
|
|
43
|
-
const result = await run();
|
|
44
|
-
recordCliTrace({
|
|
45
|
-
phase,
|
|
46
|
-
ms: Date.now() - startedAt,
|
|
47
|
-
ok: true,
|
|
48
|
-
...fields,
|
|
49
|
-
});
|
|
50
|
-
return result;
|
|
51
|
-
} catch (error) {
|
|
52
|
-
recordCliTrace({
|
|
53
|
-
phase,
|
|
54
|
-
ms: Date.now() - startedAt,
|
|
55
|
-
ok: false,
|
|
56
|
-
error: error instanceof Error ? error.message : String(error),
|
|
57
|
-
...fields,
|
|
58
|
-
});
|
|
59
|
-
throw error;
|
|
60
|
-
}
|
|
61
|
-
}
|
|
@@ -1,145 +0,0 @@
|
|
|
1
|
-
import { readFileSync } from 'node:fs';
|
|
2
|
-
import { mkdir, writeFile } from 'node:fs/promises';
|
|
3
|
-
import { homedir } from 'node:os';
|
|
4
|
-
import { join, resolve } from 'node:path';
|
|
5
|
-
import { execSync } from 'node:child_process';
|
|
6
|
-
import { parse } from 'csv-parse/sync';
|
|
7
|
-
import { stringify } from 'csv-stringify/sync';
|
|
8
|
-
import { resolveConfig } from '../config.js';
|
|
9
|
-
import { HttpClient } from '../http.js';
|
|
10
|
-
|
|
11
|
-
export function getAuthedHttpClient() {
|
|
12
|
-
const config = resolveConfig();
|
|
13
|
-
return { config, http: new HttpClient(config) };
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export async function writeOutputFile(
|
|
17
|
-
filename: string,
|
|
18
|
-
content: string,
|
|
19
|
-
): Promise<string> {
|
|
20
|
-
const outputDir = resolve(process.cwd(), 'deepline', 'data');
|
|
21
|
-
await mkdir(outputDir, { recursive: true });
|
|
22
|
-
const fullPath = join(outputDir, filename);
|
|
23
|
-
await writeFile(fullPath, content, 'utf-8');
|
|
24
|
-
return fullPath;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export function openInBrowser(url: string): void {
|
|
28
|
-
try {
|
|
29
|
-
if (process.platform === 'darwin') execSync(`open "${url}"`, { stdio: 'ignore' });
|
|
30
|
-
else if (process.platform === 'win32')
|
|
31
|
-
execSync(`start "" "${url}"`, { stdio: 'ignore' });
|
|
32
|
-
else execSync(`xdg-open "${url}"`, { stdio: 'ignore' });
|
|
33
|
-
} catch {
|
|
34
|
-
// best effort
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
export function sleep(ms: number): Promise<void> {
|
|
39
|
-
return new Promise((resolvePromise) => setTimeout(resolvePromise, ms));
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
export function collectLocalEnvInfo(): Record<string, string> {
|
|
43
|
-
return {
|
|
44
|
-
os: `${process.platform} ${process.arch}`,
|
|
45
|
-
node_version: process.version,
|
|
46
|
-
home_dir: homedir(),
|
|
47
|
-
};
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
export function readCsvRows(csvPath: string): Array<Record<string, string>> {
|
|
51
|
-
const raw = readFileSync(resolve(csvPath), 'utf-8');
|
|
52
|
-
return parse(raw, {
|
|
53
|
-
columns: true,
|
|
54
|
-
skip_empty_lines: true,
|
|
55
|
-
}) as Array<Record<string, string>>;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
export function csvStringFromRows(
|
|
59
|
-
rows: Array<Record<string, unknown>>,
|
|
60
|
-
columns?: string[],
|
|
61
|
-
): string {
|
|
62
|
-
return stringify(rows, {
|
|
63
|
-
header: true,
|
|
64
|
-
...(columns?.length ? { columns } : {}),
|
|
65
|
-
});
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
export function printJson(value: unknown): void {
|
|
69
|
-
process.stdout.write(`${JSON.stringify(value, null, 2)}\n`);
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
export function errorToJsonPayload(error: unknown): {
|
|
73
|
-
ok: false;
|
|
74
|
-
error: {
|
|
75
|
-
message: string;
|
|
76
|
-
code: string;
|
|
77
|
-
details?: Record<string, unknown>;
|
|
78
|
-
};
|
|
79
|
-
} {
|
|
80
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
81
|
-
const maybeRecord =
|
|
82
|
-
error && typeof error === 'object'
|
|
83
|
-
? (error as Record<string, unknown>)
|
|
84
|
-
: null;
|
|
85
|
-
const code =
|
|
86
|
-
typeof maybeRecord?.code === 'string'
|
|
87
|
-
? maybeRecord.code
|
|
88
|
-
: error instanceof SyntaxError
|
|
89
|
-
? 'INVALID_JSON'
|
|
90
|
-
: 'CLI_ERROR';
|
|
91
|
-
const details: Record<string, unknown> = {};
|
|
92
|
-
if (typeof maybeRecord?.statusCode === 'number') {
|
|
93
|
-
details.statusCode = maybeRecord.statusCode;
|
|
94
|
-
}
|
|
95
|
-
if (typeof maybeRecord?.status === 'number') {
|
|
96
|
-
details.status = maybeRecord.status;
|
|
97
|
-
}
|
|
98
|
-
if (
|
|
99
|
-
maybeRecord?.details &&
|
|
100
|
-
typeof maybeRecord.details === 'object' &&
|
|
101
|
-
!Array.isArray(maybeRecord.details)
|
|
102
|
-
) {
|
|
103
|
-
details.details = maybeRecord.details;
|
|
104
|
-
}
|
|
105
|
-
if (typeof maybeRecord?.cause === 'string') {
|
|
106
|
-
details.cause = maybeRecord.cause;
|
|
107
|
-
}
|
|
108
|
-
return {
|
|
109
|
-
ok: false,
|
|
110
|
-
error: {
|
|
111
|
-
message,
|
|
112
|
-
code,
|
|
113
|
-
...(Object.keys(details).length > 0 ? { details } : {}),
|
|
114
|
-
},
|
|
115
|
-
};
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
export function printJsonError(error: unknown): void {
|
|
119
|
-
printJson(errorToJsonPayload(error));
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
export function shouldEmitJson(explicitJson = false): boolean {
|
|
123
|
-
return explicitJson || !process.stdout.isTTY;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
export function argsWantJson(args: string[]): boolean {
|
|
127
|
-
return shouldEmitJson(args.includes('--json'));
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
export function createTimestampedName(prefix: string, extension: string): string {
|
|
131
|
-
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
|
|
132
|
-
return `${prefix}-${timestamp}.${extension}`;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
export async function writeCsvRowsFile(
|
|
136
|
-
prefix: string,
|
|
137
|
-
rows: Array<Record<string, unknown>>,
|
|
138
|
-
): Promise<string> {
|
|
139
|
-
const path = await writeOutputFile(createTimestampedName(prefix, 'csv'), csvStringFromRows(rows));
|
|
140
|
-
return path;
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
export function clip(value: string, maxLength: number): string {
|
|
144
|
-
return value.length > maxLength ? `${value.slice(0, maxLength - 1)}…` : value;
|
|
145
|
-
}
|
|
@@ -1,77 +0,0 @@
|
|
|
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
|
-
}
|
|
@@ -1,129 +0,0 @@
|
|
|
1
|
-
import { mkdir, appendFile } from 'node:fs/promises';
|
|
2
|
-
import { dirname } from 'node:path';
|
|
3
|
-
import type { NodeSDK } from '@opentelemetry/sdk-node';
|
|
4
|
-
import type {
|
|
5
|
-
ReadableSpan,
|
|
6
|
-
SpanExporter,
|
|
7
|
-
} from '@opentelemetry/sdk-trace-base';
|
|
8
|
-
import type { ExportResult } from '@opentelemetry/core';
|
|
9
|
-
|
|
10
|
-
const EXPORT_RESULT_SUCCESS = 0;
|
|
11
|
-
const EXPORT_RESULT_FAILED = 1;
|
|
12
|
-
|
|
13
|
-
declare global {
|
|
14
|
-
var __deeplineTracingInitPromise: Promise<boolean> | undefined;
|
|
15
|
-
var __deeplineTracingSdk: NodeSDK | undefined;
|
|
16
|
-
var __deeplineTracingShutdownPromise: Promise<void> | undefined;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
class JsonlFileSpanExporter implements SpanExporter {
|
|
20
|
-
constructor(private readonly filePath: string) {}
|
|
21
|
-
|
|
22
|
-
async export(
|
|
23
|
-
spans: ReadableSpan[],
|
|
24
|
-
resultCallback: (result: ExportResult) => void,
|
|
25
|
-
): Promise<void> {
|
|
26
|
-
try {
|
|
27
|
-
await mkdir(dirname(this.filePath), { recursive: true });
|
|
28
|
-
const lines = spans
|
|
29
|
-
.map((span) =>
|
|
30
|
-
JSON.stringify({
|
|
31
|
-
traceId: span.spanContext().traceId,
|
|
32
|
-
spanId: span.spanContext().spanId,
|
|
33
|
-
parentSpanId: span.parentSpanContext?.spanId ?? null,
|
|
34
|
-
name: span.name,
|
|
35
|
-
kind: span.kind,
|
|
36
|
-
startTime: span.startTime,
|
|
37
|
-
endTime: span.endTime,
|
|
38
|
-
attributes: span.attributes,
|
|
39
|
-
status: span.status,
|
|
40
|
-
resource: span.resource.attributes,
|
|
41
|
-
}),
|
|
42
|
-
)
|
|
43
|
-
.join('\n');
|
|
44
|
-
if (lines.length > 0) {
|
|
45
|
-
await appendFile(this.filePath, `${lines}\n`, 'utf-8');
|
|
46
|
-
}
|
|
47
|
-
resultCallback({ code: EXPORT_RESULT_SUCCESS });
|
|
48
|
-
} catch (error) {
|
|
49
|
-
resultCallback({
|
|
50
|
-
code: EXPORT_RESULT_FAILED,
|
|
51
|
-
error: error instanceof Error ? error : new Error(String(error)),
|
|
52
|
-
});
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
async shutdown(): Promise<void> {}
|
|
57
|
-
async forceFlush(): Promise<void> {}
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
async function resolveTraceExporters(): Promise<SpanExporter[]> {
|
|
61
|
-
const exporters: SpanExporter[] = [];
|
|
62
|
-
const otlpEndpoint =
|
|
63
|
-
process.env.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT?.trim() ||
|
|
64
|
-
process.env.OTEL_EXPORTER_OTLP_ENDPOINT?.trim() ||
|
|
65
|
-
'';
|
|
66
|
-
if (otlpEndpoint) {
|
|
67
|
-
const { OTLPTraceExporter } = await import(
|
|
68
|
-
'@opentelemetry/exporter-trace-otlp-http'
|
|
69
|
-
);
|
|
70
|
-
exporters.push(new OTLPTraceExporter());
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
const traceFilePath = process.env.DEEPLINE_TRACE_FILE?.trim() || '';
|
|
74
|
-
if (traceFilePath) {
|
|
75
|
-
exporters.push(new JsonlFileSpanExporter(traceFilePath));
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
return exporters;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
async function startNodeTracing(serviceName: string): Promise<boolean> {
|
|
82
|
-
const exporters = await resolveTraceExporters();
|
|
83
|
-
if (exporters.length === 0) {
|
|
84
|
-
return false;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
if (!process.env.OTEL_SERVICE_NAME) {
|
|
88
|
-
process.env.OTEL_SERVICE_NAME = serviceName;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
const [{ NodeSDK }, { BatchSpanProcessor }] = await Promise.all([
|
|
92
|
-
import('@opentelemetry/sdk-node'),
|
|
93
|
-
import('@opentelemetry/sdk-trace-base'),
|
|
94
|
-
]);
|
|
95
|
-
|
|
96
|
-
const sdk = new NodeSDK({
|
|
97
|
-
instrumentations: [],
|
|
98
|
-
spanProcessors: exporters.map(
|
|
99
|
-
(exporter) => new BatchSpanProcessor(exporter),
|
|
100
|
-
),
|
|
101
|
-
});
|
|
102
|
-
await sdk.start();
|
|
103
|
-
globalThis.__deeplineTracingSdk = sdk;
|
|
104
|
-
return true;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
export async function ensureNodeTracing(serviceName: string): Promise<boolean> {
|
|
108
|
-
globalThis.__deeplineTracingInitPromise ??= startNodeTracing(serviceName);
|
|
109
|
-
return await globalThis.__deeplineTracingInitPromise;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
export async function shutdownNodeTracing(): Promise<void> {
|
|
113
|
-
const initialized = await (globalThis.__deeplineTracingInitPromise ??
|
|
114
|
-
Promise.resolve(false));
|
|
115
|
-
if (!initialized) {
|
|
116
|
-
return;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
const sdk = globalThis.__deeplineTracingSdk;
|
|
120
|
-
if (!sdk) {
|
|
121
|
-
return;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
globalThis.__deeplineTracingShutdownPromise ??= sdk.shutdown().finally(() => {
|
|
125
|
-
globalThis.__deeplineTracingSdk = undefined;
|
|
126
|
-
globalThis.__deeplineTracingInitPromise = undefined;
|
|
127
|
-
});
|
|
128
|
-
await globalThis.__deeplineTracingShutdownPromise;
|
|
129
|
-
}
|
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
SpanKind,
|
|
3
|
-
SpanStatusCode,
|
|
4
|
-
trace,
|
|
5
|
-
type Attributes,
|
|
6
|
-
type Span,
|
|
7
|
-
} from '@opentelemetry/api';
|
|
8
|
-
|
|
9
|
-
type PrimitiveAttribute =
|
|
10
|
-
| string
|
|
11
|
-
| number
|
|
12
|
-
| boolean
|
|
13
|
-
| null
|
|
14
|
-
| undefined;
|
|
15
|
-
|
|
16
|
-
type TraceAttributes = Record<string, PrimitiveAttribute>;
|
|
17
|
-
|
|
18
|
-
function normalizeAttributes(
|
|
19
|
-
attributes: TraceAttributes | undefined,
|
|
20
|
-
): Attributes | undefined {
|
|
21
|
-
if (!attributes) {
|
|
22
|
-
return undefined;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
const normalized: Attributes = {};
|
|
26
|
-
for (const [key, value] of Object.entries(attributes)) {
|
|
27
|
-
if (value === undefined || value === null) {
|
|
28
|
-
continue;
|
|
29
|
-
}
|
|
30
|
-
if (
|
|
31
|
-
typeof value === 'string' ||
|
|
32
|
-
typeof value === 'number' ||
|
|
33
|
-
typeof value === 'boolean'
|
|
34
|
-
) {
|
|
35
|
-
normalized[key] = value;
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
return Object.keys(normalized).length > 0 ? normalized : undefined;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
function recordError(span: Span, error: unknown): void {
|
|
42
|
-
if (error instanceof Error) {
|
|
43
|
-
span.recordException(error);
|
|
44
|
-
span.setStatus({
|
|
45
|
-
code: SpanStatusCode.ERROR,
|
|
46
|
-
message: error.message,
|
|
47
|
-
});
|
|
48
|
-
return;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
const message = String(error);
|
|
52
|
-
span.recordException({ name: 'Error', message });
|
|
53
|
-
span.setStatus({
|
|
54
|
-
code: SpanStatusCode.ERROR,
|
|
55
|
-
message,
|
|
56
|
-
});
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
export async function withActiveSpan<T>(
|
|
60
|
-
name: string,
|
|
61
|
-
options: {
|
|
62
|
-
tracer?: string;
|
|
63
|
-
kind?: SpanKind;
|
|
64
|
-
attributes?: TraceAttributes;
|
|
65
|
-
},
|
|
66
|
-
fn: (span: Span) => Promise<T> | T,
|
|
67
|
-
): Promise<T> {
|
|
68
|
-
const tracer = trace.getTracer(options.tracer ?? 'deepline');
|
|
69
|
-
return await tracer.startActiveSpan(
|
|
70
|
-
name,
|
|
71
|
-
{
|
|
72
|
-
kind: options.kind,
|
|
73
|
-
attributes: normalizeAttributes(options.attributes),
|
|
74
|
-
},
|
|
75
|
-
async (span) => {
|
|
76
|
-
try {
|
|
77
|
-
const result = await fn(span);
|
|
78
|
-
span.setStatus({ code: SpanStatusCode.OK });
|
|
79
|
-
return result;
|
|
80
|
-
} catch (error) {
|
|
81
|
-
recordError(span, error);
|
|
82
|
-
throw error;
|
|
83
|
-
} finally {
|
|
84
|
-
span.end();
|
|
85
|
-
}
|
|
86
|
-
},
|
|
87
|
-
);
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
export function setSpanAttributes(
|
|
91
|
-
span: Span,
|
|
92
|
-
attributes: TraceAttributes,
|
|
93
|
-
): void {
|
|
94
|
-
const normalized = normalizeAttributes(attributes);
|
|
95
|
-
if (normalized) {
|
|
96
|
-
span.setAttributes(normalized);
|
|
97
|
-
}
|
|
98
|
-
}
|