deepline 0.1.12 → 0.1.20

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.
Files changed (82) hide show
  1. package/README.md +14 -6
  2. package/dist/cli/index.js +1346 -717
  3. package/dist/cli/index.mjs +1342 -713
  4. package/dist/index.d.mts +199 -23
  5. package/dist/index.d.ts +199 -23
  6. package/dist/index.js +221 -14
  7. package/dist/index.mjs +221 -14
  8. package/dist/repo/apps/play-runner-workers/src/coordinator-entry.ts +214 -77
  9. package/dist/repo/apps/play-runner-workers/src/dedup-do.ts +85 -60
  10. package/dist/repo/apps/play-runner-workers/src/entry.ts +385 -66
  11. package/dist/repo/sdk/src/client.ts +237 -0
  12. package/dist/repo/sdk/src/config.ts +125 -8
  13. package/dist/repo/sdk/src/http.ts +29 -5
  14. package/dist/repo/sdk/src/play.ts +19 -36
  15. package/dist/repo/sdk/src/plays/bundle-play-file.ts +22 -8
  16. package/dist/repo/sdk/src/plays/local-file-discovery.ts +207 -160
  17. package/dist/repo/sdk/src/types.ts +25 -0
  18. package/dist/repo/sdk/src/version.ts +2 -2
  19. package/dist/repo/shared_libs/play-runtime/tool-result.ts +237 -145
  20. package/dist/repo/shared_libs/plays/bundling/index.ts +206 -229
  21. package/dist/repo/shared_libs/plays/dataset.ts +28 -0
  22. package/dist/repo/shared_libs/plays/row-identity.ts +59 -4
  23. package/package.json +5 -4
  24. package/dist/cli/index.js.map +0 -1
  25. package/dist/cli/index.mjs.map +0 -1
  26. package/dist/index.js.map +0 -1
  27. package/dist/index.mjs.map +0 -1
  28. package/dist/repo/apps/play-runner-workers/src/runtime/README.md +0 -21
  29. package/dist/repo/apps/play-runner-workers/src/runtime/batching.ts +0 -177
  30. package/dist/repo/apps/play-runner-workers/src/runtime/execution-plan.ts +0 -52
  31. package/dist/repo/apps/play-runner-workers/src/runtime/tool-batch.ts +0 -100
  32. package/dist/repo/sdk/src/cli/commands/auth.ts +0 -500
  33. package/dist/repo/sdk/src/cli/commands/billing.ts +0 -188
  34. package/dist/repo/sdk/src/cli/commands/csv.ts +0 -123
  35. package/dist/repo/sdk/src/cli/commands/db.ts +0 -119
  36. package/dist/repo/sdk/src/cli/commands/feedback.ts +0 -40
  37. package/dist/repo/sdk/src/cli/commands/org.ts +0 -117
  38. package/dist/repo/sdk/src/cli/commands/play.ts +0 -3441
  39. package/dist/repo/sdk/src/cli/commands/tools.ts +0 -687
  40. package/dist/repo/sdk/src/cli/dataset-stats.ts +0 -415
  41. package/dist/repo/sdk/src/cli/index.ts +0 -148
  42. package/dist/repo/sdk/src/cli/progress.ts +0 -149
  43. package/dist/repo/sdk/src/cli/skills-sync.ts +0 -141
  44. package/dist/repo/sdk/src/cli/trace.ts +0 -61
  45. package/dist/repo/sdk/src/cli/utils.ts +0 -145
  46. package/dist/repo/sdk/src/compat.ts +0 -77
  47. package/dist/repo/shared_libs/observability/node-tracing.ts +0 -129
  48. package/dist/repo/shared_libs/observability/tracing.ts +0 -98
  49. package/dist/repo/shared_libs/play-runtime/context.ts +0 -4242
  50. package/dist/repo/shared_libs/play-runtime/ctx-contract.ts +0 -250
  51. package/dist/repo/shared_libs/play-runtime/ctx-types.ts +0 -725
  52. package/dist/repo/shared_libs/play-runtime/dataset-id.ts +0 -10
  53. package/dist/repo/shared_libs/play-runtime/db-session-crypto.ts +0 -304
  54. package/dist/repo/shared_libs/play-runtime/db-session.ts +0 -462
  55. package/dist/repo/shared_libs/play-runtime/live-events.ts +0 -214
  56. package/dist/repo/shared_libs/play-runtime/live-state-contract.ts +0 -50
  57. package/dist/repo/shared_libs/play-runtime/map-execution-frame.ts +0 -114
  58. package/dist/repo/shared_libs/play-runtime/map-row-identity.ts +0 -158
  59. package/dist/repo/shared_libs/play-runtime/progress-emitter.ts +0 -172
  60. package/dist/repo/shared_libs/play-runtime/protocol.ts +0 -121
  61. package/dist/repo/shared_libs/play-runtime/public-play-contract.ts +0 -42
  62. package/dist/repo/shared_libs/play-runtime/result-normalization.ts +0 -33
  63. package/dist/repo/shared_libs/play-runtime/runtime-api.ts +0 -1873
  64. package/dist/repo/shared_libs/play-runtime/runtime-constraints.ts +0 -2
  65. package/dist/repo/shared_libs/play-runtime/runtime-pg-driver-neon-serverless.ts +0 -201
  66. package/dist/repo/shared_libs/play-runtime/runtime-pg-driver-pg.ts +0 -48
  67. package/dist/repo/shared_libs/play-runtime/runtime-pg-driver.ts +0 -84
  68. package/dist/repo/shared_libs/play-runtime/static-pipeline-types.ts +0 -147
  69. package/dist/repo/shared_libs/play-runtime/suspension.ts +0 -68
  70. package/dist/repo/shared_libs/play-runtime/tracing.ts +0 -31
  71. package/dist/repo/shared_libs/play-runtime/waterfall-replay.ts +0 -75
  72. package/dist/repo/shared_libs/play-runtime/worker-api-types.ts +0 -140
  73. package/dist/repo/shared_libs/plays/artifact-transport.ts +0 -14
  74. package/dist/repo/shared_libs/plays/artifact-types.ts +0 -49
  75. package/dist/repo/shared_libs/plays/compiler-manifest.ts +0 -186
  76. package/dist/repo/shared_libs/plays/definition.ts +0 -264
  77. package/dist/repo/shared_libs/plays/file-refs.ts +0 -11
  78. package/dist/repo/shared_libs/plays/rate-limit-scheduler.ts +0 -206
  79. package/dist/repo/shared_libs/plays/resolve-static-pipeline.ts +0 -164
  80. package/dist/repo/shared_libs/plays/runtime-validation.ts +0 -395
  81. package/dist/repo/shared_libs/temporal/constants.ts +0 -39
  82. 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
- }