@vidiemme/copilot-proxy 0.1.0

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 (57) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +57 -0
  3. package/dist/attribution.d.ts +34 -0
  4. package/dist/attribution.js +118 -0
  5. package/dist/attribution.js.map +1 -0
  6. package/dist/autostart.d.ts +10 -0
  7. package/dist/autostart.js +119 -0
  8. package/dist/autostart.js.map +1 -0
  9. package/dist/cli.d.ts +2 -0
  10. package/dist/cli.js +393 -0
  11. package/dist/cli.js.map +1 -0
  12. package/dist/collector/forwarder.d.ts +44 -0
  13. package/dist/collector/forwarder.js +203 -0
  14. package/dist/collector/forwarder.js.map +1 -0
  15. package/dist/config.d.ts +44 -0
  16. package/dist/config.js +105 -0
  17. package/dist/config.js.map +1 -0
  18. package/dist/defaults.d.ts +33 -0
  19. package/dist/defaults.js +35 -0
  20. package/dist/defaults.js.map +1 -0
  21. package/dist/gitRepository.d.ts +21 -0
  22. package/dist/gitRepository.js +147 -0
  23. package/dist/gitRepository.js.map +1 -0
  24. package/dist/paths.d.ts +12 -0
  25. package/dist/paths.js +39 -0
  26. package/dist/paths.js.map +1 -0
  27. package/dist/projectDetector.d.ts +48 -0
  28. package/dist/projectDetector.js +130 -0
  29. package/dist/projectDetector.js.map +1 -0
  30. package/dist/proxy/handler.d.ts +5 -0
  31. package/dist/proxy/handler.js +149 -0
  32. package/dist/proxy/handler.js.map +1 -0
  33. package/dist/proxy/headers.d.ts +6 -0
  34. package/dist/proxy/headers.js +52 -0
  35. package/dist/proxy/headers.js.map +1 -0
  36. package/dist/proxy/inspectRequest.d.ts +18 -0
  37. package/dist/proxy/inspectRequest.js +41 -0
  38. package/dist/proxy/inspectRequest.js.map +1 -0
  39. package/dist/server.d.ts +7 -0
  40. package/dist/server.js +66 -0
  41. package/dist/server.js.map +1 -0
  42. package/dist/usage/collector.d.ts +25 -0
  43. package/dist/usage/collector.js +63 -0
  44. package/dist/usage/collector.js.map +1 -0
  45. package/dist/usage/parseAnthropic.d.ts +23 -0
  46. package/dist/usage/parseAnthropic.js +83 -0
  47. package/dist/usage/parseAnthropic.js.map +1 -0
  48. package/dist/usage/parseOpenAI.d.ts +24 -0
  49. package/dist/usage/parseOpenAI.js +72 -0
  50. package/dist/usage/parseOpenAI.js.map +1 -0
  51. package/dist/usage/sse.d.ts +17 -0
  52. package/dist/usage/sse.js +59 -0
  53. package/dist/usage/sse.js.map +1 -0
  54. package/dist/usage/types.d.ts +23 -0
  55. package/dist/usage/types.js +19 -0
  56. package/dist/usage/types.js.map +1 -0
  57. package/package.json +50 -0
