feat: Complete zCode CLI X with Telegram bot integration

- Add full Telegram bot functionality with Z.AI API integration
- Implement 4 tools: Bash, FileEdit, WebSearch, Git
- Add 3 agents: Code Reviewer, Architect, DevOps Engineer
- Add 6 skills for common coding tasks
- Add systemd service file for 24/7 operation
- Add nginx configuration for HTTPS webhook
- Add comprehensive documentation
- Implement WebSocket server for real-time updates
- Add logging system with Winston
- Add environment validation

🤖 zCode CLI X - Agentic coder with Z.AI + Telegram integration
This commit is contained in:
admin
2026-05-05 09:01:26 +00:00
Unverified
parent 4a7035dd92
commit 875c7f9b91
24688 changed files with 3224957 additions and 221 deletions

View File

@@ -0,0 +1,12 @@
import type { IExporterTransport } from '../exporter-transport';
import type { HeadersFactory } from '../configuration/otlp-http-configuration';
export interface FetchTransportParameters {
url: string;
headers: HeadersFactory;
}
/**
* Creates an exporter transport that uses `fetch` to send the data
* @param parameters applied to each request made by transport
*/
export declare function createFetchTransport(parameters: FetchTransportParameters): IExporterTransport;
//# sourceMappingURL=fetch-transport.d.ts.map

View File

@@ -0,0 +1,133 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
import { diag } from '@opentelemetry/api';
import { isExportHTTPErrorRetryable, parseRetryAfterToMills, } from '../is-export-retryable';
/**
* Maximum total body size for concurrent keepalive requests.
* Browsers enforce a 64KiB cumulative limit across all pending keepalive requests.
* We use 60KB to leave headroom for headers.
* @see https://github.com/whatwg/fetch/issues/679
* @see https://blog.huli.tw/2025/01/06/en/navigator-sendbeacon-64kib-and-source-code/
*/
const MAX_KEEPALIVE_BODY_SIZE = 60 * 1024;
/**
* Maximum concurrent keepalive requests.
* Chrome enforces 9 concurrent keepalive fetch requests per renderer process.
* @see https://github.com/whatwg/fetch/issues/679
* Quote: "If the renderer process is processing more than 9 requests with keepalive set, we reject a new request"
*/
const MAX_KEEPALIVE_REQUESTS = 9;
/**
* Track cumulative pending body size across all in-flight keepalive requests.
* This is necessary because the 64KiB limit is cumulative, not per-request.
*/
let pendingBodySize = 0;
/**
* Track number of pending keepalive requests.
*/
let pendingKeepaliveCount = 0;
class FetchTransport {
_parameters;
constructor(parameters) {
this._parameters = parameters;
}
async send(data, timeoutMillis) {
const abortController = new AbortController();
const timeout = setTimeout(() => abortController.abort(), timeoutMillis);
// Fetch API may be wrapped by an instrumentation like `@opentelemetry/instrumentation-fetch`.
// In that case the instrumentation would create a new Span for this request
// because the context manager cannot keep the context after `await` calls.
// This creates an indirect endless loop Export -> Span -> Export
// By using the `__original` function the instrumentation can't intercept the call
// and no Span will be created breaking the vicious cycle
let fetchApi = globalThis.fetch;
// @ts-expect-error -- fetch could be wrapped
if (typeof fetchApi.__original === 'function') {
// @ts-expect-error -- fetch could be wrapped
fetchApi = fetchApi.__original;
}
const requestSize = data.byteLength;
// Determine if we can use keepalive based on cumulative browser limits.
// We must check BEFORE adding to pending totals to avoid exceeding limits.
const wouldExceedSize = pendingBodySize + requestSize > MAX_KEEPALIVE_BODY_SIZE;
const wouldExceedCount = pendingKeepaliveCount >= MAX_KEEPALIVE_REQUESTS;
const useKeepalive = !wouldExceedSize && !wouldExceedCount;
if (useKeepalive) {
pendingBodySize += requestSize;
pendingKeepaliveCount++;
}
else {
const reason = wouldExceedSize ? 'size limit' : 'count limit';
diag.debug(`keepalive disabled: ${(requestSize / 1024).toFixed(1)}KB payload, ${pendingKeepaliveCount} pending (${reason})`);
}
try {
const url = new URL(this._parameters.url);
const response = await fetchApi(url.href, {
method: 'POST',
headers: await this._parameters.headers(),
body: data,
signal: abortController.signal,
keepalive: useKeepalive,
mode: globalThis.location
? globalThis.location.origin === url.origin
? 'same-origin'
: 'cors'
: 'no-cors',
});
if (response.status >= 200 && response.status <= 299) {
diag.debug(`export response success (status: ${response.status})`);
return { status: 'success' };
}
else if (isExportHTTPErrorRetryable(response.status)) {
diag.warn(`export response retryable (status: ${response.status})`);
const retryAfter = response.headers.get('Retry-After');
const retryInMillis = parseRetryAfterToMills(retryAfter);
return { status: 'retryable', retryInMillis };
}
diag.error(`export response failure (status: ${response.status})`);
return {
status: 'failure',
error: new Error(`Fetch request failed with non-retryable status ${response.status}`),
};
}
catch (error) {
if (isFetchNetworkErrorRetryable(error)) {
diag.warn(`export request retryable (network error: ${error})`);
return {
status: 'retryable',
error: new Error('Fetch request encountered a network error', {
cause: error,
}),
};
}
diag.error(`export request failure (error: ${error})`);
return {
status: 'failure',
error: new Error('Fetch request errored', { cause: error }),
};
}
finally {
clearTimeout(timeout);
if (useKeepalive) {
pendingBodySize -= requestSize;
pendingKeepaliveCount--;
}
}
}
shutdown() {
// Intentionally left empty, nothing to do.
}
}
/**
* Creates an exporter transport that uses `fetch` to send the data
* @param parameters applied to each request made by transport
*/
export function createFetchTransport(parameters) {
return new FetchTransport(parameters);
}
function isFetchNetworkErrorRetryable(error) {
return error instanceof TypeError && !error.cause;
}
//# sourceMappingURL=fetch-transport.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,4 @@
import type { IExporterTransport } from '../exporter-transport';
import type { NodeHttpRequestParameters } from './node-http-transport-types';
export declare function createHttpExporterTransport(parameters: NodeHttpRequestParameters): IExporterTransport;
//# sourceMappingURL=http-exporter-transport.d.ts.map

