@respan/cli 0.6.9 → 0.7.1
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.
- package/dist/hooks/claude-code.cjs +1 -1
- package/dist/hooks/codex-cli.cjs +71 -49
- package/dist/hooks/codex-cli.js +76 -57
- package/dist/hooks/gemini-cli.cjs +68 -79
- package/dist/hooks/gemini-cli.js +83 -91
- package/dist/hooks/shared.js +1 -1
- package/oclif.manifest.json +937 -937
- package/package.json +1 -1
package/dist/hooks/gemini-cli.js
CHANGED
|
@@ -142,6 +142,36 @@ function detectModel(hookData) {
|
|
|
142
142
|
return String(llmReq.model ?? '') || 'gemini-cli';
|
|
143
143
|
}
|
|
144
144
|
// ── Span construction ─────────────────────────────────────────────
|
|
145
|
+
function buildToolSpan(detail, idx, traceUniqueId, rootSpanId, safeId, turnTs, workflowName, beginTime, endTime) {
|
|
146
|
+
const toolName = detail?.name ?? '';
|
|
147
|
+
const toolArgs = detail?.args ?? detail?.input ?? {};
|
|
148
|
+
const toolOutput = detail?.output ?? '';
|
|
149
|
+
const displayName = toolName ? toolDisplayName(toolName) : `Call ${idx + 1}`;
|
|
150
|
+
const toolInputStr = toolName ? formatToolInput(toolName, toolArgs) : '';
|
|
151
|
+
const toolMeta = {};
|
|
152
|
+
if (toolName)
|
|
153
|
+
toolMeta.tool_name = toolName;
|
|
154
|
+
if (detail?.error)
|
|
155
|
+
toolMeta.error = detail.error;
|
|
156
|
+
const toolStart = detail?.start_time ?? beginTime;
|
|
157
|
+
const toolEnd = detail?.end_time ?? endTime;
|
|
158
|
+
const toolLat = latencySeconds(toolStart, toolEnd);
|
|
159
|
+
return {
|
|
160
|
+
trace_unique_id: traceUniqueId,
|
|
161
|
+
span_unique_id: `gcli_${safeId}_${turnTs}_tool_${idx + 1}`,
|
|
162
|
+
span_parent_id: rootSpanId,
|
|
163
|
+
span_name: `Tool: ${displayName}`,
|
|
164
|
+
span_workflow_name: workflowName,
|
|
165
|
+
span_path: toolName ? `tool_${toolName}` : 'tool_call',
|
|
166
|
+
provider_id: '',
|
|
167
|
+
metadata: toolMeta,
|
|
168
|
+
input: toolInputStr,
|
|
169
|
+
output: truncate(toolOutput, MAX_CHARS),
|
|
170
|
+
timestamp: toolEnd,
|
|
171
|
+
start_time: toolStart,
|
|
172
|
+
...(toolLat !== undefined ? { latency: toolLat } : {}),
|
|
173
|
+
};
|
|
174
|
+
}
|
|
145
175
|
function buildSpans(hookData, outputText, tokens, config, startTimeIso, toolTurns, toolDetails, thoughtsTokens, textRounds, roundStartTimes) {
|
|
146
176
|
const spans = [];
|
|
147
177
|
const sessionId = String(hookData.session_id ?? '');
|
|
@@ -233,76 +263,18 @@ function buildSpans(hookData, outputText, tokens, config, startTimeIso, toolTurn
|
|
|
233
263
|
}
|
|
234
264
|
// Tool spans that come after this round (before next round)
|
|
235
265
|
if (r < rounds.length - 1) {
|
|
236
|
-
// Emit all tools between this round and the next
|
|
237
266
|
while (toolIdx < toolDetails.length) {
|
|
238
|
-
|
|
239
|
-
const toolName = detail?.name ?? '';
|
|
240
|
-
const toolArgs = detail?.args ?? detail?.input ?? {};
|
|
241
|
-
const toolOutput = detail?.output ?? '';
|
|
242
|
-
const displayName = toolName ? toolDisplayName(toolName) : `Call ${toolIdx + 1}`;
|
|
243
|
-
const toolInputStr = toolName ? formatToolInput(toolName, toolArgs) : '';
|
|
244
|
-
const toolMeta = {};
|
|
245
|
-
if (toolName)
|
|
246
|
-
toolMeta.tool_name = toolName;
|
|
247
|
-
if (detail?.error)
|
|
248
|
-
toolMeta.error = detail.error;
|
|
249
|
-
const toolStart = detail?.start_time ?? beginTime;
|
|
250
|
-
const toolEnd = detail?.end_time ?? endTime;
|
|
251
|
-
const toolLat = latencySeconds(toolStart, toolEnd);
|
|
252
|
-
spans.push({
|
|
253
|
-
trace_unique_id: traceUniqueId,
|
|
254
|
-
span_unique_id: `gcli_${safeId}_${turnTs}_tool_${toolIdx + 1}`,
|
|
255
|
-
span_parent_id: rootSpanId,
|
|
256
|
-
span_name: `Tool: ${displayName}`,
|
|
257
|
-
span_workflow_name: workflowName,
|
|
258
|
-
span_path: toolName ? `tool_${toolName}` : 'tool_call',
|
|
259
|
-
provider_id: '',
|
|
260
|
-
metadata: toolMeta,
|
|
261
|
-
input: toolInputStr,
|
|
262
|
-
output: truncate(toolOutput, MAX_CHARS),
|
|
263
|
-
timestamp: toolEnd,
|
|
264
|
-
start_time: toolStart,
|
|
265
|
-
...(toolLat !== undefined ? { latency: toolLat } : {}),
|
|
266
|
-
});
|
|
267
|
+
spans.push(buildToolSpan(toolDetails[toolIdx], toolIdx, traceUniqueId, rootSpanId, safeId, turnTs, workflowName, beginTime, endTime));
|
|
267
268
|
toolIdx++;
|
|
268
|
-
// If next tool starts after next round's start time, break — it belongs to a later gap
|
|
269
269
|
const nextDetail = toolDetails[toolIdx];
|
|
270
270
|
if (nextDetail && roundStarts[r + 1] && nextDetail.start_time && nextDetail.start_time > roundStarts[r + 1])
|
|
271
271
|
break;
|
|
272
272
|
}
|
|
273
273
|
}
|
|
274
274
|
}
|
|
275
|
-
// Any remaining tools not yet emitted
|
|
275
|
+
// Any remaining tools not yet emitted
|
|
276
276
|
while (toolIdx < toolDetails.length) {
|
|
277
|
-
|
|
278
|
-
const toolName = detail?.name ?? '';
|
|
279
|
-
const toolArgs = detail?.args ?? detail?.input ?? {};
|
|
280
|
-
const toolOutput = detail?.output ?? '';
|
|
281
|
-
const displayName = toolName ? toolDisplayName(toolName) : `Call ${toolIdx + 1}`;
|
|
282
|
-
const toolInputStr = toolName ? formatToolInput(toolName, toolArgs) : '';
|
|
283
|
-
const toolMeta = {};
|
|
284
|
-
if (toolName)
|
|
285
|
-
toolMeta.tool_name = toolName;
|
|
286
|
-
if (detail?.error)
|
|
287
|
-
toolMeta.error = detail.error;
|
|
288
|
-
const toolStart = detail?.start_time ?? beginTime;
|
|
289
|
-
const toolEnd = detail?.end_time ?? endTime;
|
|
290
|
-
const toolLat = latencySeconds(toolStart, toolEnd);
|
|
291
|
-
spans.push({
|
|
292
|
-
trace_unique_id: traceUniqueId,
|
|
293
|
-
span_unique_id: `gcli_${safeId}_${turnTs}_tool_${toolIdx + 1}`,
|
|
294
|
-
span_parent_id: rootSpanId,
|
|
295
|
-
span_name: `Tool: ${displayName}`,
|
|
296
|
-
span_workflow_name: workflowName,
|
|
297
|
-
span_path: toolName ? `tool_${toolName}` : 'tool_call',
|
|
298
|
-
provider_id: '',
|
|
299
|
-
metadata: toolMeta,
|
|
300
|
-
input: toolInputStr,
|
|
301
|
-
output: truncate(toolOutput, MAX_CHARS),
|
|
302
|
-
timestamp: toolEnd,
|
|
303
|
-
start_time: toolStart,
|
|
304
|
-
...(toolLat !== undefined ? { latency: toolLat } : {}),
|
|
305
|
-
});
|
|
277
|
+
spans.push(buildToolSpan(toolDetails[toolIdx], toolIdx, traceUniqueId, rootSpanId, safeId, turnTs, workflowName, beginTime, endTime));
|
|
306
278
|
toolIdx++;
|
|
307
279
|
}
|
|
308
280
|
// Reasoning span
|
|
@@ -448,7 +420,6 @@ function processBeforeTool(hookData) {
|
|
|
448
420
|
// Increment send_version to cancel any pending delayed sends —
|
|
449
421
|
// the turn isn't done yet, a tool is about to execute.
|
|
450
422
|
state.send_version = (state.send_version ?? 0) + 1;
|
|
451
|
-
state.tool_turns = (state.tool_turns ?? 0) + 1;
|
|
452
423
|
saveStreamState(sessionId, state);
|
|
453
424
|
}
|
|
454
425
|
function processAfterTool(hookData) {
|
|
@@ -624,66 +595,87 @@ function processChunk(hookData) {
|
|
|
624
595
|
launchDelayedSend(sessionId, state.send_version, spans, creds.apiKey, creds.baseUrl);
|
|
625
596
|
}
|
|
626
597
|
// ── Main ──────────────────────────────────────────────────────────
|
|
627
|
-
function
|
|
598
|
+
function processChunkInWorker(dataFile) {
|
|
628
599
|
try {
|
|
600
|
+
const raw = fs.readFileSync(dataFile, 'utf-8');
|
|
601
|
+
fs.unlinkSync(dataFile);
|
|
629
602
|
if (!raw.trim())
|
|
630
603
|
return;
|
|
631
604
|
const hookData = JSON.parse(raw);
|
|
632
|
-
const event = String(hookData.hook_event_name ?? '');
|
|
633
605
|
const unlock = acquireLock(LOCK_PATH);
|
|
634
606
|
try {
|
|
635
|
-
|
|
636
|
-
processBeforeTool(hookData);
|
|
637
|
-
}
|
|
638
|
-
else if (event === 'AfterTool') {
|
|
639
|
-
processAfterTool(hookData);
|
|
640
|
-
}
|
|
641
|
-
else {
|
|
642
|
-
processChunk(hookData);
|
|
643
|
-
}
|
|
607
|
+
processChunk(hookData);
|
|
644
608
|
}
|
|
645
609
|
finally {
|
|
646
610
|
unlock?.();
|
|
647
611
|
}
|
|
648
612
|
}
|
|
649
613
|
catch (e) {
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
else {
|
|
654
|
-
log('ERROR', `Hook error: ${e}`);
|
|
614
|
+
log('ERROR', `Worker error: ${e}`);
|
|
615
|
+
try {
|
|
616
|
+
fs.unlinkSync(dataFile);
|
|
655
617
|
}
|
|
618
|
+
catch { }
|
|
656
619
|
}
|
|
657
620
|
}
|
|
658
621
|
function main() {
|
|
659
|
-
// Worker mode:
|
|
622
|
+
// Worker mode: process chunk from temp file
|
|
660
623
|
if (process.env._RESPAN_GEM_WORKER === '1') {
|
|
661
|
-
const
|
|
662
|
-
|
|
624
|
+
const dataFile = process.env._RESPAN_GEM_FILE ?? '';
|
|
625
|
+
if (dataFile)
|
|
626
|
+
processChunkInWorker(dataFile);
|
|
663
627
|
return;
|
|
664
628
|
}
|
|
665
|
-
// Read stdin synchronously, respond immediately, fork worker, exit
|
|
666
629
|
let raw = '';
|
|
667
630
|
try {
|
|
668
631
|
raw = fs.readFileSync(0, 'utf-8');
|
|
669
632
|
}
|
|
670
633
|
catch { }
|
|
634
|
+
// Respond immediately so Gemini CLI doesn't block
|
|
671
635
|
process.stdout.write('{}\n');
|
|
672
636
|
if (!raw.trim()) {
|
|
673
637
|
process.exit(0);
|
|
674
638
|
}
|
|
675
639
|
try {
|
|
676
|
-
const
|
|
677
|
-
const
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
640
|
+
const hookData = JSON.parse(raw);
|
|
641
|
+
const event = String(hookData.hook_event_name ?? '');
|
|
642
|
+
if (event === 'BeforeTool' || event === 'AfterTool') {
|
|
643
|
+
// Tool events are fast (just state updates) and must run in order.
|
|
644
|
+
// Process inline, don't fork.
|
|
645
|
+
const unlock = acquireLock(LOCK_PATH);
|
|
646
|
+
try {
|
|
647
|
+
if (event === 'BeforeTool')
|
|
648
|
+
processBeforeTool(hookData);
|
|
649
|
+
else
|
|
650
|
+
processAfterTool(hookData);
|
|
651
|
+
}
|
|
652
|
+
finally {
|
|
653
|
+
unlock?.();
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
else {
|
|
657
|
+
// AfterModel chunks: fork to background so Gemini CLI doesn't block.
|
|
658
|
+
// Write data to temp file (avoids env var size limits).
|
|
659
|
+
const dataFile = path.join(STATE_DIR, `respan_chunk_${process.pid}.json`);
|
|
660
|
+
fs.mkdirSync(STATE_DIR, { recursive: true });
|
|
661
|
+
fs.writeFileSync(dataFile, raw);
|
|
662
|
+
try {
|
|
663
|
+
const scriptPath = __filename || process.argv[1];
|
|
664
|
+
const child = execFile('node', [scriptPath], {
|
|
665
|
+
env: { ...process.env, _RESPAN_GEM_WORKER: '1', _RESPAN_GEM_FILE: dataFile },
|
|
666
|
+
stdio: 'ignore',
|
|
667
|
+
detached: true,
|
|
668
|
+
});
|
|
669
|
+
child.unref();
|
|
670
|
+
}
|
|
671
|
+
catch (e) {
|
|
672
|
+
// Fallback: run inline
|
|
673
|
+
processChunkInWorker(dataFile);
|
|
674
|
+
}
|
|
675
|
+
}
|
|
683
676
|
}
|
|
684
677
|
catch (e) {
|
|
685
|
-
|
|
686
|
-
mainWorker(raw);
|
|
678
|
+
log('ERROR', `Hook error: ${e}`);
|
|
687
679
|
}
|
|
688
680
|
process.exit(0);
|
|
689
681
|
}
|
package/dist/hooks/shared.js
CHANGED