diff --git a/packages/core/src/agents/a2a-client-manager.ts b/packages/core/src/agents/a2a-client-manager.ts index c15d34179c..d8a2d9146b 100644 --- a/packages/core/src/agents/a2a-client-manager.ts +++ b/packages/core/src/agents/a2a-client-manager.ts @@ -21,8 +21,6 @@ import { RestTransportFactory, createAuthenticatingFetchWithRetry, } from '@a2a-js/sdk/client'; -import { GrpcTransportFactory } from '@a2a-js/sdk/client/grpc'; -import * as grpc from '@grpc/grpc-js'; import { v4 as uuidv4 } from 'uuid'; import { Agent as UndiciAgent, ProxyAgent } from 'undici'; import { normalizeAgentCard } from './a2aUtils.js'; @@ -129,6 +127,8 @@ export class A2AClientManager { agentCard.additionalInterfaces?.find((i) => i.transport === 'GRPC') ?.url ?? agentCard.url; + const { GrpcTransportFactory } = await import('@a2a-js/sdk/client/grpc'); + const grpc = await import('@grpc/grpc-js'); const clientOptions = ClientFactoryOptions.createFrom( ClientFactoryOptions.default, { diff --git a/packages/core/src/services/gitService.ts b/packages/core/src/services/gitService.ts index 5409b1a526..7e294a50d7 100644 --- a/packages/core/src/services/gitService.ts +++ b/packages/core/src/services/gitService.ts @@ -8,10 +8,16 @@ import * as fs from 'node:fs/promises'; import * as path from 'node:path'; import { isNodeError } from '../utils/errors.js'; import { spawnAsync } from '../utils/shell-utils.js'; -import { simpleGit, CheckRepoActions, type SimpleGit } from 'simple-git'; +import type { SimpleGit } from 'simple-git'; import type { Storage } from '../config/storage.js'; import { debugLogger } from '../utils/debugLogger.js'; + +async function getSimpleGit() { + const { simpleGit, CheckRepoActions } = await import('simple-git'); + return { simpleGit, CheckRepoActions }; +} + export class GitService { private projectRoot: string; private storage: Storage; @@ -79,6 +85,7 @@ export class GitService { const shadowRepoEnv = this.getShadowRepoEnv(repoDir); await fs.writeFile(shadowRepoEnv.GIT_CONFIG_SYSTEM, ''); + const { simpleGit, CheckRepoActions } = await getSimpleGit(); const repo = simpleGit(repoDir).env(shadowRepoEnv); let isRepoDefined = false; try { @@ -114,8 +121,9 @@ export class GitService { await fs.writeFile(shadowGitIgnorePath, userGitIgnoreContent); } - private get shadowGitRepository(): SimpleGit { + private async shadowGitRepository(): Promise { const repoDir = this.getHistoryDir(); + const { simpleGit } = await getSimpleGit(); return simpleGit(this.projectRoot).env({ GIT_DIR: path.join(repoDir, '.git'), GIT_WORK_TREE: this.projectRoot, @@ -124,13 +132,13 @@ export class GitService { } async getCurrentCommitHash(): Promise { - const hash = await this.shadowGitRepository.raw('rev-parse', 'HEAD'); + const hash = await (await this.shadowGitRepository()).raw('rev-parse', 'HEAD'); return hash.trim(); } async createFileSnapshot(message: string): Promise { try { - const repo = this.shadowGitRepository; + const repo = await this.shadowGitRepository(); await repo.add('.'); const status = await repo.status(); if (status.isClean()) { @@ -149,7 +157,7 @@ export class GitService { } async restoreProjectFromSnapshot(commitHash: string): Promise { - const repo = this.shadowGitRepository; + const repo = await this.shadowGitRepository(); await repo.raw(['restore', '--source', commitHash, '.']); // Removes any untracked files that were introduced post snapshot. await repo.clean('f', ['-d']); diff --git a/packages/core/src/telemetry/clearcut-logger/clearcut-logger.ts b/packages/core/src/telemetry/clearcut-logger/clearcut-logger.ts index 4791d6d1c2..877467a38e 100644 --- a/packages/core/src/telemetry/clearcut-logger/clearcut-logger.ts +++ b/packages/core/src/telemetry/clearcut-logger/clearcut-logger.ts @@ -6,7 +6,6 @@ import { createHash } from 'node:crypto'; import * as os from 'node:os'; -import si from 'systeminformation'; import { HttpsProxyAgent } from 'https-proxy-agent'; import type { StartSessionEvent, @@ -262,6 +261,7 @@ let cachedGpuInfo: string | undefined; async function refreshGpuInfo(): Promise { try { + const si = (await import('systeminformation')).default; const graphics = await si.graphics(); if (graphics.controllers && graphics.controllers.length > 0) { cachedGpuInfo = graphics.controllers.map((c) => c.model).join(', '); diff --git a/packages/core/src/telemetry/index.ts b/packages/core/src/telemetry/index.ts index ea65941e06..3be7970636 100644 --- a/packages/core/src/telemetry/index.ts +++ b/packages/core/src/telemetry/index.ts @@ -24,11 +24,7 @@ export { parseBooleanEnvFlag, parseTelemetryTargetValue, } from './config.js'; -export { - GcpTraceExporter, - GcpMetricExporter, - GcpLogExporter, -} from './gcp-exporters.js'; + export { logCliConfiguration, logUserPrompt, diff --git a/packages/core/src/telemetry/sdk.ts b/packages/core/src/telemetry/sdk.ts index bafa540790..3d2fc1600c 100644 --- a/packages/core/src/telemetry/sdk.ts +++ b/packages/core/src/telemetry/sdk.ts @@ -12,29 +12,28 @@ import { metrics, propagation, } from '@opentelemetry/api'; -import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-grpc'; -import { OTLPLogExporter } from '@opentelemetry/exporter-logs-otlp-grpc'; -import { OTLPMetricExporter } from '@opentelemetry/exporter-metrics-otlp-grpc'; -import { OTLPTraceExporter as OTLPTraceExporterHttp } from '@opentelemetry/exporter-trace-otlp-http'; -import { OTLPLogExporter as OTLPLogExporterHttp } from '@opentelemetry/exporter-logs-otlp-http'; -import { OTLPMetricExporter as OTLPMetricExporterHttp } from '@opentelemetry/exporter-metrics-otlp-http'; -import { CompressionAlgorithm } from '@opentelemetry/otlp-exporter-base'; -import { NodeSDK } from '@opentelemetry/sdk-node'; -import { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions'; -import { resourceFromAttributes } from '@opentelemetry/resources'; -import { +import type { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-grpc'; +import type { OTLPLogExporter } from '@opentelemetry/exporter-logs-otlp-grpc'; + +import type { OTLPTraceExporter as OTLPTraceExporterHttp } from '@opentelemetry/exporter-trace-otlp-http'; +import type { OTLPLogExporter as OTLPLogExporterHttp } from '@opentelemetry/exporter-logs-otlp-http'; + + +import type { NodeSDK } from '@opentelemetry/sdk-node'; + + +import type { BatchSpanProcessor, ConsoleSpanExporter, } from '@opentelemetry/sdk-trace-node'; -import { +import type { BatchLogRecordProcessor, ConsoleLogRecordExporter, } from '@opentelemetry/sdk-logs'; -import { - ConsoleMetricExporter, +import type { PeriodicExportingMetricReader, } from '@opentelemetry/sdk-metrics'; -import { HttpInstrumentation } from '@opentelemetry/instrumentation-http'; + import type { JWTInput } from 'google-auth-library'; import type { Config } from '../config/config.js'; import { SERVICE_NAME } from './constants.js'; @@ -45,11 +44,7 @@ import { FileMetricExporter, FileSpanExporter, } from './file-exporters.js'; -import { - GcpTraceExporter, - GcpMetricExporter, - GcpLogExporter, -} from './gcp-exporters.js'; +import type { GcpTraceExporter , GcpLogExporter } from './gcp-exporters.js'; import { TelemetryTarget } from './index.js'; import { debugLogger } from '../utils/debugLogger.js'; import { authEvents } from '../code_assist/oauth2.js'; @@ -205,6 +200,8 @@ export async function initializeTelemetry( return; } + const { resourceFromAttributes } = await import('@opentelemetry/resources'); + const { SemanticResourceAttributes } = await import('@opentelemetry/semantic-conventions'); const resource = resourceFromAttributes({ [SemanticResourceAttributes.SERVICE_NAME]: SERVICE_NAME, [SemanticResourceAttributes.SERVICE_VERSION]: process.version, @@ -267,10 +264,12 @@ export async function initializeTelemetry( 'using', credentials ? 'provided credentials' : 'ADC', ); + const { GcpTraceExporter } = await import('./gcp-exporters.js'); spanExporter = new GcpTraceExporter(gcpProjectId, credentials); + const { GcpLogExporter } = await import('./gcp-exporters.js'); logExporter = new GcpLogExporter(gcpProjectId, credentials); - metricReader = new PeriodicExportingMetricReader({ - exporter: new GcpMetricExporter(gcpProjectId, credentials), + metricReader = new (await import('@opentelemetry/sdk-metrics')).PeriodicExportingMetricReader({ + exporter: new (await import('./gcp-exporters.js')).GcpMetricExporter(gcpProjectId, credentials), exportIntervalMillis: 30000, }); } else if (useOtlp) { @@ -281,32 +280,36 @@ export async function initializeTelemetry( url.pathname = [url.pathname.replace(/\/$/, ''), path].join('/'); return url.href; }; + const { OTLPTraceExporter: OTLPTraceExporterHttp } = await import('@opentelemetry/exporter-trace-otlp-http'); spanExporter = new OTLPTraceExporterHttp({ url: buildUrl('v1/traces'), }); + const { OTLPLogExporter: OTLPLogExporterHttp } = await import('@opentelemetry/exporter-logs-otlp-http'); logExporter = new OTLPLogExporterHttp({ url: buildUrl('v1/logs'), }); - metricReader = new PeriodicExportingMetricReader({ - exporter: new OTLPMetricExporterHttp({ + metricReader = new (await import('@opentelemetry/sdk-metrics')).PeriodicExportingMetricReader({ + exporter: new (await import('@opentelemetry/exporter-metrics-otlp-http')).OTLPMetricExporter({ url: buildUrl('v1/metrics'), }), exportIntervalMillis: 10000, }); } else { // grpc + const { OTLPTraceExporter } = await import('@opentelemetry/exporter-trace-otlp-grpc'); spanExporter = new OTLPTraceExporter({ url: parsedEndpoint, - compression: CompressionAlgorithm.GZIP, + compression: (await import('@opentelemetry/otlp-exporter-base')).CompressionAlgorithm.GZIP, }); + const { OTLPLogExporter } = await import('@opentelemetry/exporter-logs-otlp-grpc'); logExporter = new OTLPLogExporter({ url: parsedEndpoint, - compression: CompressionAlgorithm.GZIP, + compression: (await import('@opentelemetry/otlp-exporter-base')).CompressionAlgorithm.GZIP, }); - metricReader = new PeriodicExportingMetricReader({ - exporter: new OTLPMetricExporter({ + metricReader = new (await import('@opentelemetry/sdk-metrics')).PeriodicExportingMetricReader({ + exporter: new (await import('@opentelemetry/exporter-metrics-otlp-grpc')).OTLPMetricExporter({ url: parsedEndpoint, - compression: CompressionAlgorithm.GZIP, + compression: (await import('@opentelemetry/otlp-exporter-base')).CompressionAlgorithm.GZIP, }), exportIntervalMillis: 10000, }); @@ -314,23 +317,29 @@ export async function initializeTelemetry( } else if (telemetryOutfile) { spanExporter = new FileSpanExporter(telemetryOutfile); logExporter = new FileLogExporter(telemetryOutfile); - metricReader = new PeriodicExportingMetricReader({ + metricReader = new (await import('@opentelemetry/sdk-metrics')).PeriodicExportingMetricReader({ exporter: new FileMetricExporter(telemetryOutfile), exportIntervalMillis: 10000, }); } else { + const { ConsoleSpanExporter } = await import('@opentelemetry/sdk-trace-node'); spanExporter = new ConsoleSpanExporter(); + const { ConsoleLogRecordExporter } = await import('@opentelemetry/sdk-logs'); logExporter = new ConsoleLogRecordExporter(); - metricReader = new PeriodicExportingMetricReader({ - exporter: new ConsoleMetricExporter(), + metricReader = new (await import('@opentelemetry/sdk-metrics')).PeriodicExportingMetricReader({ + exporter: new (await import('@opentelemetry/sdk-metrics')).ConsoleMetricExporter(), exportIntervalMillis: 10000, }); } // Store processor references for manual flushing + const { BatchSpanProcessor } = await import('@opentelemetry/sdk-trace-node'); spanProcessor = new BatchSpanProcessor(spanExporter); + const { BatchLogRecordProcessor } = await import('@opentelemetry/sdk-logs'); logRecordProcessor = new BatchLogRecordProcessor(logExporter); + const { NodeSDK } = await import('@opentelemetry/sdk-node'); + const { HttpInstrumentation } = await import('@opentelemetry/instrumentation-http'); sdk = new NodeSDK({ resource, spanProcessors: [spanProcessor], diff --git a/packages/core/src/utils/shell-utils.ts b/packages/core/src/utils/shell-utils.ts index 14fce36a34..da4643be45 100644 --- a/packages/core/src/utils/shell-utils.ts +++ b/packages/core/src/utils/shell-utils.ts @@ -14,7 +14,7 @@ import { type SpawnOptionsWithoutStdio, } from 'node:child_process'; import * as readline from 'node:readline'; -import { Language, Parser, Query, type Node, type Tree } from 'web-tree-sitter'; +import type { Language, Parser, Node, Tree, QueryCapture } from 'web-tree-sitter'; import { loadWasmBinary } from './fileUtils.js'; import { debugLogger } from './debugLogger.js'; import type { SandboxManager } from '../services/sandboxManager.js'; @@ -70,6 +70,8 @@ export async function resolveExecutable( return undefined; } +let _Parser: any = null; +let _Query: any = null; let bashLanguage: Language | null = null; let treeSitterInitialization: Promise | null = null; let treeSitterInitializationError: Error | null = null; @@ -113,8 +115,20 @@ async function loadBashLanguage(): Promise { ), ]); - await Parser.init({ wasmBinary: treeSitterBinary }); - bashLanguage = await Language.load(bashBinary); + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-explicit-any + const wts: any = await import('web-tree-sitter'); + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access + const ParserImpl = wts.default || wts; + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access + const LanguageImpl = wts.Language || ParserImpl.Language; + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access + _Query = wts.Query || ParserImpl.Query; + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment + _Parser = ParserImpl; + // eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access + await ParserImpl.init({ wasmBinary: treeSitterBinary }); + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access + bashLanguage = await LanguageImpl.load(bashBinary); } catch (error) { bashLanguage = null; const normalized = toError(error); @@ -213,7 +227,8 @@ function createParser(): Parser | null { } try { - const parser = new Parser(); + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-call + const parser = new _Parser() as Parser; parser.setLanguage(bashLanguage); return parser; } catch { @@ -404,9 +419,10 @@ function parseBashCommandDetails(command: string): CommandParseResult | null { if (hasError) { let query = null; try { - query = new Query(bashLanguage, '(ERROR) @error (MISSING) @missing'); + query = // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-call + new _Query(bashLanguage, '(ERROR) @error (MISSING) @missing'); const captures = query.captures(tree.rootNode); - const syntaxErrors = captures.map((capture) => { + const syntaxErrors = captures.map((capture: QueryCapture) => { const { node, name } = capture; const type = name === 'missing' ? 'Missing' : 'Error'; return `${type} node: "${node.text}" at ${node.startPosition.row}:${node.startPosition.column}`;