@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.
- package/README.md +101 -15
- package/dist/activity.d.ts +15 -1
- package/dist/activity.d.ts.map +1 -1
- package/dist/activity.js +76 -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 +8 -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 +26 -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 +38 -1
- package/dist/render.js.map +1 -1
- package/dist/report.d.ts.map +1 -1
- package/dist/report.js +82 -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 +141 -18
- package/dist/sources/claude.js.map +1 -1
- package/dist/sources/codex.d.ts.map +1 -1
- package/dist/sources/codex.js +209 -18
- 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 +73 -8
- package/dist/sources/gemini.js.map +1 -1
- package/dist/types.d.ts +61 -6
- package/dist/types.d.ts.map +1 -1
- package/llms-install.md +29 -0
- package/package.json +3 -2
package/dist/report.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { localDateOf } from "./activity.js";
|
|
1
2
|
function localNight(bucket) {
|
|
2
3
|
if (bucket.localHour >= 5)
|
|
3
4
|
return bucket.localDate;
|
|
@@ -5,6 +6,80 @@ function localNight(bucket) {
|
|
|
5
6
|
previous.setUTCDate(previous.getUTCDate() - 1);
|
|
6
7
|
return previous.toISOString().slice(0, 10);
|
|
7
8
|
}
|
|
9
|
+
/**
|
|
10
|
+
* Minutes of the clock covered, not minutes worked. Agents run in parallel —
|
|
11
|
+
* three sessions busy through the same hour are three hours of work but only
|
|
12
|
+
* one hour of the person's life, so each local hour contributes at most 60.
|
|
13
|
+
* Summing instead reported more time returned than the day contained.
|
|
14
|
+
*/
|
|
15
|
+
function wallClockMinutes(buckets, pick) {
|
|
16
|
+
const perHour = new Map();
|
|
17
|
+
for (const bucket of buckets) {
|
|
18
|
+
const cell = `${bucket.localDate}T${bucket.localHour}`;
|
|
19
|
+
perHour.set(cell, (perHour.get(cell) ?? 0) + pick(bucket));
|
|
20
|
+
}
|
|
21
|
+
let total = 0;
|
|
22
|
+
for (const minutes of perHour.values())
|
|
23
|
+
total += Math.min(60, minutes);
|
|
24
|
+
return Math.round(total);
|
|
25
|
+
}
|
|
26
|
+
const agentMinutes = (bucket) => bucket.agentActiveMinutes;
|
|
27
|
+
const attentionMinutes = (bucket) => bucket.humanAttentionMinutes;
|
|
28
|
+
function lifeReturned(buckets) {
|
|
29
|
+
const agent = wallClockMinutes(buckets, agentMinutes);
|
|
30
|
+
const attention = wallClockMinutes(buckets, attentionMinutes);
|
|
31
|
+
return Math.max(0, agent - attention);
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* How many agents were working at once, on average, across the hours that had
|
|
35
|
+
* any agent activity. Reported beside the returned time because it is the
|
|
36
|
+
* other half of the same fact: the work compressed, the day did not stretch.
|
|
37
|
+
*/
|
|
38
|
+
function parallelRatio(worked, wallClock) {
|
|
39
|
+
return wallClock > 0 ? worked / wallClock : null;
|
|
40
|
+
}
|
|
41
|
+
function median(values) {
|
|
42
|
+
if (values.length === 0)
|
|
43
|
+
return null;
|
|
44
|
+
const sorted = [...values].sort((a, b) => a - b);
|
|
45
|
+
const middle = Math.floor(sorted.length / 2);
|
|
46
|
+
return sorted.length % 2 === 1 ? sorted[middle] : Math.round((sorted[middle - 1] + sorted[middle]) / 2);
|
|
47
|
+
}
|
|
48
|
+
function todayFrom(snapshot) {
|
|
49
|
+
const localDate = localDateOf(Date.parse(snapshot.until));
|
|
50
|
+
const byDate = new Map();
|
|
51
|
+
for (const bucket of snapshot.sources.flatMap((source) => source.buckets)) {
|
|
52
|
+
const day = byDate.get(bucket.localDate) ?? [];
|
|
53
|
+
day.push(bucket);
|
|
54
|
+
byDate.set(bucket.localDate, day);
|
|
55
|
+
}
|
|
56
|
+
const todayBuckets = byDate.get(localDate) ?? [];
|
|
57
|
+
let longestRunMinutes = 0;
|
|
58
|
+
let longestRunSource = null;
|
|
59
|
+
for (const source of snapshot.sources) {
|
|
60
|
+
for (const episode of source.episodes) {
|
|
61
|
+
if (episode.localDate !== localDate)
|
|
62
|
+
continue;
|
|
63
|
+
if (episode.longestAutonomousRunMinutes > longestRunMinutes) {
|
|
64
|
+
longestRunMinutes = episode.longestAutonomousRunMinutes;
|
|
65
|
+
longestRunSource = source.name;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return {
|
|
70
|
+
today: {
|
|
71
|
+
localDate,
|
|
72
|
+
lifeReturnedMinutes: lifeReturned(todayBuckets),
|
|
73
|
+
agentActiveMinutes: Math.round(todayBuckets.reduce((sum, bucket) => sum + bucket.agentActiveMinutes, 0)),
|
|
74
|
+
humanAttentionMinutes: Math.round(todayBuckets.reduce((sum, bucket) => sum + bucket.humanAttentionMinutes, 0)),
|
|
75
|
+
longestRunMinutes: Math.round(longestRunMinutes),
|
|
76
|
+
longestRunSource,
|
|
77
|
+
},
|
|
78
|
+
baseline: median([...byDate.entries()]
|
|
79
|
+
.filter(([date]) => date !== localDate)
|
|
80
|
+
.map(([, buckets]) => lifeReturned(buckets))),
|
|
81
|
+
};
|
|
82
|
+
}
|
|
8
83
|
export function reportFrom(snapshot, stopHour = 22) {
|
|
9
84
|
const buckets = snapshot.sources.flatMap((source) => source.buckets);
|
|
10
85
|
const agentActiveMinutes = Math.round(buckets.reduce((sum, bucket) => sum + bucket.agentActiveMinutes, 0));
|
|
@@ -21,15 +96,21 @@ export function reportFrom(snapshot, stopHour = 22) {
|
|
|
21
96
|
const nextMove = afterHoursCheckIns > 0
|
|
22
97
|
? `You checked in ${afterHoursCheckIns} time${afterHoursCheckIns === 1 ? "" : "s"} after ${stopHour}:00. Let the agents finish without you tonight.`
|
|
23
98
|
: "Your stop time held. Keep the boundary and let the agents carry the night shift.";
|
|
99
|
+
const { today, baseline } = todayFrom(snapshot);
|
|
100
|
+
const agentWallClockMinutes = wallClockMinutes(buckets, agentMinutes);
|
|
24
101
|
return {
|
|
25
|
-
lifeReturnedMinutes:
|
|
102
|
+
lifeReturnedMinutes: lifeReturned(buckets),
|
|
26
103
|
agentActiveMinutes,
|
|
104
|
+
agentWallClockMinutes,
|
|
105
|
+
parallelRatio: parallelRatio(agentActiveMinutes, agentWallClockMinutes),
|
|
27
106
|
humanAttentionMinutes,
|
|
28
107
|
afterHoursCheckIns,
|
|
29
108
|
protectedNights,
|
|
30
109
|
observedNights,
|
|
31
110
|
agentTurns,
|
|
32
111
|
humanCheckIns,
|
|
112
|
+
today,
|
|
113
|
+
baselineLifeReturnedMinutes: baseline,
|
|
33
114
|
nextMove,
|
|
34
115
|
};
|
|
35
116
|
}
|
package/dist/report.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"report.js","sourceRoot":"","sources":["../src/report.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"report.js","sourceRoot":"","sources":["../src/report.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hooks fire long after the terminal that ran `npx @teeras/afterhours setup`
|
|
3
|
+
* is gone, and an npx-only user never gets a `trs` on PATH. So setup keeps its
|
|
4
|
+
* own copy of the runtime here and points the hooks at an absolute path.
|
|
5
|
+
*/
|
|
6
|
+
export declare function runtimeBinDir(home?: string): string;
|
|
7
|
+
/**
|
|
8
|
+
* Copy the running runtime into `<home>/.teeras/afterhours/bin` and return the
|
|
9
|
+
* absolute path of its `cli.js`. Re-running refreshes the copy in place, so an
|
|
10
|
+
* upgrade is a plain `npx @teeras/afterhours setup` away. Throws instead of
|
|
11
|
+
* leaving hooks pointed at a command that cannot run.
|
|
12
|
+
*/
|
|
13
|
+
export declare function installRuntime(home?: string, sourceDir?: string): string;
|
|
14
|
+
/** `trs off`'s counterpart to `installRuntime`: deletes the durable copy. */
|
|
15
|
+
export declare function removeRuntime(home?: string): boolean;
|
|
16
|
+
/** Absolute, quoted, and PATH-free: the hook keeps working after npx exits. */
|
|
17
|
+
export declare function hookCommand(cliPath: string, codex?: boolean): string;
|
|
18
|
+
//# sourceMappingURL=runtime.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"AAKA;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,IAAI,SAAY,GAAG,MAAM,CAEtD;AAqBD;;;;;GAKG;AACH,wBAAgB,cAAc,CAC5B,IAAI,SAAY,EAChB,SAAS,SAA0C,GAClD,MAAM,CAsBR;AAED,6EAA6E;AAC7E,wBAAgB,aAAa,CAAC,IAAI,SAAY,GAAG,OAAO,CAKvD;AAED,+EAA+E;AAC/E,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,UAAQ,GAAG,MAAM,CAElE"}
|
package/dist/runtime.js
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { chmodSync, copyFileSync, existsSync, mkdirSync, readdirSync, rmSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import { dirname, join } from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
/**
|
|
6
|
+
* Hooks fire long after the terminal that ran `npx @teeras/afterhours setup`
|
|
7
|
+
* is gone, and an npx-only user never gets a `trs` on PATH. So setup keeps its
|
|
8
|
+
* own copy of the runtime here and points the hooks at an absolute path.
|
|
9
|
+
*/
|
|
10
|
+
export function runtimeBinDir(home = homedir()) {
|
|
11
|
+
return join(home, ".teeras", "afterhours", "bin");
|
|
12
|
+
}
|
|
13
|
+
function copyJsTree(sourceDir, targetDir) {
|
|
14
|
+
let copied = 0;
|
|
15
|
+
for (const entry of readdirSync(sourceDir, { withFileTypes: true })) {
|
|
16
|
+
if (entry.isSymbolicLink())
|
|
17
|
+
continue;
|
|
18
|
+
const from = join(sourceDir, entry.name);
|
|
19
|
+
const to = join(targetDir, entry.name);
|
|
20
|
+
if (entry.isDirectory()) {
|
|
21
|
+
mkdirSync(to, { recursive: true });
|
|
22
|
+
copied += copyJsTree(from, to);
|
|
23
|
+
continue;
|
|
24
|
+
}
|
|
25
|
+
// Only executable modules travel: no maps, no declarations, no data.
|
|
26
|
+
if (!entry.isFile() || !entry.name.endsWith(".js"))
|
|
27
|
+
continue;
|
|
28
|
+
copyFileSync(from, to);
|
|
29
|
+
copied += 1;
|
|
30
|
+
}
|
|
31
|
+
return copied;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Copy the running runtime into `<home>/.teeras/afterhours/bin` and return the
|
|
35
|
+
* absolute path of its `cli.js`. Re-running refreshes the copy in place, so an
|
|
36
|
+
* upgrade is a plain `npx @teeras/afterhours setup` away. Throws instead of
|
|
37
|
+
* leaving hooks pointed at a command that cannot run.
|
|
38
|
+
*/
|
|
39
|
+
export function installRuntime(home = homedir(), sourceDir = dirname(fileURLToPath(import.meta.url))) {
|
|
40
|
+
const hasCli = readdirSync(sourceDir, { withFileTypes: true })
|
|
41
|
+
.some((entry) => entry.isFile() && entry.name === "cli.js");
|
|
42
|
+
if (!hasCli) {
|
|
43
|
+
throw new Error("no cli.js in the package being installed from — the runtime is incomplete. Reinstall with `npx @teeras/afterhours@latest setup`.");
|
|
44
|
+
}
|
|
45
|
+
const target = runtimeBinDir(home);
|
|
46
|
+
mkdirSync(target, { recursive: true, mode: 0o700 });
|
|
47
|
+
chmodSync(target, 0o700);
|
|
48
|
+
const copied = copyJsTree(sourceDir, target);
|
|
49
|
+
if (copied === 0) {
|
|
50
|
+
throw new Error("no runtime files in the package being installed from — nothing to install. Reinstall with `npx @teeras/afterhours@latest setup`.");
|
|
51
|
+
}
|
|
52
|
+
// Outside the npm package the `.js` files lose their module type. Node 20
|
|
53
|
+
// has no ESM auto-detection, so without this marker the hook would die with
|
|
54
|
+
// "Cannot use import statement outside a module" on supported versions.
|
|
55
|
+
writeFileSync(join(target, "package.json"), `${JSON.stringify({ type: "module", private: true }, null, 2)}\n`);
|
|
56
|
+
return join(target, "cli.js");
|
|
57
|
+
}
|
|
58
|
+
/** `trs off`'s counterpart to `installRuntime`: deletes the durable copy. */
|
|
59
|
+
export function removeRuntime(home = homedir()) {
|
|
60
|
+
const target = runtimeBinDir(home);
|
|
61
|
+
if (!existsSync(target))
|
|
62
|
+
return false;
|
|
63
|
+
rmSync(target, { recursive: true, force: true });
|
|
64
|
+
return true;
|
|
65
|
+
}
|
|
66
|
+
/** Absolute, quoted, and PATH-free: the hook keeps working after npx exits. */
|
|
67
|
+
export function hookCommand(cliPath, codex = false) {
|
|
68
|
+
return `"${process.execPath}" "${cliPath}" hook${codex ? " codex" : ""}`;
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=runtime.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime.js","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,UAAU,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAC7G,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC;;;;GAIG;AACH,MAAM,UAAU,aAAa,CAAC,IAAI,GAAG,OAAO,EAAE;IAC5C,OAAO,IAAI,CAAC,IAAI,EAAE,SAAS,EAAE,YAAY,EAAE,KAAK,CAAC,CAAC;AACpD,CAAC;AAED,SAAS,UAAU,CAAC,SAAiB,EAAE,SAAiB;IACtD,IAAI,MAAM,GAAG,CAAC,CAAC;IACf,KAAK,MAAM,KAAK,IAAI,WAAW,CAAC,SAAS,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;QACpE,IAAI,KAAK,CAAC,cAAc,EAAE;YAAE,SAAS;QACrC,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;QACzC,MAAM,EAAE,GAAG,IAAI,CAAC,SAAS,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;QACvC,IAAI,KAAK,CAAC,WAAW,EAAE,EAAE,CAAC;YACxB,SAAS,CAAC,EAAE,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;YACnC,MAAM,IAAI,UAAU,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;YAC/B,SAAS;QACX,CAAC;QACD,qEAAqE;QACrE,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC;YAAE,SAAS;QAC7D,YAAY,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QACvB,MAAM,IAAI,CAAC,CAAC;IACd,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,cAAc,CAC5B,IAAI,GAAG,OAAO,EAAE,EAChB,SAAS,GAAG,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAEnD,MAAM,MAAM,GAAG,WAAW,CAAC,SAAS,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC;SAC3D,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,EAAE,IAAI,KAAK,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC;IAC9D,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CACb,kIAAkI,CACnI,CAAC;IACJ,CAAC;IACD,MAAM,MAAM,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC;IACnC,SAAS,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IACpD,SAAS,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;IACzB,MAAM,MAAM,GAAG,UAAU,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;IAC7C,IAAI,MAAM,KAAK,CAAC,EAAE,CAAC;QACjB,MAAM,IAAI,KAAK,CACb,kIAAkI,CACnI,CAAC;IACJ,CAAC;IACD,0EAA0E;IAC1E,4EAA4E;IAC5E,wEAAwE;IACxE,aAAa,CAAC,IAAI,CAAC,MAAM,EAAE,cAAc,CAAC,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC;IAC/G,OAAO,IAAI,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;AAChC,CAAC;AAED,6EAA6E;AAC7E,MAAM,UAAU,aAAa,CAAC,IAAI,GAAG,OAAO,EAAE;IAC5C,MAAM,MAAM,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC;IACnC,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC;QAAE,OAAO,KAAK,CAAC;IACtC,MAAM,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACjD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,+EAA+E;AAC/E,MAAM,UAAU,WAAW,CAAC,OAAe,EAAE,KAAK,GAAG,KAAK;IACxD,OAAO,IAAI,OAAO,CAAC,QAAQ,MAAM,OAAO,SAAS,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;AAC3E,CAAC"}
|
package/dist/secret.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export declare function episodeSecretPath(home?: string): string;
|
|
2
|
+
/**
|
|
3
|
+
* Stable per-device secret for opaque episode ids. Device tokens rotate on
|
|
4
|
+
* expiry and re-login; keying the HMAC with a token would re-key every episode
|
|
5
|
+
* id, duplicating canonical rows server-side and orphaning hook-journal events.
|
|
6
|
+
* This secret never leaves the device and survives login cycles.
|
|
7
|
+
*/
|
|
8
|
+
export declare function ensureEpisodeSecret(home?: string): string;
|
|
9
|
+
//# sourceMappingURL=secret.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"secret.d.ts","sourceRoot":"","sources":["../src/secret.ts"],"names":[],"mappings":"AAOA,wBAAgB,iBAAiB,CAAC,IAAI,SAAY,GAAG,MAAM,CAE1D;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,SAAY,GAAG,MAAM,CAc5D"}
|
package/dist/secret.js
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { randomBytes } from "node:crypto";
|
|
2
|
+
import { chmodSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { homedir } from "node:os";
|
|
4
|
+
import { dirname, join } from "node:path";
|
|
5
|
+
const SECRET_PATTERN = /^[a-f0-9]{64}$/;
|
|
6
|
+
export function episodeSecretPath(home = homedir()) {
|
|
7
|
+
return join(home, ".teeras", "afterhours", "episode-secret");
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Stable per-device secret for opaque episode ids. Device tokens rotate on
|
|
11
|
+
* expiry and re-login; keying the HMAC with a token would re-key every episode
|
|
12
|
+
* id, duplicating canonical rows server-side and orphaning hook-journal events.
|
|
13
|
+
* This secret never leaves the device and survives login cycles.
|
|
14
|
+
*/
|
|
15
|
+
export function ensureEpisodeSecret(home = homedir()) {
|
|
16
|
+
const path = episodeSecretPath(home);
|
|
17
|
+
try {
|
|
18
|
+
const existing = readFileSync(path, "utf8").trim();
|
|
19
|
+
if (SECRET_PATTERN.test(existing))
|
|
20
|
+
return existing;
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
// Missing or unreadable: mint a fresh secret below.
|
|
24
|
+
}
|
|
25
|
+
const secret = randomBytes(32).toString("hex");
|
|
26
|
+
mkdirSync(dirname(path), { recursive: true, mode: 0o700 });
|
|
27
|
+
chmodSync(dirname(path), 0o700);
|
|
28
|
+
writeFileSync(path, `${secret}\n`, { mode: 0o600 });
|
|
29
|
+
chmodSync(path, 0o600);
|
|
30
|
+
return secret;
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=secret.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"secret.js","sourceRoot":"","sources":["../src/secret.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAC5E,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAE1C,MAAM,cAAc,GAAG,gBAAgB,CAAC;AAExC,MAAM,UAAU,iBAAiB,CAAC,IAAI,GAAG,OAAO,EAAE;IAChD,OAAO,IAAI,CAAC,IAAI,EAAE,SAAS,EAAE,YAAY,EAAE,gBAAgB,CAAC,CAAC;AAC/D,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,mBAAmB,CAAC,IAAI,GAAG,OAAO,EAAE;IAClD,MAAM,IAAI,GAAG,iBAAiB,CAAC,IAAI,CAAC,CAAC;IACrC,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC;QACnD,IAAI,cAAc,CAAC,IAAI,CAAC,QAAQ,CAAC;YAAE,OAAO,QAAQ,CAAC;IACrD,CAAC;IAAC,MAAM,CAAC;QACP,oDAAoD;IACtD,CAAC;IACD,MAAM,MAAM,GAAG,WAAW,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;IAC/C,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,aAAa,CAAC,IAAI,EAAE,GAAG,MAAM,IAAI,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IACpD,SAAS,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IACvB,OAAO,MAAM,CAAC;AAChB,CAAC"}
|
package/dist/setup.d.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export interface SetupOptions {
|
|
2
|
+
json?: boolean;
|
|
3
|
+
home?: string;
|
|
4
|
+
fetcher?: typeof fetch;
|
|
5
|
+
sleep?: (ms: number) => Promise<void>;
|
|
6
|
+
out?: (text: string) => void;
|
|
7
|
+
openUri?: (uri: string) => void;
|
|
8
|
+
/** Directory holding the runtime to copy. Defaults to the running dist. */
|
|
9
|
+
runtimeSourceDir?: string;
|
|
10
|
+
/** Whether the terminal can show QR art. Defaults to `process.stdout.isTTY`. */
|
|
11
|
+
isTty?: boolean;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* The text output is written to be quoted verbatim by a coding agent to its
|
|
15
|
+
* user. Keep every line meaningful when read on a phone, out of context.
|
|
16
|
+
*/
|
|
17
|
+
export declare function runSetup(options?: SetupOptions): Promise<void>;
|
|
18
|
+
//# sourceMappingURL=setup.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"setup.d.ts","sourceRoot":"","sources":["../src/setup.ts"],"names":[],"mappings":"AAsBA,MAAM,WAAW,YAAY;IAC3B,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,OAAO,KAAK,CAAC;IACvB,KAAK,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACtC,GAAG,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC7B,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAChC,2EAA2E;IAC3E,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,gFAAgF;IAChF,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAgCD;;;GAGG;AACH,wBAAsB,QAAQ,CAAC,OAAO,GAAE,YAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CAkHxE"}
|
package/dist/setup.js
ADDED
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import { homedir } from "node:os";
|
|
2
|
+
import { ConnectionExpiredError, openVerificationUri, pollDeviceLogin, readCredentials, removeCredentials, startDeviceLogin, syncSnapshot, teerasBaseUrl, writeCredentials, } from "./cloud.js";
|
|
3
|
+
import { collectAfterhours } from "./collect.js";
|
|
4
|
+
import { installHooks } from "./install.js";
|
|
5
|
+
import { tildePath } from "./privacy.js";
|
|
6
|
+
import { qrBlock } from "./qr.js";
|
|
7
|
+
import { renderReport } from "./render.js";
|
|
8
|
+
import { reportFrom } from "./report.js";
|
|
9
|
+
import { hookCommand, installRuntime } from "./runtime.js";
|
|
10
|
+
import { markSyncSuccess } from "./autosync.js";
|
|
11
|
+
/**
|
|
12
|
+
* "Connected" used to mean three different things at once here — account
|
|
13
|
+
* signed in, hooks installed, an agent detected — which made the screen
|
|
14
|
+
* unreadable at a glance. The account gets its own word ("signed in"); an
|
|
15
|
+
* agent is `measuring` once its hooks are in place, or `not found` if the
|
|
16
|
+
* agent itself never showed up. One agent per line: a separator between
|
|
17
|
+
* agents reads as another state when a state is two words.
|
|
18
|
+
*
|
|
19
|
+
* The `agents` label is printed once and the rest of the block indented under
|
|
20
|
+
* it, so the column reads like the single `hooks`/`sync` rows around it
|
|
21
|
+
* rather than like a repeated key.
|
|
22
|
+
*/
|
|
23
|
+
function hookLine(results) {
|
|
24
|
+
const label = (result) => result.agent === "claude-code" ? "Claude Code" : "Codex";
|
|
25
|
+
const state = (result) => {
|
|
26
|
+
switch (result.action) {
|
|
27
|
+
case "skipped":
|
|
28
|
+
return "not found";
|
|
29
|
+
case "installed":
|
|
30
|
+
case "already-installed":
|
|
31
|
+
return "measuring";
|
|
32
|
+
case "unreadable":
|
|
33
|
+
return "settings file unreadable, left untouched";
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
return results.map((result, index) => ` ${index === 0 ? "agents" : " "} ${label(result).padEnd(12)}${state(result)}`);
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* The text output is written to be quoted verbatim by a coding agent to its
|
|
40
|
+
* user. Keep every line meaningful when read on a phone, out of context.
|
|
41
|
+
*/
|
|
42
|
+
export async function runSetup(options = {}) {
|
|
43
|
+
const home = options.home ?? homedir();
|
|
44
|
+
const json = options.json === true;
|
|
45
|
+
const out = options.out ?? ((text) => process.stdout.write(text));
|
|
46
|
+
const openUri = options.openUri ?? openVerificationUri;
|
|
47
|
+
const isTty = options.isTty ?? process.stdout.isTTY === true;
|
|
48
|
+
// Hooks must survive the npx temp directory, so the runtime is copied into
|
|
49
|
+
// the home first and the fragments point at that absolute path. A hook we
|
|
50
|
+
// cannot run is silent, missing measurement — refuse rather than report it.
|
|
51
|
+
let cliPath;
|
|
52
|
+
try {
|
|
53
|
+
cliPath = installRuntime(home, options.runtimeSourceDir);
|
|
54
|
+
}
|
|
55
|
+
catch (error) {
|
|
56
|
+
// The reason has to survive — "check that your home directory is
|
|
57
|
+
// writable" is wrong guidance for a full disk, and a bare failure with no
|
|
58
|
+
// reason at all is unfixable. `error.code` (EACCES/ENOSPC/EROFS) is both
|
|
59
|
+
// the most diagnostic part and the part that carries no path; when there
|
|
60
|
+
// is no code, the message goes through `tildePath` so a home prefix
|
|
61
|
+
// cannot ride along into a transcript.
|
|
62
|
+
const code = error?.code;
|
|
63
|
+
const reason = typeof code === "string" && code.length > 0
|
|
64
|
+
? code
|
|
65
|
+
: tildePath(error instanceof Error ? error.message : String(error), home);
|
|
66
|
+
throw new Error(`could not install the local hook command (${reason}). Nothing was changed. EACCES or EROFS means ~/.teeras is not writable; ENOSPC means the disk is full. Clear that, then run \`npx @teeras/afterhours@latest setup\` again.`);
|
|
67
|
+
}
|
|
68
|
+
const hooks = installHooks(home, {
|
|
69
|
+
claude: hookCommand(cliPath),
|
|
70
|
+
codex: hookCommand(cliPath, true),
|
|
71
|
+
});
|
|
72
|
+
if (json) {
|
|
73
|
+
const redacted = hooks.map((result) => ({ ...result, path: tildePath(result.path, home) }));
|
|
74
|
+
out(`${JSON.stringify({ event: "hooks", results: redacted })}\n`);
|
|
75
|
+
}
|
|
76
|
+
// Collected once and reused for both the sync payload and the closing
|
|
77
|
+
// report — the same counts back the ask and the payoff in one breath.
|
|
78
|
+
const snapshot = collectAfterhours({ home });
|
|
79
|
+
let credentials = readCredentials(home);
|
|
80
|
+
let alreadyConnected = false;
|
|
81
|
+
if (credentials) {
|
|
82
|
+
try {
|
|
83
|
+
await syncSnapshot(snapshot, credentials, options.fetcher);
|
|
84
|
+
alreadyConnected = true;
|
|
85
|
+
}
|
|
86
|
+
catch (error) {
|
|
87
|
+
if (!(error instanceof ConnectionExpiredError))
|
|
88
|
+
throw error;
|
|
89
|
+
// The server rejected a token that still looks valid locally. Clear it
|
|
90
|
+
// and reconnect in this same run — setup must never dead-end on stale
|
|
91
|
+
// state or bounce the user to another command.
|
|
92
|
+
removeCredentials(home);
|
|
93
|
+
credentials = null;
|
|
94
|
+
if (json)
|
|
95
|
+
out(`${JSON.stringify({ event: "reconnect", reason: "connection expired" })}\n`);
|
|
96
|
+
else
|
|
97
|
+
out("\n your previous connection expired — reconnecting…\n");
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
if (!credentials) {
|
|
101
|
+
const baseUrl = teerasBaseUrl();
|
|
102
|
+
const grant = await startDeviceLogin(baseUrl, options.fetcher);
|
|
103
|
+
if (json) {
|
|
104
|
+
out(`${JSON.stringify({ event: "code", userCode: grant.userCode, verificationUri: grant.verificationUri, expiresIn: grant.expiresIn })}\n`);
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
const host = new URL(grant.verificationUri).host;
|
|
108
|
+
out([
|
|
109
|
+
"",
|
|
110
|
+
" teeras · one tap to connect",
|
|
111
|
+
"",
|
|
112
|
+
" On this computer: open",
|
|
113
|
+
` ${grant.verificationUri}`,
|
|
114
|
+
" then approve. (Sign in with Google if asked.)",
|
|
115
|
+
"",
|
|
116
|
+
` On your phone instead: go to ${host}/connect and enter ${grant.userCode}`,
|
|
117
|
+
"",
|
|
118
|
+
" While you work, agent activity counts (never content) sync to your Teeras account.",
|
|
119
|
+
" Waiting here while you continue…",
|
|
120
|
+
"",
|
|
121
|
+
].join("\n"));
|
|
122
|
+
openUri(grant.verificationUri);
|
|
123
|
+
}
|
|
124
|
+
const token = await pollDeviceLogin(baseUrl, grant, { fetcher: options.fetcher, sleep: options.sleep });
|
|
125
|
+
credentials = {
|
|
126
|
+
baseUrl,
|
|
127
|
+
accessToken: token.accessToken,
|
|
128
|
+
deviceId: token.deviceId,
|
|
129
|
+
connectedAt: new Date().toISOString(),
|
|
130
|
+
expiresAt: token.expiresAt,
|
|
131
|
+
};
|
|
132
|
+
writeCredentials(credentials, home);
|
|
133
|
+
await syncSnapshot(snapshot, credentials, options.fetcher);
|
|
134
|
+
}
|
|
135
|
+
markSyncSuccess(home);
|
|
136
|
+
if (json) {
|
|
137
|
+
out(`${JSON.stringify({ event: "connected", alreadyConnected, deviceId: credentials.deviceId, firstSync: !alreadyConnected })}\n`);
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
// The payoff, not just a status line: the same week report `trs week`
|
|
141
|
+
// prints, so the value lands in the same breath as the ask.
|
|
142
|
+
out(renderReport(snapshot, reportFrom(snapshot)));
|
|
143
|
+
out([
|
|
144
|
+
"",
|
|
145
|
+
alreadyConnected ? " ✓ signed in — refreshed your setup" : " ✓ signed in",
|
|
146
|
+
"",
|
|
147
|
+
...hookLine(hooks),
|
|
148
|
+
" hooks load when an agent starts, so measuring begins with your next session — not this one.",
|
|
149
|
+
" sync your counts-only week is on web and mobile · updates while you work",
|
|
150
|
+
"",
|
|
151
|
+
...qrBlock(isTty),
|
|
152
|
+
"",
|
|
153
|
+
].join("\n"));
|
|
154
|
+
}
|
|
155
|
+
//# sourceMappingURL=setup.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"setup.js","sourceRoot":"","sources":["../src/setup.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"claude.d.ts","sourceRoot":"","sources":["../../src/sources/claude.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"claude.d.ts","sourceRoot":"","sources":["../../src/sources/claude.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,aAAa,EAAe,MAAM,aAAa,CAAC;AAiB9D,eAAO,MAAM,YAAY,EAAE,aA+J1B,CAAC"}
|