worktrust 0.6.14 → 0.6.15

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "worktrust",
3
- "version": "0.6.14",
3
+ "version": "0.6.15",
4
4
  "description": "Couple this computer to WorkTrust: approve a code in your browser, and every AI app here reports what you built. Metadata only, no dependencies.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,8 +1,8 @@
1
1
  /**
2
2
  * --summary: what a person or a team reads from the archive, counted locally after the chain checks out. Per month the
3
3
  * hours, the tokens per model and the share of input served from the cache (where the tokens go, and so where they
4
- * can be saved); and the PARALLEL WORK: per project and day the sessions' summed time against the wall-clock time they
5
- * covered together, and the most open at once. A stretch without a project or a measured token stays out of that
4
+ * can be saved); and the PARALLEL WORK: per project and day the sessions' open spans added up against the wall-clock
5
+ * time they covered together (spans on both sides, so the sum is never below the clock), and the most open at once. A stretch without a project or a measured token stays out of that
6
6
  * figure; nothing is estimated.
7
7
  *
8
8
  * Pure: the archive's verified lines in, the lines to print out. No file, no clock, no network.
@@ -31,7 +31,7 @@ export function summaryLines(lines) {
31
31
  const edges = spans.flatMap(([from, to]) => [[from, 1], [to, -1]]).sort((a, b) => a[0] - b[0] || a[1] - b[1]);
32
32
  let open = 0, top = 0, covered = 0, since = 0;
33
33
  for (const [at, step] of edges) { if (open > 0) covered += at - since; open += step; since = at; top = Math.max(top, open); }
34
- if (top > 1) { parallelDays += 1; peak = Math.max(peak, top); summed += spans.reduce((sum, span) => sum + span[2], 0); wall += covered / 1000; }
34
+ if (top > 1) { parallelDays += 1; peak = Math.max(peak, top); summed += spans.reduce((sum, [from, to]) => sum + (to - from), 0) / 1000; wall += covered / 1000; }
35
35
  }
36
36
  if (parallelDays > 0) out.push(` parallel sessions in one project on ${parallelDays} day${parallelDays === 1 ? "" : "s"}: up to ${peak} at once, ${hours(summed)} h of session time in ${hours(wall)} h of wall-clock time`);
37
37
  }
package/preserve.mjs CHANGED
@@ -274,12 +274,12 @@ function archive(dir) {
274
274
  // A WHOLE ANSWER OR NOTHING: the hook ends with its own count; one line missing or cut is a read to refuse, never part of an archive.
275
275
  const rows = hook.stdout.split("\n").filter(Boolean), end = (() => { try { return JSON.parse(rows.at(-1) ?? "").archive_end; } catch { return undefined; } })();
276
276
  if (!Number.isInteger(end) || end !== rows.length - 1) { say(` The session hook's answer came back incomplete (${rows.length - 1} of ${end ?? "an unknown number of"} lines), so nothing was added. Run it again.`); process.exit(1); }
277
- const known = new Set(existing.lines.map((line) => line.stretch_ref)), fresh = [];
277
+ const known = new Set(existing.lines.map((line) => line.stretch_ref)), fresh = [], measured = new Set();
278
278
  let waiting = 0;
279
279
  for (const text of rows.slice(0, -1)) {
280
280
  let entry; try { entry = JSON.parse(text); } catch { continue; }
281
281
  const line = archiveLine(entry);
282
- if (!line || known.has(line.stretch_ref)) continue;
282
+ if (line && line.day < today()) measured.add(line.stretch_ref); if (!line || known.has(line.stretch_ref)) continue; // a file present twice is one stretch
283
283
  if (line.day >= today()) { waiting += 1; continue; }
284
284
  known.add(line.stretch_ref);
285
285
  fresh.push(line);
@@ -305,7 +305,7 @@ function archive(dir) {
305
305
  }
306
306
  if (fresh.length > 0 || !existing.manifest) writeFileSync(join(dir, "manifest.json"), `${JSON.stringify({ format: "worktrust-ai-evidence/1", collector_version: COLLECTOR, created: existing.manifest?.created ?? new Date().toISOString(), updated: new Date().toISOString(), clients: [...new Set(all.map((line) => line.client))].sort(), ...IDS, count: all.length, head: prev }, null, 1)}\n`);
307
307
  say();
308
- say(` ✓ ${fresh.length} new day-stretch${fresh.length === 1 ? "" : "es"} archived in ${dir}: it holds ${all.length}, the session hook measures ${rows.length - 1 - waiting} finished ones here now${key ? ", each day's root signed with this computer's device key" : ", unsigned (no device key on this computer)"}.`);
308
+ say(` ✓ ${fresh.length} new day-stretch${fresh.length === 1 ? "" : "es"} archived in ${dir}: it holds ${all.length}, the session hook measures ${measured.size} finished ones here now${key ? ", each day's root signed with this computer's device key" : ", unsigned (no device key on this computer)"}.`);
309
309
  if (waiting > 0) say(` ${waiting} of today wait for tomorrow's run: a day is archived once it is over.`);
310
310
  say(" Metadata only: clocks, counts, model names, a hash of each project's name. Nothing was sent.");
311
311
  }
package/worktrust.mjs CHANGED
@@ -69,7 +69,7 @@ const command = args.find((arg, at) => !arg.startsWith("--") && !(at > 0 && VALU
69
69
  const flag = (name) => { const at = args.indexOf(`--${name}`); return at >= 0 ? args[at + 1] : undefined; };
70
70
  const has = (name) => args.includes(`--${name}`);
71
71
  /** This CLI's version, said to the door so the app can tell which computer runs an old one (check-cli-package holds it equal to package.json). */
72
- const CLI_VERSION = "0.6.14";
72
+ const CLI_VERSION = "0.6.15";
73
73
  const ORIGIN = (flag("origin") ?? process.env.WORKTRUST_ORIGIN ?? "https://app.worktrust.io").replace(/\/$/, "");
74
74
  const MCP = flag("url") ?? process.env.WORKTRUST_MCP_URL ?? `${ORIGIN}/api/mcp`;
75
75
  const HOME_DIR = join(homedir(), ".worktrust");