View File

@@ -0,0 +1,41 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
import { sendWithHttp } from './http-transport-utils';
class HttpExporterTransport {
_utils = null;
_parameters;
constructor(parameters) {
this._parameters = parameters;
}
async send(data, timeoutMillis) {
const { agent, request } = await this._loadUtils();
const headers = await this._parameters.headers();
return sendWithHttp(request, this._parameters.url, headers, this._parameters.compression, this._parameters.userAgent, agent, data, timeoutMillis);
}
shutdown() {
// intentionally left empty, nothing to do.
}
async _loadUtils() {
let utils = this._utils;
if (utils === null) {
const protocol = new URL(this._parameters.url).protocol;
const [agent, request] = await Promise.all([
this._parameters.agentFactory(protocol),
requestFunctionFactory(protocol),
]);
utils = this._utils = { agent, request };
}
return utils;
}
}
async function requestFunctionFactory(protocol) {
const module = protocol === 'http:' ? import('http') : import('https');
const { request } = await module;
return request;
}
export function createHttpExporterTransport(parameters) {
return new HttpExporterTransport(parameters);
}
//# sourceMappingURL=http-exporter-transport.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"http-exporter-transport.js","sourceRoot":"","sources":["../../../src/transport/http-exporter-transport.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAQH,OAAO,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAQtD,MAAM,qBAAqB;IACjB,MAAM,GAAiB,IAAI,CAAC;IAC5B,WAAW,CAA4B;IAE/C,YAAY,UAAqC;QAC/C,IAAI,CAAC,WAAW,GAAG,UAAU,CAAC;IAChC,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,IAAgB,EAAE,aAAqB;QAChD,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,MAAM,IAAI,CAAC,UAAU,EAAE,CAAC;QACnD,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,OAAO,EAAE,CAAC;QAEjD,OAAO,YAAY,CACjB,OAAO,EACP,IAAI,CAAC,WAAW,CAAC,GAAG,EACpB,OAAO,EACP,IAAI,CAAC,WAAW,CAAC,WAAW,EAC5B,IAAI,CAAC,WAAW,CAAC,SAAS,EAC1B,KAAK,EACL,IAAI,EACJ,aAAa,CACd,CAAC;IACJ,CAAC;IAED,QAAQ;QACN,2CAA2C;IAC7C,CAAC;IAEO,KAAK,CAAC,UAAU;QACtB,IAAI,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC;QAExB,IAAI,KAAK,KAAK,IAAI,EAAE;YAClB,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC;YACxD,MAAM,CAAC,KAAK,EAAE,OAAO,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;gBACzC,IAAI,CAAC,WAAW,CAAC,YAAY,CAAC,QAAQ,CAAC;gBACvC,sBAAsB,CAAC,QAAQ,CAAC;aACjC,CAAC,CAAC;YACH,KAAK,GAAG,IAAI,CAAC,MAAM,GAAG,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC;SAC1C;QAED,OAAO,KAAK,CAAC;IACf,CAAC;CACF;AAED,KAAK,UAAU,sBAAsB,CACnC,QAAgB;IAEhB,MAAM,MAAM,GAAG,QAAQ,KAAK,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IACvE,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,MAAM,CAAC;IACjC,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,MAAM,UAAU,2BAA2B,CACzC,UAAqC;IAErC,OAAO,IAAI,qBAAqB,CAAC,UAAU,CAAC,CAAC;AAC/C,CAAC","sourcesContent":["/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n// NOTE: do not change these type imports to actual imports. Doing so WILL break `@opentelemetry/instrumentation-http`,\n// as they'd be imported before the http/https modules can be wrapped.\nimport type * as https from 'https';\nimport type * as http from 'http';\nimport type { ExportResponse } from '../export-response';\nimport type { IExporterTransport } from '../exporter-transport';\nimport { sendWithHttp } from './http-transport-utils';\nimport type { NodeHttpRequestParameters } from './node-http-transport-types';\n\ninterface Utils {\n agent: http.Agent | https.Agent;\n request: typeof http.request | typeof https.request;\n}\n\nclass HttpExporterTransport implements IExporterTransport {\n private _utils: Utils | null = null;\n private _parameters: NodeHttpRequestParameters;\n\n constructor(parameters: NodeHttpRequestParameters) {\n this._parameters = parameters;\n }\n\n async send(data: Uint8Array, timeoutMillis: number): Promise<ExportResponse> {\n const { agent, request } = await this._loadUtils();\n const headers = await this._parameters.headers();\n\n return sendWithHttp(\n request,\n this._parameters.url,\n headers,\n this._parameters.compression,\n this._parameters.userAgent,\n agent,\n data,\n timeoutMillis\n );\n }\n\n shutdown() {\n // intentionally left empty, nothing to do.\n }\n\n private async _loadUtils(): Promise<Utils> {\n let utils = this._utils;\n\n if (utils === null) {\n const protocol = new URL(this._parameters.url).protocol;\n const [agent, request] = await Promise.all([\n this._parameters.agentFactory(protocol),\n requestFunctionFactory(protocol),\n ]);\n utils = this._utils = { agent, request };\n }\n\n return utils;\n }\n}\n\nasync function requestFunctionFactory(\n protocol: string\n): Promise<typeof http.request | typeof https.request> {\n const module = protocol === 'http:' ? import('http') : import('https');\n const { request } = await module;\n return request;\n}\n\nexport function createHttpExporterTransport(\n parameters: NodeHttpRequestParameters\n): IExporterTransport {\n return new HttpExporterTransport(parameters);\n}\n"]}

