mirror of
https://github.com/tiennm99/ccs.git
synced 2026-09-03 16:16:29 +00:00
feat(providers): instrument copilot, cursor, and glmt across daemons and executors
Provider modules now emit structured stage tags around daemon spawn / ready / stop, executor invocations, and upstream dispatch. glmt-transformer gets cleanup-stage error conversion; legacy glmt-proxy adds minimal listen + retry instrumentation (full per-request stages live in proxy-server since glmt-proxy is compat-only). Refs #1141, #1138
This commit is contained in:
@@ -12,8 +12,11 @@ import * as http from 'http';
|
||||
import type { CursorDaemonConfig, CursorDaemonStatus } from './types';
|
||||
import { getPidFromFile, writePidToFile, removePidFile } from './cursor-daemon-pid';
|
||||
import { verifyDaemonOwnership } from './daemon-process-ownership';
|
||||
import { createLogger } from '../services/logging';
|
||||
export { getPidFromFile, writePidToFile, removePidFile } from './cursor-daemon-pid';
|
||||
|
||||
const logger = createLogger('cursor:daemon');
|
||||
|
||||
/**
|
||||
* Resolve daemon entrypoint candidates for current runtime.
|
||||
* - Dist runtime always uses JS artifact.
|
||||
@@ -125,6 +128,10 @@ export async function startDaemon(
|
||||
): Promise<{ success: boolean; pid?: number; error?: string }> {
|
||||
// Check if already running
|
||||
if (await isDaemonRunning(config.port)) {
|
||||
logger.stage('dispatch', 'cursor.daemon.already_running', 'Cursor daemon already running', {
|
||||
provider: 'cursor',
|
||||
port: config.port,
|
||||
});
|
||||
return { success: true, pid: getPidFromFile() ?? undefined };
|
||||
}
|
||||
|
||||
@@ -141,6 +148,13 @@ export async function startDaemon(
|
||||
};
|
||||
}
|
||||
|
||||
const startedAt = Date.now();
|
||||
logger.stage('dispatch', 'cursor.daemon.spawn', 'Spawning Cursor daemon', {
|
||||
provider: 'cursor',
|
||||
port: config.port,
|
||||
ghostMode: config.ghost_mode !== false,
|
||||
});
|
||||
|
||||
return new Promise((resolve) => {
|
||||
let proc: ChildProcess;
|
||||
let resolved = false;
|
||||
@@ -149,7 +163,30 @@ export async function startDaemon(
|
||||
if (resolved) return;
|
||||
resolved = true;
|
||||
if (checkTimeout) clearTimeout(checkTimeout);
|
||||
if (!result.success) removePidFile();
|
||||
if (!result.success) {
|
||||
removePidFile();
|
||||
logger.stage(
|
||||
'cleanup',
|
||||
'cursor.daemon.start_failed',
|
||||
'Cursor daemon failed to start',
|
||||
{ provider: 'cursor', port: config.port },
|
||||
{
|
||||
level: 'error',
|
||||
latencyMs: Date.now() - startedAt,
|
||||
error: result.error
|
||||
? { name: 'CursorDaemonStartError', message: result.error }
|
||||
: undefined,
|
||||
}
|
||||
);
|
||||
} else {
|
||||
logger.stage(
|
||||
'upstream',
|
||||
'cursor.daemon.ready',
|
||||
'Cursor daemon is ready',
|
||||
{ provider: 'cursor', port: config.port, pid: result.pid },
|
||||
{ latencyMs: Date.now() - startedAt }
|
||||
);
|
||||
}
|
||||
resolve(result);
|
||||
};
|
||||
|
||||
@@ -270,6 +307,10 @@ export async function stopDaemon(): Promise<{ success: boolean; error?: string }
|
||||
}
|
||||
|
||||
// Send SIGTERM to the process
|
||||
logger.stage('cleanup', 'cursor.daemon.stop', 'Sending SIGTERM to Cursor daemon', {
|
||||
provider: 'cursor',
|
||||
pid,
|
||||
});
|
||||
process.kill(pid, 'SIGTERM');
|
||||
|
||||
// Wait for process to exit (up to 5 seconds)
|
||||
|
||||
@@ -12,6 +12,9 @@ import {
|
||||
type CursorApiCredentials,
|
||||
} from './cursor-protobuf-schema.js';
|
||||
import { buildCursorConnectHeaders, generateCursorChecksum } from './cursor-client-policy.js';
|
||||
import { createLogger } from '../services/logging';
|
||||
|
||||
const logger = createLogger('cursor:executor');
|
||||
|
||||
import {
|
||||
CursorConnectFrameError,
|
||||
@@ -272,6 +275,13 @@ export class CursorExecutor {
|
||||
const headers = this.buildHeaders(credentials);
|
||||
const transformedBody = this.transformRequest(model, body, stream, credentials);
|
||||
|
||||
const startedAt = Date.now();
|
||||
logger.stage('upstream', 'cursor.upstream.request', 'Sending Cursor upstream request', {
|
||||
provider: 'cursor',
|
||||
model,
|
||||
stream,
|
||||
});
|
||||
|
||||
try {
|
||||
// Streaming requests use incremental HTTP/2 → SSE pipeline
|
||||
if (stream) {
|
||||
@@ -294,6 +304,13 @@ export class CursorExecutor {
|
||||
|
||||
if (response.status !== 200) {
|
||||
const errorText = response.body?.toString() || 'Unknown error';
|
||||
logger.stage(
|
||||
'cleanup',
|
||||
'cursor.upstream.error',
|
||||
'Cursor upstream returned non-200 status',
|
||||
{ provider: 'cursor', model, status: response.status },
|
||||
{ level: 'error', latencyMs: Date.now() - startedAt }
|
||||
);
|
||||
const errorResponse = new Response(
|
||||
JSON.stringify({
|
||||
error: {
|
||||
@@ -311,12 +328,31 @@ export class CursorExecutor {
|
||||
}
|
||||
|
||||
const transformedResponse = this.transformProtobufToJSON(response.body, model, body);
|
||||
logger.stage(
|
||||
'transform',
|
||||
'cursor.upstream.transformed',
|
||||
'Cursor response transformed',
|
||||
{ provider: 'cursor', model },
|
||||
{ latencyMs: Date.now() - startedAt }
|
||||
);
|
||||
return { response: transformedResponse, url, headers, transformedBody: body };
|
||||
} catch (error) {
|
||||
const err = error as Error;
|
||||
logger.stage(
|
||||
'cleanup',
|
||||
'cursor.upstream.exception',
|
||||
'Cursor upstream request threw',
|
||||
{ provider: 'cursor', model },
|
||||
{
|
||||
level: 'error',
|
||||
latencyMs: Date.now() - startedAt,
|
||||
error: { name: err.name, message: err.message },
|
||||
}
|
||||
);
|
||||
const errorResponse = new Response(
|
||||
JSON.stringify({
|
||||
error: {
|
||||
message: (error as Error).message,
|
||||
message: err.message,
|
||||
type: 'connection_error',
|
||||
code: '',
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user