@teeras/afterhours 0.1.1 → 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.
Files changed (84) hide show
  1. package/README.md +101 -15
  2. package/dist/activity.d.ts +15 -1
  3. package/dist/activity.d.ts.map +1 -1
  4. package/dist/activity.js +76 -1
  5. package/dist/activity.js.map +1 -1
  6. package/dist/autosync.d.ts +22 -0
  7. package/dist/autosync.d.ts.map +1 -0
  8. package/dist/autosync.js +85 -0
  9. package/dist/autosync.js.map +1 -0
  10. package/dist/cli.js +112 -9
  11. package/dist/cli.js.map +1 -1
  12. package/dist/cloud.d.ts +5 -0
  13. package/dist/cloud.d.ts.map +1 -1
  14. package/dist/cloud.js +10 -2
  15. package/dist/cloud.js.map +1 -1
  16. package/dist/collect.d.ts +3 -1
  17. package/dist/collect.d.ts.map +1 -1
  18. package/dist/collect.js +8 -7
  19. package/dist/collect.js.map +1 -1
  20. package/dist/detect.d.ts.map +1 -1
  21. package/dist/detect.js +35 -6
  22. package/dist/detect.js.map +1 -1
  23. package/dist/hooks.d.ts +29 -0
  24. package/dist/hooks.d.ts.map +1 -0
  25. package/dist/hooks.js +196 -0
  26. package/dist/hooks.js.map +1 -0
  27. package/dist/index.d.ts +9 -2
  28. package/dist/index.d.ts.map +1 -1
  29. package/dist/index.js +8 -1
  30. package/dist/index.js.map +1 -1
  31. package/dist/install.d.ts +25 -0
  32. package/dist/install.d.ts.map +1 -0
  33. package/dist/install.js +155 -0
  34. package/dist/install.js.map +1 -0
  35. package/dist/moments.d.ts +21 -0
  36. package/dist/moments.d.ts.map +1 -0
  37. package/dist/moments.js +52 -0
  38. package/dist/moments.js.map +1 -0
  39. package/dist/off.d.ts +24 -0
  40. package/dist/off.d.ts.map +1 -0
  41. package/dist/off.js +36 -0
  42. package/dist/off.js.map +1 -0
  43. package/dist/privacy.d.ts +8 -0
  44. package/dist/privacy.d.ts.map +1 -1
  45. package/dist/privacy.js +26 -1
  46. package/dist/privacy.js.map +1 -1
  47. package/dist/qr.d.ts +29 -0
  48. package/dist/qr.d.ts.map +1 -0
  49. package/dist/qr.js +92 -0
  50. package/dist/qr.js.map +1 -0
  51. package/dist/render.d.ts.map +1 -1
  52. package/dist/render.js +38 -1
  53. package/dist/render.js.map +1 -1
  54. package/dist/report.d.ts.map +1 -1
  55. package/dist/report.js +82 -1
  56. package/dist/report.js.map +1 -1
  57. package/dist/runtime.d.ts +18 -0
  58. package/dist/runtime.d.ts.map +1 -0
  59. package/dist/runtime.js +70 -0
  60. package/dist/runtime.js.map +1 -0
  61. package/dist/secret.d.ts +9 -0
  62. package/dist/secret.d.ts.map +1 -0
  63. package/dist/secret.js +32 -0
  64. package/dist/secret.js.map +1 -0
  65. package/dist/setup.d.ts +18 -0
  66. package/dist/setup.d.ts.map +1 -0
  67. package/dist/setup.js +155 -0
  68. package/dist/setup.js.map +1 -0
  69. package/dist/sources/claude.d.ts.map +1 -1
  70. package/dist/sources/claude.js +141 -18
  71. package/dist/sources/claude.js.map +1 -1
  72. package/dist/sources/codex.d.ts.map +1 -1
  73. package/dist/sources/codex.js +209 -18
  74. package/dist/sources/codex.js.map +1 -1
  75. package/dist/sources/cursor.d.ts.map +1 -1
  76. package/dist/sources/cursor.js +1 -0
  77. package/dist/sources/cursor.js.map +1 -1
  78. package/dist/sources/gemini.d.ts.map +1 -1
  79. package/dist/sources/gemini.js +73 -8
  80. package/dist/sources/gemini.js.map +1 -1
  81. package/dist/types.d.ts +61 -6
  82. package/dist/types.d.ts.map +1 -1
  83. package/llms-install.md +29 -0
  84. package/package.json +3 -2
package/README.md CHANGED
@@ -3,13 +3,20 @@
3
3
  Private AI-life balance and usage analytics for Codex, Claude Code, Gemini CLI,
4
4
  Cursor, and other coding agents.
5
5
 
