@teeras/afterhours 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 (66) hide show
  1. package/README.md +57 -0
  2. package/dist/activity.d.ts +13 -0
  3. package/dist/activity.d.ts.map +1 -0
  4. package/dist/activity.js +82 -0
  5. package/dist/activity.js.map +1 -0
  6. package/dist/cli.d.ts +3 -0
  7. package/dist/cli.d.ts.map +1 -0
  8. package/dist/cli.js +107 -0
  9. package/dist/cli.js.map +1 -0
  10. package/dist/cloud.d.ts +39 -0
  11. package/dist/cloud.d.ts.map +1 -0
  12. package/dist/cloud.js +138 -0
  13. package/dist/cloud.js.map +1 -0
  14. package/dist/collect.d.ts +12 -0
  15. package/dist/collect.d.ts.map +1 -0
  16. package/dist/collect.js +36 -0
  17. package/dist/collect.js.map +1 -0
  18. package/dist/detect.d.ts +3 -0
  19. package/dist/detect.d.ts.map +1 -0
  20. package/dist/detect.js +32 -0
  21. package/dist/detect.js.map +1 -0
  22. package/dist/files.d.ts +5 -0
  23. package/dist/files.d.ts.map +1 -0
  24. package/dist/files.js +43 -0
  25. package/dist/files.js.map +1 -0
  26. package/dist/index.d.ts +8 -0
  27. package/dist/index.d.ts.map +1 -0
  28. package/dist/index.js +7 -0
  29. package/dist/index.js.map +1 -0
  30. package/dist/privacy.d.ts +3 -0
  31. package/dist/privacy.d.ts.map +1 -0
  32. package/dist/privacy.js +14 -0
  33. package/dist/privacy.js.map +1 -0
  34. package/dist/render.d.ts +3 -0
  35. package/dist/render.d.ts.map +1 -0
  36. package/dist/render.js +22 -0
  37. package/dist/render.js.map +1 -0
  38. package/dist/report.d.ts +3 -0
  39. package/dist/report.d.ts.map +1 -0
  40. package/dist/report.js +36 -0
  41. package/dist/report.js.map +1 -0
  42. package/dist/sources/claude.d.ts +3 -0
  43. package/dist/sources/claude.d.ts.map +1 -0
  44. package/dist/sources/claude.js +61 -0
  45. package/dist/sources/claude.js.map +1 -0
  46. package/dist/sources/codex.d.ts +3 -0
  47. package/dist/sources/codex.d.ts.map +1 -0
  48. package/dist/sources/codex.js +54 -0
  49. package/dist/sources/codex.js.map +1 -0
  50. package/dist/sources/cursor.d.ts +3 -0
  51. package/dist/sources/cursor.d.ts.map +1 -0
  52. package/dist/sources/cursor.js +18 -0
  53. package/dist/sources/cursor.js.map +1 -0
  54. package/dist/sources/gemini.d.ts +3 -0
  55. package/dist/sources/gemini.d.ts.map +1 -0
  56. package/dist/sources/gemini.js +40 -0
  57. package/dist/sources/gemini.js.map +1 -0
  58. package/dist/sources/shared.d.ts +3 -0
  59. package/dist/sources/shared.d.ts.map +1 -0
  60. package/dist/sources/shared.js +20 -0
  61. package/dist/sources/shared.js.map +1 -0
  62. package/dist/types.d.ts +87 -0
  63. package/dist/types.d.ts.map +1 -0
  64. package/dist/types.js +2 -0
  65. package/dist/types.js.map +1 -0
  66. package/package.json +51 -0