View File

@@ -0,0 +1,8 @@
import type { HeadersFactory } from '../configuration/otlp-http-configuration';
export interface HttpRequestParameters {
url: string;
headers: HeadersFactory;
compression: 'gzip' | 'none';
userAgent?: string;
}
//# sourceMappingURL=http-transport-types.d.ts.map

View File

@@ -0,0 +1,6 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
export {};
//# sourceMappingURL=http-transport-types.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"http-transport-types.js","sourceRoot":"","sources":["../../../src/transport/http-transport-types.ts"],"names":[],"mappings":"AAAA;;;GAGG","sourcesContent":["/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport type { HeadersFactory } from '../configuration/otlp-http-configuration';\n\nexport interface HttpRequestParameters {\n url: string;\n headers: HeadersFactory;\n compression: 'gzip' | 'none';\n userAgent?: string;\n}\n"]}

View File

@@ -0,0 +1,19 @@
/// <reference types="node" />
/// <reference types="node" />
import type * as http from 'http';
import type * as https from 'https';
import type { ExportResponse } from '../export-response';
/**
* Sends data using http
* @param request
* @param url
* @param headers
* @param compression
* @param userAgent
* @param agent
* @param data
* @param timeoutMillis
*/
export declare function sendWithHttp(request: typeof https.request | typeof http.request, url: string, headers: Record<string, string>, compression: 'gzip' | 'none', userAgent: string | undefined, agent: http.Agent | https.Agent, data: Uint8Array, timeoutMillis: number): Promise<ExportResponse>;
export declare function compressAndSend(req: http.ClientRequest, compression: 'gzip' | 'none', data: Uint8Array, onError: (error: Error) => void): void;
//# sourceMappingURL=http-transport-utils.d.ts.map

View File

