estela 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 (186) hide show
  1. package/README.md +138 -0
  2. package/dist/billing/amortize.d.ts +44 -0
  3. package/dist/billing/amortize.d.ts.map +1 -0
  4. package/dist/billing/amortize.js +83 -0
  5. package/dist/billing/amortize.js.map +1 -0
  6. package/dist/billing/amortize.test.d.ts +2 -0
  7. package/dist/billing/amortize.test.d.ts.map +1 -0
  8. package/dist/billing/amortize.test.js +121 -0
  9. package/dist/billing/amortize.test.js.map +1 -0
  10. package/dist/billing/format.test.d.ts +2 -0
  11. package/dist/billing/format.test.d.ts.map +1 -0
  12. package/dist/billing/format.test.js +110 -0
  13. package/dist/billing/format.test.js.map +1 -0
  14. package/dist/billing/invoice.d.ts +52 -0
  15. package/dist/billing/invoice.d.ts.map +1 -0
  16. package/dist/billing/invoice.js +148 -0
  17. package/dist/billing/invoice.js.map +1 -0
  18. package/dist/billing/invoice.test.d.ts +2 -0
  19. package/dist/billing/invoice.test.d.ts.map +1 -0
  20. package/dist/billing/invoice.test.js +200 -0
  21. package/dist/billing/invoice.test.js.map +1 -0
  22. package/dist/billing/sessionize.d.ts +104 -0
  23. package/dist/billing/sessionize.d.ts.map +1 -0
  24. package/dist/billing/sessionize.js +0 -0
  25. package/dist/billing/sessionize.js.map +1 -0
  26. package/dist/billing/sessionize.test.d.ts +2 -0
  27. package/dist/billing/sessionize.test.d.ts.map +1 -0
  28. package/dist/billing/sessionize.test.js +67 -0
  29. package/dist/billing/sessionize.test.js.map +1 -0
  30. package/dist/cli.d.ts +4 -0
  31. package/dist/cli.d.ts.map +1 -0
  32. package/dist/cli.js +1091 -0
  33. package/dist/cli.js.map +1 -0
  34. package/dist/cloud/auth.d.ts +4 -0
  35. package/dist/cloud/auth.d.ts.map +1 -0
  36. package/dist/cloud/auth.js +98 -0
  37. package/dist/cloud/auth.js.map +1 -0
  38. package/dist/cloud/client.d.ts +12 -0
  39. package/dist/cloud/client.d.ts.map +1 -0
  40. package/dist/cloud/client.js +34 -0
  41. package/dist/cloud/client.js.map +1 -0
  42. package/dist/db/schema.d.ts +11 -0
  43. package/dist/db/schema.d.ts.map +1 -0
  44. package/dist/db/schema.js +424 -0
  45. package/dist/db/schema.js.map +1 -0
  46. package/dist/db/schema.test.d.ts +2 -0
  47. package/dist/db/schema.test.d.ts.map +1 -0
  48. package/dist/db/schema.test.js +335 -0
  49. package/dist/db/schema.test.js.map +1 -0
  50. package/dist/db/store.d.ts +113 -0
  51. package/dist/db/store.d.ts.map +1 -0
  52. package/dist/db/store.js +550 -0
  53. package/dist/db/store.js.map +1 -0
  54. package/dist/doctor.d.ts +24 -0
  55. package/dist/doctor.d.ts.map +1 -0
  56. package/dist/doctor.js +229 -0
  57. package/dist/doctor.js.map +1 -0
  58. package/dist/export/csv.d.ts +9 -0
  59. package/dist/export/csv.d.ts.map +1 -0
  60. package/dist/export/csv.js +81 -0
  61. package/dist/export/csv.js.map +1 -0
  62. package/dist/export/invoice-pdf.d.ts +21 -0
  63. package/dist/export/invoice-pdf.d.ts.map +1 -0
  64. package/dist/export/invoice-pdf.js +135 -0
  65. package/dist/export/invoice-pdf.js.map +1 -0
  66. package/dist/export/panel.d.ts +78 -0
  67. package/dist/export/panel.d.ts.map +1 -0
  68. package/dist/export/panel.js +693 -0
  69. package/dist/export/panel.js.map +1 -0
  70. package/dist/export/panel.test.d.ts +2 -0
  71. package/dist/export/panel.test.d.ts.map +1 -0
  72. package/dist/export/panel.test.js +273 -0
  73. package/dist/export/panel.test.js.map +1 -0
  74. package/dist/export/pdf.d.ts +32 -0
  75. package/dist/export/pdf.d.ts.map +1 -0
  76. package/dist/export/pdf.js +229 -0
  77. package/dist/export/pdf.js.map +1 -0
  78. package/dist/export/pdf.test.d.ts +2 -0
  79. package/dist/export/pdf.test.d.ts.map +1 -0
  80. package/dist/export/pdf.test.js +151 -0
  81. package/dist/export/pdf.test.js.map +1 -0
  82. package/dist/export/share.d.ts +42 -0
  83. package/dist/export/share.d.ts.map +1 -0
  84. package/dist/export/share.js +181 -0
  85. package/dist/export/share.js.map +1 -0
  86. package/dist/export/share.test.d.ts +2 -0
  87. package/dist/export/share.test.d.ts.map +1 -0
  88. package/dist/export/share.test.js +116 -0
  89. package/dist/export/share.test.js.map +1 -0
  90. package/dist/metrics/index.d.ts +101 -0
  91. package/dist/metrics/index.d.ts.map +1 -0
  92. package/dist/metrics/index.js +221 -0
  93. package/dist/metrics/index.js.map +1 -0
  94. package/dist/metrics/metrics.test.d.ts +2 -0
  95. package/dist/metrics/metrics.test.d.ts.map +1 -0
  96. package/dist/metrics/metrics.test.js +162 -0
  97. package/dist/metrics/metrics.test.js.map +1 -0
  98. package/dist/metrics/people.d.ts +46 -0
  99. package/dist/metrics/people.d.ts.map +1 -0
  100. package/dist/metrics/people.js +126 -0
  101. package/dist/metrics/people.js.map +1 -0
  102. package/dist/metrics/people.test.d.ts +2 -0
  103. package/dist/metrics/people.test.d.ts.map +1 -0
  104. package/dist/metrics/people.test.js +84 -0
  105. package/dist/metrics/people.test.js.map +1 -0
  106. package/dist/metrics/team.d.ts +41 -0
  107. package/dist/metrics/team.d.ts.map +1 -0
  108. package/dist/metrics/team.js +0 -0
  109. package/dist/metrics/team.js.map +1 -0
  110. package/dist/metrics/team.test.d.ts +2 -0
  111. package/dist/metrics/team.test.d.ts.map +1 -0
  112. package/dist/metrics/team.test.js +90 -0
  113. package/dist/metrics/team.test.js.map +1 -0
  114. package/dist/pricing/catalog.d.ts +39 -0
  115. package/dist/pricing/catalog.d.ts.map +1 -0
  116. package/dist/pricing/catalog.js +59 -0
  117. package/dist/pricing/catalog.js.map +1 -0
  118. package/dist/pricing/cost.d.ts +25 -0
  119. package/dist/pricing/cost.d.ts.map +1 -0
  120. package/dist/pricing/cost.js +50 -0
  121. package/dist/pricing/cost.js.map +1 -0
  122. package/dist/pricing/cost.test.d.ts +2 -0
  123. package/dist/pricing/cost.test.d.ts.map +1 -0
  124. package/dist/pricing/cost.test.js +74 -0
  125. package/dist/pricing/cost.test.js.map +1 -0
  126. package/dist/server.d.ts +15 -0
  127. package/dist/server.d.ts.map +1 -0
  128. package/dist/server.js +1141 -0
  129. package/dist/server.js.map +1 -0
  130. package/dist/setup.d.ts +70 -0
  131. package/dist/setup.d.ts.map +1 -0
  132. package/dist/setup.js +223 -0
  133. package/dist/setup.js.map +1 -0
  134. package/dist/setup.test.d.ts +2 -0
  135. package/dist/setup.test.d.ts.map +1 -0
  136. package/dist/setup.test.js +131 -0
  137. package/dist/setup.test.js.map +1 -0
  138. package/dist/watchers/claude.d.ts +34 -0
  139. package/dist/watchers/claude.d.ts.map +1 -0
  140. package/dist/watchers/claude.js +235 -0
  141. package/dist/watchers/claude.js.map +1 -0
  142. package/dist/watchers/claude.test.d.ts +2 -0
  143. package/dist/watchers/claude.test.d.ts.map +1 -0
  144. package/dist/watchers/claude.test.js +180 -0
  145. package/dist/watchers/claude.test.js.map +1 -0
  146. package/dist/watchers/git.d.ts +42 -0
  147. package/dist/watchers/git.d.ts.map +1 -0
  148. package/dist/watchers/git.js +218 -0
  149. package/dist/watchers/git.js.map +1 -0
  150. package/dist/watchers/git.test.d.ts +2 -0
  151. package/dist/watchers/git.test.d.ts.map +1 -0
  152. package/dist/watchers/git.test.js +73 -0
  153. package/dist/watchers/git.test.js.map +1 -0
  154. package/dist/watchers/identity.d.ts +34 -0
  155. package/dist/watchers/identity.d.ts.map +1 -0
  156. package/dist/watchers/identity.js +85 -0
  157. package/dist/watchers/identity.js.map +1 -0
  158. package/dist/watchers/identity.test.d.ts +2 -0
  159. package/dist/watchers/identity.test.d.ts.map +1 -0
  160. package/dist/watchers/identity.test.js +48 -0
  161. package/dist/watchers/identity.test.js.map +1 -0
  162. package/dist/web-ui.test.d.ts +2 -0
  163. package/dist/web-ui.test.d.ts.map +1 -0
  164. package/dist/web-ui.test.js +312 -0
  165. package/dist/web-ui.test.js.map +1 -0
  166. package/node_modules/@estela/shared/dist/index.d.ts +3 -0
  167. package/node_modules/@estela/shared/dist/index.d.ts.map +1 -0
  168. package/node_modules/@estela/shared/dist/index.js +19 -0
  169. package/node_modules/@estela/shared/dist/index.js.map +1 -0
  170. package/node_modules/@estela/shared/dist/money.d.ts +71 -0
  171. package/node_modules/@estela/shared/dist/money.d.ts.map +1 -0
  172. package/node_modules/@estela/shared/dist/money.js +171 -0
  173. package/node_modules/@estela/shared/dist/money.js.map +1 -0
  174. package/node_modules/@estela/shared/dist/types.d.ts +365 -0
  175. package/node_modules/@estela/shared/dist/types.d.ts.map +1 -0
  176. package/node_modules/@estela/shared/dist/types.js +24 -0
  177. package/node_modules/@estela/shared/dist/types.js.map +1 -0
  178. package/node_modules/@estela/shared/package.json +5 -0
  179. package/package.json +41 -0
  180. package/web/app.css +749 -0
  181. package/web/app.js +1257 -0
  182. package/web/apple-touch-icon.png +0 -0
  183. package/web/favicon.png +0 -0
  184. package/web/index.html +132 -0
  185. package/web/lib.js +94 -0
  186. package/web/logo-mark.png +0 -0