package/dist/files.js ADDED
@@ -0,0 +1,43 @@
1
+ import { existsSync, readdirSync, statSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ export function isDirectory(path) {
4
+ try {
5
+ return statSync(path).isDirectory();
6
+ }
7
+ catch {
8
+ return false;
9
+ }
10
+ }
11
+ export function exists(path) {
12
+ return existsSync(path);
13
+ }
14
+ /** Scope-bounded, symlink-free traversal of one known agent directory. */
15
+ export function recentFiles(root, sinceMs, accepts, depth = 0, output = []) {
16
+ if (depth > 7)
17
+ return output;
18
+ let entries;
19
+ try {
20
+ entries = readdirSync(root, { withFileTypes: true });
21
+ }
22
+ catch {
23
+ return output;
24
+ }
25
+ for (const entry of entries) {
26
+ if (entry.isSymbolicLink())
27
+ continue;
28
+ const path = join(root, entry.name);
29
+ if (entry.isDirectory())
30
+ recentFiles(path, sinceMs, accepts, depth + 1, output);
31
+ else if (entry.isFile() && accepts(entry.name)) {
32
+ try {
33
+ if (statSync(path).mtimeMs >= sinceMs)
34
+ output.push(path);
35
+ }
36
+ catch {
37
+ // An unreadable or concurrently removed log is simply unavailable.
38
+ }
39
+ }
40
+ }
41
+ return output;
42
+ }
43
+ //# sourceMappingURL=files.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"files.js","sourceRoot":"","sources":["../src/files.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,QAAQ,EAAe,MAAM,SAAS,CAAC;AACzE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,MAAM,UAAU,WAAW,CAAC,IAAY;IACtC,IAAI,CAAC;QACH,OAAO,QAAQ,CAAC,IAAI,CAAC,CAAC,WAAW,EAAE,CAAC;IACtC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,MAAM,UAAU,MAAM,CAAC,IAAY;IACjC,OAAO,UAAU,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC;AAED,0EAA0E;AAC1E,MAAM,UAAU,WAAW,CACzB,IAAY,EACZ,OAAe,EACf,OAAkC,EAClC,KAAK,GAAG,CAAC,EACT,SAAmB,EAAE;IAErB,IAAI,KAAK,GAAG,CAAC;QAAE,OAAO,MAAM,CAAC;IAC7B,IAAI,OAAiB,CAAC;IACtB,IAAI,CAAC;QACH,OAAO,GAAG,WAAW,CAAC,IAAI,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;IACvD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,MAAM,CAAC;IAChB,CAAC;IACD,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC5B,IAAI,KAAK,CAAC,cAAc,EAAE;YAAE,SAAS;QACrC,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;QACpC,IAAI,KAAK,CAAC,WAAW,EAAE;YAAE,WAAW,CAAC,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,GAAG,CAAC,EAAE,MAAM,CAAC,CAAC;aAC3E,IAAI,KAAK,CAAC,MAAM,EAAE,IAAI,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;YAC/C,IAAI,CAAC;gBACH,IAAI,QAAQ,CAAC,IAAI,CAAC,CAAC,OAAO,IAAI,OAAO;oBAAE,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAC3D,CAAC;YAAC,MAAM,CAAC;gBACP,mEAAmE;YACrE,CAAC;QACH,CAAC;IACH,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -0,0 +1,8 @@
1
+ export { collectAfterhours, SOURCES, type CollectOptions } from "./collect.js";
2
+ export { credentialsPath, openVerificationUri, pollDeviceLogin, readCredentials, removeCredentials, startDeviceLogin, syncSnapshot, revokeDevice, teerasBaseUrl, writeCredentials, type CloudCredentials, type DeviceGrant, } from "./cloud.js";
3
+ export { DATA_BOUNDARY } from "./privacy.js";
4
+ export { detectCodingAgents } from "./detect.js";
5
+ export { reportFrom } from "./report.js";
6
+ export { renderReport } from "./render.js";
7
+ export type { ActivityBucket, AfterhoursReport, AfterhoursSnapshot, CollectInput, DataBoundary, DetectedAgent, Fidelity, SourceAdapter, SourceId, SourceSnapshot, TokenCounts, } from "./types.js";
8
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,OAAO,EAAE,KAAK,cAAc,EAAE,MAAM,cAAc,CAAC;AAC/E,OAAO,EACL,eAAe,EACf,mBAAmB,EACnB,eAAe,EACf,eAAe,EACf,iBAAiB,EACjB,gBAAgB,EAChB,YAAY,EACZ,YAAY,EACZ,aAAa,EACb,gBAAgB,EAChB,KAAK,gBAAgB,EACrB,KAAK,WAAW,GACjB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,YAAY,EACV,cAAc,EACd,gBAAgB,EAChB,kBAAkB,EAClB,YAAY,EACZ,YAAY,EACZ,aAAa,EACb,QAAQ,EACR,aAAa,EACb,QAAQ,EACR,cAAc,EACd,WAAW,GACZ,MAAM,YAAY,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,7 @@
1
+ export { collectAfterhours, SOURCES } from "./collect.js";
2
+ export { credentialsPath, openVerificationUri, pollDeviceLogin, readCredentials, removeCredentials, startDeviceLogin, syncSnapshot, revokeDevice, teerasBaseUrl, writeCredentials, } from "./cloud.js";
3
+ export { DATA_BOUNDARY } from "./privacy.js";
4
+ export { detectCodingAgents } from "./detect.js";
5
+ export { reportFrom } from "./report.js";
6
+ export { renderReport } from "./render.js";
7
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,OAAO,EAAuB,MAAM,cAAc,CAAC;AAC/E,OAAO,EACL,eAAe,EACf,mBAAmB,EACnB,eAAe,EACf,eAAe,EACf,iBAAiB,EACjB,gBAAgB,EAChB,YAAY,EACZ,YAAY,EACZ,aAAa,EACb,gBAAgB,GAGjB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { DataBoundary } from "./types.js";
2
+ export declare const DATA_BOUNDARY: DataBoundary;
3
+ //# sourceMappingURL=privacy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"privacy.d.ts","sourceRoot":"","sources":["../src/privacy.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE/C,eAAO,MAAM,aAAa,EAAE,YAY3B,CAAC"}
@@ -0,0 +1,14 @@
1
+ export const DATA_BOUNDARY = {
2
+ mode: "local-first",
3
+ sync: "off-until-user-connects",
4
+ retained: ["hourly timestamps", "local date and hour", "event kinds", "session counts", "token counters"],
5
+ neverRetained: [
6
+ "prompt text",
7
+ "source code",
8
+ "model output",
9
+ "repository names",
10
+ "working directories",
11
+ "file paths",
12
+ ],
13
+ };
14
+ //# sourceMappingURL=privacy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"privacy.js","sourceRoot":"","sources":["../src/privacy.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,aAAa,GAAiB;IACzC,IAAI,EAAE,aAAa;IACnB,IAAI,EAAE,yBAAyB;IAC/B,QAAQ,EAAE,CAAC,mBAAmB,EAAE,qBAAqB,EAAE,aAAa,EAAE,gBAAgB,EAAE,gBAAgB,CAAC;IACzG,aAAa,EAAE;QACb,aAAa;QACb,aAAa;QACb,cAAc;QACd,kBAAkB;QAClB,qBAAqB;QACrB,YAAY;KACb;CACF,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { AfterhoursReport, AfterhoursSnapshot } from "./types.js";
2
+ export declare function renderReport(snapshot: AfterhoursSnapshot, report: AfterhoursReport): string;
3
+ //# sourceMappingURL=render.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render.d.ts","sourceRoot":"","sources":["../src/render.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAMvE,wBAAgB,YAAY,CAAC,QAAQ,EAAE,kBAAkB,EAAE,MAAM,EAAE,gBAAgB,GAAG,MAAM,CAiB3F"}
package/dist/render.js ADDED
@@ -0,0 +1,22 @@
1
+ function hours(minutes) {
2
+ return `${(minutes / 60).toFixed(1)}h`;
3
+ }
4
+ export function renderReport(snapshot, report) {
5
+ const active = snapshot.sources.filter((source) => source.configured).map((source) => source.name);
6
+ return [
7
+ "",
8
+ " teeras · afterhours",
9
+ "",
10
+ ` life returned +${hours(report.lifeReturnedMinutes)}`,
11
+ ` human attention ${hours(report.humanAttentionMinutes)} estimated`,
12
+ ` agent active time ${hours(report.agentActiveMinutes)} observed`,
13
+ ` nights protected ${report.protectedNights}/${report.observedNights}`,
14
+ "",
15
+ ` ${report.nextMove}`,
16
+ "",
17
+ ` reading locally: ${active.length > 0 ? active.join(", ") : "no supported agents found yet"}`,
18
+ " prompts, code, output, projects, and paths never enter the report.",
19
+ "",
20
+ ].join("\n");
21
+ }
22
+ //# sourceMappingURL=render.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render.js","sourceRoot":"","sources":["../src/render.ts"],"names":[],"mappings":"AAEA,SAAS,KAAK,CAAC,OAAe;IAC5B,OAAO,GAAG,CAAC,OAAO,GAAG,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC;AACzC,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,QAA4B,EAAE,MAAwB;IACjF,MAAM,MAAM,GAAG,QAAQ,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IACnG,OAAO;QACL,EAAE;QACF,uBAAuB;QACvB,EAAE;QACF,0BAA0B,KAAK,CAAC,MAAM,CAAC,mBAAmB,CAAC,EAAE;QAC7D,yBAAyB,KAAK,CAAC,MAAM,CAAC,qBAAqB,CAAC,YAAY;QACxE,yBAAyB,KAAK,CAAC,MAAM,CAAC,kBAAkB,CAAC,WAAW;QACpE,yBAAyB,MAAM,CAAC,eAAe,IAAI,MAAM,CAAC,cAAc,EAAE;QAC1E,EAAE;QACF,KAAK,MAAM,CAAC,QAAQ,EAAE;QACtB,EAAE;QACF,sBAAsB,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,+BAA+B,EAAE;QAC/F,sEAAsE;QACtE,EAAE;KACH,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { AfterhoursReport, AfterhoursSnapshot } from "./types.js";
2
+ export declare function reportFrom(snapshot: AfterhoursSnapshot, stopHour?: number): AfterhoursReport;
3
+ //# sourceMappingURL=report.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"report.d.ts","sourceRoot":"","sources":["../src/report.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAkB,gBAAgB,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AASvF,wBAAgB,UAAU,CAAC,QAAQ,EAAE,kBAAkB,EAAE,QAAQ,SAAK,GAAG,gBAAgB,CA+BxF"}
package/dist/report.js ADDED
@@ -0,0 +1,36 @@
1
+ function localNight(bucket) {
2
+ if (bucket.localHour >= 5)
3
+ return bucket.localDate;
4
+ const previous = new Date(`${bucket.localDate}T12:00:00.000Z`);
5
+ previous.setUTCDate(previous.getUTCDate() - 1);
6
+ return previous.toISOString().slice(0, 10);
7
+ }
8
+ export function reportFrom(snapshot, stopHour = 22) {
9
+ const buckets = snapshot.sources.flatMap((source) => source.buckets);
10
+ const agentActiveMinutes = Math.round(buckets.reduce((sum, bucket) => sum + bucket.agentActiveMinutes, 0));
11
+ const humanAttentionMinutes = Math.round(buckets.reduce((sum, bucket) => sum + bucket.humanAttentionMinutes, 0));
12
+ const afterHoursBuckets = buckets.filter((bucket) => {
13
+ return bucket.localHour >= stopHour || bucket.localHour < 5;
14
+ });
15
+ const afterHoursCheckIns = afterHoursBuckets.reduce((sum, bucket) => sum + bucket.humanCheckIns, 0);
16
+ const touchedNights = new Set(afterHoursBuckets.filter((bucket) => bucket.humanCheckIns > 0).map(localNight));
17
+ const observedNights = Math.max(1, Math.ceil((Date.parse(snapshot.until) - Date.parse(snapshot.since)) / 86_400_000));
18
+ const protectedNights = Math.max(0, observedNights - touchedNights.size);
19
+ const agentTurns = snapshot.sources.reduce((sum, source) => sum + source.agentTurns, 0);
20
+ const humanCheckIns = snapshot.sources.reduce((sum, source) => sum + source.humanCheckIns, 0);
21
+ const nextMove = afterHoursCheckIns > 0
22
+ ? `You checked in ${afterHoursCheckIns} time${afterHoursCheckIns === 1 ? "" : "s"} after ${stopHour}:00. Let the agents finish without you tonight.`
23
+ : "Your stop time held. Keep the boundary and let the agents carry the night shift.";
24
+ return {
25
+ lifeReturnedMinutes: Math.max(0, agentActiveMinutes - humanAttentionMinutes),
26
+ agentActiveMinutes,
27
+ humanAttentionMinutes,
28
+ afterHoursCheckIns,
29
+ protectedNights,
30
+ observedNights,
31
+ agentTurns,
32
+ humanCheckIns,
33
+ nextMove,
34
+ };
35
+ }
36
+ //# sourceMappingURL=report.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"report.js","sourceRoot":"","sources":["../src/report.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { SourceAdapter } from "../types.js";
2
+ export declare const claudeSource: SourceAdapter;
3
+ //# sourceMappingURL=claude.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"claude.d.ts","sourceRoot":"","sources":["../../src/sources/claude.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAUjD,eAAO,MAAM,YAAY,EAAE,aA2C1B,CAAC"}
@@ -0,0 +1,61 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ import { emptySource, finiteNumber, finishSource, inWindow, timestamp } from "../activity.js";
4
+ import { isDirectory, recentFiles } from "../files.js";
5
+ import { parseLines, record } from "./shared.js";
6
+ function isHumanMessage(message) {
7
+ const content = message.content;
8
+ if (typeof content === "string")
9
+ return content.length > 0;
10
+ if (!Array.isArray(content))
11
+ return false;
12
+ return content.some((part) => record(part)?.type !== "tool_result");
13
+ }
14
+ export const claudeSource = {
15
+ id: "claude-code",
16
+ name: "Claude Code",
17
+ collect({ home, sinceMs, untilMs }) {
18
+ const root = join(home, ".claude");
19
+ const source = emptySource("claude-code", "Claude Code", isDirectory(root));
20
+ if (!source.configured)
21
+ return source;
22
+ const events = [];
23
+ for (const path of recentFiles(join(root, "projects"), sinceMs, (name) => name.endsWith(".jsonl"))) {
24
+ let rows;
25
+ try {
26
+ rows = parseLines(readFileSync(path, "utf8"));
27
+ }
28
+ catch {
29
+ continue;
30
+ }
31
+ let used = false;
32
+ for (const row of rows) {
33
+ const at = timestamp(row.timestamp);
34
+ if (at == null || !inWindow(at, sinceMs, untilMs))
35
+ continue;
36
+ const message = record(row.message);
37
+ if (!message)
38
+ continue;
39
+ if (row.type === "user" && isHumanMessage(message)) {
40
+ events.push({ at, kind: "human" });
41
+ used = true;
42
+ }
43
+ if (row.type === "assistant") {
44
+ const usage = record(message.usage);
45
+ if (!usage)
46
+ continue;
47
+ source.tokens.input += finiteNumber(usage.input_tokens);
48
+ source.tokens.cached +=
49
+ finiteNumber(usage.cache_read_input_tokens) + finiteNumber(usage.cache_creation_input_tokens);
50
+ source.tokens.output += finiteNumber(usage.output_tokens);
51
+ events.push({ at, kind: "agent" });
52
+ used = true;
53
+ }
54
+ }
55
+ if (used)
56
+ source.sessions += 1;
57
+ }
58
+ return finishSource(source, events);
59
+ },
60
+ };
61
+ //# sourceMappingURL=claude.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"claude.js","sourceRoot":"","sources":["../../src/sources/claude.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { SourceAdapter } from "../types.js";
2
+ export declare const codexSource: SourceAdapter;
3
+ //# sourceMappingURL=codex.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"codex.d.ts","sourceRoot":"","sources":["../../src/sources/codex.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAGjD,eAAO,MAAM,WAAW,EAAE,aA4CzB,CAAC"}
@@ -0,0 +1,54 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ import { emptySource, finiteNumber, finishSource, inWindow, timestamp } from "../activity.js";
4
+ import { isDirectory, recentFiles } from "../files.js";
5
+ import { parseLines, record } from "./shared.js";
6
+ export const codexSource = {
7
+ id: "codex",
8
+ name: "Codex app + CLI",
9
+ collect({ home, sinceMs, untilMs }) {
10
+ const root = join(home, ".codex");
11
+ const source = emptySource("codex", "Codex app + CLI", isDirectory(root));
12
+ if (!source.configured)
13
+ return source;
14
+ const events = [];
15
+ for (const path of recentFiles(join(root, "sessions"), sinceMs, (name) => name.endsWith(".jsonl"))) {
16
+ let rows;
17
+ try {
18
+ rows = parseLines(readFileSync(path, "utf8"));
19
+ }
20
+ catch {
21
+ continue;
22
+ }
23
+ let used = false;
24
+ for (const row of rows) {
25
+ const at = timestamp(row.timestamp);
26
+ if (at == null || !inWindow(at, sinceMs, untilMs))
27
+ continue;
28
+ const payload = record(row.payload);
29
+ if (!payload)
30
+ continue;
31
+ if (row.type === "event_msg" && payload.type === "user_message") {
32
+ events.push({ at, kind: "human" });
33
+ used = true;
34
+ }
35
+ if (row.type === "event_msg" && payload.type === "token_count") {
36
+ const info = record(payload.info);
37
+ const usage = record(info?.last_token_usage);
38
+ if (!usage)
39
+ continue;
40
+ source.tokens.input += finiteNumber(usage.input_tokens);
41
+ source.tokens.cached += finiteNumber(usage.cached_input_tokens);
42
+ source.tokens.output += finiteNumber(usage.output_tokens);
43
+ source.tokens.reasoning += finiteNumber(usage.reasoning_output_tokens);
44
+ events.push({ at, kind: "agent" });
45
+ used = true;
46
+ }
47
+ }
48
+ if (used)
49
+ source.sessions += 1;
50
+ }
51
+ return finishSource(source, events);
52
+ },
53
+ };
54
+ //# sourceMappingURL=codex.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"codex.js","sourceRoot":"","sources":["../../src/sources/codex.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { SourceAdapter } from "../types.js";
2
+ export declare const cursorSource: SourceAdapter;
3
+ //# sourceMappingURL=cursor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cursor.d.ts","sourceRoot":"","sources":["../../src/sources/cursor.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAEjD,eAAO,MAAM,YAAY,EAAE,aAa1B,CAAC"}
@@ -0,0 +1,18 @@
1
+ import { join } from "node:path";
2
+ import { emptySource } from "../activity.js";
3
+ import { isDirectory } from "../files.js";
4
+ export const cursorSource = {
5
+ id: "cursor",
6
+ name: "Cursor",
7
+ collect({ home }) {
8
+ const configured = [
9
+ join(home, "Library", "Application Support", "Cursor"),
10
+ join(home, ".config", "Cursor"),
11
+ join(home, ".cursor"),
12
+ ].some(isDirectory);
13
+ // Cursor is intentionally detection-only until a stable, consented local
14
+ // activity surface exists. Afterhours never scrapes editor databases by guess.
15
+ return emptySource("cursor", "Cursor", configured);
16
+ },
17
+ };
18
+ //# sourceMappingURL=cursor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cursor.js","sourceRoot":"","sources":["../../src/sources/cursor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC7C,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAG1C,MAAM,CAAC,MAAM,YAAY,GAAkB;IACzC,EAAE,EAAE,QAAQ;IACZ,IAAI,EAAE,QAAQ;IACd,OAAO,CAAC,EAAE,IAAI,EAAE;QACd,MAAM,UAAU,GAAG;YACjB,IAAI,CAAC,IAAI,EAAE,SAAS,EAAE,qBAAqB,EAAE,QAAQ,CAAC;YACtD,IAAI,CAAC,IAAI,EAAE,SAAS,EAAE,QAAQ,CAAC;YAC/B,IAAI,CAAC,IAAI,EAAE,SAAS,CAAC;SACtB,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QACpB,yEAAyE;QACzE,+EAA+E;QAC/E,OAAO,WAAW,CAAC,QAAQ,EAAE,QAAQ,EAAE,UAAU,CAAC,CAAC;IACrD,CAAC;CACF,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { SourceAdapter } from "../types.js";
2
+ export declare const geminiSource: SourceAdapter;
3
+ //# sourceMappingURL=gemini.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gemini.d.ts","sourceRoot":"","sources":["../../src/sources/gemini.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAGjD,eAAO,MAAM,YAAY,EAAE,aA4B1B,CAAC"}
@@ -0,0 +1,40 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ import { emptySource, inWindow, timestamp } from "../activity.js";
4
+ import { isDirectory, recentFiles } from "../files.js";
5
+ import { record } from "./shared.js";
6
+ export const geminiSource = {
7
+ id: "gemini-cli",
8
+ name: "Gemini CLI",
9
+ collect({ home, sinceMs, untilMs }) {
10
+ const root = join(home, ".gemini");
11
+ const source = emptySource("gemini-cli", "Gemini CLI", isDirectory(root));
12
+ if (!source.configured)
13
+ return source;
14
+ const sessionIds = new Set();
15
+ for (const path of recentFiles(join(root, "tmp"), sinceMs, (name) => name === "logs.json")) {
16
+ let rows;
17
+ try {
18
+ rows = JSON.parse(readFileSync(path, "utf8"));
19
+ }
20
+ catch {
21
+ continue;
22
+ }
23
+ if (!Array.isArray(rows))
24
+ continue;
25
+ for (const value of rows) {
26
+ const row = record(value);
27
+ const at = timestamp(row?.timestamp);
28
+ if (at == null || !inWindow(at, sinceMs, untilMs))
29
+ continue;
30
+ if (typeof row?.sessionId === "string")
31
+ sessionIds.add(row.sessionId);
32
+ }
33
+ }
34
+ source.sessions = sessionIds.size;
35
+ if (source.sessions > 0)
36
+ source.fidelity = "sessions";
37
+ return source;
38
+ },
39
+ };
40
+ //# sourceMappingURL=gemini.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gemini.js","sourceRoot":"","sources":["../../src/sources/gemini.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAClE,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAEvD,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAErC,MAAM,CAAC,MAAM,YAAY,GAAkB;IACzC,EAAE,EAAE,YAAY;IAChB,IAAI,EAAE,YAAY;IAClB,OAAO,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE;QAChC,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;QACnC,MAAM,MAAM,GAAG,WAAW,CAAC,YAAY,EAAE,YAAY,EAAE,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC;QAC1E,IAAI,CAAC,MAAM,CAAC,UAAU;YAAE,OAAO,MAAM,CAAC;QAEtC,MAAM,UAAU,GAAG,IAAI,GAAG,EAAU,CAAC;QACrC,KAAK,MAAM,IAAI,IAAI,WAAW,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,EAAE,OAAO,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,KAAK,WAAW,CAAC,EAAE,CAAC;YAC3F,IAAI,IAAa,CAAC;YAClB,IAAI,CAAC;gBACH,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC;YAChD,CAAC;YAAC,MAAM,CAAC;gBACP,SAAS;YACX,CAAC;YACD,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC;gBAAE,SAAS;YACnC,KAAK,MAAM,KAAK,IAAI,IAAI,EAAE,CAAC;gBACzB,MAAM,GAAG,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;gBAC1B,MAAM,EAAE,GAAG,SAAS,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;gBACrC,IAAI,EAAE,IAAI,IAAI,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,OAAO,EAAE,OAAO,CAAC;oBAAE,SAAS;gBAC5D,IAAI,OAAO,GAAG,EAAE,SAAS,KAAK,QAAQ;oBAAE,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;YACxE,CAAC;QACH,CAAC;QACD,MAAM,CAAC,QAAQ,GAAG,UAAU,CAAC,IAAI,CAAC;QAClC,IAAI,MAAM,CAAC,QAAQ,GAAG,CAAC;YAAE,MAAM,CAAC,QAAQ,GAAG,UAAU,CAAC;QACtD,OAAO,MAAM,CAAC;IAChB,CAAC;CACF,CAAC"}
@@ -0,0 +1,3 @@
1
+ export declare function record(value: unknown): Record<string, unknown> | null;
2
+ export declare function parseLines(text: string): Record<string, unknown>[];
3
+ //# sourceMappingURL=shared.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shared.d.ts","sourceRoot":"","sources":["../../src/sources/shared.ts"],"names":[],"mappings":"AAAA,wBAAgB,MAAM,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAErE;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAYlE"}
@@ -0,0 +1,20 @@
1
+ export function record(value) {
2
+ return typeof value === "object" && value !== null ? value : null;
3
+ }
4
+ export function parseLines(text) {
5
+ const rows = [];
6
+ for (const line of text.split("\n")) {
7
+ if (!line.trim())
8
+ continue;
9
+ try {
10
+ const parsed = record(JSON.parse(line));
11
+ if (parsed)
12
+ rows.push(parsed);
13
+ }
14
+ catch {
15
+ // Local logs can end with a partial line while an agent is still writing.
16
+ }
17
+ }
18
+ return rows;
19
+ }
20
+ //# sourceMappingURL=shared.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shared.js","sourceRoot":"","sources":["../../src/sources/shared.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,MAAM,CAAC,KAAc;IACnC,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,CAAC,CAAC,CAAE,KAAiC,CAAC,CAAC,CAAC,IAAI,CAAC;AACjG,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,IAAY;IACrC,MAAM,IAAI,GAA8B,EAAE,CAAC;IAC3C,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;QACpC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE;YAAE,SAAS;QAC3B,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC;YACxC,IAAI,MAAM;gBAAE,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAChC,CAAC;QAAC,MAAM,CAAC;YACP,0EAA0E;QAC5E,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"}
@@ -0,0 +1,87 @@
1
+ /** Known ids use readable slugs; third-party adapters may provide their own. */
2
+ export type SourceId = string;
3
+ export type Fidelity = "activity" | "sessions" | "detected" | "none";
4
+ export interface TokenCounts {
5
+ input: number;
6
+ cached: number;
7
+ output: number;
8
+ reasoning: number;
9
+ }
10
+ export interface ActivityBucket {
11
+ /** ISO timestamp at the beginning of a local clock hour. */
12
+ hour: string;
13
+ /** Timezone-free local calendar values keep night boundaries correct. */
14
+ localDate: string;
15
+ localHour: number;
16
+ agentTurns: number;
17
+ humanCheckIns: number;
18
+ agentActiveMinutes: number;
19
+ humanAttentionMinutes: number;
20
+ }
21
+ export interface SourceSnapshot {
22
+ id: SourceId;
23
+ name: string;
24
+ configured: boolean;
25
+ fidelity: Fidelity;
26
+ sessions: number;
27
+ agentTurns: number;
28
+ humanCheckIns: number;
29
+ tokens: TokenCounts;
30
+ buckets: ActivityBucket[];
31
+ }
32
+ export interface DataBoundary {
33
+ mode: "local-first";
34
+ sync: "off-until-user-connects";
35
+ retained: readonly [
36
+ "hourly timestamps",
37
+ "local date and hour",
38
+ "event kinds",
39
+ "session counts",
40
+ "token counters"
41
+ ];
42
+ neverRetained: readonly [
43
+ "prompt text",
44
+ "source code",
45
+ "model output",
46
+ "repository names",
47
+ "working directories",
48
+ "file paths"
49
+ ];
50
+ }
51
+ export interface AfterhoursSnapshot {
52
+ schemaVersion: 2;
53
+ generatedAt: string;
54
+ since: string;
55
+ until: string;
56
+ sources: SourceSnapshot[];
57
+ boundary: DataBoundary;
58
+ }
59
+ export interface SourceAdapter {
60
+ id: SourceId;
61
+ name: string;
62
+ collect(input: CollectInput): SourceSnapshot;
63
+ }
64
+ export interface CollectInput {
65
+ home: string;
66
+ sinceMs: number;
67
+ untilMs: number;
68
+ }
69
+ export interface AfterhoursReport {
70
+ lifeReturnedMinutes: number;
71
+ agentActiveMinutes: number;
72
+ humanAttentionMinutes: number;
73
+ afterHoursCheckIns: number;
74
+ protectedNights: number;
75
+ observedNights: number;
76
+ agentTurns: number;
77
+ humanCheckIns: number;
78
+ nextMove: string;
79
+ }
80
+ export interface DetectedAgent {
81
+ id: SourceId;
82
+ name: string;
83
+ installed: boolean;
84
+ connection: "automatic" | "connector-needed";
85
+ detail: string;
86
+ }
87
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,gFAAgF;AAChF,MAAM,MAAM,QAAQ,GAAG,MAAM,CAAC;AAE9B,MAAM,MAAM,QAAQ,GAAG,UAAU,GAAG,UAAU,GAAG,UAAU,GAAG,MAAM,CAAC;AAErE,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,cAAc;IAC7B,4DAA4D;IAC5D,IAAI,EAAE,MAAM,CAAC;IACb,yEAAyE;IACzE,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,qBAAqB,EAAE,MAAM,CAAC;CAC/B;AAED,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,QAAQ,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,OAAO,CAAC;IACpB,QAAQ,EAAE,QAAQ,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,WAAW,CAAC;IACpB,OAAO,EAAE,cAAc,EAAE,CAAC;CAC3B;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,aAAa,CAAC;IACpB,IAAI,EAAE,yBAAyB,CAAC;IAChC,QAAQ,EAAE,SAAS;QACjB,mBAAmB;QACnB,qBAAqB;QACrB,aAAa;QACb,gBAAgB;QAChB,gBAAgB;KACjB,CAAC;IACF,aAAa,EAAE,SAAS;QACtB,aAAa;QACb,aAAa;QACb,cAAc;QACd,kBAAkB;QAClB,qBAAqB;QACrB,YAAY;KACb,CAAC;CACH;AAED,MAAM,WAAW,kBAAkB;IACjC,aAAa,EAAE,CAAC,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,cAAc,EAAE,CAAC;IAC1B,QAAQ,EAAE,YAAY,CAAC;CACxB;AAED,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,QAAQ,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,KAAK,EAAE,YAAY,GAAG,cAAc,CAAC;CAC9C;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,gBAAgB;IAC/B,mBAAmB,EAAE,MAAM,CAAC;IAC5B,kBAAkB,EAAE,MAAM,CAAC;IAC3B,qBAAqB,EAAE,MAAM,CAAC;IAC9B,kBAAkB,EAAE,MAAM,CAAC;IAC3B,eAAe,EAAE,MAAM,CAAC;IACxB,cAAc,EAAE,MAAM,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,QAAQ,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,OAAO,CAAC;IACnB,UAAU,EAAE,WAAW,GAAG,kBAAkB,CAAC;IAC7C,MAAM,EAAE,MAAM,CAAC;CAChB"}
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
package/package.json ADDED
@@ -0,0 +1,51 @@
1
+ {
2
+ "name": "@teeras/afterhours",
3
+ "version": "0.1.0",
4
+ "description": "Private AI-life balance and usage analytics for Codex, Claude Code, Gemini CLI, Cursor, and other coding agents.",
5
+ "homepage": "https://teeras.com",
6
+ "keywords": [
7
+ "ai-life-balance",
8
+ "coding-agents",
9
+ "codex",
10
+ "claude-code",
11
+ "gemini-cli",
12
+ "cursor",
13
+ "developer-wellbeing",
14
+ "usage-analytics",
15
+ "local-first"
16
+ ],
17
+ "license": "Apache-2.0",
18
+ "type": "module",
19
+ "bin": {
20
+ "afterhours": "dist/cli.js",
21
+ "trs": "dist/cli.js"
22
+ },
23
+ "main": "./dist/index.js",
24
+ "types": "./dist/index.d.ts",
25
+ "exports": {
26
+ ".": {
27
+ "types": "./dist/index.d.ts",
28
+ "import": "./dist/index.js"
29
+ }
30
+ },
31
+ "files": [
32
+ "dist",
33
+ "README.md"
34
+ ],
35
+ "publishConfig": {
36
+ "access": "public"
37
+ },
38
+ "engines": {
39
+ "node": ">=20"
40
+ },
41
+ "scripts": {
42
+ "build": "tsc -p tsconfig.build.json",
43
+ "typecheck": "tsc --noEmit",
44
+ "test": "vitest run"
45
+ },
46
+ "devDependencies": {
47
+ "@types/node": "^26.1.0",
48
+ "typescript": "^6.0.3",
49
+ "vitest": "^4.1.10"
50
+ }
51
+ }