6
+ ## For AI agents
7
+
8
+ Installing this for your user? Run `npx @teeras/afterhours setup`, relay the
9
+ printed code and link to your user, and let the command finish. Full
10
+ instructions: [`llms-install.md`](./llms-install.md). Counts only — prompts,
11
+ code, output, repo names, and paths never leave the machine.
12
+
6
13
  ```sh
7
14
  npx @teeras/afterhours
8
15
  ```
9
16
 
10
- The first run opens Google sign-in before reading activity, then returns to the
11
- terminal and prints the first report. A Teeras account is required. Install it
12
- once and the short command is simply:
17
+ The first run reads supported agent activity locally and prints a seven-day
18
+ view of agent activity, human attention, and after-hours check-ins without an
19
+ account. Install it once and the short command is simply:
13
20
 
14
21
  ```sh
15
22
  trs
@@ -19,19 +26,33 @@ trs login
19
26
  trs sync
20
27
  trs sources
21
28
  trs privacy
29
+ trs hook-config codex
30
+ trs hook-config claude
22
31
  ```
23
32
 
24
33
  Afterhours parses the local session histories already written by supported
25
34
  coding agents and immediately reduces them to counts and hourly buckets. It
26
35
  never retains or returns prompt text, source code, model output, repository
27
36
  names, working directories, or file paths. Only the documented aggregate
28
- snapshot syncs after account approval.
37
+ snapshot syncs after the user connects Teeras.
29
38
 
30
- Initial sources:
39
+ Support matrix:
31
40
 
32
- - Codex app and CLI (`~/.codex/sessions`)
33
- - Claude Code (`~/.claude/projects`)
34
- - Gemini CLI (`~/.gemini/tmp`, session-level fidelity)
41
+ - Codex desktop app and Codex CLI: activity, token, model, and episode
42
+ measurement from the shared `~/.codex/sessions` history. A distinct
43
+ `CODEX_HOME` is collected alongside the default App home.
44
+ - Claude Code CLI: activity, token, model, and episode measurement from
45
+ `~/.claude/projects`, plus a distinct `CLAUDE_CONFIG_DIR` when configured.
46
+ Subagent transcripts are excluded from primary episode counts rather than
47
+ double-counted as human delegations. Claude Code Desktop keeps separate
48
+ history, so its shared opt-in lifecycle hook is the trustworthy live path.
49
+ - Gemini CLI: activity, token, model, and episode measurement from current
50
+ `~/.gemini/tmp/*/chats` histories; older `logs.json` builds retain honest
51
+ session-level fidelity.
52
+ - Claude desktop: detected separately. General Claude chat and Cowork activity
53
+ are not claimed as measured because those surfaces do not expose a stable,
54
+ content-free local lifecycle feed. Claude Code remains a separate first-class
55
+ source.
35
56
  - Cursor detection, ready for a consented adapter once its local schema is stable
36
57
 
37
58
  `trs connect` also detects Windsurf, VS Code + Copilot, Zed, Amp, OpenCode,
@@ -40,18 +61,83 @@ the rest are reported honestly as requiring a source adapter. The exported
40
61
  `SourceAdapter` contract lets any coding agent add counts-only support without
41
62
  changing the Afterhours core.
42
63
 
43
- ## Keep your history in Teeras
64
+ ## Keep measuring across web and mobile
44
65
 
45
- The first `npx @teeras/afterhours` (or `trs login`) opens Teeras in the browser.
46
- Continue with Google, approve the counts-only boundary, and the CLI receives a
47
- revocable 90-day device token. Google OAuth tokens never enter the npm package.
66
+ The first report stays on the computer. Run `trs connect` when you want to keep
67
+ your history in Teeras. Continue with Google, approve the counts-only boundary,
68
+ and the CLI receives a revocable 30-day device token. Google OAuth tokens never
69
+ enter the npm package.
48
70
 
49
- Once connected, every deliberate `trs` run refreshes the account history. Use
50
- `trs sync` to sync without printing the report, or `trs logout` to remove this
51
- computer's token. There is no background daemon and no automatic LLM call.
71
+ Connecting syncs the first report immediately. After that, a counts-only
72
+ snapshot syncs as a side effect of your own agent activity: a lifecycle hook
73
+ event can trigger one detached, rate-limited sync (at most one every 45
74
+ minutes), so web and mobile stay current while you work. Every deliberate `trs`
75
+ run also refreshes the history. Use `trs sync` to sync without printing the
76
+ report, or `trs logout` to remove this computer's token. Nothing runs as a
77
+ background daemon, the hook itself makes no network call, and there is no
78
+ automatic LLM call.
52
79
 
53
80
  The default command (and `trs week`) prints a calm seven-day report. Use
54
81
  `--json` to inspect the privacy-safe snapshot consumed by the dashboard and
55
82
  optional MCP adapter. The cloud keeps one replaceable snapshot per device per
56
83
  day in a 790-day active-history window; the dashboard lets users export or
57
84
  delete that history.
