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