estela 0.1.39 → 0.2.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.
- package/README.md +96 -0
- package/dist/billing/assign.d.ts +45 -0
- package/dist/billing/assign.d.ts.map +1 -0
- package/dist/billing/assign.js +159 -0
- package/dist/billing/assign.js.map +1 -0
- package/dist/billing/assign.test.d.ts +2 -0
- package/dist/billing/assign.test.d.ts.map +1 -0
- package/dist/billing/assign.test.js +153 -0
- package/dist/billing/assign.test.js.map +1 -0
- package/dist/bin.js +23 -1
- package/dist/bin.js.map +1 -1
- package/dist/cli-import-recover.test.d.ts +2 -0
- package/dist/cli-import-recover.test.d.ts.map +1 -0
- package/dist/cli-import-recover.test.js +191 -0
- package/dist/cli-import-recover.test.js.map +1 -0
- package/dist/cli-quota.test.d.ts +2 -0
- package/dist/cli-quota.test.d.ts.map +1 -0
- package/dist/cli-quota.test.js +281 -0
- package/dist/cli-quota.test.js.map +1 -0
- package/dist/cli-statusline.test.d.ts +2 -0
- package/dist/cli-statusline.test.d.ts.map +1 -0
- package/dist/cli-statusline.test.js +333 -0
- package/dist/cli-statusline.test.js.map +1 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +350 -49
- package/dist/cli.js.map +1 -1
- package/dist/db/store.d.ts +12 -1
- package/dist/db/store.d.ts.map +1 -1
- package/dist/db/store.js +52 -0
- package/dist/db/store.js.map +1 -1
- package/dist/demo.d.ts.map +1 -1
- package/dist/demo.js +53 -0
- package/dist/demo.js.map +1 -1
- package/dist/i18n/en.d.ts.map +1 -1
- package/dist/i18n/en.js +68 -0
- package/dist/i18n/en.js.map +1 -1
- package/dist/metrics/quota.d.ts +226 -0
- package/dist/metrics/quota.d.ts.map +1 -0
- package/dist/metrics/quota.js +291 -0
- package/dist/metrics/quota.js.map +1 -0
- package/dist/metrics/quota.test.d.ts +2 -0
- package/dist/metrics/quota.test.d.ts.map +1 -0
- package/dist/metrics/quota.test.js +325 -0
- package/dist/metrics/quota.test.js.map +1 -0
- package/dist/project-of.d.ts +52 -0
- package/dist/project-of.d.ts.map +1 -0
- package/dist/project-of.js +124 -0
- package/dist/project-of.js.map +1 -0
- package/dist/project-of.test.d.ts +2 -0
- package/dist/project-of.test.d.ts.map +1 -0
- package/dist/project-of.test.js +203 -0
- package/dist/project-of.test.js.map +1 -0
- package/dist/quota.d.ts +27 -0
- package/dist/quota.d.ts.map +1 -0
- package/dist/quota.js +79 -0
- package/dist/quota.js.map +1 -0
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +17 -26
- package/dist/server.js.map +1 -1
- package/dist/server.test.js +37 -0
- package/dist/server.test.js.map +1 -1
- package/dist/statusline-cache.d.ts +35 -0
- package/dist/statusline-cache.d.ts.map +1 -0
- package/dist/statusline-cache.js +57 -0
- package/dist/statusline-cache.js.map +1 -0
- package/dist/statusline-install.d.ts +52 -0
- package/dist/statusline-install.d.ts.map +1 -0
- package/dist/statusline-install.js +115 -0
- package/dist/statusline-install.js.map +1 -0
- package/dist/statusline-run.d.ts +8 -0
- package/dist/statusline-run.d.ts.map +1 -0
- package/dist/statusline-run.js +256 -0
- package/dist/statusline-run.js.map +1 -0
- package/dist/statusline.d.ts +71 -0
- package/dist/statusline.d.ts.map +1 -0
- package/dist/statusline.js +139 -0
- package/dist/statusline.js.map +1 -0
- package/dist/statusline.test.d.ts +2 -0
- package/dist/statusline.test.d.ts.map +1 -0
- package/dist/statusline.test.js +197 -0
- package/dist/statusline.test.js.map +1 -0
- package/dist/watchers/claude.d.ts +11 -1
- package/dist/watchers/claude.d.ts.map +1 -1
- package/dist/watchers/claude.js +32 -15
- package/dist/watchers/claude.js.map +1 -1
- package/package.json +1 -1
- package/web/app.css +36 -0
- package/web/app.js +61 -1
- package/web/i18n.js +28 -0
- package/web/index.html +4 -1
package/README.md
CHANGED
|
@@ -116,6 +116,11 @@ Two things trip people up before Estela even starts:
|
|
|
116
116
|
the standalone Node, then `nvm install lts` and `nvm use lts` from an
|
|
117
117
|
administrator terminal. `where.exe npm` should list a single path.
|
|
118
118
|
|
|
119
|
+
A session in a subfolder or a git worktree counts for its repository's
|
|
120
|
+
project. If an import would add hours to what you had already imported, Estela
|
|
121
|
+
shows you how many per project and asks first (`estela import --yes` skips the
|
|
122
|
+
question); it never moves hours between projects or touches an approved day.
|
|
123
|
+
|
|
119
124
|
## Reporting to a client
|
|
120
125
|
|
|
121
126
|
Projects are created as internal and without a rate, because making one up
|
|
@@ -189,6 +194,35 @@ different machine, pass `--token` with the existing one, or your client ends up
|
|
|
189
194
|
with a dead link. The Free plan allows one published dashboard at a time; Pro
|
|
190
195
|
and Teams have no limit.
|
|
191
196
|
|
|
197
|
+
The dashboard's footer says "Hours filled in by Estela", linking to
|
|
198
|
+
getestela.dev. On Pro and Teams, `--no-credit` removes that line.
|
|
199
|
+
|
|
200
|
+
## Showing what you built
|
|
201
|
+
|
|
202
|
+
A badge for your project's README, with its measured hours and what the AI
|
|
203
|
+
cost:
|
|
204
|
+
|
|
205
|
+
```sh
|
|
206
|
+
estela badge # the project for the repo you're in
|
|
207
|
+
estela badge --project acme-web
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
It prints a Markdown line with a static [shields.io](https://shields.io) image,
|
|
211
|
+
something like `Built in 42 h · $18 of AI`. Nothing is hosted and nothing is
|
|
212
|
+
looked up: the numbers are baked into the URL, so run it again to update them.
|
|
213
|
+
It only counts **measured** hours, never the ones estimated from commits.
|
|
214
|
+
|
|
215
|
+
And a receipt of your week (or of your whole history) as a PNG:
|
|
216
|
+
|
|
217
|
+
```sh
|
|
218
|
+
estela receipt --week
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
Measured versus estimated hours, the AI cost, what caching saved you, and the
|
|
222
|
+
most expensive project and branch. It's drawn on your machine and stays there.
|
|
223
|
+
Project and branch names are left out by default, because they're usually a
|
|
224
|
+
client's; add `--names` to include them.
|
|
225
|
+
|
|
192
226
|
## What the AI actually costs
|
|
193
227
|
|
|
194
228
|
On a flat subscription, your real spend isn't the sum of the tokens: it's the
|
|
@@ -202,6 +236,68 @@ estela ai-cost
|
|
|
202
236
|
Cache is counted separately because it's usually most of the bill — ignoring it
|
|
203
237
|
underestimated spend fivefold.
|
|
204
238
|
|
|
239
|
+
## Which project is eating your quota
|
|
240
|
+
|
|
241
|
+
On Claude Max/Pro or Codex with a subscription, the quota runs out before you
|
|
242
|
+
expect it and nothing tells you where it went. `estela quota` splits it by
|
|
243
|
+
project:
|
|
244
|
+
|
|
245
|
+
```sh
|
|
246
|
+
estela quota # current 5-hour window and this week, per project
|
|
247
|
+
estela quota --weighted # the % by list price instead of by tokens
|
|
248
|
+
estela quota --json # the same, for scripts
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
Tokens (input, output, cache read and cache write) per project, for the current
|
|
252
|
+
5-hour window and for this week (Monday to today, in your time zone), plus this
|
|
253
|
+
week against the average of your previous 4 over the same stretch. Claude Code
|
|
254
|
+
and Codex are shown separately: they draw on different quotas. It's also a card
|
|
255
|
+
in the Summary tab of `estela web`, and it's free.
|
|
256
|
+
|
|
257
|
+
A session in a subfolder or in a git worktree counts for its repo's project;
|
|
258
|
+
a folder that isn't a project shows up under its git root's name. The % is
|
|
259
|
+
by tokens by default; `--weighted` splits it by each model's list price
|
|
260
|
+
instead, which is not how the provider counts your limit. With the status
|
|
261
|
+
line installed (below), the 5-hour window is the one Claude Code reports
|
|
262
|
+
rather than an estimate.
|
|
263
|
+
|
|
264
|
+
What it never does is tell you how much you have left: Anthropic and OpenAI
|
|
265
|
+
don't publish their plans' limits, so any "X tokens left" would be made up. The
|
|
266
|
+
5-hour window is an estimate — how its start is rounded isn't public, and
|
|
267
|
+
anything you use on claude.ai or another machine counts too but isn't on this
|
|
268
|
+
one. Copilot is left out because it doesn't record reliable tokens. Everything
|
|
269
|
+
is calculated on your machine.
|
|
270
|
+
|
|
271
|
+
## A line under every Claude Code session
|
|
272
|
+
|
|
273
|
+
```sh
|
|
274
|
+
npm install -g estela # the status line needs estela on your PATH
|
|
275
|
+
estela statusline install
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
In a repository that isn't a project yet it shows `estela · web (no project) ·
|
|
279
|
+
estela setup`, and outside git just `estela · no project`. Each Claude Code
|
|
280
|
+
session in a project shows a line like this:
|
|
281
|
+
|
|
282
|
+
```
|
|
283
|
+
estela · Brightside Health › brightside-portal · today 2h14 · this project 21% · week 38% · 5h 62%
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
Hours measured today on this project (the same number the report will
|
|
287
|
+
give), this project's share of your Claude Code usage this week, and — only
|
|
288
|
+
if Claude Code sends them — its own weekly and 5-hour limit percentages, as
|
|
289
|
+
they come. Estela never estimates a limit or counts down to a reset. The
|
|
290
|
+
5-hour figure turns amber from 80%. On a narrow terminal the line drops the
|
|
291
|
+
5-hour figure first, then the week, then shortens the name, then this
|
|
292
|
+
project, then today; `estela` always stays. It runs in about 50 ms and only
|
|
293
|
+
reads what's new in the current session's transcript.
|
|
294
|
+
|
|
295
|
+
`install` merges the block into `~/.claude/settings.json` without touching
|
|
296
|
+
anything else, after making a backup. If you already have a status line it
|
|
297
|
+
won't overwrite it: `--force` replaces it, `--append` shows yours after
|
|
298
|
+
Estela's. `estela statusline uninstall` removes it (and puts yours back if
|
|
299
|
+
you used `--append`).
|
|
300
|
+
|
|
205
301
|
## Hours no import will infer
|
|
206
302
|
|
|
207
303
|
Meetings, travel, research, and development without an agent that didn't leave
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { DatabaseSync } from "node:sqlite";
|
|
2
|
+
import type { TimeEntry } from "@estela/shared";
|
|
3
|
+
import { type WorkBlock } from "./sessionize.js";
|
|
4
|
+
/**
|
|
5
|
+
* De bloques de trabajo a imputaciones: a qué proyecto va cada uno y con qué
|
|
6
|
+
* id se guarda. Lo usan `estela import` y el import automático del panel, que
|
|
7
|
+
* antes lo tenían copiado cada uno por su lado.
|
|
8
|
+
*
|
|
9
|
+
* La raíz git (worktrees, subcarpetas de un repo sin la carpeta exacta
|
|
10
|
+
* registrada) recupera horas que antes se perdían, con tres reglas:
|
|
11
|
+
*
|
|
12
|
+
* 1. Solo se reasigna lo que hoy NO tiene proyecto. La carpeta tal cual se
|
|
13
|
+
* mira primero, así que nada de lo ya asignado cambia de proyecto.
|
|
14
|
+
* 2. Un día aprobado o facturado de ese proyecto no se toca: las horas que se
|
|
15
|
+
* recuperarían ahí se informan (`blocked`), no se aplican.
|
|
16
|
+
* 3. Recuperar es opcional (`recover`): la primera vez lo confirma la persona.
|
|
17
|
+
*/
|
|
18
|
+
export interface PlannedEntry {
|
|
19
|
+
readonly entry: Omit<TimeEntry, "id"> & {
|
|
20
|
+
id: string;
|
|
21
|
+
};
|
|
22
|
+
/** Segundos que esta imputación gana por la raíz git (0 si no cambia). */
|
|
23
|
+
readonly recoveredSeconds: number;
|
|
24
|
+
}
|
|
25
|
+
export interface RecoveryBlocked {
|
|
26
|
+
readonly projectId: string;
|
|
27
|
+
readonly day: string;
|
|
28
|
+
readonly seconds: number;
|
|
29
|
+
}
|
|
30
|
+
export interface AssignmentPlan {
|
|
31
|
+
readonly entries: readonly PlannedEntry[];
|
|
32
|
+
/** Bloques tras agrupar por rama y día, con y sin proyecto. */
|
|
33
|
+
readonly grouped: number;
|
|
34
|
+
/** De esos, los que siguen sin proyecto. */
|
|
35
|
+
readonly unassigned: number;
|
|
36
|
+
/** Segundos recuperados que SÍ se aplican, por proyecto. */
|
|
37
|
+
readonly recovered: ReadonlyMap<string, number>;
|
|
38
|
+
/** Lo que se recuperaría en días aprobados o facturados: no se aplica. */
|
|
39
|
+
readonly blocked: readonly RecoveryBlocked[];
|
|
40
|
+
}
|
|
41
|
+
export declare function entryId(projectId: string, day: string, branch: string | null): string;
|
|
42
|
+
export declare function planEntries(db: DatabaseSync, raw: readonly WorkBlock[], options: {
|
|
43
|
+
readonly recover: boolean;
|
|
44
|
+
}): AssignmentPlan;
|
|
45
|
+
//# sourceMappingURL=assign.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"assign.d.ts","sourceRoot":"","sources":["../../src/billing/assign.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAKhD,OAAO,EAAsC,KAAK,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAErF;;;;;;;;;;;;;GAaG;AAEH,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,KAAK,EAAE,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,GAAG;QAAE,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC;IACvD,0EAA0E;IAC1E,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;CACnC;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,OAAO,EAAE,SAAS,YAAY,EAAE,CAAC;IAC1C,+DAA+D;IAC/D,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,4CAA4C;IAC5C,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,4DAA4D;IAC5D,QAAQ,CAAC,SAAS,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChD,0EAA0E;IAC1E,QAAQ,CAAC,OAAO,EAAE,SAAS,eAAe,EAAE,CAAC;CAC9C;AAED,wBAAgB,OAAO,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAKrF;AAED,wBAAgB,WAAW,CACzB,EAAE,EAAE,YAAY,EAAE,GAAG,EAAE,SAAS,SAAS,EAAE,EAAE,OAAO,EAAE;IAAE,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;CAAE,GAClF,cAAc,CAwEhB"}
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.entryId = entryId;
|
|
37
|
+
exports.planEntries = planEntries;
|
|
38
|
+
const shared_1 = require("@estela/shared");
|
|
39
|
+
const store = __importStar(require("../db/store.js"));
|
|
40
|
+
const project_of_js_1 = require("../project-of.js");
|
|
41
|
+
const sessionize_js_1 = require("./sessionize.js");
|
|
42
|
+
function entryId(projectId, day, branch) {
|
|
43
|
+
// Determinista (proyecto + día + rama) para que reimportar actualice en vez
|
|
44
|
+
// de duplicar. La fecha, la LOCAL: la misma que usa el servidor.
|
|
45
|
+
const slug = (branch ?? "sin-rama").replace(/[^a-zA-Z0-9]+/g, "-");
|
|
46
|
+
return `te_${projectId}_${day}_${slug}`;
|
|
47
|
+
}
|
|
48
|
+
function planEntries(db, raw, options) {
|
|
49
|
+
const ownerOf = (0, project_of_js_1.ownerResolver)(db);
|
|
50
|
+
const direct = (repo) => (repo ? store.projectForRepo(db, repo) : null);
|
|
51
|
+
const viaRoot = (repo) => {
|
|
52
|
+
if (!repo)
|
|
53
|
+
return null;
|
|
54
|
+
const o = ownerOf(repo);
|
|
55
|
+
return o.kind === "project" ? o.projectId : null;
|
|
56
|
+
};
|
|
57
|
+
// Lo de siempre: la carpeta tal cual. Se agrupa por PROYECTO, no por ruta:
|
|
58
|
+
// `repo/` y `repo/src/UI` son el mismo proyecto, y agrupar por ruta daba dos
|
|
59
|
+
// bloques que luego se pisaban al guardarse con el mismo id.
|
|
60
|
+
const keyDirect = (b) => direct(b.repoPath) ?? b.repoPath ?? "";
|
|
61
|
+
const groupedBase = (0, sessionize_js_1.groupByBranchAndDay)(raw, keyDirect);
|
|
62
|
+
const base = byId(groupedBase, direct);
|
|
63
|
+
if (!options.recover) {
|
|
64
|
+
return {
|
|
65
|
+
entries: [...base.values()].map((g) => ({ entry: toEntry(db, g.projectId, g.block), recoveredSeconds: 0 })),
|
|
66
|
+
grouped: groupedBase.length,
|
|
67
|
+
unassigned: groupedBase.filter((b) => !direct(b.repoPath)).length,
|
|
68
|
+
recovered: new Map(), blocked: [],
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
// Con la raíz git, pero solo para lo que no tenía proyecto (regla 1).
|
|
72
|
+
const assigned = (repo) => direct(repo) ?? viaRoot(repo);
|
|
73
|
+
const keyFull = (b) => assigned(b.repoPath) ?? b.repoPath ?? "";
|
|
74
|
+
const groupedFull = (0, sessionize_js_1.groupByBranchAndDay)(raw, keyFull);
|
|
75
|
+
const full = byId(groupedFull, assigned);
|
|
76
|
+
// Qué imputaciones cambian: las que reciben algún bloque recuperado.
|
|
77
|
+
const touched = new Set();
|
|
78
|
+
for (const b of raw) {
|
|
79
|
+
if (direct(b.repoPath))
|
|
80
|
+
continue;
|
|
81
|
+
const p = viaRoot(b.repoPath);
|
|
82
|
+
if (p)
|
|
83
|
+
touched.add(entryId(p, (0, shared_1.localDate)(b.startedAt), b.branch));
|
|
84
|
+
}
|
|
85
|
+
const lockedDays = new Map();
|
|
86
|
+
const locked = (projectId, day) => {
|
|
87
|
+
const k = `${projectId}\0${day}`;
|
|
88
|
+
if (!lockedDays.has(k)) {
|
|
89
|
+
lockedDays.set(k, !!db.prepare(`SELECT 1 FROM time_entries WHERE project_id = ? AND local_date = ?
|
|
90
|
+
AND (approved = 1 OR invoice_id IS NOT NULL) LIMIT 1`).get(projectId, day));
|
|
91
|
+
}
|
|
92
|
+
return lockedDays.get(k);
|
|
93
|
+
};
|
|
94
|
+
const entries = [];
|
|
95
|
+
const recovered = new Map();
|
|
96
|
+
const blocked = [];
|
|
97
|
+
for (const [id, g] of full) {
|
|
98
|
+
const before = base.get(id);
|
|
99
|
+
if (!touched.has(id)) {
|
|
100
|
+
entries.push({ entry: toEntry(db, g.projectId, g.block), recoveredSeconds: 0 });
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
103
|
+
const gain = Math.max(0, g.block.seconds - (before?.block.seconds ?? 0));
|
|
104
|
+
const day = (0, shared_1.localDate)(g.block.startedAt);
|
|
105
|
+
if (locked(g.projectId, day)) {
|
|
106
|
+
// Regla 2: el día se queda como estaba; si antes no había nada, nada.
|
|
107
|
+
if (before)
|
|
108
|
+
entries.push({ entry: toEntry(db, before.projectId, before.block), recoveredSeconds: 0 });
|
|
109
|
+
if (gain > 0)
|
|
110
|
+
blocked.push({ projectId: g.projectId, day, seconds: gain });
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
entries.push({ entry: toEntry(db, g.projectId, g.block), recoveredSeconds: gain });
|
|
114
|
+
if (gain > 0)
|
|
115
|
+
recovered.set(g.projectId, (recovered.get(g.projectId) ?? 0) + gain);
|
|
116
|
+
}
|
|
117
|
+
return {
|
|
118
|
+
entries, grouped: groupedFull.length,
|
|
119
|
+
unassigned: groupedFull.filter((b) => !assigned(b.repoPath)).length,
|
|
120
|
+
recovered, blocked,
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
/** Los bloques agrupados con proyecto, por el id con el que se guardarían. */
|
|
124
|
+
function byId(blocks, projectOf) {
|
|
125
|
+
const out = new Map();
|
|
126
|
+
for (const block of blocks) {
|
|
127
|
+
const projectId = projectOf(block.repoPath);
|
|
128
|
+
if (!projectId)
|
|
129
|
+
continue;
|
|
130
|
+
out.set(entryId(projectId, (0, shared_1.localDate)(block.startedAt), block.branch), { projectId, block });
|
|
131
|
+
}
|
|
132
|
+
return out;
|
|
133
|
+
}
|
|
134
|
+
function toEntry(db, projectId, block) {
|
|
135
|
+
const project = store.getProject(db, projectId);
|
|
136
|
+
return {
|
|
137
|
+
id: entryId(projectId, (0, shared_1.localDate)(block.startedAt), block.branch),
|
|
138
|
+
projectId,
|
|
139
|
+
startedAt: block.startedAt,
|
|
140
|
+
endedAt: block.endedAt,
|
|
141
|
+
seconds: block.seconds,
|
|
142
|
+
description: (0, sessionize_js_1.describeBlock)(block),
|
|
143
|
+
billable: project.billable,
|
|
144
|
+
invoiceId: null,
|
|
145
|
+
aiCost: block.aiCost,
|
|
146
|
+
// Un bloque deducido solo de commits no tuvo agente: ni segundos de
|
|
147
|
+
// agente ni agente al que atribuirlos.
|
|
148
|
+
agentSeconds: block.turnCount > 0 ? block.seconds : 0,
|
|
149
|
+
commitHashes: block.commits.map((c) => c.hash),
|
|
150
|
+
agents: block.turnCount > 0 ? [...(block.agents ?? ["claude-code"])] : [],
|
|
151
|
+
source: block.turnCount > 0 ? "agent" : "commit",
|
|
152
|
+
kind: "development",
|
|
153
|
+
branch: block.branch,
|
|
154
|
+
// El ticket se saca aquí: en la base solo quedan los hashes de los
|
|
155
|
+
// commits, y es en sus mensajes donde va el `AB#1234`.
|
|
156
|
+
workItems: (0, shared_1.extractWorkItems)(block.branch, block.commits.map((c) => c.subject), project.tracker),
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
//# sourceMappingURL=assign.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"assign.js","sourceRoot":"","sources":["../../src/billing/assign.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"assign.test.d.ts","sourceRoot":"","sources":["../../src/billing/assign.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
const strict_1 = __importDefault(require("node:assert/strict"));
|
|
40
|
+
const node_fs_1 = require("node:fs");
|
|
41
|
+
const node_os_1 = require("node:os");
|
|
42
|
+
const node_path_1 = require("node:path");
|
|
43
|
+
const node_test_1 = require("node:test");
|
|
44
|
+
const shared_1 = require("@estela/shared");
|
|
45
|
+
const schema_js_1 = require("../db/schema.js");
|
|
46
|
+
const store = __importStar(require("../db/store.js"));
|
|
47
|
+
const paths_js_1 = require("../paths.js");
|
|
48
|
+
const assign_js_1 = require("./assign.js");
|
|
49
|
+
const sessionize_js_1 = require("./sessionize.js");
|
|
50
|
+
/**
|
|
51
|
+
* Las reglas de la recuperación de horas por raíz git, en el sitio donde se
|
|
52
|
+
* deciden. Esto acaba en facturas: lo que no puede pasar es mover horas de un
|
|
53
|
+
* proyecto a otro, o cambiar un día que alguien ya aprobó o facturó.
|
|
54
|
+
*/
|
|
55
|
+
const n = (p) => (0, paths_js_1.normalizePath)(p);
|
|
56
|
+
/** main/ es un repo registrado como "web"; wt/ es un worktree suyo, sin registrar. */
|
|
57
|
+
function escenario() {
|
|
58
|
+
const base = (0, node_fs_1.realpathSync)((0, node_fs_1.mkdtempSync)((0, node_path_1.join)((0, node_os_1.tmpdir)(), "estela-assign-")));
|
|
59
|
+
const main = (0, node_path_1.join)(base, "web");
|
|
60
|
+
(0, node_fs_1.mkdirSync)((0, node_path_1.join)(main, ".git", "worktrees", "wt"), { recursive: true });
|
|
61
|
+
(0, node_fs_1.writeFileSync)((0, node_path_1.join)(main, ".git", "worktrees", "wt", "commondir"), "../..\n");
|
|
62
|
+
const wt = (0, node_path_1.join)(base, "web-wt");
|
|
63
|
+
(0, node_fs_1.mkdirSync)(wt);
|
|
64
|
+
(0, node_fs_1.writeFileSync)((0, node_path_1.join)(wt, ".git"), `gitdir: ${(0, node_path_1.join)(main, ".git", "worktrees", "wt")}\n`);
|
|
65
|
+
const db = (0, schema_js_1.openDatabase)(":memory:");
|
|
66
|
+
store.upsertClient(db, { id: "c", name: "C", currency: "EUR" });
|
|
67
|
+
store.upsertProject(db, { id: "web", clientId: "c", name: "Web", repoPaths: [n(main)], billable: true,
|
|
68
|
+
roundingMinutes: 0, aiCostPolicy: "absorbed", kind: "client" });
|
|
69
|
+
return { base, main, wt, db };
|
|
70
|
+
}
|
|
71
|
+
let k = 0;
|
|
72
|
+
/** Turnos seguidos de un minuto, en `cwd`, desde `start`. */
|
|
73
|
+
function sesion(cwd, start, minutos, branch = "main") {
|
|
74
|
+
return Array.from({ length: minutos }, (_, i) => ({
|
|
75
|
+
agent: "claude-code", turnId: `t${k++}`, sessionId: `s-${cwd}`,
|
|
76
|
+
at: new Date(start.getTime() + i * 60_000), model: "claude-sonnet-4-6",
|
|
77
|
+
repoPath: n(cwd), branch, producerVersion: null,
|
|
78
|
+
tokens: { input: 1, output: 1, cacheRead: 1, cacheWrite5m: 0, cacheWrite1h: 0 },
|
|
79
|
+
}));
|
|
80
|
+
}
|
|
81
|
+
const dia = (d, h = 10) => new Date(2026, 9, d, h, 0);
|
|
82
|
+
(0, node_test_1.test)("sin recuperar: el worktree se queda sin proyecto, como antes", () => {
|
|
83
|
+
const { main, wt, db } = escenario();
|
|
84
|
+
const blocks = (0, sessionize_js_1.sessionize)([...sesion(main, dia(5), 30), ...sesion(wt, dia(5, 14), 30, "feature")]);
|
|
85
|
+
const plan = (0, assign_js_1.planEntries)(db, blocks, { recover: false });
|
|
86
|
+
strict_1.default.deepEqual(plan.entries.map((e) => e.entry.branch), ["main"]);
|
|
87
|
+
strict_1.default.equal(plan.unassigned, 1);
|
|
88
|
+
});
|
|
89
|
+
(0, node_test_1.test)("recuperando: el worktree va a su proyecto, y se cuenta cuánto se recuperó", () => {
|
|
90
|
+
const { main, wt, db } = escenario();
|
|
91
|
+
const blocks = (0, sessionize_js_1.sessionize)([...sesion(main, dia(5), 30), ...sesion(wt, dia(5, 14), 30, "feature")]);
|
|
92
|
+
const plan = (0, assign_js_1.planEntries)(db, blocks, { recover: true });
|
|
93
|
+
const feature = plan.entries.find((e) => e.entry.branch === "feature");
|
|
94
|
+
strict_1.default.equal(feature.entry.projectId, "web");
|
|
95
|
+
strict_1.default.equal(feature.entry.id, (0, assign_js_1.entryId)("web", (0, shared_1.localDate)(dia(5)), "feature"));
|
|
96
|
+
strict_1.default.ok(feature.recoveredSeconds > 0);
|
|
97
|
+
strict_1.default.equal(plan.recovered.get("web"), feature.recoveredSeconds);
|
|
98
|
+
strict_1.default.equal(plan.unassigned, 0);
|
|
99
|
+
// Lo que ya estaba asignado no gana ni pierde nada.
|
|
100
|
+
strict_1.default.equal(plan.entries.find((e) => e.entry.branch === "main").recoveredSeconds, 0);
|
|
101
|
+
});
|
|
102
|
+
(0, node_test_1.test)("nunca mueve horas de un proyecto a otro: la carpeta registrada manda sobre la raíz git", () => {
|
|
103
|
+
// `web/packages/api` es su propio proyecto aunque su raíz git sea la de web.
|
|
104
|
+
const { main, db } = escenario();
|
|
105
|
+
const api = (0, node_path_1.join)(main, "packages", "api");
|
|
106
|
+
(0, node_fs_1.mkdirSync)(api, { recursive: true });
|
|
107
|
+
store.upsertProject(db, { id: "api", clientId: "c", name: "API", repoPaths: [n(api)], billable: true,
|
|
108
|
+
roundingMinutes: 0, aiCostPolicy: "absorbed", kind: "client" });
|
|
109
|
+
const plan = (0, assign_js_1.planEntries)(db, (0, sessionize_js_1.sessionize)(sesion(api, dia(6), 40)), { recover: true });
|
|
110
|
+
strict_1.default.deepEqual(plan.entries.map((e) => e.entry.projectId), ["api"]);
|
|
111
|
+
strict_1.default.equal(plan.recovered.size, 0);
|
|
112
|
+
});
|
|
113
|
+
(0, node_test_1.test)("un día aprobado o facturado no se toca: se informa con su fecha y sus horas", () => {
|
|
114
|
+
const { main, wt, db } = escenario();
|
|
115
|
+
// Lo de siempre, ya importado y aprobado el día 5.
|
|
116
|
+
const antes = (0, assign_js_1.planEntries)(db, (0, sessionize_js_1.sessionize)(sesion(main, dia(5), 30)), { recover: false });
|
|
117
|
+
for (const { entry } of antes.entries)
|
|
118
|
+
store.saveTimeEntry(db, entry);
|
|
119
|
+
db.prepare("UPDATE time_entries SET approved = 1 WHERE local_date = ?").run((0, shared_1.localDate)(dia(5)));
|
|
120
|
+
// Y uno facturado el día 6.
|
|
121
|
+
const otro = (0, assign_js_1.planEntries)(db, (0, sessionize_js_1.sessionize)(sesion(main, dia(6), 20)), { recover: false });
|
|
122
|
+
for (const { entry } of otro.entries)
|
|
123
|
+
store.saveTimeEntry(db, entry);
|
|
124
|
+
db.prepare(`INSERT INTO invoices (id, number, client_id, project_id, issued_at, cutoff_at, period_start,
|
|
125
|
+
currency, subtotal_minor, total_minor, total_seconds, lines_json) VALUES ('inv1', 'INF-1', 'c', 'web', ?, ?, ?, 'EUR', 0, 0, 0, '[]')`)
|
|
126
|
+
.run(dia(8).toISOString(), dia(6, 23).toISOString(), dia(1).toISOString());
|
|
127
|
+
db.prepare("UPDATE time_entries SET invoice_id = 'inv1' WHERE local_date = ?").run((0, shared_1.localDate)(dia(6)));
|
|
128
|
+
const blocks = (0, sessionize_js_1.sessionize)([
|
|
129
|
+
...sesion(main, dia(5), 30), // lo que ya había
|
|
130
|
+
...sesion(wt, dia(5, 14), 30), // worktree, misma rama y día aprobado: se fusionaría
|
|
131
|
+
...sesion(wt, dia(6, 15), 25, "fix"), // worktree en día facturado
|
|
132
|
+
...sesion(wt, dia(7, 9), 45, "feature"), // worktree en día libre
|
|
133
|
+
]);
|
|
134
|
+
const plan = (0, assign_js_1.planEntries)(db, blocks, { recover: true });
|
|
135
|
+
// El día 5 se guarda exactamente como estaba: sin los minutos del worktree.
|
|
136
|
+
const del5 = plan.entries.find((e) => e.entry.id === (0, assign_js_1.entryId)("web", (0, shared_1.localDate)(dia(5)), "main"));
|
|
137
|
+
strict_1.default.equal(del5.entry.seconds, antes.entries[0].entry.seconds);
|
|
138
|
+
// El 6 no gana una imputación nueva; el 7 sí.
|
|
139
|
+
strict_1.default.ok(!plan.entries.some((e) => e.entry.id === (0, assign_js_1.entryId)("web", (0, shared_1.localDate)(dia(6)), "fix")));
|
|
140
|
+
strict_1.default.ok(plan.entries.some((e) => e.entry.id === (0, assign_js_1.entryId)("web", (0, shared_1.localDate)(dia(7)), "feature")));
|
|
141
|
+
strict_1.default.deepEqual(plan.blocked.map((b) => [b.projectId, b.day]).sort(), [["web", (0, shared_1.localDate)(dia(5))], ["web", (0, shared_1.localDate)(dia(6))]]);
|
|
142
|
+
strict_1.default.ok(plan.blocked.every((b) => b.seconds > 0));
|
|
143
|
+
strict_1.default.deepEqual([...plan.recovered.keys()], ["web"], "solo lo del día 7");
|
|
144
|
+
});
|
|
145
|
+
(0, node_test_1.test)("una sesión del worktree a la misma hora que la del repo no suma nada: se unen intervalos", () => {
|
|
146
|
+
// Dos sesiones a la vez son la misma persona: import une los intervalos, y
|
|
147
|
+
// recuperar no puede contar dos veces el mismo rato.
|
|
148
|
+
const { main, wt, db } = escenario();
|
|
149
|
+
const plan = (0, assign_js_1.planEntries)(db, (0, sessionize_js_1.sessionize)([...sesion(main, dia(9), 30), ...sesion(wt, dia(9), 30)]), { recover: true });
|
|
150
|
+
strict_1.default.equal(plan.recovered.size, 0);
|
|
151
|
+
strict_1.default.equal(plan.entries.length, 1);
|
|
152
|
+
});
|
|
153
|
+
//# sourceMappingURL=assign.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"assign.test.js","sourceRoot":"","sources":["../../src/billing/assign.test.ts"],"names":[],"mappings":"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"}
|
package/dist/bin.js
CHANGED
|
@@ -20,7 +20,29 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
20
20
|
const node_version_js_1 = require("./node-version.js");
|
|
21
21
|
const index_js_1 = require("./i18n/index.js");
|
|
22
22
|
const MIN_NODE = "22.5.0";
|
|
23
|
-
|
|
23
|
+
// `estela statusline` (y no `statusline install|uninstall`, que son de la CLI):
|
|
24
|
+
// lo ejecuta Claude Code en cada evento de la sesión y tiene 100 ms. Va por un
|
|
25
|
+
// camino aparte que no carga cli.js, y en un Node viejo imprime "estela" y
|
|
26
|
+
// sale con 0 en vez de un error: la status line no se rompe nunca.
|
|
27
|
+
const sub = process.argv[3];
|
|
28
|
+
const esStatusline = process.argv[2] === "statusline" &&
|
|
29
|
+
!["install", "uninstall", "help", "--help"].includes(sub ?? "");
|
|
30
|
+
if (esStatusline) {
|
|
31
|
+
// El aviso de que node:sqlite es experimental iría a stderr en cada evento.
|
|
32
|
+
process.removeAllListeners("warning");
|
|
33
|
+
process.on("warning", () => { });
|
|
34
|
+
if ((0, node_version_js_1.tooOld)(process.version, MIN_NODE)) {
|
|
35
|
+
process.stdout.write("estela\n");
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
const i = process.argv.indexOf("--lang");
|
|
39
|
+
(0, index_js_1.setLang)((0, index_js_1.detectLang)({ flag: i >= 0 ? process.argv[i + 1] : undefined, env: process.env, platform: process.platform }));
|
|
40
|
+
void import("./statusline-run.js")
|
|
41
|
+
.then((m) => m.runStatusline(process.argv.slice(3)))
|
|
42
|
+
.catch(() => { process.stdout.write("estela\n"); });
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
else if ((0, node_version_js_1.tooOld)(process.version, MIN_NODE)) {
|
|
24
46
|
// Aquí aún no se ha leído ningún argumento: `--lang` se busca a mano.
|
|
25
47
|
const i = process.argv.indexOf("--lang");
|
|
26
48
|
(0, index_js_1.setLang)((0, index_js_1.detectLang)({ flag: i >= 0 ? process.argv[i + 1] : undefined, env: process.env, platform: process.platform }));
|
package/dist/bin.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bin.js","sourceRoot":"","sources":["../src/bin.ts"],"names":[],"mappings":";;AAEA;;;;;;;;;;;;;;;GAeG;;AAEH,uDAA2D;AAC3D,8CAA0D;AAE1D,MAAM,QAAQ,GAAG,QAAQ,CAAC;AAE1B,IAAI,IAAA,wBAAM,EAAC,OAAO,CAAC,OAAO,EAAE,QAAQ,CAAC,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"bin.js","sourceRoot":"","sources":["../src/bin.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli-import-recover.test.d.ts","sourceRoot":"","sources":["../src/cli-import-recover.test.ts"],"names":[],"mappings":""}
|