85
+
86
+
87
+ ## Optional Codex and Claude Code lifecycle hooks
88
+
89
+ Afterhours can measure episode boundaries and permission requests more
90
+ accurately from opt-in lifecycle hooks.
91
+
92
+ ```sh
93
+ # install the fragments for the agents found on this computer
94
+ npx @teeras/afterhours setup
95
+ ```
96
+
97
+ Running `setup` is the consent: it merges the Teeras fragment into the agents
98
+ it finds on this computer, leaves every other hook untouched, and is safe to
99
+ run again — a second run reports what is already connected instead of
100
+ duplicating it. The hook command it writes is an absolute path inside
101
+ `~/.teeras/afterhours`, so it keeps working after the `npx` directory is gone.
102
+ Remove the Teeras entries from the settings file to stop hook measurement, or
103
+ run `trs off` to do that for you — it clears the Teeras entries from both
104
+ agents' settings and deletes the local runtime, leaving every other hook and
105
+ your local journal and credentials untouched. If a settings file cannot be
106
+ read, `trs off` leaves both that file and the runtime alone and says so: the
107
+ entry it could not remove still points at the runtime, and deleting it would
108
+ leave your agent calling a command that is gone.
109
+
110
+ To review the configuration and merge it by hand instead:
111
+
112
+ ```sh
113
+ # Codex CLI and Codex desktop app
114
+ trs hook-config codex
115
+
116
+ # Claude Code
117
+ trs hook-config claude
118
+ ```
119
+
120
+ For Codex, review the fragment and merge it into
121
+ `$CODEX_HOME/hooks.json` (normally `~/.codex/hooks.json`), then review and trust
122
+ the hook in Codex. The user-level Codex configuration is shared by the desktop
123
+ app and CLI. For Claude Code, merge the fragment into
124
+ `$CLAUDE_CONFIG_DIR/settings.json` (normally `~/.claude/settings.json`).
125
+ Claude Code Desktop and CLI share hooks even though they maintain separate
126
+ session histories.
127
+
128
+ The hook command reduces each event immediately to a timestamp, an opaque
129
+ device-keyed session id, and one allowlisted lifecycle kind. The id is keyed
130
+ with a stable per-device secret (never a rotating login token), so episodes
131
+ stay consistent across logins. Prompt text, working directories, transcript
132
+ paths, tool inputs, notification messages, and outputs are discarded before
133
+ the local journal is written.
134
+
135
+ `trs sources` reports Codex and Claude Code hook status separately — event
136
+ count and last event time — so a misconfigured or logged-out hook is visible
137
+ instead of silently recording nothing. Passive local-history measurement
138
+ continues even if hooks are unavailable in an older client version.
139
+
140
+ Hooks improve measurement only. They do not currently trigger Walk Mode or
141
+ claim that an autonomous runway prediction is valid. Teeras first asks
142
+ participants to confirm episode boundaries and attention estimates in order to
143
+ validate the measurement contract.
@@ -1,13 +1,27 @@
1
- import type { ActivityBucket, SourceId, SourceSnapshot, TokenCounts } from "./types.js";
1
+ import type { ActivityBucket, DelegationEpisode, EpisodeConfidence, SourceId, SourceSnapshot, TokenCounts } from "./types.js";
2
2
  export interface LocalEvent {
3
3
  at: number;
4
4
  kind: "agent" | "human";
5
5
  }
6
6
  export declare const EMPTY_TOKENS: TokenCounts;
7
7
  export declare function finiteNumber(value: unknown): number;
8
+ export declare function optionalFiniteNumber(value: unknown): number | null;
9
+ export declare function addToken(total: number | null, value: number | null): number | null;
8
10
  export declare function timestamp(value: unknown): number | null;
9
11
  export declare function inWindow(at: number, sinceMs: number, untilMs: number): boolean;
12
+ /** Local calendar date in the same format buckets and episodes use. */
13
+ export declare function localDateOf(at: number): string;
14
+ export declare function opaqueEpisodeId(secret: string, sourceId: string, sourceSession: string): string;
10
15
  export declare function bucketsFrom(events: LocalEvent[]): ActivityBucket[];
11
16
  export declare function emptySource(id: SourceId, name: string, configured: boolean): SourceSnapshot;
17
+ export declare function delegationEpisode(input: {
18
+ id: string;
19
+ adapterVersion: string;
20
+ events: LocalEvent[];
21
+ tokens: TokenCounts;
22
+ untilMs: number;
23
+ confidence?: EpisodeConfidence;
24
+ modelFamily?: string | null;
25
+ }): DelegationEpisode | null;
12
26
  export declare function finishSource(source: SourceSnapshot, events: LocalEvent[], fidelity?: SourceSnapshot["fidelity"]): SourceSnapshot;