@@ -0,0 +1,83 @@
1
+ import { coerceTokenCount, EMPTY_USAGE } from './types.js';
2
+ function asObject(value) {
3
+ return typeof value === 'object' && value !== null ? value : undefined;
4
+ }
5
+ /**
6
+ * Estrae i token da un blocco `usage` in formato Anthropic Messages API.
7
+ *
8
+ * In Anthropic `input_tokens` esclude gia' i token di cache, quindi non
9
+ * serve alcuna sottrazione.
10
+ */
11
+ export function readAnthropicUsageBlock(usage) {
12
+ const u = asObject(usage);
13
+ if (!u)
14
+ return undefined;
15
+ return {
16
+ inputTokens: coerceTokenCount(u.input_tokens),
17
+ cachedInputTokens: coerceTokenCount(u.cache_read_input_tokens),
18
+ cacheWriteTokens: coerceTokenCount(u.cache_creation_input_tokens),
19
+ outputTokens: coerceTokenCount(u.output_tokens),
20
+ };
21
+ }
22
+ /**
23
+ * Accumulatore per uno stream Anthropic.
24
+ *
25
+ * Lo stream distribuisce l'informazione su due eventi:
26
+ * - `message_start` porta gli input token (definitivi) e un output parziale
27
+ * - `message_delta` porta gli output token cumulativi finali
28
+ */
29
+ export class AnthropicUsageAccumulator {
30
+ usage = { ...EMPTY_USAGE };
31
+ seen = false;
32
+ handleEvent(payload) {
33
+ const event = asObject(payload);
34
+ if (!event)
35
+ return;
36
+ const type = typeof event.type === 'string' ? event.type : undefined;
37
+ if (type === 'message_start') {
38
+ const message = asObject(event.message);
39
+ if (!message)
40
+ return;
41
+ const parsed = readAnthropicUsageBlock(message.usage);
42
+ if (parsed) {
43
+ this.usage = parsed;
44
+ this.seen = true;
45
+ }
46
+ if (typeof message.model === 'string')
47
+ this.usage.model = message.model;
48
+ return;
49
+ }
50
+ if (type === 'message_delta') {
51
+ const parsed = readAnthropicUsageBlock(event.usage);
52
+ if (!parsed)
53
+ return;
54
+ this.seen = true;
55
+ // I contatori di message_delta sono cumulativi: si sovrascrive solo
56
+ // cio' che l'evento dichiara effettivamente.
57
+ if (parsed.outputTokens > 0)
58
+ this.usage.outputTokens = parsed.outputTokens;
59
+ if (parsed.inputTokens > 0)
60
+ this.usage.inputTokens = parsed.inputTokens;
61
+ if (parsed.cachedInputTokens > 0)
62
+ this.usage.cachedInputTokens = parsed.cachedInputTokens;
63
+ if (parsed.cacheWriteTokens > 0)
64
+ this.usage.cacheWriteTokens = parsed.cacheWriteTokens;
65
+ }
66
+ }
67
+ result() {
68
+ return this.seen ? { ...this.usage } : undefined;
69
+ }
70
+ }
71
+ /** Risposta Anthropic non-streaming. */
72
+ export function parseAnthropicResponse(body) {
73
+ const root = asObject(body);
74
+ if (!root)
75
+ return undefined;
76
+ const parsed = readAnthropicUsageBlock(root.usage);
77
+ if (!parsed)
78
+ return undefined;
79
+ if (typeof root.model === 'string')
80
+ parsed.model = root.model;
81
+ return parsed;
82
+ }
83
+ //# sourceMappingURL=parseAnthropic.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parseAnthropic.js","sourceRoot":"","sources":["../../src/usage/parseAnthropic.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ import { type ParsedUsage } from './types.js';
2
+ /**
3
+ * Estrae i token da un blocco `usage` in formato OpenAI / Copilot chat completions.
4
+ *
5
+ * Attenzione: `prompt_tokens` INCLUDE i `cached_tokens`. Vanno sottratti,
6
+ * altrimenti i token in cache verrebbero fatturati due volte (a tariffa piena
7
+ * e a tariffa cache).
8
+ */
9
+ export declare function readOpenAIUsageBlock(usage: unknown): ParsedUsage | undefined;
10
+ /**
11
+ * Accumulatore per uno stream OpenAI/Copilot.
12
+ *
13
+ * L'usage arriva tipicamente in un unico chunk finale (richiede
14
+ * `stream_options: { include_usage: true }`). Alcuni gateway lo ripetono su
15
+ * piu' chunk: si tiene sempre l'ultimo blocco non vuoto.
16
+ */
17
+ export declare class OpenAIUsageAccumulator {
18
+ private usage;
19
+ private model;
20
+ handleEvent(payload: unknown): void;
21
+ result(): ParsedUsage | undefined;
22
+ }
23
+ /** Risposta OpenAI/Copilot non-streaming. */
24
+ export declare function parseOpenAIResponse(body: unknown): ParsedUsage | undefined;
@@ -0,0 +1,72 @@
1
+ import { coerceTokenCount } from './types.js';
2
+ function asObject(value) {
3
+ return typeof value === 'object' && value !== null ? value : undefined;
4
+ }
5
+ /**
6
+ * Estrae i token da un blocco `usage` in formato OpenAI / Copilot chat completions.
7
+ *
8
+ * Attenzione: `prompt_tokens` INCLUDE i `cached_tokens`. Vanno sottratti,
9
+ * altrimenti i token in cache verrebbero fatturati due volte (a tariffa piena
10
+ * e a tariffa cache).
11
+ */
12
+ export function readOpenAIUsageBlock(usage) {
13
+ const u = asObject(usage);
14
+ if (!u)
15
+ return undefined;
16
+ const promptTokens = coerceTokenCount(u.prompt_tokens ?? u.input_tokens);
17
+ const promptDetails = asObject(u.prompt_tokens_details ?? u.input_tokens_details);
18
+ const cachedTokens = coerceTokenCount(promptDetails?.cached_tokens);
19
+ const outputTokens = coerceTokenCount(u.completion_tokens ?? u.output_tokens);
20
+ return {
21
+ inputTokens: Math.max(0, promptTokens - cachedTokens),
22
+ cachedInputTokens: cachedTokens,
23
+ // Il formato OpenAI non espone il cache write: resta a zero.
24
+ cacheWriteTokens: 0,
25
+ outputTokens,
26
+ };
27
+ }
28
+ /**
29
+ * Accumulatore per uno stream OpenAI/Copilot.
30
+ *
31
+ * L'usage arriva tipicamente in un unico chunk finale (richiede
32
+ * `stream_options: { include_usage: true }`). Alcuni gateway lo ripetono su
33
+ * piu' chunk: si tiene sempre l'ultimo blocco non vuoto.
34
+ */
35
+ export class OpenAIUsageAccumulator {
36
+ usage;
37
+ model;
38
+ handleEvent(payload) {
39
+ const chunk = asObject(payload);
40
+ if (!chunk)
41
+ return;
42
+ if (typeof chunk.model === 'string' && chunk.model.length > 0) {
43
+ this.model = chunk.model;
44
+ }
45
+ if (chunk.usage === null || chunk.usage === undefined)
46
+ return;
47
+ const parsed = readOpenAIUsageBlock(chunk.usage);
48
+ if (!parsed)
49
+ return;
50
+ if (parsed.inputTokens + parsed.cachedInputTokens + parsed.outputTokens === 0)
51
+ return;
52
+ this.usage = parsed;
53
+ }
54
+ result() {
55
+ if (!this.usage)
56
+ return undefined;
57
+ return { ...this.usage, model: this.usage.model ?? this.model };
58
+ }
59
+ }
60
+ /** Risposta OpenAI/Copilot non-streaming. */
61
+ export function parseOpenAIResponse(body) {
62
+ const root = asObject(body);
63
+ if (!root)
64
+ return undefined;
65
+ const parsed = readOpenAIUsageBlock(root.usage);
66
+ if (!parsed)
67
+ return undefined;
68
+ if (typeof root.model === 'string')
69
+ parsed.model = root.model;
70
+ return parsed;
71
+ }
72
+ //# sourceMappingURL=parseOpenAI.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parseOpenAI.js","sourceRoot":"","sources":["../../src/usage/parseOpenAI.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAoB,MAAM,YAAY,CAAC;AAIhE,SAAS,QAAQ,CAAC,KAAc;IAC9B,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,CAAC,CAAC,CAAE,KAAc,CAAC,CAAC,CAAC,SAAS,CAAC;AACnF,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,oBAAoB,CAAC,KAAc;IACjD,MAAM,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IAC1B,IAAI,CAAC,CAAC;QAAE,OAAO,SAAS,CAAC;IAEzB,MAAM,YAAY,GAAG,gBAAgB,CAAC,CAAC,CAAC,aAAa,IAAI,CAAC,CAAC,YAAY,CAAC,CAAC;IACzE,MAAM,aAAa,GAAG,QAAQ,CAAC,CAAC,CAAC,qBAAqB,IAAI,CAAC,CAAC,oBAAoB,CAAC,CAAC;IAClF,MAAM,YAAY,GAAG,gBAAgB,CAAC,aAAa,EAAE,aAAa,CAAC,CAAC;IACpE,MAAM,YAAY,GAAG,gBAAgB,CAAC,CAAC,CAAC,iBAAiB,IAAI,CAAC,CAAC,aAAa,CAAC,CAAC;IAE9E,OAAO;QACL,WAAW,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,YAAY,GAAG,YAAY,CAAC;QACrD,iBAAiB,EAAE,YAAY;QAC/B,6DAA6D;QAC7D,gBAAgB,EAAE,CAAC;QACnB,YAAY;KACb,CAAC;AACJ,CAAC;AAED;;;;;;GAMG;AACH,MAAM,OAAO,sBAAsB;IACzB,KAAK,CAA0B;IAC/B,KAAK,CAAqB;IAElC,WAAW,CAAC,OAAgB;QAC1B,MAAM,KAAK,GAAG,QAAQ,CAAC,OAAO,CAAC,CAAC;QAChC,IAAI,CAAC,KAAK;YAAE,OAAO;QAEnB,IAAI,OAAO,KAAK,CAAC,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC9D,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC;QAC3B,CAAC;QAED,IAAI,KAAK,CAAC,KAAK,KAAK,IAAI,IAAI,KAAK,CAAC,KAAK,KAAK,SAAS;YAAE,OAAO;QAC9D,MAAM,MAAM,GAAG,oBAAoB,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QACjD,IAAI,CAAC,MAAM;YAAE,OAAO;QACpB,IAAI,MAAM,CAAC,WAAW,GAAG,MAAM,CAAC,iBAAiB,GAAG,MAAM,CAAC,YAAY,KAAK,CAAC;YAAE,OAAO;QACtF,IAAI,CAAC,KAAK,GAAG,MAAM,CAAC;IACtB,CAAC;IAED,MAAM;QACJ,IAAI,CAAC,IAAI,CAAC,KAAK;YAAE,OAAO,SAAS,CAAC;QAClC,OAAO,EAAE,GAAG,IAAI,CAAC,KAAK,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;IAClE,CAAC;CACF;AAED,6CAA6C;AAC7C,MAAM,UAAU,mBAAmB,CAAC,IAAa;IAC/C,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC;IAC5B,IAAI,CAAC,IAAI;QAAE,OAAO,SAAS,CAAC;IAC5B,MAAM,MAAM,GAAG,oBAAoB,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAChD,IAAI,CAAC,MAAM;QAAE,OAAO,SAAS,CAAC;IAC9B,IAAI,OAAO,IAAI,CAAC,KAAK,KAAK,QAAQ;QAAE,MAAM,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC;IAC9D,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Parser incrementale di Server-Sent Events.
3
+ *
4
+ * Accetta chunk arbitrari (anche spezzati a meta' di un evento) e invoca il
5
+ * callback una volta per ogni payload `data:` deserializzabile come JSON.
6
+ * I payload non-JSON (es. `[DONE]`) vengono ignorati silenziosamente.
7
+ */
8
+ export declare class SseParser {
9
+ private readonly onPayload;
10
+ private buffer;
11
+ private readonly decoder;
12
+ constructor(onPayload: (payload: unknown) => void);
13
+ push(chunk: Uint8Array): void;
14
+ end(): void;
15
+ private drain;
16
+ private emit;
17
+ }
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Parser incrementale di Server-Sent Events.
3
+ *
4
+ * Accetta chunk arbitrari (anche spezzati a meta' di un evento) e invoca il
5
+ * callback una volta per ogni payload `data:` deserializzabile come JSON.
6
+ * I payload non-JSON (es. `[DONE]`) vengono ignorati silenziosamente.
7
+ */
8
+ export class SseParser {
9
+ onPayload;
10
+ buffer = '';
11
+ decoder = new TextDecoder('utf-8');
12
+ constructor(onPayload) {
13
+ this.onPayload = onPayload;
14
+ }
15
+ push(chunk) {
16
+ this.buffer += this.decoder.decode(chunk, { stream: true });
17
+ this.drain(false);
18
+ }
19
+ end() {
20
+ this.buffer += this.decoder.decode();
21
+ this.drain(true);
22
+ this.buffer = '';
23
+ }
24
+ drain(flush) {
25
+ // Gli eventi SSE sono separati da una riga vuota; si accettano sia \n\n
26
+ // che \r\n\r\n.
27
+ const normalized = this.buffer.replace(/\r\n/g, '\n');
28
+ const parts = normalized.split('\n\n');
29
+ const tail = flush ? '' : (parts.pop() ?? '');
30
+ this.buffer = tail;
31
+ for (const part of parts) {
32
+ if (part.trim().length === 0)
33
+ continue;
34
+ this.emit(part);
35
+ }
36
+ if (flush && tail.trim().length > 0)
37
+ this.emit(tail);
38
+ }
39
+ emit(rawEvent) {
40
+ const dataLines = [];
41
+ for (const line of rawEvent.split('\n')) {
42
+ if (!line.startsWith('data:'))
43
+ continue;
44
+ dataLines.push(line.slice(5).trimStart());
45
+ }
46
+ if (dataLines.length === 0)
47
+ return;
48
+ const data = dataLines.join('\n');
49
+ if (data === '[DONE]')
50
+ return;
51
+ try {
52
+ this.onPayload(JSON.parse(data));
53
+ }
54
+ catch {
55
+ // Payload non JSON: non e' un evento di usage, si ignora.
56
+ }
57
+ }
58
+ }
59
+ //# sourceMappingURL=sse.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sse.js","sourceRoot":"","sources":["../../src/usage/sse.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,MAAM,OAAO,SAAS;IAIS;IAHrB,MAAM,GAAG,EAAE,CAAC;IACH,OAAO,GAAG,IAAI,WAAW,CAAC,OAAO,CAAC,CAAC;IAEpD,YAA6B,SAAqC;QAArC,cAAS,GAAT,SAAS,CAA4B;IAAG,CAAC;IAEtE,IAAI,CAAC,KAAiB;QACpB,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;QAC5D,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IACpB,CAAC;IAED,GAAG;QACD,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC;QACrC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QACjB,IAAI,CAAC,MAAM,GAAG,EAAE,CAAC;IACnB,CAAC;IAEO,KAAK,CAAC,KAAc;QAC1B,wEAAwE;QACxE,gBAAgB;QAChB,MAAM,UAAU,GAAG,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;QACtD,MAAM,KAAK,GAAG,UAAU,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QACvC,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC;QAC9C,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC;QAEnB,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC;gBAAE,SAAS;YACvC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAClB,CAAC;QAED,IAAI,KAAK,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC;YAAE,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACvD,CAAC;IAEO,IAAI,CAAC,QAAgB;QAC3B,MAAM,SAAS,GAAa,EAAE,CAAC;QAC/B,KAAK,MAAM,IAAI,IAAI,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;YACxC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC;gBAAE,SAAS;YACxC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC;QAC5C,CAAC;QACD,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO;QAEnC,MAAM,IAAI,GAAG,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAClC,IAAI,IAAI,KAAK,QAAQ;YAAE,OAAO;QAE9B,IAAI,CAAC;YACH,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC;QACnC,CAAC;QAAC,MAAM,CAAC;YACP,0DAA0D;QAC5D,CAAC;IACH,CAAC;CACF"}
@@ -0,0 +1,23 @@
1
+ import { totalInputTokens, type TokenCounters } from '@vidiemme/copilot-usage-contract';
2
+ /**
3
+ * Contatori di token normalizzati, indipendenti dal protocollo upstream.
4
+ *
5
+ * Convenzione (allineata alla tariffazione GitHub Copilot):
6
+ * - inputTokens = token di input NON serviti da cache
7
+ * - cachedInputTokens = token di input letti dalla cache
8
+ * - cacheWriteTokens = token scritti in cache (solo modelli Anthropic)
9
+ * - outputTokens = token generati
10
+ *
11
+ * Importante: le famiglie non sono sovrapposte. I parser che ricevono
12
+ * un `prompt_tokens` comprensivo dei cached devono sottrarli.
13
+ */
14
+ export type TokenUsage = TokenCounters;
15
+ export { totalInputTokens };
16
+ export interface ParsedUsage extends TokenUsage {
17
+ /** Id modello dichiarato dalla risposta upstream, se presente. */
18
+ model?: string;
19
+ }
20
+ export declare const EMPTY_USAGE: TokenUsage;
21
+ export declare function isUsageEmpty(usage: TokenUsage): boolean;
22
+ declare function toInt(value: unknown): number;
23
+ export { toInt as coerceTokenCount };
@@ -0,0 +1,19 @@
1
+ import { totalInputTokens } from '@vidiemme/copilot-usage-contract';
2
+ export { totalInputTokens };
3
+ export const EMPTY_USAGE = {
4
+ inputTokens: 0,
5
+ cachedInputTokens: 0,
6
+ cacheWriteTokens: 0,
7
+ outputTokens: 0,
8
+ };
9
+ export function isUsageEmpty(usage) {
10
+ return (usage.inputTokens === 0 &&
11
+ usage.cachedInputTokens === 0 &&
12
+ usage.cacheWriteTokens === 0 &&
13
+ usage.outputTokens === 0);
14
+ }
15
+ function toInt(value) {
16
+ return typeof value === 'number' && Number.isFinite(value) ? Math.max(0, Math.trunc(value)) : 0;
17
+ }
18
+ export { toInt as coerceTokenCount };
19
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/usage/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAsB,MAAM,kCAAkC,CAAC;AAgBxF,OAAO,EAAE,gBAAgB,EAAE,CAAC;AAO5B,MAAM,CAAC,MAAM,WAAW,GAAe;IACrC,WAAW,EAAE,CAAC;IACd,iBAAiB,EAAE,CAAC;IACpB,gBAAgB,EAAE,CAAC;IACnB,YAAY,EAAE,CAAC;CAChB,CAAC;AAEF,MAAM,UAAU,YAAY,CAAC,KAAiB;IAC5C,OAAO,CACL,KAAK,CAAC,WAAW,KAAK,CAAC;QACvB,KAAK,CAAC,iBAAiB,KAAK,CAAC;QAC7B,KAAK,CAAC,gBAAgB,KAAK,CAAC;QAC5B,KAAK,CAAC,YAAY,KAAK,CAAC,CACzB,CAAC;AACJ,CAAC;AAED,SAAS,KAAK,CAAC,KAAc;IAC3B,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAClG,CAAC;AAED,OAAO,EAAE,KAAK,IAAI,gBAAgB,EAAE,CAAC"}
package/package.json ADDED
@@ -0,0 +1,50 @@
1
+ {
2
+ "name": "@vidiemme/copilot-proxy",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "description": "Proxy locale: misura il consumo di token di GitHub Copilot e lo attribuisce al progetto",
6
+ "license": "MIT",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/vidiemme/copilot-usage.git",
10
+ "directory": "packages/client"
11
+ },
12
+ "homepage": "https://github.com/vidiemme/copilot-usage#readme",
13
+ "keywords": [
14
+ "github-copilot",
15
+ "token-usage",
16
+ "cost-attribution",
17
+ "proxy"
18
+ ],
19
+ "bin": {
20
+ "copilot-proxy": "dist/cli.js"
21
+ },
22
+ "files": [
23
+ "dist"
24
+ ],
25
+ "publishConfig": {
26
+ "access": "public"
27
+ },
28
+ "engines": {
29
+ "node": ">=22.9"
30
+ },
31
+ "scripts": {
32
+ "build": "tsc -b",
33
+ "dev": "tsx watch --env-file-if-exists=.env src/cli.ts start",
34
+ "start": "node dist/cli.js start",
35
+ "smoke": "npm run build && node scripts/smoke.mjs",
36
+ "verify-detection": "npm run build && node scripts/verify-detection.mjs",
37
+ "verify-e2e": "npm run build && node --env-file-if-exists=.env scripts/verify-e2e.mjs",
38
+ "prepublishOnly": "npm run build"
39
+ },
40
+ "dependencies": {
41
+ "@vidiemme/copilot-usage-contract": "^0.1.0",
42
+ "fastify": "^5.2.1",
43
+ "undici": "^7.3.0"
44
+ },
45
+ "devDependencies": {
46
+ "@types/node": "^22.10.7",
47
+ "tsx": "^4.19.2",
48
+ "typescript": "^5.7.3"
49
+ }
50
+ }