@teeras/afterhours 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +57 -0
- package/dist/activity.d.ts +13 -0
- package/dist/activity.d.ts.map +1 -0
- package/dist/activity.js +82 -0
- package/dist/activity.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +107 -0
- package/dist/cli.js.map +1 -0
- package/dist/cloud.d.ts +39 -0
- package/dist/cloud.d.ts.map +1 -0
- package/dist/cloud.js +138 -0
- package/dist/cloud.js.map +1 -0
- package/dist/collect.d.ts +12 -0
- package/dist/collect.d.ts.map +1 -0
- package/dist/collect.js +36 -0
- package/dist/collect.js.map +1 -0
- package/dist/detect.d.ts +3 -0
- package/dist/detect.d.ts.map +1 -0
- package/dist/detect.js +32 -0
- package/dist/detect.js.map +1 -0
- package/dist/files.d.ts +5 -0
- package/dist/files.d.ts.map +1 -0
- package/dist/files.js +43 -0
- package/dist/files.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -0
- package/dist/privacy.d.ts +3 -0
- package/dist/privacy.d.ts.map +1 -0
- package/dist/privacy.js +14 -0
- package/dist/privacy.js.map +1 -0
- package/dist/render.d.ts +3 -0
- package/dist/render.d.ts.map +1 -0
- package/dist/render.js +22 -0
- package/dist/render.js.map +1 -0
- package/dist/report.d.ts +3 -0
- package/dist/report.d.ts.map +1 -0
- package/dist/report.js +36 -0
- package/dist/report.js.map +1 -0
- package/dist/sources/claude.d.ts +3 -0
- package/dist/sources/claude.d.ts.map +1 -0
- package/dist/sources/claude.js +61 -0
- package/dist/sources/claude.js.map +1 -0
- package/dist/sources/codex.d.ts +3 -0
- package/dist/sources/codex.d.ts.map +1 -0
- package/dist/sources/codex.js +54 -0
- package/dist/sources/codex.js.map +1 -0
- package/dist/sources/cursor.d.ts +3 -0
- package/dist/sources/cursor.d.ts.map +1 -0
- package/dist/sources/cursor.js +18 -0
- package/dist/sources/cursor.js.map +1 -0
- package/dist/sources/gemini.d.ts +3 -0
- package/dist/sources/gemini.d.ts.map +1 -0
- package/dist/sources/gemini.js +40 -0
- package/dist/sources/gemini.js.map +1 -0
- package/dist/sources/shared.d.ts +3 -0
- package/dist/sources/shared.d.ts.map +1 -0
- package/dist/sources/shared.js +20 -0
- package/dist/sources/shared.js.map +1 -0
- package/dist/types.d.ts +87 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +51 -0
package/README.md
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# Teeras Afterhours
|
|
2
|
+
|
|
3
|
+
Private AI-life balance and usage analytics for Codex, Claude Code, Gemini CLI,
|
|
4
|
+
Cursor, and other coding agents.
|
|
5
|
+
|
|
6
|
+
```sh
|
|
7
|
+
npx @teeras/afterhours
|
|
8
|
+
```
|
|
9
|
+
|
|
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:
|
|
13
|
+
|
|
14
|
+
```sh
|
|
15
|
+
trs
|
|
16
|
+
trs week
|
|
17
|
+
trs connect
|
|
18
|
+
trs login
|
|
19
|
+
trs sync
|
|
20
|
+
trs sources
|
|
21
|
+
trs privacy
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Afterhours parses the local session histories already written by supported
|
|
25
|
+
coding agents and immediately reduces them to counts and hourly buckets. It
|
|
26
|
+
never retains or returns prompt text, source code, model output, repository
|
|
27
|
+
names, working directories, or file paths. Only the documented aggregate
|
|
28
|
+
snapshot syncs after account approval.
|
|
29
|
+
|
|
30
|
+
Initial sources:
|
|
31
|
+
|
|
32
|
+
- Codex app and CLI (`~/.codex/sessions`)
|
|
33
|
+
- Claude Code (`~/.claude/projects`)
|
|
34
|
+
- Gemini CLI (`~/.gemini/tmp`, session-level fidelity)
|
|
35
|
+
- Cursor detection, ready for a consented adapter once its local schema is stable
|
|
36
|
+
|
|
37
|
+
`trs connect` also detects Windsurf, VS Code + Copilot, Zed, Amp, OpenCode,
|
|
38
|
+
Goose, and Aider. Automatic sources connect from their existing local history;
|
|
39
|
+
the rest are reported honestly as requiring a source adapter. The exported
|
|
40
|
+
`SourceAdapter` contract lets any coding agent add counts-only support without
|
|
41
|
+
changing the Afterhours core.
|
|
42
|
+
|
|
43
|
+
## Keep your history in Teeras
|
|
44
|
+
|
|
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.
|
|
48
|
+
|
|
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.
|
|
52
|
+
|
|
53
|
+
The default command (and `trs week`) prints a calm seven-day report. Use
|
|
54
|
+
`--json` to inspect the privacy-safe snapshot consumed by the dashboard and
|
|
55
|
+
optional MCP adapter. The cloud keeps one replaceable snapshot per device per
|
|
56
|
+
day in a 790-day active-history window; the dashboard lets users export or
|
|
57
|
+
delete that history.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { ActivityBucket, SourceId, SourceSnapshot, TokenCounts } from "./types.js";
|
|
2
|
+
export interface LocalEvent {
|
|
3
|
+
at: number;
|
|
4
|
+
kind: "agent" | "human";
|
|
5
|
+
}
|
|
6
|
+
export declare const EMPTY_TOKENS: TokenCounts;
|
|
7
|
+
export declare function finiteNumber(value: unknown): number;
|
|
8
|
+
export declare function timestamp(value: unknown): number | null;
|
|
9
|
+
export declare function inWindow(at: number, sinceMs: number, untilMs: number): boolean;
|
|
10
|
+
export declare function bucketsFrom(events: LocalEvent[]): ActivityBucket[];
|
|
11
|
+
export declare function emptySource(id: SourceId, name: string, configured: boolean): SourceSnapshot;
|
|
12
|
+
export declare function finishSource(source: SourceSnapshot, events: LocalEvent[], fidelity?: SourceSnapshot["fidelity"]): SourceSnapshot;
|
|
13
|
+
//# sourceMappingURL=activity.d.ts.map
|
|
@@ -0,0 +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"}
|
package/dist/activity.js
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
export const EMPTY_TOKENS = { input: 0, cached: 0, output: 0, reasoning: 0 };
|
|
2
|
+
export function finiteNumber(value) {
|
|
3
|
+
return typeof value === "number" && Number.isFinite(value) ? Math.max(0, value) : 0;
|
|
4
|
+
}
|
|
5
|
+
export function timestamp(value) {
|
|
6
|
+
if (typeof value !== "string" && typeof value !== "number")
|
|
7
|
+
return null;
|
|
8
|
+
const parsed = typeof value === "number" ? value : Date.parse(value);
|
|
9
|
+
return Number.isFinite(parsed) ? parsed : null;
|
|
10
|
+
}
|
|
11
|
+
export function inWindow(at, sinceMs, untilMs) {
|
|
12
|
+
return at >= sinceMs && at <= untilMs;
|
|
13
|
+
}
|
|
14
|
+
function localClock(at) {
|
|
15
|
+
const d = new Date(at);
|
|
16
|
+
const localDate = [d.getFullYear(), String(d.getMonth() + 1).padStart(2, "0"), String(d.getDate()).padStart(2, "0")].join("-");
|
|
17
|
+
const hourOfDay = d.getHours();
|
|
18
|
+
d.setMinutes(0, 0, 0);
|
|
19
|
+
return { hour: d.toISOString(), localDate, localHour: hourOfDay };
|
|
20
|
+
}
|
|
21
|
+
function observedMinutes(events, capMinutes) {
|
|
22
|
+
const byHour = new Map();
|
|
23
|
+
const sorted = [...new Set(events)].sort((a, b) => a - b);
|
|
24
|
+
for (let i = 0; i < sorted.length; i += 1) {
|
|
25
|
+
const at = sorted[i];
|
|
26
|
+
if (at == null)
|
|
27
|
+
continue;
|
|
28
|
+
const next = sorted[i + 1];
|
|
29
|
+
const minutes = next == null ? 1 : Math.max(1, Math.min(capMinutes, (next - at) / 60_000));
|
|
30
|
+
const key = localClock(at).hour;
|
|
31
|
+
byHour.set(key, (byHour.get(key) ?? 0) + minutes);
|
|
32
|
+
}
|
|
33
|
+
return byHour;
|
|
34
|
+
}
|
|
35
|
+
export function bucketsFrom(events) {
|
|
36
|
+
const agent = events.filter((event) => event.kind === "agent").map((event) => event.at);
|
|
37
|
+
const human = events.filter((event) => event.kind === "human").map((event) => event.at);
|
|
38
|
+
const agentMinutes = observedMinutes(agent, 5);
|
|
39
|
+
const humanMinutes = observedMinutes(human, 10);
|
|
40
|
+
const rows = new Map();
|
|
41
|
+
for (const event of events) {
|
|
42
|
+
const clock = localClock(event.at);
|
|
43
|
+
const hour = clock.hour;
|
|
44
|
+
const row = rows.get(hour) ?? {
|
|
45
|
+
hour,
|
|
46
|
+
localDate: clock.localDate,
|
|
47
|
+
localHour: clock.localHour,
|
|
48
|
+
agentTurns: 0,
|
|
49
|
+
humanCheckIns: 0,
|
|
50
|
+
agentActiveMinutes: agentMinutes.get(hour) ?? 0,
|
|
51
|
+
humanAttentionMinutes: humanMinutes.get(hour) ?? 0,
|
|
52
|
+
};
|
|
53
|
+
if (event.kind === "agent")
|
|
54
|
+
row.agentTurns += 1;
|
|
55
|
+
else
|
|
56
|
+
row.humanCheckIns += 1;
|
|
57
|
+
rows.set(hour, row);
|
|
58
|
+
}
|
|
59
|
+
return [...rows.values()].sort((a, b) => a.hour.localeCompare(b.hour));
|
|
60
|
+
}
|
|
61
|
+
export function emptySource(id, name, configured) {
|
|
62
|
+
return {
|
|
63
|
+
id,
|
|
64
|
+
name,
|
|
65
|
+
configured,
|
|
66
|
+
fidelity: configured ? "detected" : "none",
|
|
67
|
+
sessions: 0,
|
|
68
|
+
agentTurns: 0,
|
|
69
|
+
humanCheckIns: 0,
|
|
70
|
+
tokens: { ...EMPTY_TOKENS },
|
|
71
|
+
buckets: [],
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
export function finishSource(source, events, fidelity = "activity") {
|
|
75
|
+
source.agentTurns = events.filter((event) => event.kind === "agent").length;
|
|
76
|
+
source.humanCheckIns = events.filter((event) => event.kind === "human").length;
|
|
77
|
+
source.buckets = bucketsFrom(events);
|
|
78
|
+
if (source.sessions > 0 || events.length > 0)
|
|
79
|
+
source.fidelity = fidelity;
|
|
80
|
+
return source;
|
|
81
|
+
}
|
|
82
|
+
//# sourceMappingURL=activity.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"activity.js","sourceRoot":"","sources":["../src/activity.ts"],"names":[],"mappings":"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"}
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { collectAfterhours } from "./collect.js";
|
|
3
|
+
import { openVerificationUri, pollDeviceLogin, readCredentials, removeCredentials, revokeDevice, startDeviceLogin, syncSnapshot, teerasBaseUrl, writeCredentials, } from "./cloud.js";
|
|
4
|
+
import { detectCodingAgents } from "./detect.js";
|
|
5
|
+
import { DATA_BOUNDARY } from "./privacy.js";
|
|
6
|
+
import { renderReport } from "./render.js";
|
|
7
|
+
import { reportFrom } from "./report.js";
|
|
8
|
+
async function requireAccount() {
|
|
9
|
+
const existing = readCredentials();
|
|
10
|
+
if (existing)
|
|
11
|
+
return existing;
|
|
12
|
+
const baseUrl = teerasBaseUrl();
|
|
13
|
+
const grant = await startDeviceLogin(baseUrl);
|
|
14
|
+
process.stdout.write(`\n teeras · connect your private Afterhours\n\n ${grant.userCode}\n ${grant.verificationUri}\n\n Waiting here while you continue with Google…\n`);
|
|
15
|
+
openVerificationUri(grant.verificationUri);
|
|
16
|
+
const token = await pollDeviceLogin(baseUrl, grant);
|
|
17
|
+
const credentials = {
|
|
18
|
+
baseUrl,
|
|
19
|
+
accessToken: token.accessToken,
|
|
20
|
+
deviceId: token.deviceId,
|
|
21
|
+
connectedAt: new Date().toISOString(),
|
|
22
|
+
expiresAt: token.expiresAt,
|
|
23
|
+
};
|
|
24
|
+
writeCredentials(credentials);
|
|
25
|
+
process.stdout.write("\n ✓ account connected · now reading counts locally.\n\n");
|
|
26
|
+
return credentials;
|
|
27
|
+
}
|
|
28
|
+
async function main() {
|
|
29
|
+
const command = process.argv[2];
|
|
30
|
+
if (command === "privacy") {
|
|
31
|
+
process.stdout.write(`${JSON.stringify(DATA_BOUNDARY, null, 2)}\n`);
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
if (command === "connect") {
|
|
35
|
+
await requireAccount();
|
|
36
|
+
const installed = detectCodingAgents().filter((agent) => agent.installed);
|
|
37
|
+
const lines = installed.length === 0
|
|
38
|
+
? [" no coding agents found yet — install one, then run `trs connect` again."]
|
|
39
|
+
: installed.map((agent) => {
|
|
40
|
+
const mark = agent.connection === "automatic" ? "✓" : "·";
|
|
41
|
+
const state = agent.connection === "automatic" ? "connected locally" : "detected · connector needed";
|
|
42
|
+
return ` ${mark} ${agent.name.padEnd(22)} ${state}`;
|
|
43
|
+
});
|
|
44
|
+
process.stdout.write(["", " teeras · connect", "", ...lines, "", " Prompt, code, and output are never retained or synced.", ""].join("\n"));
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
if (command === "login") {
|
|
48
|
+
const credentials = await requireAccount();
|
|
49
|
+
const snapshot = collectAfterhours();
|
|
50
|
+
await syncSnapshot(snapshot, credentials);
|
|
51
|
+
process.stdout.write("\n ✓ connected · your first counts-only week is in Teeras.\n\n");
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
if (command === "logout") {
|
|
55
|
+
const credentials = readCredentials();
|
|
56
|
+
const revoked = credentials ? await revokeDevice(credentials) : false;
|
|
57
|
+
const removed = removeCredentials();
|
|
58
|
+
process.stdout.write(`\n ${removed ? `✓ disconnected${revoked ? " and revoked" : " locally"}` : "this computer was not connected"}.\n\n`);
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
if (command === "sync") {
|
|
62
|
+
const credentials = await requireAccount();
|
|
63
|
+
const result = await syncSnapshot(collectAfterhours(), credentials);
|
|
64
|
+
process.stdout.write(`\n ✓ ${result.stored ? "your AI life moved forward" : "already beautifully up to date"}.\n\n`);
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
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");
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
const credentials = await requireAccount();
|
|
72
|
+
const snapshot = collectAfterhours();
|
|
73
|
+
if (command === "--json" || command === "json") {
|
|
74
|
+
process.stdout.write(`${JSON.stringify(snapshot, null, 2)}\n`);
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
if (command == null || command === "week" || command === "setup") {
|
|
78
|
+
process.stdout.write(renderReport(snapshot, reportFrom(snapshot)));
|
|
79
|
+
try {
|
|
80
|
+
await syncSnapshot(snapshot, credentials);
|
|
81
|
+
process.stdout.write(" ✓ history synced to your Teeras account\n\n");
|
|
82
|
+
}
|
|
83
|
+
catch (error) {
|
|
84
|
+
const message = error instanceof Error ? error.message : "sync unavailable";
|
|
85
|
+
process.stdout.write(` local report is safe · ${message}\n\n`);
|
|
86
|
+
}
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
if (command === "sources") {
|
|
90
|
+
const rows = snapshot.sources.map((source) => ({
|
|
91
|
+
source: source.name,
|
|
92
|
+
status: source.configured ? source.fidelity : "not found",
|
|
93
|
+
sessions: source.sessions,
|
|
94
|
+
}));
|
|
95
|
+
process.stdout.write(`${JSON.stringify(rows, null, 2)}\n`);
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
try {
|
|
100
|
+
await main();
|
|
101
|
+
}
|
|
102
|
+
catch (error) {
|
|
103
|
+
const message = error instanceof Error ? error.message : "local activity could not be read";
|
|
104
|
+
process.stderr.write(`afterhours · ${message}\n`);
|
|
105
|
+
process.exitCode = 1;
|
|
106
|
+
}
|
|
107
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
package/dist/cloud.d.ts
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { AfterhoursSnapshot } from "./types.js";
|
|
2
|
+
export interface CloudCredentials {
|
|
3
|
+
baseUrl: string;
|
|
4
|
+
accessToken: string;
|
|
5
|
+
deviceId: string;
|
|
6
|
+
connectedAt: string;
|
|
7
|
+
expiresAt: string;
|
|
8
|
+
}
|
|
9
|
+
export interface DeviceGrant {
|
|
10
|
+
deviceCode: string;
|
|
11
|
+
userCode: string;
|
|
12
|
+
verificationUri: string;
|
|
13
|
+
expiresIn: number;
|
|
14
|
+
interval: number;
|
|
15
|
+
}
|
|
16
|
+
interface DeviceToken {
|
|
17
|
+
accessToken: string;
|
|
18
|
+
tokenType: "Bearer";
|
|
19
|
+
deviceId: string;
|
|
20
|
+
expiresAt: string;
|
|
21
|
+
}
|
|
22
|
+
type Fetcher = typeof fetch;
|
|
23
|
+
export declare function teerasBaseUrl(value?: string | undefined): string;
|
|
24
|
+
export declare function credentialsPath(home?: string): string;
|
|
25
|
+
export declare function readCredentials(home?: string): CloudCredentials | null;
|
|
26
|
+
export declare function writeCredentials(credentials: CloudCredentials, home?: string): void;
|
|
27
|
+
export declare function removeCredentials(home?: string): boolean;
|
|
28
|
+
export declare function openVerificationUri(uri: string): void;
|
|
29
|
+
export declare function startDeviceLogin(baseUrl: string, fetcher?: Fetcher): Promise<DeviceGrant>;
|
|
30
|
+
export declare function pollDeviceLogin(baseUrl: string, grant: DeviceGrant, options?: {
|
|
31
|
+
fetcher?: Fetcher;
|
|
32
|
+
sleep?: (ms: number) => Promise<void>;
|
|
33
|
+
}): Promise<DeviceToken>;
|
|
34
|
+
export declare function revokeDevice(credentials: CloudCredentials, fetcher?: Fetcher): Promise<boolean>;
|
|
35
|
+
export declare function syncSnapshot(snapshot: AfterhoursSnapshot, credentials: CloudCredentials, fetcher?: Fetcher): Promise<{
|
|
36
|
+
stored: boolean;
|
|
37
|
+
}>;
|
|
38
|
+
export {};
|
|
39
|
+
//# sourceMappingURL=cloud.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cloud.d.ts","sourceRoot":"","sources":["../src/cloud.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAErD,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,WAAW;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,eAAe,EAAE,MAAM,CAAC;IACxB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,UAAU,WAAW;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,QAAQ,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,KAAK,OAAO,GAAG,OAAO,KAAK,CAAC;AAI5B,wBAAgB,aAAa,CAAC,KAAK,qBAAyB,GAAG,MAAM,CAOpE;AAED,wBAAgB,eAAe,CAAC,IAAI,SAAY,GAAG,MAAM,CAExD;AAED,wBAAgB,eAAe,CAAC,IAAI,SAAY,GAAG,gBAAgB,GAAG,IAAI,CAqBzE;AAED,wBAAgB,gBAAgB,CAAC,WAAW,EAAE,gBAAgB,EAAE,IAAI,SAAY,GAAG,IAAI,CAMtF;AAED,wBAAgB,iBAAiB,CAAC,IAAI,SAAY,GAAG,OAAO,CAO3D;AAED,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAQrD;AAMD,wBAAsB,gBAAgB,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,GAAE,OAAe,GAAG,OAAO,CAAC,WAAW,CAAC,CActG;AAED,wBAAsB,eAAe,CACnC,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,WAAW,EAClB,OAAO,GAAE;IAAE,OAAO,CAAC,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;CAAO,GACzE,OAAO,CAAC,WAAW,CAAC,CA0BtB;AAED,wBAAsB,YAAY,CAChC,WAAW,EAAE,gBAAgB,EAC7B,OAAO,GAAE,OAAe,GACvB,OAAO,CAAC,OAAO,CAAC,CAUlB;AAED,wBAAsB,YAAY,CAChC,QAAQ,EAAE,kBAAkB,EAC5B,WAAW,EAAE,gBAAgB,EAC7B,OAAO,GAAE,OAAe,GACvB,OAAO,CAAC;IAAE,MAAM,EAAE,OAAO,CAAA;CAAE,CAAC,CAa9B"}
|
package/dist/cloud.js
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { chmodSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import { dirname, join } from "node:path";
|
|
4
|
+
import { spawn } from "node:child_process";
|
|
5
|
+
const TOKEN_PATTERN = /^[A-Za-z0-9_-]{32,128}$/;
|
|
6
|
+
const UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
7
|
+
export function teerasBaseUrl(value = process.env.TEERAS_URL) {
|
|
8
|
+
const candidate = value || "https://teeras.com";
|
|
9
|
+
const url = new URL(candidate);
|
|
10
|
+
if (url.protocol !== "https:" && url.hostname !== "localhost" && url.hostname !== "127.0.0.1") {
|
|
11
|
+
throw new Error("TEERAS_URL must use HTTPS (localhost is allowed for development)");
|
|
12
|
+
}
|
|
13
|
+
return url.origin;
|
|
14
|
+
}
|
|
15
|
+
export function credentialsPath(home = homedir()) {
|
|
16
|
+
return join(home, ".teeras", "afterhours", "credentials.json");
|
|
17
|
+
}
|
|
18
|
+
export function readCredentials(home = homedir()) {
|
|
19
|
+
try {
|
|
20
|
+
const parsed = JSON.parse(readFileSync(credentialsPath(home), "utf8"));
|
|
21
|
+
if (typeof parsed.baseUrl !== "string" ||
|
|
22
|
+
typeof parsed.accessToken !== "string" || !TOKEN_PATTERN.test(parsed.accessToken) ||
|
|
23
|
+
typeof parsed.deviceId !== "string" || !UUID_PATTERN.test(parsed.deviceId) ||
|
|
24
|
+
typeof parsed.connectedAt !== "string" || !Number.isFinite(Date.parse(parsed.connectedAt)) ||
|
|
25
|
+
typeof parsed.expiresAt !== "string" || !Number.isFinite(Date.parse(parsed.expiresAt)) ||
|
|
26
|
+
Date.parse(parsed.expiresAt) <= Date.now())
|
|
27
|
+
return null;
|
|
28
|
+
return {
|
|
29
|
+
baseUrl: teerasBaseUrl(parsed.baseUrl),
|
|
30
|
+
accessToken: parsed.accessToken,
|
|
31
|
+
deviceId: parsed.deviceId,
|
|
32
|
+
connectedAt: parsed.connectedAt,
|
|
33
|
+
expiresAt: parsed.expiresAt,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
export function writeCredentials(credentials, home = homedir()) {
|
|
41
|
+
const path = credentialsPath(home);
|
|
42
|
+
mkdirSync(dirname(path), { recursive: true, mode: 0o700 });
|
|
43
|
+
chmodSync(dirname(path), 0o700);
|
|
44
|
+
writeFileSync(path, `${JSON.stringify(credentials, null, 2)}\n`, { mode: 0o600 });
|
|
45
|
+
chmodSync(path, 0o600);
|
|
46
|
+
}
|
|
47
|
+
export function removeCredentials(home = homedir()) {
|
|
48
|
+
try {
|
|
49
|
+
rmSync(credentialsPath(home));
|
|
50
|
+
return true;
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
export function openVerificationUri(uri) {
|
|
57
|
+
const command = process.platform === "darwin" ? "open" : process.platform === "win32" ? "cmd" : "xdg-open";
|
|
58
|
+
const args = process.platform === "win32" ? ["/c", "start", "", uri] : [uri];
|
|
59
|
+
try {
|
|
60
|
+
spawn(command, args, { detached: true, stdio: "ignore" }).unref();
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
// The URL and one-time code remain visible, so a missing opener is harmless.
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
function object(value) {
|
|
67
|
+
return typeof value === "object" && value !== null ? value : null;
|
|
68
|
+
}
|
|
69
|
+
export async function startDeviceLogin(baseUrl, fetcher = fetch) {
|
|
70
|
+
const response = await fetcher(`${baseUrl}/api/afterhours/device/start`, { method: "POST" });
|
|
71
|
+
if (!response.ok)
|
|
72
|
+
throw new Error("Teeras could not start the connection");
|
|
73
|
+
const value = object(await response.json());
|
|
74
|
+
if (!value ||
|
|
75
|
+
typeof value.deviceCode !== "string" || !TOKEN_PATTERN.test(value.deviceCode) ||
|
|
76
|
+
typeof value.userCode !== "string" || !/^[A-Z0-9]{4}-[A-Z0-9]{4}$/.test(value.userCode) ||
|
|
77
|
+
typeof value.verificationUri !== "string" ||
|
|
78
|
+
new URL(value.verificationUri).origin !== new URL(baseUrl).origin ||
|
|
79
|
+
typeof value.expiresIn !== "number" || value.expiresIn < 60 || value.expiresIn > 900 ||
|
|
80
|
+
typeof value.interval !== "number" || value.interval < 1 || value.interval > 10)
|
|
81
|
+
throw new Error("Teeras returned an invalid connection response");
|
|
82
|
+
return value;
|
|
83
|
+
}
|
|
84
|
+
export async function pollDeviceLogin(baseUrl, grant, options = {}) {
|
|
85
|
+
const fetcher = options.fetcher ?? fetch;
|
|
86
|
+
const sleep = options.sleep ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
|
|
87
|
+
const deadline = Date.now() + grant.expiresIn * 1000;
|
|
88
|
+
while (Date.now() < deadline) {
|
|
89
|
+
const response = await fetcher(`${baseUrl}/api/afterhours/device/token`, {
|
|
90
|
+
method: "POST",
|
|
91
|
+
headers: { "content-type": "application/json" },
|
|
92
|
+
body: JSON.stringify({ deviceCode: grant.deviceCode }),
|
|
93
|
+
});
|
|
94
|
+
const value = object(await response.json().catch(() => null));
|
|
95
|
+
if (response.ok && value) {
|
|
96
|
+
if (typeof value.accessToken === "string" && TOKEN_PATTERN.test(value.accessToken) &&
|
|
97
|
+
value.tokenType === "Bearer" &&
|
|
98
|
+
typeof value.deviceId === "string" && UUID_PATTERN.test(value.deviceId) &&
|
|
99
|
+
typeof value.expiresAt === "string" && Date.parse(value.expiresAt) > Date.now())
|
|
100
|
+
return value;
|
|
101
|
+
throw new Error("Teeras returned an invalid device token");
|
|
102
|
+
}
|
|
103
|
+
if (value?.error !== "authorization_pending") {
|
|
104
|
+
throw new Error(value?.error === "expired_token" ? "The connection code expired" : "Teeras rejected the connection");
|
|
105
|
+
}
|
|
106
|
+
await sleep(Math.max(1, grant.interval) * 1000);
|
|
107
|
+
}
|
|
108
|
+
throw new Error("The connection code expired");
|
|
109
|
+
}
|
|
110
|
+
export async function revokeDevice(credentials, fetcher = fetch) {
|
|
111
|
+
try {
|
|
112
|
+
const response = await fetcher(`${credentials.baseUrl}/api/afterhours/device/revoke`, {
|
|
113
|
+
method: "POST",
|
|
114
|
+
headers: { authorization: `Bearer ${credentials.accessToken}` },
|
|
115
|
+
});
|
|
116
|
+
return response.ok;
|
|
117
|
+
}
|
|
118
|
+
catch {
|
|
119
|
+
return false;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
export async function syncSnapshot(snapshot, credentials, fetcher = fetch) {
|
|
123
|
+
const response = await fetcher(`${credentials.baseUrl}/api/afterhours/sync`, {
|
|
124
|
+
method: "POST",
|
|
125
|
+
headers: {
|
|
126
|
+
authorization: `Bearer ${credentials.accessToken}`,
|
|
127
|
+
"content-type": "application/json",
|
|
128
|
+
},
|
|
129
|
+
body: JSON.stringify(snapshot),
|
|
130
|
+
});
|
|
131
|
+
if (response.status === 401)
|
|
132
|
+
throw new Error("This device connection expired. Run `trs login` again.");
|
|
133
|
+
if (!response.ok)
|
|
134
|
+
throw new Error("Teeras could not sync this report");
|
|
135
|
+
const value = object(await response.json());
|
|
136
|
+
return { stored: value?.stored === true };
|
|
137
|
+
}
|
|
138
|
+
//# sourceMappingURL=cloud.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cloud.js","sourceRoot":"","sources":["../src/cloud.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { AfterhoursSnapshot, SourceAdapter } from "./types.js";
|
|
2
|
+
export declare const SOURCES: readonly SourceAdapter[];
|
|
3
|
+
export interface CollectOptions {
|
|
4
|
+
home?: string;
|
|
5
|
+
since?: Date;
|
|
6
|
+
until?: Date;
|
|
7
|
+
sources?: readonly SourceAdapter[];
|
|
8
|
+
}
|
|
9
|
+
export declare function collectAfterhoursLocal(options?: CollectOptions): AfterhoursSnapshot;
|
|
10
|
+
/** Public collection boundary: activity is not read before account connection. */
|
|
11
|
+
export declare function collectAfterhours(options?: CollectOptions): AfterhoursSnapshot;
|
|
12
|
+
//# sourceMappingURL=collect.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"collect.d.ts","sourceRoot":"","sources":["../src/collect.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEpE,eAAO,MAAM,OAAO,EAAE,SAAS,aAAa,EAA4D,CAAC;AAEzG,MAAM,WAAW,cAAc;IAC7B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,IAAI,CAAC;IACb,KAAK,CAAC,EAAE,IAAI,CAAC;IACb,OAAO,CAAC,EAAE,SAAS,aAAa,EAAE,CAAC;CACpC;AAED,wBAAgB,sBAAsB,CAAC,OAAO,GAAE,cAAmB,GAAG,kBAAkB,CAiBvF;AAED,kFAAkF;AAClF,wBAAgB,iBAAiB,CAAC,OAAO,GAAE,cAAmB,GAAG,kBAAkB,CAMlF"}
|
package/dist/collect.js
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { homedir } from "node:os";
|
|
2
|
+
import { readCredentials } from "./cloud.js";
|
|
3
|
+
import { DATA_BOUNDARY } from "./privacy.js";
|
|
4
|
+
import { claudeSource } from "./sources/claude.js";
|
|
5
|
+
import { codexSource } from "./sources/codex.js";
|
|
6
|
+
import { cursorSource } from "./sources/cursor.js";
|
|
7
|
+
import { geminiSource } from "./sources/gemini.js";
|
|
8
|
+
export const SOURCES = [claudeSource, codexSource, geminiSource, cursorSource];
|
|
9
|
+
export function collectAfterhoursLocal(options = {}) {
|
|
10
|
+
const until = options.until ?? new Date();
|
|
11
|
+
const since = options.since ?? new Date(until.getTime() - 7 * 24 * 60 * 60 * 1000);
|
|
12
|
+
if (since.getTime() > until.getTime())
|
|
13
|
+
throw new Error("since must be before until");
|
|
14
|
+
const input = {
|
|
15
|
+
home: options.home ?? homedir(),
|
|
16
|
+
sinceMs: since.getTime(),
|
|
17
|
+
untilMs: until.getTime(),
|
|
18
|
+
};
|
|
19
|
+
return {
|
|
20
|
+
schemaVersion: 2,
|
|
21
|
+
generatedAt: until.toISOString(),
|
|
22
|
+
since: since.toISOString(),
|
|
23
|
+
until: until.toISOString(),
|
|
24
|
+
sources: (options.sources ?? SOURCES).map((source) => source.collect(input)),
|
|
25
|
+
boundary: DATA_BOUNDARY,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
/** Public collection boundary: activity is not read before account connection. */
|
|
29
|
+
export function collectAfterhours(options = {}) {
|
|
30
|
+
const home = options.home ?? homedir();
|
|
31
|
+
if (!readCredentials(home)) {
|
|
32
|
+
throw new Error("Afterhours needs a Teeras account. Run `npx @teeras/afterhours` once to connect with Google.");
|
|
33
|
+
}
|
|
34
|
+
return collectAfterhoursLocal({ ...options, home });
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=collect.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"collect.js","sourceRoot":"","sources":["../src/collect.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAGnD,MAAM,CAAC,MAAM,OAAO,GAA6B,CAAC,YAAY,EAAE,WAAW,EAAE,YAAY,EAAE,YAAY,CAAC,CAAC;AASzG,MAAM,UAAU,sBAAsB,CAAC,UAA0B,EAAE;IACjE,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,IAAI,IAAI,EAAE,CAAC;IAC1C,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC;IACnF,IAAI,KAAK,CAAC,OAAO,EAAE,GAAG,KAAK,CAAC,OAAO,EAAE;QAAE,MAAM,IAAI,KAAK,CAAC,4BAA4B,CAAC,CAAC;IACrF,MAAM,KAAK,GAAG;QACZ,IAAI,EAAE,OAAO,CAAC,IAAI,IAAI,OAAO,EAAE;QAC/B,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE;QACxB,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE;KACzB,CAAC;IACF,OAAO;QACL,aAAa,EAAE,CAAC;QAChB,WAAW,EAAE,KAAK,CAAC,WAAW,EAAE;QAChC,KAAK,EAAE,KAAK,CAAC,WAAW,EAAE;QAC1B,KAAK,EAAE,KAAK,CAAC,WAAW,EAAE;QAC1B,OAAO,EAAE,CAAC,OAAO,CAAC,OAAO,IAAI,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;QAC5E,QAAQ,EAAE,aAAa;KACxB,CAAC;AACJ,CAAC;AAED,kFAAkF;AAClF,MAAM,UAAU,iBAAiB,CAAC,UAA0B,EAAE;IAC5D,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,IAAI,OAAO,EAAE,CAAC;IACvC,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,EAAE,CAAC;QAC3B,MAAM,IAAI,KAAK,CAAC,8FAA8F,CAAC,CAAC;IAClH,CAAC;IACD,OAAO,sBAAsB,CAAC,EAAE,GAAG,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;AACtD,CAAC"}
|
package/dist/detect.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"detect.d.ts","sourceRoot":"","sources":["../src/detect.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAyBhD,wBAAgB,kBAAkB,CAAC,IAAI,SAAY,GAAG,aAAa,EAAE,CAapE"}
|
package/dist/detect.js
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { homedir } from "node:os";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { exists, isDirectory } from "./files.js";
|
|
4
|
+
const appSupport = (home, name) => join(home, "Library", "Application Support", name);
|
|
5
|
+
const DETECTORS = [
|
|
6
|
+
{ id: "codex", name: "Codex app + CLI", automatic: true, paths: (home) => [join(home, ".codex")] },
|
|
7
|
+
{ id: "claude-code", name: "Claude Code", automatic: true, paths: (home) => [join(home, ".claude")] },
|
|
8
|
+
{ id: "gemini-cli", name: "Gemini CLI", automatic: true, paths: (home) => [join(home, ".gemini")] },
|
|
9
|
+
{ id: "cursor", name: "Cursor", automatic: false, paths: (home) => [appSupport(home, "Cursor"), join(home, ".config", "Cursor"), join(home, ".cursor")] },
|
|
10
|
+
{ id: "windsurf", name: "Windsurf", automatic: false, paths: (home) => [appSupport(home, "Windsurf"), join(home, ".codeium", "windsurf")] },
|
|
11
|
+
{ id: "vscode-copilot", name: "VS Code + Copilot", automatic: false, paths: (home) => [appSupport(home, "Code"), join(home, ".config", "Code")] },
|
|
12
|
+
{ id: "zed", name: "Zed", automatic: false, paths: (home) => [appSupport(home, "Zed"), join(home, ".config", "zed")] },
|
|
13
|
+
{ id: "amp", name: "Amp", automatic: false, paths: (home) => [join(home, ".amp")] },
|
|
14
|
+
{ id: "opencode", name: "OpenCode", automatic: false, paths: (home) => [join(home, ".config", "opencode"), join(home, ".local", "share", "opencode")] },
|
|
15
|
+
{ id: "goose", name: "Goose", automatic: false, paths: (home) => [join(home, ".config", "goose"), appSupport(home, "Goose")] },
|
|
16
|
+
{ id: "aider", name: "Aider", automatic: false, paths: (home) => [join(home, ".aider.conf.yml"), join(home, ".aider")] },
|
|
17
|
+
];
|
|
18
|
+
export function detectCodingAgents(home = homedir()) {
|
|
19
|
+
return DETECTORS.map((detector) => {
|
|
20
|
+
const installed = detector.paths(home).some((path) => isDirectory(path) || exists(path));
|
|
21
|
+
return {
|
|
22
|
+
id: detector.id,
|
|
23
|
+
name: detector.name,
|
|
24
|
+
installed,
|
|
25
|
+
connection: detector.automatic ? "automatic" : "connector-needed",
|
|
26
|
+
detail: detector.automatic
|
|
27
|
+
? "local history connects automatically; app and CLI activity is deduplicated at the source"
|
|
28
|
+
: "detected locally; a consented source adapter is needed before activity is counted",
|
|
29
|
+
};
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=detect.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"detect.js","sourceRoot":"","sources":["../src/detect.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAUjD,MAAM,UAAU,GAAG,CAAC,IAAY,EAAE,IAAY,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,SAAS,EAAE,qBAAqB,EAAE,IAAI,CAAC,CAAC;AAEtG,MAAM,SAAS,GAAwB;IACrC,EAAE,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,iBAAiB,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC,EAAE;IAClG,EAAE,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,aAAa,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC,EAAE;IACrG,EAAE,EAAE,EAAE,YAAY,EAAE,IAAI,EAAE,YAAY,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC,EAAE;IACnG,EAAE,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,QAAQ,EAAE,SAAS,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,UAAU,CAAC,IAAI,EAAE,QAAQ,CAAC,EAAE,IAAI,CAAC,IAAI,EAAE,SAAS,EAAE,QAAQ,CAAC,EAAE,IAAI,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC,EAAE;IACzJ,EAAE,EAAE,EAAE,UAAU,EAAE,IAAI,EAAE,UAAU,EAAE,SAAS,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,UAAU,CAAC,IAAI,EAAE,UAAU,CAAC,EAAE,IAAI,CAAC,IAAI,EAAE,UAAU,EAAE,UAAU,CAAC,CAAC,EAAE;IAC3I,EAAE,EAAE,EAAE,gBAAgB,EAAE,IAAI,EAAE,mBAAmB,EAAE,SAAS,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,UAAU,CAAC,IAAI,EAAE,MAAM,CAAC,EAAE,IAAI,CAAC,IAAI,EAAE,SAAS,EAAE,MAAM,CAAC,CAAC,EAAE;IACjJ,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,UAAU,CAAC,IAAI,EAAE,KAAK,CAAC,EAAE,IAAI,CAAC,IAAI,EAAE,SAAS,EAAE,KAAK,CAAC,CAAC,EAAE;IACtH,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,EAAE;IACnF,EAAE,EAAE,EAAE,UAAU,EAAE,IAAI,EAAE,UAAU,EAAE,SAAS,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,SAAS,EAAE,UAAU,CAAC,EAAE,IAAI,CAAC,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,UAAU,CAAC,CAAC,EAAE;IACvJ,EAAE,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,SAAS,EAAE,OAAO,CAAC,EAAE,UAAU,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,EAAE;IAC9H,EAAE,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,iBAAiB,CAAC,EAAE,IAAI,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC,EAAE;CACzH,CAAC;AAEF,MAAM,UAAU,kBAAkB,CAAC,IAAI,GAAG,OAAO,EAAE;IACjD,OAAO,SAAS,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,EAAE;QAChC,MAAM,SAAS,GAAG,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC;QACzF,OAAO;YACL,EAAE,EAAE,QAAQ,CAAC,EAAE;YACf,IAAI,EAAE,QAAQ,CAAC,IAAI;YACnB,SAAS;YACT,UAAU,EAAE,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,kBAAkB;YACjE,MAAM,EAAE,QAAQ,CAAC,SAAS;gBACxB,CAAC,CAAC,0FAA0F;gBAC5F,CAAC,CAAC,mFAAmF;SACxF,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC"}
|
package/dist/files.d.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export declare function isDirectory(path: string): boolean;
|
|
2
|
+
export declare function exists(path: string): boolean;
|
|
3
|
+
/** Scope-bounded, symlink-free traversal of one known agent directory. */
|
|
4
|
+
export declare function recentFiles(root: string, sinceMs: number, accepts: (name: string) => boolean, depth?: number, output?: string[]): string[];
|
|
5
|
+
//# sourceMappingURL=files.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"files.d.ts","sourceRoot":"","sources":["../src/files.ts"],"names":[],"mappings":"AAGA,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAMjD;AAED,wBAAgB,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAE5C;AAED,0EAA0E;AAC1E,wBAAgB,WAAW,CACzB,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,EAClC,KAAK,SAAI,EACT,MAAM,GAAE,MAAM,EAAO,GACpB,MAAM,EAAE,CAqBV"}
|