@@ -0,0 +1,148 @@
import * as zlib from 'zlib';
import { Readable } from 'stream';
import { isExportHTTPErrorRetryable, parseRetryAfterToMills, } from '../is-export-retryable';
import { OTLPExporterError } from '../types';
import { VERSION } from '../version';
const DEFAULT_USER_AGENT = `OTel-OTLP-Exporter-JavaScript/${VERSION}`;
/**
* Sends data using http
* @param request
* @param url
* @param headers
* @param compression
* @param userAgent
* @param agent
* @param data
* @param timeoutMillis
*/
export function sendWithHttp(request, url, headers, compression, userAgent, agent, data, timeoutMillis) {
return new Promise(resolve => {
const parsedUrl = new URL(url);
if (userAgent) {
headers['User-Agent'] = `${userAgent} ${DEFAULT_USER_AGENT}`;
}
else {
headers['User-Agent'] = DEFAULT_USER_AGENT;
}
const options = {
hostname: parsedUrl.hostname,
port: parsedUrl.port,
path: parsedUrl.pathname,
method: 'POST',
headers,
agent,
};
const req = request(options, (res) => {
const responseData = [];
res.on('data', chunk => responseData.push(chunk));
res.on('end', () => {
if (res.statusCode && res.statusCode <= 299) {
resolve({
status: 'success',
data: Buffer.concat(responseData),
});
}
else if (res.statusCode &&
isExportHTTPErrorRetryable(res.statusCode)) {
resolve({
status: 'retryable',
retryInMillis: parseRetryAfterToMills(res.headers['retry-after']),
});
}
else {
const error = new OTLPExporterError(res.statusMessage, res.statusCode, Buffer.concat(responseData).toString());
resolve({
status: 'failure',
error,
});
}
});
res.on('error', (error) => {
// Note: 'end' may still be emitted after 'error' on the same response object, since we're resolving a promise,
// the first call to resolve() will determine the final state.
if (res.statusCode && res.statusCode <= 299) {
// If the response is successful but an error occurs while reading the response,
// we consider it a success since the data has been sent successfully.
resolve({
status: 'success',
});
}
else if (res.statusCode &&
isExportHTTPErrorRetryable(res.statusCode)) {
resolve({
status: 'retryable',
error: error,
retryInMillis: parseRetryAfterToMills(res.headers['retry-after']),
});
}
else {
resolve({
status: 'failure',
error,
});
}
});
});
req.setTimeout(timeoutMillis, () => {
req.destroy();
resolve({
status: 'retryable',
error: new Error('Request timed out'),
});
});
req.on('error', (error) => {
if (isHttpTransportNetworkErrorRetryable(error)) {
resolve({
status: 'retryable',
error,
});
}
else {
resolve({
status: 'failure',
error,
});
}
});
compressAndSend(req, compression, data, (error) => {
resolve({
status: 'failure',
error,
});
});
});
}
export function compressAndSend(req, compression, data, onError) {
let dataStream = readableFromUint8Array(data);
if (compression === 'gzip') {
req.setHeader('Content-Encoding', 'gzip');
dataStream = dataStream
.on('error', onError)
.pipe(zlib.createGzip())
.on('error', onError);
}
dataStream.pipe(req).on('error', onError);
}
function readableFromUint8Array(buff) {
const readable = new Readable();
readable.push(buff);
readable.push(null);
return readable;
}
function isHttpTransportNetworkErrorRetryable(error) {
const RETRYABLE_NETWORK_ERROR_CODES = new Set([
'ECONNRESET',
'ECONNREFUSED',
'EPIPE',
'ETIMEDOUT',
'EAI_AGAIN',
'ENOTFOUND',
'ENETUNREACH',
'EHOSTUNREACH',
]);
if ('code' in error && typeof error.code === 'string') {
return RETRYABLE_NETWORK_ERROR_CODES.has(error.code);
}
return false;
}
//# sourceMappingURL=http-transport-utils.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,6 @@
import type { HttpAgentFactory } from '../configuration/otlp-node-http-configuration';
import type { HttpRequestParameters } from './http-transport-types';
export interface NodeHttpRequestParameters extends HttpRequestParameters {
agentFactory: HttpAgentFactory;
}
//# sourceMappingURL=node-http-transport-types.d.ts.map

View File

@@ -0,0 +1,6 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
export {};
//# sourceMappingURL=node-http-transport-types.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"node-http-transport-types.js","sourceRoot":"","sources":["../../../src/transport/node-http-transport-types.ts"],"names":[],"mappings":"AAAA;;;GAGG","sourcesContent":["/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport type { HttpAgentFactory } from '../configuration/otlp-node-http-configuration';\nimport type { HttpRequestParameters } from './http-transport-types';\n\nexport interface NodeHttpRequestParameters extends HttpRequestParameters {\n agentFactory: HttpAgentFactory;\n}\n"]}