@teeras/afterhours 0.1.1 → 0.3.1
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 +101 -15
- package/dist/activity.d.ts +35 -1
- package/dist/activity.d.ts.map +1 -1
- package/dist/activity.js +148 -1
- package/dist/activity.js.map +1 -1
- package/dist/autosync.d.ts +22 -0
- package/dist/autosync.d.ts.map +1 -0
- package/dist/autosync.js +85 -0
- package/dist/autosync.js.map +1 -0
- package/dist/cli.js +112 -9
- package/dist/cli.js.map +1 -1
- package/dist/cloud.d.ts +5 -0
- package/dist/cloud.d.ts.map +1 -1
- package/dist/cloud.js +10 -2
- package/dist/cloud.js.map +1 -1
- package/dist/collect.d.ts +3 -1
- package/dist/collect.d.ts.map +1 -1
- package/dist/collect.js +19 -7
- package/dist/collect.js.map +1 -1
- package/dist/detect.d.ts.map +1 -1
- package/dist/detect.js +35 -6
- package/dist/detect.js.map +1 -1
- package/dist/hooks.d.ts +29 -0
- package/dist/hooks.d.ts.map +1 -0
- package/dist/hooks.js +196 -0
- package/dist/hooks.js.map +1 -0
- package/dist/index.d.ts +9 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -1
- package/dist/index.js.map +1 -1
- package/dist/install.d.ts +25 -0
- package/dist/install.d.ts.map +1 -0
- package/dist/install.js +155 -0
- package/dist/install.js.map +1 -0
- package/dist/moments.d.ts +21 -0
- package/dist/moments.d.ts.map +1 -0
- package/dist/moments.js +52 -0
- package/dist/moments.js.map +1 -0
- package/dist/off.d.ts +24 -0
- package/dist/off.d.ts.map +1 -0
- package/dist/off.js +36 -0
- package/dist/off.js.map +1 -0
- package/dist/privacy.d.ts +8 -0
- package/dist/privacy.d.ts.map +1 -1
- package/dist/privacy.js +27 -1
- package/dist/privacy.js.map +1 -1
- package/dist/qr.d.ts +29 -0
- package/dist/qr.d.ts.map +1 -0
- package/dist/qr.js +92 -0
- package/dist/qr.js.map +1 -0
- package/dist/render.d.ts.map +1 -1
- package/dist/render.js +61 -1
- package/dist/render.js.map +1 -1
- package/dist/report.d.ts.map +1 -1
- package/dist/report.js +115 -1
- package/dist/report.js.map +1 -1
- package/dist/runtime.d.ts +18 -0
- package/dist/runtime.d.ts.map +1 -0
- package/dist/runtime.js +70 -0
- package/dist/runtime.js.map +1 -0
- package/dist/secret.d.ts +9 -0
- package/dist/secret.d.ts.map +1 -0
- package/dist/secret.js +32 -0
- package/dist/secret.js.map +1 -0
- package/dist/setup.d.ts +18 -0
- package/dist/setup.d.ts.map +1 -0
- package/dist/setup.js +155 -0
- package/dist/setup.js.map +1 -0
- package/dist/sources/claude.d.ts.map +1 -1
- package/dist/sources/claude.js +162 -18
- package/dist/sources/claude.js.map +1 -1
- package/dist/sources/codex.d.ts.map +1 -1
- package/dist/sources/codex.js +240 -21
- package/dist/sources/codex.js.map +1 -1
- package/dist/sources/cursor.d.ts.map +1 -1
- package/dist/sources/cursor.js +1 -0
- package/dist/sources/cursor.js.map +1 -1
- package/dist/sources/gemini.d.ts.map +1 -1
- package/dist/sources/gemini.js +85 -8
- package/dist/sources/gemini.js.map +1 -1
- package/dist/types.d.ts +109 -6
- package/dist/types.d.ts.map +1 -1
- package/llms-install.md +29 -0
- package/package.json +3 -2
package/dist/hooks.js
ADDED
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import { appendFileSync, chmodSync, mkdirSync, readFileSync, statSync, writeFileSync, } from "node:fs";
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import { dirname, join } from "node:path";
|
|
4
|
+
import { opaqueEpisodeId, timestamp } from "./activity.js";
|
|
5
|
+
const MAX_HOOK_INPUT_BYTES = 64 * 1024;
|
|
6
|
+
const MAX_JOURNAL_BYTES = 1024 * 1024;
|
|
7
|
+
const JOURNAL_RETENTION_MS = 32 * 86_400_000;
|
|
8
|
+
export function hookJournalPath(home = homedir()) {
|
|
9
|
+
return join(home, ".teeras", "afterhours", "hook-events.jsonl");
|
|
10
|
+
}
|
|
11
|
+
function record(value) {
|
|
12
|
+
return typeof value === "object" && value !== null && !Array.isArray(value)
|
|
13
|
+
? value
|
|
14
|
+
: null;
|
|
15
|
+
}
|
|
16
|
+
function signal(input) {
|
|
17
|
+
switch (input.hook_event_name) {
|
|
18
|
+
case "SessionStart":
|
|
19
|
+
return "session_started";
|
|
20
|
+
case "UserPromptSubmit":
|
|
21
|
+
return "human_prompt";
|
|
22
|
+
case "Stop":
|
|
23
|
+
return "agent_stopped";
|
|
24
|
+
case "StopFailure":
|
|
25
|
+
return "agent_failed";
|
|
26
|
+
case "PermissionRequest":
|
|
27
|
+
return "permission_requested";
|
|
28
|
+
case "SessionEnd":
|
|
29
|
+
return "session_ended";
|
|
30
|
+
case "Notification":
|
|
31
|
+
if (input.notification_type === "permission_prompt")
|
|
32
|
+
return "permission_requested";
|
|
33
|
+
if (input.notification_type === "idle_prompt")
|
|
34
|
+
return "human_needed";
|
|
35
|
+
return null;
|
|
36
|
+
default:
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
function compactJournal(path, now) {
|
|
41
|
+
try {
|
|
42
|
+
if (statSync(path).size <= MAX_JOURNAL_BYTES)
|
|
43
|
+
return;
|
|
44
|
+
const retained = readContentFreeHookEvents(dirname(dirname(dirname(path))), now - JOURNAL_RETENTION_MS, now);
|
|
45
|
+
writeFileSync(path, retained.map((event) => JSON.stringify(event)).join("\n") + (retained.length ? "\n" : ""), {
|
|
46
|
+
mode: 0o600,
|
|
47
|
+
});
|
|
48
|
+
chmodSync(path, 0o600);
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
// A failed compaction must never interrupt the agent.
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Reduce an agent hook payload immediately. Prompt, cwd, transcript path,
|
|
56
|
+
* messages, tool inputs, and model output are neither returned nor written.
|
|
57
|
+
* The episode secret is the stable per-device HMAC key (never a rotating
|
|
58
|
+
* token), so journal events keep joining log-derived episodes across logins.
|
|
59
|
+
*/
|
|
60
|
+
function ingestAgentHook(raw, sourceId, episodeSecret, home = homedir(), now = Date.now()) {
|
|
61
|
+
if (Buffer.byteLength(raw, "utf8") > MAX_HOOK_INPUT_BYTES)
|
|
62
|
+
return null;
|
|
63
|
+
let input;
|
|
64
|
+
try {
|
|
65
|
+
input = record(JSON.parse(raw));
|
|
66
|
+
}
|
|
67
|
+
catch {
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
const sessionId = typeof input?.session_id === "string" && input.session_id.length <= 200
|
|
71
|
+
? input.session_id
|
|
72
|
+
: null;
|
|
73
|
+
const nextSignal = input ? signal(input) : null;
|
|
74
|
+
if (!sessionId || !nextSignal)
|
|
75
|
+
return null;
|
|
76
|
+
const event = {
|
|
77
|
+
schemaVersion: 1,
|
|
78
|
+
at: now,
|
|
79
|
+
sourceId,
|
|
80
|
+
episodeId: opaqueEpisodeId(episodeSecret, sourceId, sessionId),
|
|
81
|
+
signal: nextSignal,
|
|
82
|
+
modelFamily: typeof input?.model === "string" && /^[a-z0-9._-]{1,80}$/i.test(input.model)
|
|
83
|
+
? input.model
|
|
84
|
+
: null,
|
|
85
|
+
};
|
|
86
|
+
const path = hookJournalPath(home);
|
|
87
|
+
mkdirSync(dirname(path), { recursive: true, mode: 0o700 });
|
|
88
|
+
chmodSync(dirname(path), 0o700);
|
|
89
|
+
appendFileSync(path, `${JSON.stringify(event)}\n`, { mode: 0o600 });
|
|
90
|
+
chmodSync(path, 0o600);
|
|
91
|
+
compactJournal(path, now);
|
|
92
|
+
return event;
|
|
93
|
+
}
|
|
94
|
+
export function ingestClaudeHook(raw, episodeSecret, home = homedir(), now = Date.now()) {
|
|
95
|
+
return ingestAgentHook(raw, "claude-code", episodeSecret, home, now);
|
|
96
|
+
}
|
|
97
|
+
export function ingestCodexHook(raw, episodeSecret, home = homedir(), now = Date.now()) {
|
|
98
|
+
return ingestAgentHook(raw, "codex", episodeSecret, home, now);
|
|
99
|
+
}
|
|
100
|
+
export function readContentFreeHookEvents(home = homedir(), sinceMs = 0, untilMs = Date.now()) {
|
|
101
|
+
let text;
|
|
102
|
+
try {
|
|
103
|
+
text = readFileSync(hookJournalPath(home), "utf8");
|
|
104
|
+
}
|
|
105
|
+
catch {
|
|
106
|
+
return [];
|
|
107
|
+
}
|
|
108
|
+
const events = [];
|
|
109
|
+
for (const line of text.split("\n")) {
|
|
110
|
+
if (!line)
|
|
111
|
+
continue;
|
|
112
|
+
let value;
|
|
113
|
+
try {
|
|
114
|
+
value = record(JSON.parse(line));
|
|
115
|
+
}
|
|
116
|
+
catch {
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
const at = timestamp(value?.at);
|
|
120
|
+
if (value?.schemaVersion !== 1 ||
|
|
121
|
+
at == null || at < sinceMs || at > untilMs ||
|
|
122
|
+
(value.sourceId !== "claude-code" && value.sourceId !== "codex") ||
|
|
123
|
+
typeof value.episodeId !== "string" || !/^[a-f0-9]{40}$/.test(value.episodeId) ||
|
|
124
|
+
(value.signal !== "session_started" &&
|
|
125
|
+
value.signal !== "human_prompt" &&
|
|
126
|
+
value.signal !== "agent_stopped" &&
|
|
127
|
+
value.signal !== "agent_failed" &&
|
|
128
|
+
value.signal !== "permission_requested" &&
|
|
129
|
+
value.signal !== "human_needed" &&
|
|
130
|
+
value.signal !== "session_ended") ||
|
|
131
|
+
(value.modelFamily !== null &&
|
|
132
|
+
(typeof value.modelFamily !== "string" || !/^[a-z0-9._-]{1,80}$/i.test(value.modelFamily))))
|
|
133
|
+
continue;
|
|
134
|
+
events.push(value);
|
|
135
|
+
}
|
|
136
|
+
return events.sort((a, b) => a.at - b.at);
|
|
137
|
+
}
|
|
138
|
+
// 5 seconds absorbs Node cold-start on a loaded machine; a killed hook is a
|
|
139
|
+
// silently missing measurement, which is worse than a briefly slower event.
|
|
140
|
+
const HOOK_TIMEOUT_SECONDS = 5;
|
|
141
|
+
export function claudeHookSettings(command = "trs hook") {
|
|
142
|
+
const entry = { hooks: [{ type: "command", command, timeout: HOOK_TIMEOUT_SECONDS }] };
|
|
143
|
+
return {
|
|
144
|
+
hooks: {
|
|
145
|
+
SessionStart: [entry],
|
|
146
|
+
UserPromptSubmit: [entry],
|
|
147
|
+
PermissionRequest: [entry],
|
|
148
|
+
Stop: [entry],
|
|
149
|
+
StopFailure: [entry],
|
|
150
|
+
SessionEnd: [entry],
|
|
151
|
+
Notification: [{
|
|
152
|
+
matcher: "idle_prompt",
|
|
153
|
+
hooks: [{ type: "command", command, timeout: HOOK_TIMEOUT_SECONDS }],
|
|
154
|
+
}],
|
|
155
|
+
},
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* User-level Codex hooks are read from the same CODEX_HOME configuration by
|
|
160
|
+
* the Codex app and CLI. SessionEnd has a documented three-second maximum.
|
|
161
|
+
*/
|
|
162
|
+
export const CODEX_HOOKS_DESCRIPTION = "Teeras content-free delegation episode signals.";
|
|
163
|
+
export function codexHookSettings(command = "trs hook codex") {
|
|
164
|
+
const entry = (timeout = HOOK_TIMEOUT_SECONDS) => ({
|
|
165
|
+
hooks: [{ type: "command", command, timeout }],
|
|
166
|
+
});
|
|
167
|
+
return {
|
|
168
|
+
description: CODEX_HOOKS_DESCRIPTION,
|
|
169
|
+
hooks: {
|
|
170
|
+
SessionStart: [entry()],
|
|
171
|
+
UserPromptSubmit: [entry()],
|
|
172
|
+
PermissionRequest: [entry()],
|
|
173
|
+
Stop: [entry()],
|
|
174
|
+
SessionEnd: [entry(3)],
|
|
175
|
+
},
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
/** Visibility for `trs sources`: a stale or empty journal must be seen, not guessed. */
|
|
179
|
+
export function hookJournalReport(home = homedir(), now = Date.now(), sourceId) {
|
|
180
|
+
let journalExists = true;
|
|
181
|
+
try {
|
|
182
|
+
statSync(hookJournalPath(home));
|
|
183
|
+
}
|
|
184
|
+
catch {
|
|
185
|
+
journalExists = false;
|
|
186
|
+
}
|
|
187
|
+
const events = readContentFreeHookEvents(home, 0, now)
|
|
188
|
+
.filter((event) => sourceId == null || event.sourceId === sourceId);
|
|
189
|
+
const last = events.at(-1);
|
|
190
|
+
return {
|
|
191
|
+
journalExists,
|
|
192
|
+
eventCount: events.length,
|
|
193
|
+
lastEventAt: last ? new Date(last.at).toISOString() : null,
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
//# sourceMappingURL=hooks.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hooks.js","sourceRoot":"","sources":["../src/hooks.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,cAAc,EACd,SAAS,EACT,SAAS,EACT,YAAY,EACZ,QAAQ,EACR,aAAa,GACd,MAAM,SAAS,CAAC;AACjB,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAsB3D,MAAM,oBAAoB,GAAG,EAAE,GAAG,IAAI,CAAC;AACvC,MAAM,iBAAiB,GAAG,IAAI,GAAG,IAAI,CAAC;AACtC,MAAM,oBAAoB,GAAG,EAAE,GAAG,UAAU,CAAC;AAE7C,MAAM,UAAU,eAAe,CAAC,IAAI,GAAG,OAAO,EAAE;IAC9C,OAAO,IAAI,CAAC,IAAI,EAAE,SAAS,EAAE,YAAY,EAAE,mBAAmB,CAAC,CAAC;AAClE,CAAC;AAED,SAAS,MAAM,CAAC,KAAc;IAC5B,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QACzE,CAAC,CAAC,KAAgC;QAClC,CAAC,CAAC,IAAI,CAAC;AACX,CAAC;AAED,SAAS,MAAM,CAAC,KAA8B;IAC5C,QAAQ,KAAK,CAAC,eAAe,EAAE,CAAC;QAC9B,KAAK,cAAc;YACjB,OAAO,iBAAiB,CAAC;QAC3B,KAAK,kBAAkB;YACrB,OAAO,cAAc,CAAC;QACxB,KAAK,MAAM;YACT,OAAO,eAAe,CAAC;QACzB,KAAK,aAAa;YAChB,OAAO,cAAc,CAAC;QACxB,KAAK,mBAAmB;YACtB,OAAO,sBAAsB,CAAC;QAChC,KAAK,YAAY;YACf,OAAO,eAAe,CAAC;QACzB,KAAK,cAAc;YACjB,IAAI,KAAK,CAAC,iBAAiB,KAAK,mBAAmB;gBAAE,OAAO,sBAAsB,CAAC;YACnF,IAAI,KAAK,CAAC,iBAAiB,KAAK,aAAa;gBAAE,OAAO,cAAc,CAAC;YACrE,OAAO,IAAI,CAAC;QACd;YACE,OAAO,IAAI,CAAC;IAChB,CAAC;AACH,CAAC;AAED,SAAS,cAAc,CAAC,IAAY,EAAE,GAAW;IAC/C,IAAI,CAAC;QACH,IAAI,QAAQ,CAAC,IAAI,CAAC,CAAC,IAAI,IAAI,iBAAiB;YAAE,OAAO;QACrD,MAAM,QAAQ,GAAG,yBAAyB,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,GAAG,GAAG,oBAAoB,EAAE,GAAG,CAAC,CAAC;QAC7G,aAAa,CAAC,IAAI,EAAE,QAAQ,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE;YAC7G,IAAI,EAAE,KAAK;SACZ,CAAC,CAAC;QACH,SAAS,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IACzB,CAAC;IAAC,MAAM,CAAC;QACP,sDAAsD;IACxD,CAAC;AACH,CAAC;AAED;;;;;GAKG;AACH,SAAS,eAAe,CACtB,GAAW,EACX,QAAsB,EACtB,aAAqB,EACrB,IAAI,GAAG,OAAO,EAAE,EAChB,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE;IAEhB,IAAI,MAAM,CAAC,UAAU,CAAC,GAAG,EAAE,MAAM,CAAC,GAAG,oBAAoB;QAAE,OAAO,IAAI,CAAC;IACvE,IAAI,KAAqC,CAAC;IAC1C,IAAI,CAAC;QACH,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC;IAClC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;IACD,MAAM,SAAS,GAAG,OAAO,KAAK,EAAE,UAAU,KAAK,QAAQ,IAAI,KAAK,CAAC,UAAU,CAAC,MAAM,IAAI,GAAG;QACvF,CAAC,CAAC,KAAK,CAAC,UAAU;QAClB,CAAC,CAAC,IAAI,CAAC;IACT,MAAM,UAAU,GAAG,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IAChD,IAAI,CAAC,SAAS,IAAI,CAAC,UAAU;QAAE,OAAO,IAAI,CAAC;IAE3C,MAAM,KAAK,GAAyB;QAClC,aAAa,EAAE,CAAC;QAChB,EAAE,EAAE,GAAG;QACP,QAAQ;QACR,SAAS,EAAE,eAAe,CAAC,aAAa,EAAE,QAAQ,EAAE,SAAS,CAAC;QAC9D,MAAM,EAAE,UAAU;QAClB,WAAW,EAAE,OAAO,KAAK,EAAE,KAAK,KAAK,QAAQ,IAAI,sBAAsB,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC;YACvF,CAAC,CAAC,KAAK,CAAC,KAAK;YACb,CAAC,CAAC,IAAI;KACT,CAAC;IACF,MAAM,IAAI,GAAG,eAAe,CAAC,IAAI,CAAC,CAAC;IACnC,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IAC3D,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,KAAK,CAAC,CAAC;IAChC,cAAc,CAAC,IAAI,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IACpE,SAAS,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IACvB,cAAc,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;IAC1B,OAAO,KAAK,CAAC;AACf,CAAC;AAED,MAAM,UAAU,gBAAgB,CAC9B,GAAW,EACX,aAAqB,EACrB,IAAI,GAAG,OAAO,EAAE,EAChB,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE;IAEhB,OAAO,eAAe,CAAC,GAAG,EAAE,aAAa,EAAE,aAAa,EAAE,IAAI,EAAE,GAAG,CAAC,CAAC;AACvE,CAAC;AAED,MAAM,UAAU,eAAe,CAC7B,GAAW,EACX,aAAqB,EACrB,IAAI,GAAG,OAAO,EAAE,EAChB,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE;IAEhB,OAAO,eAAe,CAAC,GAAG,EAAE,OAAO,EAAE,aAAa,EAAE,IAAI,EAAE,GAAG,CAAC,CAAC;AACjE,CAAC;AAED,MAAM,UAAU,yBAAyB,CACvC,IAAI,GAAG,OAAO,EAAE,EAChB,OAAO,GAAG,CAAC,EACX,OAAO,GAAG,IAAI,CAAC,GAAG,EAAE;IAEpB,IAAI,IAAY,CAAC;IACjB,IAAI,CAAC;QACH,IAAI,GAAG,YAAY,CAAC,eAAe,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC,CAAC;IACrD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;IACD,MAAM,MAAM,GAA2B,EAAE,CAAC;IAC1C,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;QACpC,IAAI,CAAC,IAAI;YAAE,SAAS;QACpB,IAAI,KAAqC,CAAC;QAC1C,IAAI,CAAC;YACH,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC;QACnC,CAAC;QAAC,MAAM,CAAC;YACP,SAAS;QACX,CAAC;QACD,MAAM,EAAE,GAAG,SAAS,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;QAChC,IACE,KAAK,EAAE,aAAa,KAAK,CAAC;YAC1B,EAAE,IAAI,IAAI,IAAI,EAAE,GAAG,OAAO,IAAI,EAAE,GAAG,OAAO;YAC1C,CAAC,KAAK,CAAC,QAAQ,KAAK,aAAa,IAAI,KAAK,CAAC,QAAQ,KAAK,OAAO,CAAC;YAChE,OAAO,KAAK,CAAC,SAAS,KAAK,QAAQ,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC;YAC9E,CAAC,KAAK,CAAC,MAAM,KAAK,iBAAiB;gBACjC,KAAK,CAAC,MAAM,KAAK,cAAc;gBAC/B,KAAK,CAAC,MAAM,KAAK,eAAe;gBAChC,KAAK,CAAC,MAAM,KAAK,cAAc;gBAC/B,KAAK,CAAC,MAAM,KAAK,sBAAsB;gBACvC,KAAK,CAAC,MAAM,KAAK,cAAc;gBAC/B,KAAK,CAAC,MAAM,KAAK,eAAe,CAAC;YACnC,CAAC,KAAK,CAAC,WAAW,KAAK,IAAI;gBACzB,CAAC,OAAO,KAAK,CAAC,WAAW,KAAK,QAAQ,IAAI,CAAC,sBAAsB,CAAC,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC,CAAC;YAC7F,SAAS;QACX,MAAM,CAAC,IAAI,CAAC,KAAwC,CAAC,CAAC;IACxD,CAAC;IACD,OAAO,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC;AAC5C,CAAC;AAED,4EAA4E;AAC5E,4EAA4E;AAC5E,MAAM,oBAAoB,GAAG,CAAC,CAAC;AAE/B,MAAM,UAAU,kBAAkB,CAAC,OAAO,GAAG,UAAU;IACrD,MAAM,KAAK,GAAG,EAAE,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,OAAO,EAAE,oBAAoB,EAAE,CAAC,EAAE,CAAC;IACvF,OAAO;QACL,KAAK,EAAE;YACL,YAAY,EAAE,CAAC,KAAK,CAAC;YACrB,gBAAgB,EAAE,CAAC,KAAK,CAAC;YACzB,iBAAiB,EAAE,CAAC,KAAK,CAAC;YAC1B,IAAI,EAAE,CAAC,KAAK,CAAC;YACb,WAAW,EAAE,CAAC,KAAK,CAAC;YACpB,UAAU,EAAE,CAAC,KAAK,CAAC;YACnB,YAAY,EAAE,CAAC;oBACb,OAAO,EAAE,aAAa;oBACtB,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,OAAO,EAAE,oBAAoB,EAAE,CAAC;iBACrE,CAAC;SACH;KACF,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAAG,iDAAiD,CAAC;AAEzF,MAAM,UAAU,iBAAiB,CAAC,OAAO,GAAG,gBAAgB;IAC1D,MAAM,KAAK,GAAG,CAAC,OAAO,GAAG,oBAAoB,EAAE,EAAE,CAAC,CAAC;QACjD,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC;KAC/C,CAAC,CAAC;IACH,OAAO;QACL,WAAW,EAAE,uBAAuB;QACpC,KAAK,EAAE;YACL,YAAY,EAAE,CAAC,KAAK,EAAE,CAAC;YACvB,gBAAgB,EAAE,CAAC,KAAK,EAAE,CAAC;YAC3B,iBAAiB,EAAE,CAAC,KAAK,EAAE,CAAC;YAC5B,IAAI,EAAE,CAAC,KAAK,EAAE,CAAC;YACf,UAAU,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;SACvB;KACF,CAAC;AACJ,CAAC;AAQD,wFAAwF;AACxF,MAAM,UAAU,iBAAiB,CAC/B,IAAI,GAAG,OAAO,EAAE,EAChB,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,EAChB,QAAuB;IAEvB,IAAI,aAAa,GAAG,IAAI,CAAC;IACzB,IAAI,CAAC;QACH,QAAQ,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC,CAAC;IAClC,CAAC;IAAC,MAAM,CAAC;QACP,aAAa,GAAG,KAAK,CAAC;IACxB,CAAC;IACD,MAAM,MAAM,GAAG,yBAAyB,CAAC,IAAI,EAAE,CAAC,EAAE,GAAG,CAAC;SACnD,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,QAAQ,IAAI,IAAI,IAAI,KAAK,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC;IACtE,MAAM,IAAI,GAAG,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IAC3B,OAAO;QACL,aAAa;QACb,UAAU,EAAE,MAAM,CAAC,MAAM;QACzB,WAAW,EAAE,IAAI,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,IAAI;KAC3D,CAAC;AACJ,CAAC"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,8 +1,15 @@
|
|
|
1
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";
|
|
2
|
+
export { ConnectionExpiredError, credentialsPath, openVerificationUri, pollDeviceLogin, readCredentials, removeCredentials, startDeviceLogin, syncSnapshot, revokeDevice, teerasBaseUrl, writeCredentials, type CloudCredentials, type DeviceGrant, } from "./cloud.js";
|
|
3
3
|
export { DATA_BOUNDARY } from "./privacy.js";
|
|
4
|
+
export { claudeHookSettings, codexHookSettings, hookJournalPath, hookJournalReport, ingestClaudeHook, ingestCodexHook, readContentFreeHookEvents, type ContentFreeHookEvent, type HookJournalReport, type HookSignal, type HookSourceId, } from "./hooks.js";
|
|
5
|
+
export { ensureEpisodeSecret, episodeSecretPath } from "./secret.js";
|
|
4
6
|
export { detectCodingAgents } from "./detect.js";
|
|
5
7
|
export { reportFrom } from "./report.js";
|
|
6
8
|
export { renderReport } from "./render.js";
|
|
7
|
-
export
|
|
9
|
+
export { installClaudeHooks, installCodexHooks, installHooks, type HookCommands, type HookInstallResult, } from "./install.js";
|
|
10
|
+
export { hookCommand, installRuntime, runtimeBinDir } from "./runtime.js";
|
|
11
|
+
export { runOff, type OffDeps, type OffResult, type RuntimeOutcome } from "./off.js";
|
|
12
|
+
export { AUTO_SYNC_WINDOW_MS, markSyncAttempt, markSyncSuccess, maybeSpawnAutoSync, readSyncStamp, runAutoSync, shouldAutoSync, syncStampPath, type SyncStamp, } from "./autosync.js";
|
|
13
|
+
export { runSetup, type SetupOptions } from "./setup.js";
|
|
14
|
+
export type { ActivityBucket, AfterhoursReport, AfterhoursToday, AfterhoursSnapshot, CollectInput, DataBoundary, DelegationEpisode, DetectedAgent, EpisodeConfidence, EpisodeStatus, Fidelity, SourceAdapter, SourceId, SourceSnapshot, TokenCounts, } from "./types.js";
|
|
8
15
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +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"}
|
|
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,sBAAsB,EACtB,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,EACL,kBAAkB,EAClB,iBAAiB,EACjB,eAAe,EACf,iBAAiB,EACjB,gBAAgB,EAChB,eAAe,EACf,yBAAyB,EACzB,KAAK,oBAAoB,EACzB,KAAK,iBAAiB,EACtB,KAAK,UAAU,EACf,KAAK,YAAY,GAClB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AACrE,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EACL,kBAAkB,EAClB,iBAAiB,EACjB,YAAY,EACZ,KAAK,YAAY,EACjB,KAAK,iBAAiB,GACvB,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC1E,OAAO,EAAE,MAAM,EAAE,KAAK,OAAO,EAAE,KAAK,SAAS,EAAE,KAAK,cAAc,EAAE,MAAM,UAAU,CAAC;AACrF,OAAO,EACL,mBAAmB,EACnB,eAAe,EACf,eAAe,EACf,kBAAkB,EAClB,aAAa,EACb,WAAW,EACX,cAAc,EACd,aAAa,EACb,KAAK,SAAS,GACf,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,QAAQ,EAAE,KAAK,YAAY,EAAE,MAAM,YAAY,CAAC;AACzD,YAAY,EACV,cAAc,EACd,gBAAgB,EAChB,eAAe,EACf,kBAAkB,EAClB,YAAY,EACZ,YAAY,EACZ,iBAAiB,EACjB,aAAa,EACb,iBAAiB,EACjB,aAAa,EACb,QAAQ,EACR,aAAa,EACb,QAAQ,EACR,cAAc,EACd,WAAW,GACZ,MAAM,YAAY,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
export { collectAfterhours, SOURCES } from "./collect.js";
|
|
2
|
-
export { credentialsPath, openVerificationUri, pollDeviceLogin, readCredentials, removeCredentials, startDeviceLogin, syncSnapshot, revokeDevice, teerasBaseUrl, writeCredentials, } from "./cloud.js";
|
|
2
|
+
export { ConnectionExpiredError, credentialsPath, openVerificationUri, pollDeviceLogin, readCredentials, removeCredentials, startDeviceLogin, syncSnapshot, revokeDevice, teerasBaseUrl, writeCredentials, } from "./cloud.js";
|
|
3
3
|
export { DATA_BOUNDARY } from "./privacy.js";
|
|
4
|
+
export { claudeHookSettings, codexHookSettings, hookJournalPath, hookJournalReport, ingestClaudeHook, ingestCodexHook, readContentFreeHookEvents, } from "./hooks.js";
|
|
5
|
+
export { ensureEpisodeSecret, episodeSecretPath } from "./secret.js";
|
|
4
6
|
export { detectCodingAgents } from "./detect.js";
|
|
5
7
|
export { reportFrom } from "./report.js";
|
|
6
8
|
export { renderReport } from "./render.js";
|
|
9
|
+
export { installClaudeHooks, installCodexHooks, installHooks, } from "./install.js";
|
|
10
|
+
export { hookCommand, installRuntime, runtimeBinDir } from "./runtime.js";
|
|
11
|
+
export { runOff } from "./off.js";
|
|
12
|
+
export { AUTO_SYNC_WINDOW_MS, markSyncAttempt, markSyncSuccess, maybeSpawnAutoSync, readSyncStamp, runAutoSync, shouldAutoSync, syncStampPath, } from "./autosync.js";
|
|
13
|
+
export { runSetup } from "./setup.js";
|
|
7
14
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +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"}
|
|
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,sBAAsB,EACtB,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,EACL,kBAAkB,EAClB,iBAAiB,EACjB,eAAe,EACf,iBAAiB,EACjB,gBAAgB,EAChB,eAAe,EACf,yBAAyB,GAK1B,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AACrE,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EACL,kBAAkB,EAClB,iBAAiB,EACjB,YAAY,GAGb,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC1E,OAAO,EAAE,MAAM,EAAqD,MAAM,UAAU,CAAC;AACrF,OAAO,EACL,mBAAmB,EACnB,eAAe,EACf,eAAe,EACf,kBAAkB,EAClB,aAAa,EACb,WAAW,EACX,cAAc,EACd,aAAa,GAEd,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,QAAQ,EAAqB,MAAM,YAAY,CAAC"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export interface HookInstallResult {
|
|
2
|
+
agent: "claude-code" | "codex";
|
|
3
|
+
detected: boolean;
|
|
4
|
+
path: string;
|
|
5
|
+
action: "installed" | "already-installed" | "skipped" | "unreadable";
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Commands default to `trs hook`, which is right for the hand-merged
|
|
9
|
+
* `trs hook-config` path. Setup passes absolute commands instead, because an
|
|
10
|
+
* npx-only user has no `trs` on PATH once the install directory is gone.
|
|
11
|
+
*/
|
|
12
|
+
export interface HookCommands {
|
|
13
|
+
claude?: string;
|
|
14
|
+
codex?: string;
|
|
15
|
+
}
|
|
16
|
+
export declare function installClaudeHooks(home?: string, command?: string): HookInstallResult;
|
|
17
|
+
export declare function installCodexHooks(home?: string, command?: string): HookInstallResult;
|
|
18
|
+
export declare function installHooks(home?: string, commands?: HookCommands): HookInstallResult[];
|
|
19
|
+
export interface HookRemoveResult {
|
|
20
|
+
agent: HookInstallResult["agent"];
|
|
21
|
+
path: string;
|
|
22
|
+
action: "removed" | "not-installed" | "unreadable";
|
|
23
|
+
}
|
|
24
|
+
export declare function uninstallHooks(home?: string): HookRemoveResult[];
|
|
25
|
+
//# sourceMappingURL=install.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"install.d.ts","sourceRoot":"","sources":["../src/install.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,aAAa,GAAG,OAAO,CAAC;IAC/B,QAAQ,EAAE,OAAO,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,WAAW,GAAG,mBAAmB,GAAG,SAAS,GAAG,YAAY,CAAC;CACtE;AAiFD;;;;GAIG;AACH,MAAM,WAAW,YAAY;IAC3B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,wBAAgB,kBAAkB,CAAC,IAAI,SAAY,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,iBAAiB,CAGxF;AAED,wBAAgB,iBAAiB,CAAC,IAAI,SAAY,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,iBAAiB,CAGvF;AAED,wBAAgB,YAAY,CAAC,IAAI,SAAY,EAAE,QAAQ,GAAE,YAAiB,GAAG,iBAAiB,EAAE,CAE/F;AAED,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,iBAAiB,CAAC,OAAO,CAAC,CAAC;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,SAAS,GAAG,eAAe,GAAG,YAAY,CAAC;CACpD;AAqDD,wBAAgB,cAAc,CAAC,IAAI,SAAY,GAAG,gBAAgB,EAAE,CAOnE"}
|
package/dist/install.js
ADDED
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import { existsSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import { isAbsolute, join } from "node:path";
|
|
4
|
+
import { isDirectory } from "./files.js";
|
|
5
|
+
import { CODEX_HOOKS_DESCRIPTION, claudeHookSettings, codexHookSettings } from "./hooks.js";
|
|
6
|
+
// Same override rule as detect.ts: env roots apply only for the real home.
|
|
7
|
+
function agentRoot(home, key, fallback) {
|
|
8
|
+
const configured = process.env[key]?.trim();
|
|
9
|
+
return home === homedir() && configured && isAbsolute(configured) ? configured : fallback;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* A file that exists but fails to parse, or whose top level isn't an object
|
|
13
|
+
* (e.g. a JSON array), is `unreadable` — never treated as empty. Merging on
|
|
14
|
+
* top of `{}` would silently overwrite whatever the user actually had there
|
|
15
|
+
* (their `permissions.deny`, other hooks, env, MCP config) with no warning.
|
|
16
|
+
*/
|
|
17
|
+
function readSettings(path) {
|
|
18
|
+
if (!existsSync(path))
|
|
19
|
+
return { kind: "absent" };
|
|
20
|
+
let raw;
|
|
21
|
+
try {
|
|
22
|
+
raw = readFileSync(path, "utf8");
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
return { kind: "unreadable" };
|
|
26
|
+
}
|
|
27
|
+
let parsed;
|
|
28
|
+
try {
|
|
29
|
+
parsed = JSON.parse(raw);
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
return { kind: "unreadable" };
|
|
33
|
+
}
|
|
34
|
+
if (typeof parsed === "object" && parsed !== null && !Array.isArray(parsed)) {
|
|
35
|
+
return { kind: "object", value: parsed };
|
|
36
|
+
}
|
|
37
|
+
return { kind: "unreadable" };
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Ours in either form: the PATH-based `trs hook` a hand-merged user pasted, or
|
|
41
|
+
* the absolute durable command setup installs. Recognizing both keeps a second
|
|
42
|
+
* run from appending a duplicate hook to a working configuration.
|
|
43
|
+
*/
|
|
44
|
+
function isTeerasHookCommand(command) {
|
|
45
|
+
if (typeof command !== "string")
|
|
46
|
+
return false;
|
|
47
|
+
const normalized = command.replaceAll("\\", "/");
|
|
48
|
+
return normalized.includes("trs hook") || normalized.includes(".teeras/afterhours/bin/cli.js");
|
|
49
|
+
}
|
|
50
|
+
function hasTeerasHook(entries) {
|
|
51
|
+
if (!Array.isArray(entries))
|
|
52
|
+
return false;
|
|
53
|
+
return entries.some((entry) => {
|
|
54
|
+
const hooks = entry?.hooks;
|
|
55
|
+
return Array.isArray(hooks) && hooks.some((hook) => isTeerasHookCommand(hook?.command));
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
function mergeHooks(path, fragment, agent, detected) {
|
|
59
|
+
if (!detected)
|
|
60
|
+
return { agent, detected, path, action: "skipped" };
|
|
61
|
+
const read = readSettings(path);
|
|
62
|
+
if (read.kind === "unreadable")
|
|
63
|
+
return { agent, detected, path, action: "unreadable" };
|
|
64
|
+
const settings = read.kind === "object" ? read.value : {};
|
|
65
|
+
const existingHooks = typeof settings.hooks === "object" && settings.hooks !== null && !Array.isArray(settings.hooks)
|
|
66
|
+
? settings.hooks
|
|
67
|
+
: {};
|
|
68
|
+
let changed = false;
|
|
69
|
+
for (const [event, entries] of Object.entries(fragment.hooks)) {
|
|
70
|
+
if (hasTeerasHook(existingHooks[event]))
|
|
71
|
+
continue;
|
|
72
|
+
const current = Array.isArray(existingHooks[event]) ? existingHooks[event] : [];
|
|
73
|
+
existingHooks[event] = [...current, ...entries];
|
|
74
|
+
changed = true;
|
|
75
|
+
}
|
|
76
|
+
if (!changed)
|
|
77
|
+
return { agent, detected, path, action: "already-installed" };
|
|
78
|
+
const next = { ...settings, ...(agent === "codex" ? { description: fragment.description } : {}), hooks: existingHooks };
|
|
79
|
+
writeFileSync(path, `${JSON.stringify(next, null, 2)}\n`);
|
|
80
|
+
return { agent, detected, path, action: "installed" };
|
|
81
|
+
}
|
|
82
|
+
export function installClaudeHooks(home = homedir(), command) {
|
|
83
|
+
const root = agentRoot(home, "CLAUDE_CONFIG_DIR", join(home, ".claude"));
|
|
84
|
+
return mergeHooks(join(root, "settings.json"), claudeHookSettings(command), "claude-code", isDirectory(root));
|
|
85
|
+
}
|
|
86
|
+
export function installCodexHooks(home = homedir(), command) {
|
|
87
|
+
const root = agentRoot(home, "CODEX_HOME", join(home, ".codex"));
|
|
88
|
+
return mergeHooks(join(root, "hooks.json"), codexHookSettings(command), "codex", isDirectory(root));
|
|
89
|
+
}
|
|
90
|
+
export function installHooks(home = homedir(), commands = {}) {
|
|
91
|
+
return [installClaudeHooks(home, commands.claude), installCodexHooks(home, commands.codex)];
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* `trs off`'s uninstall. Removes only entries whose command is ours (either
|
|
95
|
+
* form `isTeerasHookCommand` recognizes), event by event, and never rewrites
|
|
96
|
+
* a settings file it cannot parse — the same refusal `mergeHooks` honors on
|
|
97
|
+
* install applies symmetrically here.
|
|
98
|
+
*/
|
|
99
|
+
function removeTeerasHooks(path, agent) {
|
|
100
|
+
const read = readSettings(path);
|
|
101
|
+
if (read.kind === "absent")
|
|
102
|
+
return { agent, path, action: "not-installed" };
|
|
103
|
+
if (read.kind === "unreadable")
|
|
104
|
+
return { agent, path, action: "unreadable" };
|
|
105
|
+
const settings = read.value;
|
|
106
|
+
const hooks = settings.hooks;
|
|
107
|
+
if (typeof hooks !== "object" || hooks === null || Array.isArray(hooks)) {
|
|
108
|
+
return { agent, path, action: "not-installed" };
|
|
109
|
+
}
|
|
110
|
+
let changed = false;
|
|
111
|
+
const nextHooks = {};
|
|
112
|
+
for (const [event, entries] of Object.entries(hooks)) {
|
|
113
|
+
if (!Array.isArray(entries)) {
|
|
114
|
+
nextHooks[event] = entries;
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
const kept = entries.filter((entry) => {
|
|
118
|
+
const entryHooks = entry?.hooks;
|
|
119
|
+
const isOurs = Array.isArray(entryHooks) && entryHooks.some((hook) => isTeerasHookCommand(hook?.command));
|
|
120
|
+
if (isOurs)
|
|
121
|
+
changed = true;
|
|
122
|
+
return !isOurs;
|
|
123
|
+
});
|
|
124
|
+
if (kept.length > 0)
|
|
125
|
+
nextHooks[event] = kept;
|
|
126
|
+
}
|
|
127
|
+
if (!changed)
|
|
128
|
+
return { agent, path, action: "not-installed" };
|
|
129
|
+
// Removal has to be able to reach zero. A file left holding `"hooks": {}`
|
|
130
|
+
// and our own `"description": "Teeras…"` still announces Teeras to anyone
|
|
131
|
+
// who opens it, right after `trs off` said everything was removed — so the
|
|
132
|
+
// empty husk goes too, and a file that was only ever ours is deleted rather
|
|
133
|
+
// than rewritten as `{}`. Anything the user put there is untouched: the
|
|
134
|
+
// description is dropped only when it is verbatim the one we write.
|
|
135
|
+
const { hooks: _removed, ...rest } = settings;
|
|
136
|
+
const next = Object.keys(nextHooks).length > 0 ? { ...rest, hooks: nextHooks } : rest;
|
|
137
|
+
if (Object.keys(nextHooks).length === 0 && next.description === CODEX_HOOKS_DESCRIPTION) {
|
|
138
|
+
delete next.description;
|
|
139
|
+
}
|
|
140
|
+
if (Object.keys(next).length === 0) {
|
|
141
|
+
rmSync(path, { force: true });
|
|
142
|
+
return { agent, path, action: "removed" };
|
|
143
|
+
}
|
|
144
|
+
writeFileSync(path, `${JSON.stringify(next, null, 2)}\n`);
|
|
145
|
+
return { agent, path, action: "removed" };
|
|
146
|
+
}
|
|
147
|
+
export function uninstallHooks(home = homedir()) {
|
|
148
|
+
const claudeRoot = agentRoot(home, "CLAUDE_CONFIG_DIR", join(home, ".claude"));
|
|
149
|
+
const codexRoot = agentRoot(home, "CODEX_HOME", join(home, ".codex"));
|
|
150
|
+
return [
|
|
151
|
+
removeTeerasHooks(join(claudeRoot, "settings.json"), "claude-code"),
|
|
152
|
+
removeTeerasHooks(join(codexRoot, "hooks.json"), "codex"),
|
|
153
|
+
];
|
|
154
|
+
}
|
|
155
|
+
//# sourceMappingURL=install.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"install.js","sourceRoot":"","sources":["../src/install.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAC1E,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,EAAE,uBAAuB,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAgB5F,2EAA2E;AAC3E,SAAS,SAAS,CAAC,IAAY,EAAE,GAAuC,EAAE,QAAgB;IACxF,MAAM,UAAU,GAAG,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,IAAI,EAAE,CAAC;IAC5C,OAAO,IAAI,KAAK,OAAO,EAAE,IAAI,UAAU,IAAI,UAAU,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,QAAQ,CAAC;AAC5F,CAAC;AAED;;;;;GAKG;AACH,SAAS,YAAY,CAAC,IAAY;IAChC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;IACjD,IAAI,GAAW,CAAC;IAChB,IAAI,CAAC;QACH,GAAG,GAAG,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IACnC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,IAAI,EAAE,YAAY,EAAE,CAAC;IAChC,CAAC;IACD,IAAI,MAAe,CAAC;IACpB,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC3B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,IAAI,EAAE,YAAY,EAAE,CAAC;IAChC,CAAC;IACD,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;QAC5E,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAc,EAAE,CAAC;IACnD,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,YAAY,EAAE,CAAC;AAChC,CAAC;AAED;;;;GAIG;AACH,SAAS,mBAAmB,CAAC,OAAgB;IAC3C,IAAI,OAAO,OAAO,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IAC9C,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;IACjD,OAAO,UAAU,CAAC,QAAQ,CAAC,UAAU,CAAC,IAAI,UAAU,CAAC,QAAQ,CAAC,+BAA+B,CAAC,CAAC;AACjG,CAAC;AAED,SAAS,aAAa,CAAC,OAAgB;IACrC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC;QAAE,OAAO,KAAK,CAAC;IAC1C,OAAO,OAAO,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE;QAC5B,MAAM,KAAK,GAAI,KAAqB,EAAE,KAAK,CAAC;QAC5C,OAAO,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,mBAAmB,CAAE,IAAa,EAAE,OAAO,CAAC,CAAC,CAAC;IACpG,CAAC,CAAC,CAAC;AACL,CAAC;AAED,SAAS,UAAU,CAAC,IAAY,EAAE,QAAc,EAAE,KAAiC,EAAE,QAAiB;IACpG,IAAI,CAAC,QAAQ;QAAE,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC;IACnE,MAAM,IAAI,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC;IAChC,IAAI,IAAI,CAAC,IAAI,KAAK,YAAY;QAAE,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE,YAAY,EAAE,CAAC;IACvF,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;IAC1D,MAAM,aAAa,GAAG,OAAO,QAAQ,CAAC,KAAK,KAAK,QAAQ,IAAI,QAAQ,CAAC,KAAK,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC;QACnH,CAAC,CAAE,QAAQ,CAAC,KAAc;QAC1B,CAAC,CAAC,EAAE,CAAC;IACP,IAAI,OAAO,GAAG,KAAK,CAAC;IACpB,KAAK,MAAM,CAAC,KAAK,EAAE,OAAO,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,KAAa,CAAC,EAAE,CAAC;QACtE,IAAI,aAAa,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;YAAE,SAAS;QAClD,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAE,aAAa,CAAC,KAAK,CAAe,CAAC,CAAC,CAAC,EAAE,CAAC;QAC/F,aAAa,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,OAAO,EAAE,GAAI,OAAqB,CAAC,CAAC;QAC/D,OAAO,GAAG,IAAI,CAAC;IACjB,CAAC;IACD,IAAI,CAAC,OAAO;QAAE,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE,mBAAmB,EAAE,CAAC;IAC5E,MAAM,IAAI,GAAG,EAAE,GAAG,QAAQ,EAAE,GAAG,CAAC,KAAK,KAAK,OAAO,CAAC,CAAC,CAAC,EAAE,WAAW,EAAG,QAAiB,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,aAAa,EAAE,CAAC;IAClI,aAAa,CAAC,IAAI,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC;IAC1D,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC;AACxD,CAAC;AAYD,MAAM,UAAU,kBAAkB,CAAC,IAAI,GAAG,OAAO,EAAE,EAAE,OAAgB;IACnE,MAAM,IAAI,GAAG,SAAS,CAAC,IAAI,EAAE,mBAAmB,EAAE,IAAI,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC,CAAC;IACzE,OAAO,UAAU,CAAC,IAAI,CAAC,IAAI,EAAE,eAAe,CAAC,EAAE,kBAAkB,CAAC,OAAO,CAAC,EAAE,aAAa,EAAE,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC;AAChH,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,IAAI,GAAG,OAAO,EAAE,EAAE,OAAgB;IAClE,MAAM,IAAI,GAAG,SAAS,CAAC,IAAI,EAAE,YAAY,EAAE,IAAI,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC;IACjE,OAAO,UAAU,CAAC,IAAI,CAAC,IAAI,EAAE,YAAY,CAAC,EAAE,iBAAiB,CAAC,OAAO,CAAC,EAAE,OAAO,EAAE,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC;AACtG,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,IAAI,GAAG,OAAO,EAAE,EAAE,WAAyB,EAAE;IACxE,OAAO,CAAC,kBAAkB,CAAC,IAAI,EAAE,QAAQ,CAAC,MAAM,CAAC,EAAE,iBAAiB,CAAC,IAAI,EAAE,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC;AAC9F,CAAC;AAQD;;;;;GAKG;AACH,SAAS,iBAAiB,CAAC,IAAY,EAAE,KAAiC;IACxE,MAAM,IAAI,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC;IAChC,IAAI,IAAI,CAAC,IAAI,KAAK,QAAQ;QAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,eAAe,EAAE,CAAC;IAC5E,IAAI,IAAI,CAAC,IAAI,KAAK,YAAY;QAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,YAAY,EAAE,CAAC;IAC7E,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC;IAC5B,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC;IAC7B,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACxE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,eAAe,EAAE,CAAC;IAClD,CAAC;IACD,IAAI,OAAO,GAAG,KAAK,CAAC;IACpB,MAAM,SAAS,GAAS,EAAE,CAAC;IAC3B,KAAK,MAAM,CAAC,KAAK,EAAE,OAAO,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAa,CAAC,EAAE,CAAC;QAC7D,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;YAC5B,SAAS,CAAC,KAAK,CAAC,GAAG,OAAO,CAAC;YAC3B,SAAS;QACX,CAAC;QACD,MAAM,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE;YACpC,MAAM,UAAU,GAAI,KAAqB,EAAE,KAAK,CAAC;YACjD,MAAM,MAAM,GAAG,KAAK,CAAC,OAAO,CAAC,UAAU,CAAC,IAAI,UAAU,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,mBAAmB,CAAE,IAAa,EAAE,OAAO,CAAC,CAAC,CAAC;YACpH,IAAI,MAAM;gBAAE,OAAO,GAAG,IAAI,CAAC;YAC3B,OAAO,CAAC,MAAM,CAAC;QACjB,CAAC,CAAC,CAAC;QACH,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC;YAAE,SAAS,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC;IAC/C,CAAC;IACD,IAAI,CAAC,OAAO;QAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,eAAe,EAAE,CAAC;IAE9D,0EAA0E;IAC1E,0EAA0E;IAC1E,2EAA2E;IAC3E,4EAA4E;IAC5E,wEAAwE;IACxE,oEAAoE;IACpE,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,GAAG,IAAI,EAAE,GAAG,QAAQ,CAAC;IAC9C,MAAM,IAAI,GAAS,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,GAAG,IAAI,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;IAC5F,IAAI,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,MAAM,KAAK,CAAC,IAAI,IAAI,CAAC,WAAW,KAAK,uBAAuB,EAAE,CAAC;QACxF,OAAO,IAAI,CAAC,WAAW,CAAC;IAC1B,CAAC;IACD,IAAI,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACnC,MAAM,CAAC,IAAI,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QAC9B,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC;IAC5C,CAAC;IACD,aAAa,CAAC,IAAI,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC;IAC1D,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC;AAC5C,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,IAAI,GAAG,OAAO,EAAE;IAC7C,MAAM,UAAU,GAAG,SAAS,CAAC,IAAI,EAAE,mBAAmB,EAAE,IAAI,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC,CAAC;IAC/E,MAAM,SAAS,GAAG,SAAS,CAAC,IAAI,EAAE,YAAY,EAAE,IAAI,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC;IACtE,OAAO;QACL,iBAAiB,CAAC,IAAI,CAAC,UAAU,EAAE,eAAe,CAAC,EAAE,aAAa,CAAC;QACnE,iBAAiB,CAAC,IAAI,CAAC,SAAS,EAAE,YAAY,CAAC,EAAE,OAAO,CAAC;KAC1D,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { AiMoment, AiMomentArtifactKind, AiMomentVisibility } from "./types.js";
|
|
2
|
+
/** Hash bytes locally; the artifact itself is intentionally not part of a snapshot. */
|
|
3
|
+
export declare function artifactFingerprint(bytes: Uint8Array): string;
|
|
4
|
+
export declare function createAiMoment(input: {
|
|
5
|
+
capturedAt?: Date | string;
|
|
6
|
+
sourceId: string;
|
|
7
|
+
episodeId?: string | null;
|
|
8
|
+
intent: string;
|
|
9
|
+
artifactKind: AiMomentArtifactKind;
|
|
10
|
+
artifactHash: string;
|
|
11
|
+
artifactBytes?: number | null;
|
|
12
|
+
artifactLabel?: string | null;
|
|
13
|
+
satisfaction?: number | null;
|
|
14
|
+
reflection?: string | null;
|
|
15
|
+
outcome?: AiMoment["outcome"];
|
|
16
|
+
visibility?: AiMomentVisibility;
|
|
17
|
+
}): AiMoment;
|
|
18
|
+
export declare function momentsPath(home?: string): string;
|
|
19
|
+
export declare function readAiMoments(home?: string): AiMoment[];
|
|
20
|
+
export declare function appendAiMoment(moment: AiMoment, home?: string): void;
|
|
21
|
+
//# sourceMappingURL=moments.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"moments.d.ts","sourceRoot":"","sources":["../src/moments.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,QAAQ,EAAE,oBAAoB,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAErF,uFAAuF;AACvF,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,CAE7D;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE;IACpC,UAAU,CAAC,EAAE,IAAI,GAAG,MAAM,CAAC;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,oBAAoB,CAAC;IACnC,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,OAAO,CAAC,EAAE,QAAQ,CAAC,SAAS,CAAC,CAAC;IAC9B,UAAU,CAAC,EAAE,kBAAkB,CAAC;CACjC,GAAG,QAAQ,CAqBX;AAED,wBAAgB,WAAW,CAAC,IAAI,SAAY,GAAG,MAAM,CAAqD;AAE1G,wBAAgB,aAAa,CAAC,IAAI,SAAY,GAAG,QAAQ,EAAE,CAK1D;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,QAAQ,EAAE,IAAI,SAAY,GAAG,IAAI,CAOvE"}
|
package/dist/moments.js
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
2
|
+
import { chmodSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { homedir } from "node:os";
|
|
5
|
+
/** Hash bytes locally; the artifact itself is intentionally not part of a snapshot. */
|
|
6
|
+
export function artifactFingerprint(bytes) {
|
|
7
|
+
return createHash("sha256").update(bytes).digest("hex");
|
|
8
|
+
}
|
|
9
|
+
export function createAiMoment(input) {
|
|
10
|
+
if (!/^[a-f0-9]{64}$/i.test(input.artifactHash))
|
|
11
|
+
throw new Error("artifactHash must be a SHA-256 hex digest");
|
|
12
|
+
const intent = input.intent.trim();
|
|
13
|
+
if (!intent || intent.length > 500)
|
|
14
|
+
throw new Error("intent must be 1–500 characters");
|
|
15
|
+
const capturedAt = new Date(input.capturedAt ?? Date.now());
|
|
16
|
+
if (!Number.isFinite(capturedAt.getTime()))
|
|
17
|
+
throw new Error("capturedAt must be a valid date");
|
|
18
|
+
return {
|
|
19
|
+
id: randomUUID(),
|
|
20
|
+
capturedAt: capturedAt.toISOString(),
|
|
21
|
+
sourceId: input.sourceId,
|
|
22
|
+
episodeId: input.episodeId ?? null,
|
|
23
|
+
intent,
|
|
24
|
+
artifactKind: input.artifactKind,
|
|
25
|
+
artifactHash: input.artifactHash.toLowerCase(),
|
|
26
|
+
artifactBytes: input.artifactBytes ?? null,
|
|
27
|
+
artifactLabel: input.artifactLabel?.trim() || null,
|
|
28
|
+
satisfaction: input.satisfaction ?? null,
|
|
29
|
+
reflection: input.reflection?.trim() || null,
|
|
30
|
+
outcome: input.outcome ?? "kept",
|
|
31
|
+
visibility: input.visibility ?? "private",
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
export function momentsPath(home = homedir()) { return join(home, ".teeras", "ai-moments.json"); }
|
|
35
|
+
export function readAiMoments(home = homedir()) {
|
|
36
|
+
try {
|
|
37
|
+
const parsed = JSON.parse(readFileSync(momentsPath(home), "utf8"));
|
|
38
|
+
return Array.isArray(parsed) ? parsed.filter((item) => !!item && typeof item === "object") : [];
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
return [];
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
export function appendAiMoment(moment, home = homedir()) {
|
|
45
|
+
const path = momentsPath(home);
|
|
46
|
+
const existing = readAiMoments(home).filter((item) => item.id !== moment.id);
|
|
47
|
+
const directory = path.slice(0, path.lastIndexOf("/"));
|
|
48
|
+
mkdirSync(directory, { recursive: true, mode: 0o700 });
|
|
49
|
+
writeFileSync(path, JSON.stringify([...existing, moment], null, 2), { mode: 0o600 });
|
|
50
|
+
chmodSync(path, 0o600);
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=moments.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"moments.js","sourceRoot":"","sources":["../src/moments.ts"],"names":[],"mappings":"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"}
|
package/dist/off.d.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { type HookRemoveResult } from "./install.js";
|
|
2
|
+
/**
|
|
3
|
+
* What happened to the durable runtime copy in `~/.teeras/afterhours/bin`.
|
|
4
|
+
*
|
|
5
|
+
* `kept` is the safety case, and the reason this lives in its own module
|
|
6
|
+
* instead of inline in the CLI. A settings file we could not read still
|
|
7
|
+
* contains its Teeras hook entry — that entry names the runtime by absolute
|
|
8
|
+
* path. Deleting the runtime anyway would leave every later agent lifecycle
|
|
9
|
+
* event invoking a file that is gone, which is exactly the "a hook must never
|
|
10
|
+
* interrupt the agent" rule broken by the off switch itself. So an unreadable
|
|
11
|
+
* settings file means the runtime stays, and the person is told why.
|
|
12
|
+
*/
|
|
13
|
+
export type RuntimeOutcome = "removed" | "nothing-to-remove" | "kept";
|
|
14
|
+
export interface OffResult {
|
|
15
|
+
hooks: HookRemoveResult[];
|
|
16
|
+
runtime: RuntimeOutcome;
|
|
17
|
+
lines: string[];
|
|
18
|
+
}
|
|
19
|
+
export interface OffDeps {
|
|
20
|
+
uninstall?: (home: string) => HookRemoveResult[];
|
|
21
|
+
removeRuntime?: (home: string) => boolean;
|
|
22
|
+
}
|
|
23
|
+
export declare function runOff(home?: string, deps?: OffDeps): OffResult;
|
|
24
|
+
//# sourceMappingURL=off.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"off.d.ts","sourceRoot":"","sources":["../src/off.ts"],"names":[],"mappings":"AACA,OAAO,EAAkB,KAAK,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAGrE;;;;;;;;;;GAUG;AACH,MAAM,MAAM,cAAc,GAAG,SAAS,GAAG,mBAAmB,GAAG,MAAM,CAAC;AAEtE,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,gBAAgB,EAAE,CAAC;IAC1B,OAAO,EAAE,cAAc,CAAC;IACxB,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB;AAED,MAAM,WAAW,OAAO;IACtB,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,gBAAgB,EAAE,CAAC;IACjD,aAAa,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;CAC3C;AAMD,wBAAgB,MAAM,CAAC,IAAI,GAAE,MAAkB,EAAE,IAAI,GAAE,OAAY,GAAG,SAAS,CAyC9E"}
|
package/dist/off.js
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { homedir } from "node:os";
|
|
2
|
+
import { uninstallHooks } from "./install.js";
|
|
3
|
+
import { removeRuntime } from "./runtime.js";
|
|
4
|
+
function label(agent) {
|
|
5
|
+
return agent === "claude-code" ? "Claude Code" : "Codex";
|
|
6
|
+
}
|
|
7
|
+
export function runOff(home = homedir(), deps = {}) {
|
|
8
|
+
const uninstall = deps.uninstall ?? uninstallHooks;
|
|
9
|
+
const remove = deps.removeRuntime ?? removeRuntime;
|
|
10
|
+
const hooks = uninstall(home);
|
|
11
|
+
const removedAgents = hooks.filter((result) => result.action === "removed").map((result) => label(result.agent));
|
|
12
|
+
const unreadableAgents = hooks.filter((result) => result.action === "unreadable").map((result) => label(result.agent));
|
|
13
|
+
const runtime = unreadableAgents.length > 0
|
|
14
|
+
? "kept"
|
|
15
|
+
: remove(home)
|
|
16
|
+
? "removed"
|
|
17
|
+
: "nothing-to-remove";
|
|
18
|
+
const runtimeLine = runtime === "removed"
|
|
19
|
+
? "removed · ~/.teeras/afterhours/bin"
|
|
20
|
+
: runtime === "kept"
|
|
21
|
+
? "kept · a hook still points at it"
|
|
22
|
+
: "nothing to remove";
|
|
23
|
+
const lines = [
|
|
24
|
+
"",
|
|
25
|
+
" teeras · off",
|
|
26
|
+
"",
|
|
27
|
+
` hooks ${removedAgents.length > 0 ? `removed · ${removedAgents.join(", ")}` : "nothing to remove"}`,
|
|
28
|
+
` runtime ${runtimeLine}`,
|
|
29
|
+
];
|
|
30
|
+
if (unreadableAgents.length > 0) {
|
|
31
|
+
lines.push(` left untouched ${unreadableAgents.join(", ")} settings file could not be read`, " that file still calls the Teeras hook, so the runtime it calls was left in place.", " repair the settings file (or delete its Teeras hook entry by hand), then run `trs off` again.");
|
|
32
|
+
}
|
|
33
|
+
lines.push("", " still on this computer: your local journal and account credentials.", " run `trs logout` too if you want the account disconnected as well.", "");
|
|
34
|
+
return { hooks, runtime, lines };
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=off.js.map
|
package/dist/off.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"off.js","sourceRoot":"","sources":["../src/off.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,cAAc,EAAyB,MAAM,cAAc,CAAC;AACrE,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AA0B7C,SAAS,KAAK,CAAC,KAAgC;IAC7C,OAAO,KAAK,KAAK,aAAa,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,OAAO,CAAC;AAC3D,CAAC;AAED,MAAM,UAAU,MAAM,CAAC,OAAe,OAAO,EAAE,EAAE,OAAgB,EAAE;IACjE,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,IAAI,cAAc,CAAC;IACnD,MAAM,MAAM,GAAG,IAAI,CAAC,aAAa,IAAI,aAAa,CAAC;IAEnD,MAAM,KAAK,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC;IAC9B,MAAM,aAAa,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;IACjH,MAAM,gBAAgB,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,MAAM,KAAK,YAAY,CAAC,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;IAEvH,MAAM,OAAO,GAAmB,gBAAgB,CAAC,MAAM,GAAG,CAAC;QACzD,CAAC,CAAC,MAAM;QACR,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC;YACZ,CAAC,CAAC,SAAS;YACX,CAAC,CAAC,mBAAmB,CAAC;IAE1B,MAAM,WAAW,GAAG,OAAO,KAAK,SAAS;QACvC,CAAC,CAAC,oCAAoC;QACtC,CAAC,CAAC,OAAO,KAAK,MAAM;YAClB,CAAC,CAAC,kCAAkC;YACpC,CAAC,CAAC,mBAAmB,CAAC;IAE1B,MAAM,KAAK,GAAG;QACZ,EAAE;QACF,gBAAgB;QAChB,EAAE;QACF,cAAc,aAAa,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,aAAa,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,mBAAmB,EAAE;QACxG,cAAc,WAAW,EAAE;KAC5B,CAAC;IACF,IAAI,gBAAgB,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAChC,KAAK,CAAC,IAAI,CACR,qBAAqB,gBAAgB,CAAC,IAAI,CAAC,IAAI,CAAC,kCAAkC,EAClF,qFAAqF,EACrF,iGAAiG,CAClG,CAAC;IACJ,CAAC;IACD,KAAK,CAAC,IAAI,CACR,EAAE,EACF,uEAAuE,EACvE,sEAAsE,EACtE,EAAE,CACH,CAAC;IACF,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;AACnC,CAAC"}
|
package/dist/privacy.d.ts
CHANGED
|
@@ -1,3 +1,11 @@
|
|
|
1
1
|
import type { DataBoundary } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Replace a leading home-directory prefix with `~`. Setup's `--json` output
|
|
4
|
+
* and error messages are read by a coding agent and often relayed verbatim
|
|
5
|
+
* into an LLM transcript sent to a third party — absolute paths there leak
|
|
6
|
+
* the operator's real username. Returns the path unchanged if it is not
|
|
7
|
+
* under `home` (no partial-segment matches, e.g. `/Users/alice2`).
|
|
8
|
+
*/
|
|
9
|
+
export declare function tildePath(path: string, home?: string): string;
|
|
2
10
|
export declare const DATA_BOUNDARY: DataBoundary;
|
|
3
11
|
//# sourceMappingURL=privacy.d.ts.map
|
package/dist/privacy.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"privacy.d.ts","sourceRoot":"","sources":["../src/privacy.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"privacy.d.ts","sourceRoot":"","sources":["../src/privacy.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE/C;;;;;;GAMG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,GAAE,MAAkB,GAAG,MAAM,CAIxE;AAED,eAAO,MAAM,aAAa,EAAE,YAuB3B,CAAC"}
|