@@ -0,0 +1,365 @@
1
+ import type { AiCost, Currency, Money } from "./money.js";
2
+ /** Agentes de los que sabemos leer. */
3
+ export type AgentKind = "claude-code" | "codex" | "cursor" | "gemini-cli";
4
+ /**
5
+ * Un turno de agente ya normalizado.
6
+ *
7
+ * Es la frontera con el formato externo: por encima de este tipo nadie sabe que
8
+ * existe un `.jsonl`, ni un `cache_creation_input_tokens`. Cuando Anthropic
9
+ * cambie su formato, cambia el adaptador y nada más.
10
+ */
11
+ export interface AgentTurn {
12
+ readonly agent: AgentKind;
13
+ /** Identidad del turno en el origen. Es la clave de deduplicación. */
14
+ readonly turnId: string;
15
+ readonly sessionId: string;
16
+ readonly at: Date;
17
+ readonly model: string;
18
+ /** Ruta del proyecto donde ocurrió (`cwd` en el transcript). */
19
+ readonly repoPath: string | null;
20
+ readonly branch: string | null;
21
+ readonly tokens: TokenUsage;
22
+ /** Versión de la herramienta que escribió el registro. Para diagnóstico de deriva. */
23
+ readonly producerVersion: string | null;
24
+ }
25
+ /**
26
+ * Uso de tokens. Los cuatro campos son obligatorios porque los cuatro cuestan
27
+ * dinero: en sesiones reales la caché representa la mayoría del gasto, y
28
+ * omitirla no infravalora un poco el coste, lo infravalora varias veces.
29
+ */
30
+ export interface TokenUsage {
31
+ readonly input: number;
32
+ readonly output: number;
33
+ readonly cacheRead: number;
34
+ /** Escritura de caché con TTL de 5 minutos (multiplicador 1.25x). */
35
+ readonly cacheWrite5m: number;
36
+ /** Escritura de caché con TTL de 1 hora (multiplicador 2x). */
37
+ readonly cacheWrite1h: number;
38
+ }
39
+ export declare const EMPTY_USAGE: TokenUsage;
40
+ /** Un commit leído de git. Nunca lo escribimos nosotros: solo observamos. */
41
+ export interface CommitRecord {
42
+ readonly repoPath: string;
43
+ readonly hash: string;
44
+ readonly at: Date;
45
+ readonly authorEmail: string;
46
+ /**
47
+ * Nombre del autor tal y como lo escribe git.
48
+ *
49
+ * Hace falta porque una misma persona commitea con varios correos — en el
50
+ * repositorio de Accuro, Denys aparece con @accuro.es y con @paipe.com. Sin
51
+ * el nombre, la vista de equipo lo cuenta dos veces y deja de ser creíble.
52
+ */
53
+ readonly authorName: string;
54
+ readonly branch: string | null;
55
+ readonly subject: string;
56
+ readonly linesAdded: number;
57
+ readonly linesDeleted: number;
58
+ /** Ficheros tocados. Habilita medir reescritura. */
59
+ readonly files: readonly string[];
60
+ }
61
+ /**
62
+ * Resultado de leer una fuente externa.
63
+ *
64
+ * `unknownRecords` es el canario: si un día se dispara, el formato cambió y hay
65
+ * que avisar en la interfaz. Un número de facturación nunca debe degradarse en
66
+ * silencio.
67
+ */
68
+ export interface ParseReport {
69
+ readonly filesRead: number;
70
+ readonly recordsSeen: number;
71
+ readonly turnsAccepted: number;
72
+ /** Turnos descartados por ser repetición de un `turnId` ya visto. */
73
+ readonly duplicatesDropped: number;
74
+ /** Registros cuyo `type` no reconocemos. Esperado y benigno mientras sea estable. */
75
+ readonly unknownRecords: number;
76
+ /** Registros que sí deberíamos entender pero venían malformados. Esto sí es alarma. */
77
+ readonly malformedRecords: number;
78
+ readonly producerVersions: readonly string[];
79
+ readonly warnings: readonly string[];
80
+ }
81
+ /**
82
+ * Cómo se paga la IA. Cambia por completo qué significa el número.
83
+ *
84
+ * - "api" : pagas por token. El coste calculado es tu gasto exacto.
85
+ * - "subscription" : pagas una cuota fija. Lo calculado por token es solo una
86
+ * señal de consumo; el gasto real es la cuota, repartida
87
+ * entre proyectos en proporción a lo que consumió cada uno.
88
+ */
89
+ export type AiCostBasis = "api" | "subscription";
90
+ /**
91
+ * Quién paga la IA. Determina quién puede verla.
92
+ *
93
+ * No es un permiso configurable, es una consecuencia. Si la pagas tú, el gasto
94
+ * es tuyo y nadie más tiene por qué verlo: un cliente que descubre qué parte
95
+ * del trabajo generó una IA tiene un argumento nuevo para negociar tu tarifa, y
96
+ * eso no es evidencia de tu trabajo. Si la paga la empresa para su equipo, es su
97
+ * dinero y le corresponde verlo.
98
+ *
99
+ * Al seguir al dinero en vez de a una casilla, la regla no se puede activar por
100
+ * error ni por presión de un cliente.
101
+ *
102
+ * ## Tres consecuencias para la vista de equipo
103
+ *
104
+ * **1. Nunca por persona, siempre por proyecto.** La pregunta que una empresa
105
+ * puede responder es "cuánto nos cuesta este proyecto en IA"; "cuánto gasta
106
+ * Ana" no lo es. Un coste por persona es una divulgación del gasto de alguien y
107
+ * un proxy para juzgarle: quien más consume puede estar en el código más
108
+ * difícil, no siendo peor. Mismo error que ordenar a la gente por horas.
109
+ *
110
+ * **2. El agregado excluye a quien se la paga él, o se despeja restando.** Si
111
+ * el total del proyecto incluyera al colaborador externo, el cliente conoce lo
112
+ * que paga de los suyos y obtiene el del externo con una resta. El agregado
113
+ * protege tan poco como el detalle si mezcla pagadores.
114
+ *
115
+ * **3. Se declara, nunca se detecta.** El demonio lee los transcripts: ve el
116
+ * modelo y los tokens, jamás de quién es la tarjeta. Inferir que la empresa
117
+ * paga porque alguien está en su espacio de trabajo expondría el gasto personal
118
+ * de quien lleva su propia suscripción. Por eso el valor por defecto es `self`:
119
+ * equivocarse hacia ahí no revela nada de nadie.
120
+ */
121
+ export type AiPayer = "self" | "organization";
122
+ /** Una suscripción de precio fijo (Claude Max, Cursor, ChatGPT Plus...). */
123
+ export interface Subscription {
124
+ readonly id: string;
125
+ readonly name: string;
126
+ readonly monthlyFee: Money;
127
+ readonly effectiveFrom: Date;
128
+ readonly effectiveTo: Date | null;
129
+ }
130
+ /**
131
+ * Reparto de una cuota fija entre proyectos, para un mes concreto.
132
+ *
133
+ * Es un valor **derivado**, nunca almacenado en la imputación: si mañana
134
+ * importas más trabajo de ese mismo mes, el reparto cambia. Se congela solo al
135
+ * emitir una factura.
136
+ */
137
+ export interface AmortizedShare {
138
+ readonly projectId: string;
139
+ /** Mes al que corresponde, en formato YYYY-MM. */
140
+ readonly month: string;
141
+ /** Consumo del proyecto en tarifa API equivalente. */
142
+ readonly consumption: AiCost;
143
+ /** Consumo de todos los proyectos ese mes. El denominador del reparto. */
144
+ readonly monthTotal: AiCost;
145
+ /** Fracción de la cuota que le toca a este proyecto. */
146
+ readonly share: number;
147
+ /** Parte de la cuota imputable al proyecto. Esto sí es dinero real. */
148
+ readonly amount: Money;
149
+ }
150
+ export interface Client {
151
+ readonly id: string;
152
+ readonly name: string;
153
+ /** Moneda en la que se le factura a este cliente. */
154
+ readonly currency: Currency;
155
+ readonly taxId?: string;
156
+ readonly email?: string;
157
+ readonly address?: string;
158
+ }
159
+ /**
160
+ * Un proyecto: la unidad que se pacta con un cliente.
161
+ *
162
+ * La tarifa vive aquí y no en el cliente porque el mismo cliente puede pactar
163
+ * precios distintos por proyecto, y porque una tarifa cambia con el tiempo sin
164
+ * que deban recalcularse las facturas ya emitidas (ver `RatePeriod`).
165
+ */
166
+ /**
167
+ * Qué relación tienes con el trabajo de un proyecto.
168
+ *
169
+ * No es lo mismo un cliente sin tarifa que un empleo: en el primero falta un
170
+ * dato, en el segundo el dato no existe. Confundirlos hace que el panel enseñe
171
+ * un "valor del trabajo" que nadie te va a pagar, y una cifra falsa delante de
172
+ * un cliente vale menos que ninguna.
173
+ *
174
+ * - "client" : le facturas por horas. Tiene tarifa y va en los informes.
175
+ * - "employment" : trabajo por nómina. Se mide el tiempo y el consumo de IA,
176
+ * pero no hay importe ni informe que mandar.
177
+ * - "internal" : cosas tuyas. Ni importe ni informe.
178
+ */
179
+ export type ProjectKind = "client" | "employment" | "internal";
180
+ export declare const PROJECT_KIND_LABELS: Record<ProjectKind, string>;
181
+ export interface Project {
182
+ readonly id: string;
183
+ readonly clientId: string;
184
+ readonly name: string;
185
+ /** Rutas locales de repositorio que pertenecen a este proyecto. */
186
+ readonly repoPaths: readonly string[];
187
+ readonly billable: boolean;
188
+ /** Bloque mínimo de facturación en minutos. 0 = al segundo exacto. */
189
+ readonly roundingMinutes: number;
190
+ /**
191
+ * Cómo se trata el coste de IA frente al cliente:
192
+ * - "absorbed" : lo asumes tú. La factura no lo menciona. (Por defecto.)
193
+ * - "passthrough" : se repercute como línea aparte, convertido a la moneda
194
+ * del cliente con el tipo de cambio del día de emisión.
195
+ */
196
+ readonly aiCostPolicy: "absorbed" | "passthrough";
197
+ readonly kind: ProjectKind;
198
+ }
199
+ /**
200
+ * Una tarifa con vigencia. Subir el precio a mitad de proyecto no puede
201
+ * reescribir lo ya trabajado, así que las tarifas se apilan por fecha en vez de
202
+ * sobrescribirse.
203
+ */
204
+ export interface RatePeriod {
205
+ readonly projectId: string;
206
+ readonly hourlyRate: Money;
207
+ readonly effectiveFrom: Date;
208
+ readonly effectiveTo: Date | null;
209
+ }
210
+ /**
211
+ * De dónde salió una imputación.
212
+ *
213
+ * Importa porque reimportar rehace lo deducido de los agentes, y no puede
214
+ * llevarse por delante lo que escribiste a mano.
215
+ */
216
+ /**
217
+ * De donde salio una entrada de tiempo.
218
+ *
219
+ * - `agent`: deducida de los turnos de un agente. La mas precisa.
220
+ * - `commit`: deducida solo de los commits, para trabajo hecho sin agente. El
221
+ * tiempo anterior al primer commit de cada tanda es una estimacion,
222
+ * asi que es menos fiable y conviene que se distinga.
223
+ * - `manual`: la escribiste tu. Un import nunca la toca.
224
+ */
225
+ export type EntrySource = "agent" | "commit" | "manual";
226
+ export declare const ENTRY_SOURCE_LABELS: Record<EntrySource, string>;
227
+ /**
228
+ * Tipo de trabajo.
229
+ *
230
+ * Un proyecto no son solo commits. Las reuniones, los viajes, la investigación
231
+ * y los spikes son horas reales que se facturan igual, y sin poder registrarlas
232
+ * el parte del día miente por defecto: enseña solo la parte que dejó rastro en
233
+ * Git y hace parecer que el resto no ocurrió.
234
+ */
235
+ export type WorkKind = "development" | "meeting" | "research" | "review" | "travel" | "support" | "other";
236
+ export declare const WORK_KIND_LABELS: Record<WorkKind, string>;
237
+ /** Un bloque de trabajo imputado. Es lo que acaba en una línea de informe. */
238
+ export interface TimeEntry {
239
+ readonly id: string;
240
+ readonly projectId: string;
241
+ readonly startedAt: Date;
242
+ readonly endedAt: Date;
243
+ readonly seconds: number;
244
+ readonly description: string;
245
+ readonly billable: boolean;
246
+ /** `null` mientras no se haya emitido factura. */
247
+ readonly invoiceId: string | null;
248
+ readonly aiCost: AiCost;
249
+ readonly agentSeconds: number;
250
+ readonly commitHashes: readonly string[];
251
+ readonly agents: readonly AgentKind[];
252
+ readonly source: EntrySource;
253
+ readonly kind: WorkKind;
254
+ /** Rama del trabajo. Permite agrupar el esfuerzo por funcionalidad. */
255
+ readonly branch: string | null;
256
+ }
257
+ export interface InvoiceLine {
258
+ readonly description: string;
259
+ readonly seconds: number;
260
+ readonly hourlyRate: Money;
261
+ readonly amount: Money;
262
+ }
263
+ /**
264
+ * Una factura emitida.
265
+ *
266
+ * Es un documento **inmutable**. Guarda su propio tipo de cambio y sus propias
267
+ * tarifas: si mañana cambia el euro o subes el precio por hora, esta factura
268
+ * debe seguir dando exactamente el mismo total.
269
+ */
270
+ export interface Invoice {
271
+ readonly id: string;
272
+ readonly number: string;
273
+ readonly clientId: string;
274
+ readonly projectId: string;
275
+ readonly issuedAt: Date;
276
+ /** Fecha de corte: se incluye todo lo trabajado hasta aquí, inclusive. */
277
+ readonly cutoffAt: Date;
278
+ readonly periodStart: Date;
279
+ readonly currency: Currency;
280
+ readonly lines: readonly InvoiceLine[];
281
+ readonly subtotal: Money;
282
+ readonly total: Money;
283
+ readonly totalSeconds: number;
284
+ /** Coste de IA del periodo, en USD, siempre registrado aunque no se repercuta. */
285
+ readonly aiCost: AiCost;
286
+ /** Tipo de cambio USD -> moneda de la factura, capturado al emitir. */
287
+ readonly usdFxRate: number | null;
288
+ readonly aiCostBilled: Money | null;
289
+ /**
290
+ * Parte de la cuota de suscripción imputable a este periodo, congelada al
291
+ * emitir.
292
+ *
293
+ * Sin congelarla, reimprimir un informe de agosto en diciembre daría otra
294
+ * cifra: el reparto se recalcula con todo el consumo conocido, y en
295
+ * diciembre se conoce más. Un documento que cambia solo no es un documento.
296
+ */
297
+ readonly aiAmortized: Money | null;
298
+ readonly notes?: string;
299
+ }
300
+ /**
301
+ * Tu propio plan, no el de un equipo al que te hayan invitado.
302
+ *
303
+ * `free` es 100% local, como siempre. `pro` sincroniza tu propio historial a
304
+ * tu propia nube para tener el mismo dashboard en varias máquinas — sigue
305
+ * siendo solo tuyo, nadie más lo ve.
306
+ */
307
+ export type CloudPlan = "free" | "pro";
308
+ /**
309
+ * Qué sale de esta máquina para un proyecto, y por qué.
310
+ *
311
+ * - "personal": es tu cuenta Pro sincronizando tu propio trabajo.
312
+ * - "team" : te invitaron a un proyecto de un equipo. Solo ESE proyecto
313
+ * sincroniza, nunca el resto de tu trabajo, aunque también
314
+ * seas Pro por tu cuenta.
315
+ *
316
+ * Un proyecto tiene un único alcance a la vez: nunca los dos scopes compiten
317
+ * por la misma fila.
318
+ */
319
+ export type ProjectSyncScope = "personal" | "team";
320
+ /**
321
+ * Tu decisión sobre compartir tu uso de IA en un proyecto de equipo.
322
+ *
323
+ * Solo existe cuando quien invitó lo pidió explícitamente y tú eres empleado
324
+ * de esa organización, nunca para un freelance (ver `InviteeKind`). Aceptar
325
+ * el proyecto y conceder esto son decisiones separadas: puedes aceptar el
326
+ * primero y declinar la segunda, y esa combinación es válida y se comunica
327
+ * como tal, no como un dato que falta.
328
+ */
329
+ export type AiConsentDecision = "pending" | "granted" | "declined";
330
+ /**
331
+ * Cómo clasifica quien invita a la persona que invita a un proyecto.
332
+ *
333
+ * Cambia el trato posible, no es cosmético: un freelance nunca puede exponer
334
+ * su consumo de IA a quien le paga la factura (`requestsAiVisibility` es
335
+ * estructuralmente imposible para "freelancer"); un empleado de la propia
336
+ * empresa sí puede, porque el gasto sale del bolsillo del empleador, no del
337
+ * suyo — pero solo si además consiente (`AiConsentDecision`).
338
+ */
339
+ export type InviteeKind = "freelancer" | "employee";
340
+ /** La cuenta cloud vinculada a esta máquina. Si no existe, todo es local. */
341
+ export interface CloudAccount {
342
+ readonly accountId: string;
343
+ readonly email: string;
344
+ readonly plan: CloudPlan;
345
+ readonly deviceToken: string;
346
+ readonly apiBaseUrl: string;
347
+ readonly linkedAt: Date;
348
+ }
349
+ /** Qué proyecto local corresponde a qué proyecto remoto, y bajo qué alcance. */
350
+ export interface ProjectSync {
351
+ readonly projectId: string;
352
+ readonly scope: ProjectSyncScope;
353
+ readonly remoteProjectId: string;
354
+ /** Solo cuando `scope` es "team". */
355
+ readonly remoteOrgId: string | null;
356
+ readonly inviteToken: string | null;
357
+ }
358
+ /** Tu consentimiento de IA para un proyecto de equipo concreto. */
359
+ export interface AiConsentRecord {
360
+ readonly projectId: string;
361
+ readonly requested: boolean;
362
+ readonly decision: AiConsentDecision;
363
+ readonly decidedAt: Date | null;
364
+ }
365
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.WORK_KIND_LABELS = exports.ENTRY_SOURCE_LABELS = exports.PROJECT_KIND_LABELS = exports.EMPTY_USAGE = void 0;
4
+ exports.EMPTY_USAGE = {
5
+ input: 0, output: 0, cacheRead: 0, cacheWrite5m: 0, cacheWrite1h: 0,
6
+ };
7
+ exports.PROJECT_KIND_LABELS = {
8
+ client: "Cliente",
9
+ employment: "Nómina",
10
+ internal: "Interno",
11
+ };
12
+ exports.ENTRY_SOURCE_LABELS = {
13
+ agent: "con agente", commit: "deducido de commits", manual: "anadido a mano",
14
+ };
15
+ exports.WORK_KIND_LABELS = {
16
+ development: "Desarrollo",
17
+ meeting: "Reunión",
18
+ research: "Investigación",
19
+ review: "Revisión",
20
+ travel: "Desplazamiento",
21
+ support: "Soporte",
22
+ other: "Otro",
23
+ };
24
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":";;;AA8Ca,QAAA,WAAW,GAAe;IACrC,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,YAAY,EAAE,CAAC,EAAE,YAAY,EAAE,CAAC;CACpE,CAAC;AA8JW,QAAA,mBAAmB,GAAgC;IAC9D,MAAM,EAAE,SAAS;IACjB,UAAU,EAAE,QAAQ;IACpB,QAAQ,EAAE,SAAS;CACpB,CAAC;AAkDW,QAAA,mBAAmB,GAAgC;IAC9D,KAAK,EAAE,YAAY,EAAE,MAAM,EAAE,qBAAqB,EAAE,MAAM,EAAE,gBAAgB;CAC7E,CAAC;AAmBW,QAAA,gBAAgB,GAA6B;IACxD,WAAW,EAAE,YAAY;IACzB,OAAO,EAAE,SAAS;IAClB,QAAQ,EAAE,eAAe;IACzB,MAAM,EAAE,UAAU;IAClB,MAAM,EAAE,gBAAgB;IACxB,OAAO,EAAE,SAAS;IAClB,KAAK,EAAE,MAAM;CACd,CAAC"}
@@ -0,0 +1,5 @@
1
+ {
2
+ "name": "@estela/shared",
3
+ "version": "0.1.0",
4
+ "main": "dist/index.js"
5
+ }
package/package.json ADDED
@@ -0,0 +1,41 @@
1
+ {
2
+ "name": "estela",
3
+ "version": "0.1.0",
4
+ "description": "Registro de horas de desarrollo que no hay que rellenar: lee lo que tus agentes de IA y tu Git ya escribieron en disco.",
5
+ "keywords": [
6
+ "time-tracking",
7
+ "git",
8
+ "claude-code",
9
+ "ai-cost",
10
+ "freelance"
11
+ ],
12
+ "license": "MIT",
13
+ "type": "commonjs",
14
+ "main": "dist/index.js",
15
+ "bin": {
16
+ "estela": "dist/cli.js"
17
+ },
18
+ "engines": {
19
+ "node": ">=22.5.0"
20
+ },
21
+ "repository": {
22
+ "type": "git",
23
+ "url": "git+https://gitlab.com/jdaniel.leonruiz/estela.git"
24
+ },
25
+ "homepage": "https://getestela.dev",
26
+ "files": [
27
+ "dist",
28
+ "web",
29
+ "README.md"
30
+ ],
31
+ "dependencies": {
32
+ "@estela/shared": "0.1.0"
33
+ },
34
+ "bundleDependencies": [
35
+ "@estela/shared"
36
+ ],
37
+ "scripts": {
38
+ "prepack": "node ../../scripts/prepack.js",
39
+ "postpack": "node ../../scripts/postpack.js"
40
+ }
41
+ }