13
27
  //# sourceMappingURL=activity.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"activity.d.ts","sourceRoot":"","sources":["../src/activity.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,QAAQ,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAExF,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC;CACzB;AAED,eAAO,MAAM,YAAY,EAAE,WAA8D,CAAC;AAE1F,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAEnD;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAIvD;AAED,wBAAgB,QAAQ,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAE9E;AAwBD,wBAAgB,WAAW,CAAC,MAAM,EAAE,UAAU,EAAE,GAAG,cAAc,EAAE,CAyBlE;AAED,wBAAgB,WAAW,CAAC,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,OAAO,GAAG,cAAc,CAY3F;AAED,wBAAgB,YAAY,CAC1B,MAAM,EAAE,cAAc,EACtB,MAAM,EAAE,UAAU,EAAE,EACpB,QAAQ,GAAE,cAAc,CAAC,UAAU,CAAc,GAChD,cAAc,CAMhB"}
1
+ {"version":3,"file":"activity.d.ts","sourceRoot":"","sources":["../src/activity.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,cAAc,EACd,iBAAiB,EACjB,iBAAiB,EACjB,QAAQ,EACR,cAAc,EACd,WAAW,EACZ,MAAM,YAAY,CAAC;AAEpB,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC;CACzB;AAED,eAAO,MAAM,YAAY,EAAE,WAA0E,CAAC;AAEtG,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAEnD;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAElE;AAED,wBAAgB,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,GAAG,IAAI,CAElF;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAIvD;AAED,wBAAgB,QAAQ,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAE9E;AAED,uEAAuE;AACvE,wBAAgB,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAE9C;AAUD,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,GAAG,MAAM,CAQ/F;AAgBD,wBAAgB,WAAW,CAAC,MAAM,EAAE,UAAU,EAAE,GAAG,cAAc,EAAE,CAyBlE;AAED,wBAAgB,WAAW,CAAC,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,OAAO,GAAG,cAAc,CAa3F;AA2BD,wBAAgB,iBAAiB,CAAC,KAAK,EAAE;IACvC,EAAE,EAAE,MAAM,CAAC;IACX,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE,UAAU,EAAE,CAAC;IACrB,MAAM,EAAE,WAAW,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,iBAAiB,CAAC;IAC/B,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B,GAAG,iBAAiB,GAAG,IAAI,CA8B3B;AAED,wBAAgB,YAAY,CAC1B,MAAM,EAAE,cAAc,EACtB,MAAM,EAAE,UAAU,EAAE,EACpB,QAAQ,GAAE,cAAc,CAAC,UAAU,CAAc,GAChD,cAAc,CAMhB"}
package/dist/activity.js CHANGED
@@ -1,7 +1,14 @@
1
- export const EMPTY_TOKENS = { input: 0, cached: 0, output: 0, reasoning: 0 };
1
+ import { createHmac } from "node:crypto";
2
+ export const EMPTY_TOKENS = { input: null, cached: null, output: null, reasoning: null };
2
3
  export function finiteNumber(value) {
3
4
  return typeof value === "number" && Number.isFinite(value) ? Math.max(0, value) : 0;
4
5
  }
6
+ export function optionalFiniteNumber(value) {
7
+ return typeof value === "number" && Number.isFinite(value) ? Math.max(0, value) : null;
8
+ }
9
+ export function addToken(total, value) {
10
+ return value == null ? total : (total ?? 0) + value;
11
+ }
5
12
  export function timestamp(value) {
6
13
  if (typeof value !== "string" && typeof value !== "number")
7
14
  return null;
@@ -11,6 +18,10 @@ export function timestamp(value) {
11
18
  export function inWindow(at, sinceMs, untilMs) {
12
19
  return at >= sinceMs && at <= untilMs;
13
20
  }
21
+ /** Local calendar date in the same format buckets and episodes use. */
22
+ export function localDateOf(at) {
23
+ return localClock(at).localDate;
24
+ }
14
25
  function localClock(at) {
15
26
  const d = new Date(at);
16
27
  const localDate = [d.getFullYear(), String(d.getMonth() + 1).padStart(2, "0"), String(d.getDate()).padStart(2, "0")].join("-");
@@ -18,6 +29,15 @@ function localClock(at) {
18
29
  d.setMinutes(0, 0, 0);
19
30
  return { hour: d.toISOString(), localDate, localHour: hourOfDay };
20
31
  }
32
+ export function opaqueEpisodeId(secret, sourceId, sourceSession) {
33
+ return createHmac("sha256", secret)
34
+ .update("afterhours/delegation-episode/v1\0")
35
+ .update(sourceId)
36
+ .update("\0")
37
+ .update(sourceSession)
38
+ .digest("hex")
39
+ .slice(0, 40);
40
+ }
21
41
  function observedMinutes(events, capMinutes) {
22
42
  const byHour = new Map();
23
43
  const sorted = [...new Set(events)].sort((a, b) => a - b);
@@ -69,6 +89,61 @@ export function emptySource(id, name, configured) {
69
89
  humanCheckIns: 0,
70
90
  tokens: { ...EMPTY_TOKENS },
71
91
  buckets: [],
92
+ episodes: [],
93
+ };
94
+ }
95
+ function minutesFrom(events, kind, capMinutes) {
96
+ return [...observedMinutes(events.filter((event) => event.kind === kind).map((event) => event.at), capMinutes).values()].reduce((sum, value) => sum + value, 0);
97
+ }
98
+ function longestAutonomousRun(events) {
99
+ const sorted = [...events].sort((a, b) => a.at - b.at);
100
+ let current = 0;
101
+ let longest = 0;
102
+ for (let index = 0; index < sorted.length; index += 1) {
103
+ const event = sorted[index];
104
+ if (!event)
105
+ continue;
106
+ if (event.kind === "human") {
107
+ current = 0;
108
+ continue;
109
+ }
110
+ const next = sorted[index + 1];
111
+ current += next == null ? 1 : Math.max(1, Math.min(5, (next.at - event.at) / 60_000));
112
+ longest = Math.max(longest, current);
113
+ }
114
+ return Math.round(longest * 100) / 100;
115
+ }
116
+ export function delegationEpisode(input) {
117
+ if (input.events.length === 0)
118
+ return null;
119
+ const events = [...input.events].sort((a, b) => a.at - b.at);
120
+ const first = events[0];
121
+ const last = events.at(-1);
122
+ if (!first || !last)
123
+ return null;
124
+ const humanCheckIns = events.filter((event) => event.kind === "human").length;
125
+ const clock = localClock(first.at);
126
+ return {
127
+ id: input.id,
128
+ startedAt: new Date(first.at).toISOString(),
129
+ endedAt: new Date(last.at).toISOString(),
130
+ localDate: clock.localDate,
131
+ localStartHour: clock.localHour,
132
+ status: input.untilMs - last.at <= 5 * 60_000 ? "open" : "observed_end",
133
+ confidence: input.confidence ?? "high",
134
+ metricVersion: "delegation-episode-v1",
135
+ adapterVersion: input.adapterVersion,
136
+ modelFamily: input.modelFamily ?? null,
137
+ agentTurns: events.filter((event) => event.kind === "agent").length,
138
+ humanCheckIns,
139
+ humanReEntries: Math.max(0, humanCheckIns - 1),
140
+ agentActiveMinutes: Math.round(minutesFrom(events, "agent", 5) * 100) / 100,
141
+ humanAttentionMinutes: Math.round(minutesFrom(events, "human", 10) * 100) / 100,
142
+ longestAutonomousRunMinutes: longestAutonomousRun(events),
143
+ approvalRequests: null,
144
+ failureEvents: null,
145
+ reworkEvents: null,
146
+ tokens: { ...input.tokens },
72
147
  };
73
148
  }
74
149
  export function finishSource(source, events, fidelity = "activity") {
@@ -1 +1 @@
1
- {"version":3,"file":"activity.js","sourceRoot":"","sources":["../src/activity.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"activity.js","sourceRoot":"","sources":["../src/activity.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ import { spawn } from "node:child_process";
2
+ export declare const AUTO_SYNC_WINDOW_MS: number;
3
+ export interface SyncStamp {
4
+ attemptedAt: string | null;
5
+ succeededAt: string | null;
6
+ }
7
+ export declare function syncStampPath(home?: string): string;
8
+ export declare function readSyncStamp(home?: string): SyncStamp;
9
+ export declare function markSyncAttempt(home?: string, now?: number): void;
10
+ export declare function markSyncSuccess(home?: string, now?: number): void;
11
+ export declare function shouldAutoSync(home?: string, now?: number, windowMs?: number): boolean;
12
+ /**
13
+ * Authoritative, child-side sync. Attempts are stamped before the network call
14
+ * so a failing server is retried at the window cadence, never per hook event.
15
+ */
16
+ export declare function runAutoSync(home?: string, fetcher?: typeof fetch, now?: number): Promise<boolean>;
17
+ /**
18
+ * Cheap parent-side decision inside `trs hook`: never network I/O here — the
19
+ * hook must return within the agent's timeout. The child re-checks the window.
20
+ */
21
+ export declare function maybeSpawnAutoSync(cliPath: string, home?: string, spawner?: typeof spawn, now?: number): boolean;
22
+ //# sourceMappingURL=autosync.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"autosync.d.ts","sourceRoot":"","sources":["../src/autosync.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAO3C,eAAO,MAAM,mBAAmB,QAAc,CAAC;AAE/C,MAAM,WAAW,SAAS;IACxB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B;AAED,wBAAgB,aAAa,CAAC,IAAI,SAAY,GAAG,MAAM,CAEtD;AAED,wBAAgB,aAAa,CAAC,IAAI,SAAY,GAAG,SAAS,CASzD;AAUD,wBAAgB,eAAe,CAAC,IAAI,SAAY,EAAE,GAAG,SAAa,GAAG,IAAI,CAExE;AAED,wBAAgB,eAAe,CAAC,IAAI,SAAY,EAAE,GAAG,SAAa,GAAG,IAAI,CAExE;AAED,wBAAgB,cAAc,CAAC,IAAI,SAAY,EAAE,GAAG,SAAa,EAAE,QAAQ,SAAsB,GAAG,OAAO,CAW1G;AAED;;;GAGG;AACH,wBAAsB,WAAW,CAAC,IAAI,SAAY,EAAE,OAAO,GAAE,OAAO,KAAa,EAAE,GAAG,SAAa,GAAG,OAAO,CAAC,OAAO,CAAC,CAarH;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,MAAM,EACf,IAAI,SAAY,EAChB,OAAO,GAAE,OAAO,KAAa,EAC7B,GAAG,SAAa,GACf,OAAO,CAST"}
@@ -0,0 +1,85 @@
1
+ import { spawn } from "node:child_process";
2
+ import { chmodSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
3
+ import { homedir } from "node:os";
4
+ import { dirname, join } from "node:path";
5
+ import { readCredentials, syncSnapshot } from "./cloud.js";
6
+ import { collectAfterhours } from "./collect.js";
7
+ export const AUTO_SYNC_WINDOW_MS = 45 * 60_000;
8
+ export function syncStampPath(home = homedir()) {
9
+ return join(home, ".teeras", "afterhours", "last-sync.json");
10
+ }
11
+ export function readSyncStamp(home = homedir()) {
12
+ try {
13
+ const value = JSON.parse(readFileSync(syncStampPath(home), "utf8"));
14
+ const iso = (candidate) => typeof candidate === "string" && Number.isFinite(Date.parse(candidate)) ? candidate : null;
15
+ return { attemptedAt: iso(value.attemptedAt), succeededAt: iso(value.succeededAt) };
16
+ }
17
+ catch {
18
+ return { attemptedAt: null, succeededAt: null };
19
+ }
20
+ }
21
+ function writeSyncStamp(stamp, home) {
22
+ const path = syncStampPath(home);
23
+ mkdirSync(dirname(path), { recursive: true, mode: 0o700 });
24
+ chmodSync(dirname(path), 0o700);
25
+ writeFileSync(path, `${JSON.stringify(stamp, null, 2)}\n`, { mode: 0o600 });
26
+ chmodSync(path, 0o600);
27
+ }
28
+ export function markSyncAttempt(home = homedir(), now = Date.now()) {
29
+ writeSyncStamp({ ...readSyncStamp(home), attemptedAt: new Date(now).toISOString() }, home);
30
+ }
31
+ export function markSyncSuccess(home = homedir(), now = Date.now()) {
32
+ writeSyncStamp({ ...readSyncStamp(home), succeededAt: new Date(now).toISOString() }, home);
33
+ }
34
+ export function shouldAutoSync(home = homedir(), now = Date.now(), windowMs = AUTO_SYNC_WINDOW_MS) {
35
+ if (!readCredentials(home))
36
+ return false;
37
+ const stamp = readSyncStamp(home);
38
+ // Manual commands (`setup`, `connect`, `login`, `sync`, `week`) stamp success
39
+ // only, so the window has to answer to whichever happened last — otherwise a
40
+ // hook event seconds after `trs setup` spawns a redundant sync.
41
+ const times = [stamp.attemptedAt, stamp.succeededAt]
42
+ .map((value) => (value ? Date.parse(value) : null))
43
+ .filter((value) => value != null);
44
+ if (times.length === 0)
45
+ return true;
46
+ return now - Math.max(...times) >= windowMs;
47
+ }
48
+ /**
49
+ * Authoritative, child-side sync. Attempts are stamped before the network call
50
+ * so a failing server is retried at the window cadence, never per hook event.
51
+ */
52
+ export async function runAutoSync(home = homedir(), fetcher = fetch, now = Date.now()) {
53
+ if (!shouldAutoSync(home, now))
54
+ return false;
55
+ const credentials = readCredentials(home);
56
+ if (!credentials)
57
+ return false;
58
+ markSyncAttempt(home, now);
59
+ try {
60
+ await syncSnapshot(collectAfterhours({ home }), credentials, fetcher);
61
+ markSyncSuccess(home, now);
62
+ return true;
63
+ }
64
+ catch {
65
+ // Failure is visible via `trs sources` (last attempt vs last success).
66
+ return false;
67
+ }
68
+ }
69
+ /**
70
+ * Cheap parent-side decision inside `trs hook`: never network I/O here — the
71
+ * hook must return within the agent's timeout. The child re-checks the window.
72
+ */
73
+ export function maybeSpawnAutoSync(cliPath, home = homedir(), spawner = spawn, now = Date.now()) {
74
+ if (!shouldAutoSync(home, now))
75
+ return false;
76
+ try {
77
+ const child = spawner(process.execPath, [cliPath, "autosync"], { detached: true, stdio: "ignore" });
78
+ child.unref();
79
+ return true;
80
+ }
81
+ catch {
82
+ return false;
83
+ }
84
+ }
85
+ //# sourceMappingURL=autosync.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"autosync.js","sourceRoot":"","sources":["../src/autosync.ts"],"names":[],"mappings":"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"}
package/dist/cli.js CHANGED
@@ -1,10 +1,18 @@
1
1
  #!/usr/bin/env node
2
+ import { readFileSync } from "node:fs";
3
+ import { fileURLToPath } from "node:url";
2
4
  import { collectAfterhours } from "./collect.js";
3
5
  import { openVerificationUri, pollDeviceLogin, readCredentials, removeCredentials, revokeDevice, startDeviceLogin, syncSnapshot, teerasBaseUrl, writeCredentials, } from "./cloud.js";
4
6
  import { detectCodingAgents } from "./detect.js";
7
+ import { runOff } from "./off.js";
5
8
  import { DATA_BOUNDARY } from "./privacy.js";
9
+ import { qrBlock } from "./qr.js";
6
10
  import { renderReport } from "./render.js";
7
11
  import { reportFrom } from "./report.js";
12
+ import { claudeHookSettings, codexHookSettings, hookJournalReport, ingestClaudeHook, ingestCodexHook, } from "./hooks.js";
13
+ import { ensureEpisodeSecret } from "./secret.js";
14
+ import { maybeSpawnAutoSync, markSyncSuccess, readSyncStamp, runAutoSync } from "./autosync.js";
15
+ import { runSetup } from "./setup.js";
8
16
  async function requireAccount() {
9
17
  const existing = readCredentials();
10
18
  if (existing)
@@ -27,12 +35,49 @@ async function requireAccount() {
27
35
  }
28
36
  async function main() {
29
37
  const command = process.argv[2];
38
+ if (command === "hook") {
39
+ // Recording stays gated on an account connection; `trs sources` reports a
40
+ // stale journal so this silent skip is visible instead of a mystery.
41
+ if (!readCredentials())
42
+ return;
43
+ try {
44
+ const raw = readFileSync(0, "utf8");
45
+ if (process.argv[3] === "codex")
46
+ ingestCodexHook(raw, ensureEpisodeSecret());
47
+ else
48
+ ingestClaudeHook(raw, ensureEpisodeSecret());
49
+ // Sync as a side effect of working: detached child, never blocks the hook.
50
+ maybeSpawnAutoSync(fileURLToPath(import.meta.url));
51
+ }
52
+ catch {
53
+ // Hooks are observability only and must never interrupt the agent.
54
+ }
55
+ return;
56
+ }
57
+ if (command === "autosync") {
58
+ // Hidden child command spawned by the hook branch; safe to run manually.
59
+ await runAutoSync();
60
+ return;
61
+ }
62
+ if (command === "hook-config") {
63
+ const source = process.argv[3] ?? "claude";
64
+ if (source === "codex") {
65
+ process.stdout.write(`${JSON.stringify(codexHookSettings(), null, 2)}\n`);
66
+ }
67
+ else if (source === "claude") {
68
+ process.stdout.write(`${JSON.stringify(claudeHookSettings(), null, 2)}\n`);
69
+ }
70
+ else {
71
+ process.stdout.write("usage: trs hook-config <codex|claude>\n");
72
+ }
73
+ return;
74
+ }
30
75
  if (command === "privacy") {
31
76
  process.stdout.write(`${JSON.stringify(DATA_BOUNDARY, null, 2)}\n`);
32
77
  return;
33
78
  }
34
79
  if (command === "connect") {
35
- await requireAccount();
80
+ const credentials = await requireAccount();
36
81
  const installed = detectCodingAgents().filter((agent) => agent.installed);
37
82
  const lines = installed.length === 0
38
83
  ? [" no coding agents found yet — install one, then run `trs connect` again."]
@@ -41,13 +86,25 @@ async function main() {
41
86
  const state = agent.connection === "automatic" ? "connected locally" : "detected · connector needed";
42
87
  return ` ${mark} ${agent.name.padEnd(22)} ${state}`;
43
88
  });
44
- process.stdout.write(["", " teeras · connect", "", ...lines, "", " Prompt, code, and output are never retained or synced.", ""].join("\n"));
89
+ await syncSnapshot(collectAfterhours(), credentials);
90
+ markSyncSuccess();
91
+ process.stdout.write([
92
+ "",
93
+ " teeras · connected",
94
+ "",
95
+ ...lines,
96
+ "",
97
+ " ✓ your first week is ready on web and mobile",
98
+ " Prompt, code, and output are never retained or synced.",
99
+ "",
100
+ ].join("\n"));
45
101
  return;
46
102
  }
47
103
  if (command === "login") {
48
104
  const credentials = await requireAccount();
49
105
  const snapshot = collectAfterhours();
50
106
  await syncSnapshot(snapshot, credentials);
107
+ markSyncSuccess();
51
108
  process.stdout.write("\n ✓ connected · your first counts-only week is in Teeras.\n\n");
52
109
  return;
53
110
  }
@@ -58,32 +115,55 @@ async function main() {
58
115
  process.stdout.write(`\n ${removed ? `✓ disconnected${revoked ? " and revoked" : " locally"}` : "this computer was not connected"}.\n\n`);
59
116
  return;
60
117
  }
118
+ if (command === "off") {
119
+ process.stdout.write(runOff().lines.join("\n"));
120
+ return;
121
+ }
61
122
  if (command === "sync") {
62
123
  const credentials = await requireAccount();
63
124
  const result = await syncSnapshot(collectAfterhours(), credentials);
125
+ markSyncSuccess();
64
126
  process.stdout.write(`\n ✓ ${result.stored ? "your AI life moved forward" : "already beautifully up to date"}.\n\n`);
65
127
  return;
66
128
  }
67
- if (command != null && !["week", "sources", "json", "--json", "setup"].includes(command)) {
68
- process.stdout.write("usage: trs <week|connect|login|sync|logout|sources|privacy|json>\n");
129
+ if (command === "setup") {
130
+ await runSetup({ json: process.argv.includes("--json") });
69
131
  return;
70
132
  }
71
- const credentials = await requireAccount();
133
+ if (command != null && !["week", "sources", "json", "--json", "autosync"].includes(command)) {
134
+ process.stdout.write("usage: trs <setup|week|connect|login|sync|logout|off|sources|privacy|json|hook-config>\n");
135
+ return;
136
+ }
137
+ const credentials = readCredentials();
72
138
  const snapshot = collectAfterhours();
139
+ if (!credentials && (command === "json" || command === "--json" || command === "sources")) {
140
+ process.stderr.write(" not connected yet · `npx @teeras/afterhours setup` links web + mobile\n");
141
+ }
73
142
  if (command === "--json" || command === "json") {
74
143
  process.stdout.write(`${JSON.stringify(snapshot, null, 2)}\n`);
75
144
  return;
76
145
  }
77
- if (command == null || command === "week" || command === "setup") {
146
+ if (command == null || command === "week") {
78
147
  process.stdout.write(renderReport(snapshot, reportFrom(snapshot)));
148
+ if (!credentials) {
149
+ process.stdout.write(" Your baseline starts here.\n");
150
+ process.stdout.write(" See AI activity, sleep, and recovery together: `npx @teeras/afterhours setup`\n\n");
151
+ return;
152
+ }
79
153
  try {
80
- await syncSnapshot(snapshot, credentials);
81
- process.stdout.write(" ✓ history synced to your Teeras account\n\n");
154
+ const synced = await syncSnapshot(snapshot, credentials);
155
+ markSyncSuccess();
156
+ process.stdout.write(" ✓ history synced to your Teeras account\n");
157
+ if (!synced.reflectedToday) {
158
+ process.stdout.write(" ten-second check-in waiting · teeras.com/app\n");
159
+ }
160
+ process.stdout.write("\n");
82
161
  }
83
162
  catch (error) {
84
163
  const message = error instanceof Error ? error.message : "sync unavailable";
85
164
  process.stdout.write(` local report is safe · ${message}\n\n`);
86
165
  }
166
+ process.stdout.write(`${qrBlock(process.stdout.isTTY === true).join("\n")}\n\n`);
87
167
  return;
88
168
  }
89
169
  if (command === "sources") {
@@ -92,7 +172,30 @@ async function main() {
92
172
  status: source.configured ? source.fidelity : "not found",
93
173
  sessions: source.sessions,
94
174
  }));
95
- process.stdout.write(`${JSON.stringify(rows, null, 2)}\n`);
175
+ const journal = hookJournalReport();
176
+ const claudeJournal = hookJournalReport(undefined, undefined, "claude-code");
177
+ const codexJournal = hookJournalReport(undefined, undefined, "codex");
178
+ const hookStatus = (report, install) => journal.journalExists
179
+ ? {
180
+ status: report.eventCount > 0 ? "recording" : "no events yet",
181
+ events: report.eventCount,
182
+ lastEventAt: report.lastEventAt,
183
+ ...(report.eventCount === 0 ? { install } : {}),
184
+ }
185
+ : { status: "not installed", install };
186
+ const claudeHook = hookStatus(claudeJournal, "trs hook-config claude");
187
+ const codexHook = hookStatus(codexJournal, "trs hook-config codex");
188
+ const stamp = readSyncStamp();
189
+ process.stdout.write(`${JSON.stringify({
190
+ sources: rows,
191
+ codexHook,
192
+ claudeHook,
193
+ cloudSync: {
194
+ connected: credentials != null,
195
+ lastAttemptAt: stamp.attemptedAt,
196
+ lastSuccessAt: stamp.succeededAt,
197
+ },
198
+ }, null, 2)}\n`);
96
199
  return;
97
200
  }
98
201
  }