@nocoo/pew 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/dist/bin.d.ts +3 -0
- package/dist/bin.d.ts.map +1 -0
- package/dist/bin.js +5 -0
- package/dist/bin.js.map +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +221 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/login.d.ts +34 -0
- package/dist/commands/login.d.ts.map +1 -0
- package/dist/commands/login.js +108 -0
- package/dist/commands/login.js.map +1 -0
- package/dist/commands/status.d.ts +19 -0
- package/dist/commands/status.d.ts.map +1 -0
- package/dist/commands/status.js +35 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/commands/sync.d.ts +49 -0
- package/dist/commands/sync.d.ts.map +1 -0
- package/dist/commands/sync.js +267 -0
- package/dist/commands/sync.js.map +1 -0
- package/dist/commands/upload.d.ts +53 -0
- package/dist/commands/upload.d.ts.map +1 -0
- package/dist/commands/upload.js +185 -0
- package/dist/commands/upload.js.map +1 -0
- package/dist/config/manager.d.ts +14 -0
- package/dist/config/manager.d.ts.map +1 -0
- package/dist/config/manager.js +32 -0
- package/dist/config/manager.js.map +1 -0
- package/dist/discovery/sources.d.ts +37 -0
- package/dist/discovery/sources.d.ts.map +1 -0
- package/dist/discovery/sources.js +110 -0
- package/dist/discovery/sources.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/dist/parsers/claude.d.ts +34 -0
- package/dist/parsers/claude.d.ts.map +1 -0
- package/dist/parsers/claude.js +101 -0
- package/dist/parsers/claude.js.map +1 -0
- package/dist/parsers/gemini.d.ts +38 -0
- package/dist/parsers/gemini.d.ts.map +1 -0
- package/dist/parsers/gemini.js +142 -0
- package/dist/parsers/gemini.js.map +1 -0
- package/dist/parsers/openclaw.d.ts +23 -0
- package/dist/parsers/openclaw.d.ts.map +1 -0
- package/dist/parsers/openclaw.js +85 -0
- package/dist/parsers/openclaw.js.map +1 -0
- package/dist/parsers/opencode.d.ts +35 -0
- package/dist/parsers/opencode.d.ts.map +1 -0
- package/dist/parsers/opencode.js +118 -0
- package/dist/parsers/opencode.js.map +1 -0
- package/dist/storage/cursor-store.d.ts +14 -0
- package/dist/storage/cursor-store.d.ts.map +1 -0
- package/dist/storage/cursor-store.js +34 -0
- package/dist/storage/cursor-store.js.map +1 -0
- package/dist/storage/local-queue.d.ts +30 -0
- package/dist/storage/local-queue.d.ts.map +1 -0
- package/dist/storage/local-queue.js +70 -0
- package/dist/storage/local-queue.js.map +1 -0
- package/dist/utils/buckets.d.ts +17 -0
- package/dist/utils/buckets.d.ts.map +1 -0
- package/dist/utils/buckets.js +38 -0
- package/dist/utils/buckets.js.map +1 -0
- package/dist/utils/paths.d.ts +17 -0
- package/dist/utils/paths.d.ts.map +1 -0
- package/dist/utils/paths.js +21 -0
- package/dist/utils/paths.js.map +1 -0
- package/package.json +44 -0
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
import { diffTotals } from "./gemini.js";
|
|
3
|
+
/** Coerce to non-negative integer */
|
|
4
|
+
function toNonNegInt(v) {
|
|
5
|
+
const n = Number(v);
|
|
6
|
+
if (!Number.isFinite(n) || n < 0)
|
|
7
|
+
return 0;
|
|
8
|
+
return Math.floor(n);
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Coerce an epoch value to milliseconds.
|
|
12
|
+
* Values < 1e12 are treated as seconds and multiplied by 1000.
|
|
13
|
+
*/
|
|
14
|
+
export function coerceEpochMs(v) {
|
|
15
|
+
const n = Number(v);
|
|
16
|
+
if (!Number.isFinite(n) || n <= 0)
|
|
17
|
+
return 0;
|
|
18
|
+
if (n < 1e12)
|
|
19
|
+
return Math.floor(n * 1000);
|
|
20
|
+
return Math.floor(n);
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Normalize OpenCode's token object to our TokenDelta format.
|
|
24
|
+
*
|
|
25
|
+
* OpenCode fields:
|
|
26
|
+
* input + cache.write → inputTokens
|
|
27
|
+
* cache.read → cachedInputTokens
|
|
28
|
+
* output → outputTokens
|
|
29
|
+
* reasoning → reasoningOutputTokens
|
|
30
|
+
*/
|
|
31
|
+
export function normalizeOpenCodeTokens(tokens) {
|
|
32
|
+
if (!tokens || typeof tokens !== "object")
|
|
33
|
+
return null;
|
|
34
|
+
const cache = tokens.cache;
|
|
35
|
+
const cacheWrite = toNonNegInt(cache?.write);
|
|
36
|
+
const cacheRead = toNonNegInt(cache?.read);
|
|
37
|
+
return {
|
|
38
|
+
inputTokens: toNonNegInt(tokens.input) + cacheWrite,
|
|
39
|
+
cachedInputTokens: cacheRead,
|
|
40
|
+
outputTokens: toNonNegInt(tokens.output),
|
|
41
|
+
reasoningOutputTokens: toNonNegInt(tokens.reasoning),
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
/** Check if a TokenDelta is all zeros */
|
|
45
|
+
function isAllZero(d) {
|
|
46
|
+
return (d.inputTokens === 0 &&
|
|
47
|
+
d.cachedInputTokens === 0 &&
|
|
48
|
+
d.outputTokens === 0 &&
|
|
49
|
+
d.reasoningOutputTokens === 0);
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Parse a single OpenCode message JSON file.
|
|
53
|
+
*
|
|
54
|
+
* Each file is a standalone JSON object for one message.
|
|
55
|
+
* Uses diff against previous totals to compute incremental deltas
|
|
56
|
+
* (reuses diffTotals from Gemini parser — same logic).
|
|
57
|
+
*/
|
|
58
|
+
export async function parseOpenCodeFile(opts) {
|
|
59
|
+
const { filePath, lastTotals } = opts;
|
|
60
|
+
let raw;
|
|
61
|
+
try {
|
|
62
|
+
raw = await readFile(filePath, "utf8");
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
return { delta: null, messageKey: null, lastTotals };
|
|
66
|
+
}
|
|
67
|
+
if (!raw.trim()) {
|
|
68
|
+
return { delta: null, messageKey: null, lastTotals };
|
|
69
|
+
}
|
|
70
|
+
let msg;
|
|
71
|
+
try {
|
|
72
|
+
msg = JSON.parse(raw);
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
return { delta: null, messageKey: null, lastTotals };
|
|
76
|
+
}
|
|
77
|
+
// Only process assistant messages
|
|
78
|
+
if (msg.role !== "assistant") {
|
|
79
|
+
return { delta: null, messageKey: null, lastTotals };
|
|
80
|
+
}
|
|
81
|
+
// Derive message key
|
|
82
|
+
const sessionId = typeof msg.sessionID === "string" ? msg.sessionID : null;
|
|
83
|
+
const msgId = typeof msg.id === "string" ? msg.id : null;
|
|
84
|
+
const messageKey = sessionId && msgId ? `${sessionId}|${msgId}` : null;
|
|
85
|
+
// Normalize tokens
|
|
86
|
+
const currentTotals = normalizeOpenCodeTokens(msg.tokens);
|
|
87
|
+
if (!currentTotals) {
|
|
88
|
+
return { delta: null, messageKey, lastTotals };
|
|
89
|
+
}
|
|
90
|
+
// Diff against previous
|
|
91
|
+
const tokenDelta = diffTotals(currentTotals, lastTotals);
|
|
92
|
+
if (!tokenDelta || isAllZero(tokenDelta)) {
|
|
93
|
+
return { delta: null, messageKey, lastTotals: currentTotals };
|
|
94
|
+
}
|
|
95
|
+
// Extract timestamp from time.completed or time.created
|
|
96
|
+
const time = msg.time;
|
|
97
|
+
const timestampMs = coerceEpochMs(time?.completed) || coerceEpochMs(time?.created);
|
|
98
|
+
if (!timestampMs) {
|
|
99
|
+
return { delta: null, messageKey, lastTotals };
|
|
100
|
+
}
|
|
101
|
+
// Extract model
|
|
102
|
+
const model = typeof msg.modelID === "string"
|
|
103
|
+
? msg.modelID.trim()
|
|
104
|
+
: typeof msg.model === "string"
|
|
105
|
+
? msg.model.trim()
|
|
106
|
+
: "unknown";
|
|
107
|
+
return {
|
|
108
|
+
delta: {
|
|
109
|
+
source: "opencode",
|
|
110
|
+
model,
|
|
111
|
+
timestamp: new Date(timestampMs).toISOString(),
|
|
112
|
+
tokens: tokenDelta,
|
|
113
|
+
},
|
|
114
|
+
messageKey,
|
|
115
|
+
lastTotals: currentTotals,
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
//# sourceMappingURL=opencode.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"opencode.js","sourceRoot":"","sources":["../../src/parsers/opencode.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { CursorState } from "@pew/core";
|
|
2
|
+
/**
|
|
3
|
+
* Persists incremental parsing cursors to disk.
|
|
4
|
+
* Stored at ~/.config/pew/cursors.json
|
|
5
|
+
*/
|
|
6
|
+
export declare class CursorStore {
|
|
7
|
+
readonly filePath: string;
|
|
8
|
+
constructor(storeDir: string);
|
|
9
|
+
/** Load cursor state from disk. Returns empty state if file doesn't exist or is corrupted. */
|
|
10
|
+
load(): Promise<CursorState>;
|
|
11
|
+
/** Save cursor state to disk, creating the directory if needed. */
|
|
12
|
+
save(state: CursorState): Promise<void>;
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=cursor-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cursor-store.d.ts","sourceRoot":"","sources":["../../src/storage/cursor-store.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAS7C;;;GAGG;AACH,qBAAa,WAAW;IACtB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;gBAEd,QAAQ,EAAE,MAAM;IAI5B,8FAA8F;IACxF,IAAI,IAAI,OAAO,CAAC,WAAW,CAAC;IASlC,mEAAmE;IAC7D,IAAI,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;CAK9C"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { readFile, writeFile, mkdir } from "node:fs/promises";
|
|
2
|
+
import { join, dirname } from "node:path";
|
|
3
|
+
const CURSORS_FILE = "cursors.json";
|
|
4
|
+
/** Creates a fresh empty cursor state */
|
|
5
|
+
function emptyCursorState() {
|
|
6
|
+
return { version: 1, files: {}, updatedAt: null };
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Persists incremental parsing cursors to disk.
|
|
10
|
+
* Stored at ~/.config/pew/cursors.json
|
|
11
|
+
*/
|
|
12
|
+
export class CursorStore {
|
|
13
|
+
filePath;
|
|
14
|
+
constructor(storeDir) {
|
|
15
|
+
this.filePath = join(storeDir, CURSORS_FILE);
|
|
16
|
+
}
|
|
17
|
+
/** Load cursor state from disk. Returns empty state if file doesn't exist or is corrupted. */
|
|
18
|
+
async load() {
|
|
19
|
+
try {
|
|
20
|
+
const raw = await readFile(this.filePath, "utf-8");
|
|
21
|
+
return JSON.parse(raw);
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
return emptyCursorState();
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
/** Save cursor state to disk, creating the directory if needed. */
|
|
28
|
+
async save(state) {
|
|
29
|
+
const dir = dirname(this.filePath);
|
|
30
|
+
await mkdir(dir, { recursive: true });
|
|
31
|
+
await writeFile(this.filePath, JSON.stringify(state, null, 2) + "\n");
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=cursor-store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cursor-store.js","sourceRoot":"","sources":["../../src/storage/cursor-store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAC9D,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAG1C,MAAM,YAAY,GAAG,cAAc,CAAC;AAEpC,yCAAyC;AACzC,SAAS,gBAAgB;IACvB,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC;AACpD,CAAC;AAED;;;GAGG;AACH,MAAM,OAAO,WAAW;IACb,QAAQ,CAAS;IAE1B,YAAY,QAAgB;QAC1B,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,QAAQ,EAAE,YAAY,CAAC,CAAC;IAC/C,CAAC;IAED,8FAA8F;IAC9F,KAAK,CAAC,IAAI;QACR,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC,IAAI,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;YACnD,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAgB,CAAC;QACxC,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,gBAAgB,EAAE,CAAC;QAC5B,CAAC;IACH,CAAC;IAED,mEAAmE;IACnE,KAAK,CAAC,IAAI,CAAC,KAAkB;QAC3B,MAAM,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACnC,MAAM,KAAK,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACtC,MAAM,SAAS,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;IACxE,CAAC;CACF"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { QueueRecord } from "@pew/core";
|
|
2
|
+
/**
|
|
3
|
+
* Append-only local queue for usage records.
|
|
4
|
+
* Records are stored as JSONL, and an offset file tracks upload progress.
|
|
5
|
+
*/
|
|
6
|
+
export declare class LocalQueue {
|
|
7
|
+
readonly queuePath: string;
|
|
8
|
+
private readonly statePath;
|
|
9
|
+
private readonly dir;
|
|
10
|
+
constructor(storeDir: string);
|
|
11
|
+
/** Ensure the directory exists */
|
|
12
|
+
private ensureDir;
|
|
13
|
+
/** Append a single record to the queue */
|
|
14
|
+
append(record: QueueRecord): Promise<void>;
|
|
15
|
+
/** Append multiple records to the queue in a single write */
|
|
16
|
+
appendBatch(records: QueueRecord[]): Promise<void>;
|
|
17
|
+
/**
|
|
18
|
+
* Read records from the queue starting at a byte offset.
|
|
19
|
+
* Returns parsed records and the new offset (end of file).
|
|
20
|
+
*/
|
|
21
|
+
readFromOffset(offset: number): Promise<{
|
|
22
|
+
records: QueueRecord[];
|
|
23
|
+
newOffset: number;
|
|
24
|
+
}>;
|
|
25
|
+
/** Save the upload byte offset to the state file */
|
|
26
|
+
saveOffset(offset: number): Promise<void>;
|
|
27
|
+
/** Load the upload byte offset. Returns 0 if not found or corrupted. */
|
|
28
|
+
loadOffset(): Promise<number>;
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=local-queue.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"local-queue.d.ts","sourceRoot":"","sources":["../../src/storage/local-queue.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAK7C;;;GAGG;AACH,qBAAa,UAAU;IACrB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAS;gBAEjB,QAAQ,EAAE,MAAM;IAM5B,kCAAkC;YACpB,SAAS;IAIvB,0CAA0C;IACpC,MAAM,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAKhD,6DAA6D;IACvD,WAAW,CAAC,OAAO,EAAE,WAAW,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAOxD;;;OAGG;IACG,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC;QAC5C,OAAO,EAAE,WAAW,EAAE,CAAC;QACvB,SAAS,EAAE,MAAM,CAAC;KACnB,CAAC;IAgBF,oDAAoD;IAC9C,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAQ/C,wEAAwE;IAClE,UAAU,IAAI,OAAO,CAAC,MAAM,CAAC;CASpC"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { readFile, writeFile, appendFile, mkdir } from "node:fs/promises";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
const QUEUE_FILE = "queue.jsonl";
|
|
4
|
+
const STATE_FILE = "queue.state.json";
|
|
5
|
+
/**
|
|
6
|
+
* Append-only local queue for usage records.
|
|
7
|
+
* Records are stored as JSONL, and an offset file tracks upload progress.
|
|
8
|
+
*/
|
|
9
|
+
export class LocalQueue {
|
|
10
|
+
queuePath;
|
|
11
|
+
statePath;
|
|
12
|
+
dir;
|
|
13
|
+
constructor(storeDir) {
|
|
14
|
+
this.dir = storeDir;
|
|
15
|
+
this.queuePath = join(storeDir, QUEUE_FILE);
|
|
16
|
+
this.statePath = join(storeDir, STATE_FILE);
|
|
17
|
+
}
|
|
18
|
+
/** Ensure the directory exists */
|
|
19
|
+
async ensureDir() {
|
|
20
|
+
await mkdir(this.dir, { recursive: true });
|
|
21
|
+
}
|
|
22
|
+
/** Append a single record to the queue */
|
|
23
|
+
async append(record) {
|
|
24
|
+
await this.ensureDir();
|
|
25
|
+
await appendFile(this.queuePath, JSON.stringify(record) + "\n");
|
|
26
|
+
}
|
|
27
|
+
/** Append multiple records to the queue in a single write */
|
|
28
|
+
async appendBatch(records) {
|
|
29
|
+
if (records.length === 0)
|
|
30
|
+
return;
|
|
31
|
+
await this.ensureDir();
|
|
32
|
+
const data = records.map((r) => JSON.stringify(r)).join("\n") + "\n";
|
|
33
|
+
await appendFile(this.queuePath, data);
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Read records from the queue starting at a byte offset.
|
|
37
|
+
* Returns parsed records and the new offset (end of file).
|
|
38
|
+
*/
|
|
39
|
+
async readFromOffset(offset) {
|
|
40
|
+
let raw;
|
|
41
|
+
try {
|
|
42
|
+
raw = await readFile(this.queuePath, "utf-8");
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
return { records: [], newOffset: 0 };
|
|
46
|
+
}
|
|
47
|
+
const slice = raw.slice(offset);
|
|
48
|
+
const lines = slice.split("\n").filter((line) => line.trim().length > 0);
|
|
49
|
+
const records = lines.map((line) => JSON.parse(line));
|
|
50
|
+
const newOffset = Buffer.byteLength(raw, "utf-8");
|
|
51
|
+
return { records, newOffset };
|
|
52
|
+
}
|
|
53
|
+
/** Save the upload byte offset to the state file */
|
|
54
|
+
async saveOffset(offset) {
|
|
55
|
+
await this.ensureDir();
|
|
56
|
+
await writeFile(this.statePath, JSON.stringify({ offset }) + "\n");
|
|
57
|
+
}
|
|
58
|
+
/** Load the upload byte offset. Returns 0 if not found or corrupted. */
|
|
59
|
+
async loadOffset() {
|
|
60
|
+
try {
|
|
61
|
+
const raw = await readFile(this.statePath, "utf-8");
|
|
62
|
+
const state = JSON.parse(raw);
|
|
63
|
+
return state.offset ?? 0;
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
return 0;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=local-queue.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"local-queue.js","sourceRoot":"","sources":["../../src/storage/local-queue.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { TokenDelta } from "@pew/core";
|
|
2
|
+
/**
|
|
3
|
+
* Floor a timestamp to the nearest half-hour UTC boundary.
|
|
4
|
+
* Returns ISO string like "2026-03-07T10:00:00.000Z" or "2026-03-07T10:30:00.000Z".
|
|
5
|
+
* Returns null if the timestamp is unparseable.
|
|
6
|
+
*/
|
|
7
|
+
export declare function toUtcHalfHourStart(ts: string | number): string | null;
|
|
8
|
+
/**
|
|
9
|
+
* Create a composite bucket key from source, model, and half-hour start.
|
|
10
|
+
* Format: "source|model|hourStart"
|
|
11
|
+
*/
|
|
12
|
+
export declare function bucketKey(source: string, model: string, hourStart: string): string;
|
|
13
|
+
/** Create a TokenDelta with all zeros */
|
|
14
|
+
export declare function emptyTokenDelta(): TokenDelta;
|
|
15
|
+
/** Add delta values into target in place */
|
|
16
|
+
export declare function addTokens(target: TokenDelta, delta: TokenDelta): void;
|
|
17
|
+
//# sourceMappingURL=buckets.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"buckets.d.ts","sourceRoot":"","sources":["../../src/utils/buckets.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAE5C;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,IAAI,CAoBrE;AAED;;;GAGG;AACH,wBAAgB,SAAS,CACvB,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,MAAM,GAChB,MAAM,CAER;AAED,yCAAyC;AACzC,wBAAgB,eAAe,IAAI,UAAU,CAO5C;AAED,4CAA4C;AAC5C,wBAAgB,SAAS,CAAC,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,UAAU,GAAG,IAAI,CAKrE"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Floor a timestamp to the nearest half-hour UTC boundary.
|
|
3
|
+
* Returns ISO string like "2026-03-07T10:00:00.000Z" or "2026-03-07T10:30:00.000Z".
|
|
4
|
+
* Returns null if the timestamp is unparseable.
|
|
5
|
+
*/
|
|
6
|
+
export function toUtcHalfHourStart(ts) {
|
|
7
|
+
const dt = new Date(ts);
|
|
8
|
+
if (!Number.isFinite(dt.getTime()))
|
|
9
|
+
return null;
|
|
10
|
+
const minutes = dt.getUTCMinutes();
|
|
11
|
+
const halfMinute = minutes >= 30 ? 30 : 0;
|
|
12
|
+
const bucketStart = new Date(Date.UTC(dt.getUTCFullYear(), dt.getUTCMonth(), dt.getUTCDate(), dt.getUTCHours(), halfMinute, 0, 0));
|
|
13
|
+
return bucketStart.toISOString();
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Create a composite bucket key from source, model, and half-hour start.
|
|
17
|
+
* Format: "source|model|hourStart"
|
|
18
|
+
*/
|
|
19
|
+
export function bucketKey(source, model, hourStart) {
|
|
20
|
+
return `${source}|${model}|${hourStart}`;
|
|
21
|
+
}
|
|
22
|
+
/** Create a TokenDelta with all zeros */
|
|
23
|
+
export function emptyTokenDelta() {
|
|
24
|
+
return {
|
|
25
|
+
inputTokens: 0,
|
|
26
|
+
cachedInputTokens: 0,
|
|
27
|
+
outputTokens: 0,
|
|
28
|
+
reasoningOutputTokens: 0,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
/** Add delta values into target in place */
|
|
32
|
+
export function addTokens(target, delta) {
|
|
33
|
+
target.inputTokens += delta.inputTokens;
|
|
34
|
+
target.cachedInputTokens += delta.cachedInputTokens;
|
|
35
|
+
target.outputTokens += delta.outputTokens;
|
|
36
|
+
target.reasoningOutputTokens += delta.reasoningOutputTokens;
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=buckets.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"buckets.js","sourceRoot":"","sources":["../../src/utils/buckets.ts"],"names":[],"mappings":"AAEA;;;;GAIG;AACH,MAAM,UAAU,kBAAkB,CAAC,EAAmB;IACpD,MAAM,EAAE,GAAG,IAAI,IAAI,CAAC,EAAE,CAAC,CAAC;IACxB,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC;QAAE,OAAO,IAAI,CAAC;IAEhD,MAAM,OAAO,GAAG,EAAE,CAAC,aAAa,EAAE,CAAC;IACnC,MAAM,UAAU,GAAG,OAAO,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IAE1C,MAAM,WAAW,GAAG,IAAI,IAAI,CAC1B,IAAI,CAAC,GAAG,CACN,EAAE,CAAC,cAAc,EAAE,EACnB,EAAE,CAAC,WAAW,EAAE,EAChB,EAAE,CAAC,UAAU,EAAE,EACf,EAAE,CAAC,WAAW,EAAE,EAChB,UAAU,EACV,CAAC,EACD,CAAC,CACF,CACF,CAAC;IAEF,OAAO,WAAW,CAAC,WAAW,EAAE,CAAC;AACnC,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,SAAS,CACvB,MAAc,EACd,KAAa,EACb,SAAiB;IAEjB,OAAO,GAAG,MAAM,IAAI,KAAK,IAAI,SAAS,EAAE,CAAC;AAC3C,CAAC;AAED,yCAAyC;AACzC,MAAM,UAAU,eAAe;IAC7B,OAAO;QACL,WAAW,EAAE,CAAC;QACd,iBAAiB,EAAE,CAAC;QACpB,YAAY,EAAE,CAAC;QACf,qBAAqB,EAAE,CAAC;KACzB,CAAC;AACJ,CAAC;AAED,4CAA4C;AAC5C,MAAM,UAAU,SAAS,CAAC,MAAkB,EAAE,KAAiB;IAC7D,MAAM,CAAC,WAAW,IAAI,KAAK,CAAC,WAAW,CAAC;IACxC,MAAM,CAAC,iBAAiB,IAAI,KAAK,CAAC,iBAAiB,CAAC;IACpD,MAAM,CAAC,YAAY,IAAI,KAAK,CAAC,YAAY,CAAC;IAC1C,MAAM,CAAC,qBAAqB,IAAI,KAAK,CAAC,qBAAqB,CAAC;AAC9D,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolve default paths for Pew state and AI tool data.
|
|
3
|
+
* All paths can be overridden for testing.
|
|
4
|
+
*/
|
|
5
|
+
export declare function resolveDefaultPaths(home?: string): {
|
|
6
|
+
/** Pew state directory: ~/.config/pew/ */
|
|
7
|
+
stateDir: string;
|
|
8
|
+
/** Claude Code data: ~/.claude */
|
|
9
|
+
claudeDir: string;
|
|
10
|
+
/** Gemini CLI data: ~/.gemini */
|
|
11
|
+
geminiDir: string;
|
|
12
|
+
/** OpenCode message storage: ~/.local/share/opencode/storage/message */
|
|
13
|
+
openCodeMessageDir: string;
|
|
14
|
+
/** OpenClaw data: ~/.openclaw */
|
|
15
|
+
openclawDir: string;
|
|
16
|
+
};
|
|
17
|
+
//# sourceMappingURL=paths.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"paths.d.ts","sourceRoot":"","sources":["../../src/utils/paths.ts"],"names":[],"mappings":"AAGA;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,SAAY;IAEhD,0CAA0C;;IAE1C,kCAAkC;;IAElC,iCAAiC;;IAEjC,wEAAwE;;IASxE,iCAAiC;;EAGpC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { homedir } from "node:os";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
/**
|
|
4
|
+
* Resolve default paths for Pew state and AI tool data.
|
|
5
|
+
* All paths can be overridden for testing.
|
|
6
|
+
*/
|
|
7
|
+
export function resolveDefaultPaths(home = homedir()) {
|
|
8
|
+
return {
|
|
9
|
+
/** Pew state directory: ~/.config/pew/ */
|
|
10
|
+
stateDir: join(home, ".config", "pew"),
|
|
11
|
+
/** Claude Code data: ~/.claude */
|
|
12
|
+
claudeDir: join(home, ".claude"),
|
|
13
|
+
/** Gemini CLI data: ~/.gemini */
|
|
14
|
+
geminiDir: join(home, ".gemini"),
|
|
15
|
+
/** OpenCode message storage: ~/.local/share/opencode/storage/message */
|
|
16
|
+
openCodeMessageDir: join(home, ".local", "share", "opencode", "storage", "message"),
|
|
17
|
+
/** OpenClaw data: ~/.openclaw */
|
|
18
|
+
openclawDir: join(home, ".openclaw"),
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=paths.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"paths.js","sourceRoot":"","sources":["../../src/utils/paths.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC;;;GAGG;AACH,MAAM,UAAU,mBAAmB,CAAC,IAAI,GAAG,OAAO,EAAE;IAClD,OAAO;QACL,0CAA0C;QAC1C,QAAQ,EAAE,IAAI,CAAC,IAAI,EAAE,SAAS,EAAE,KAAK,CAAC;QACtC,kCAAkC;QAClC,SAAS,EAAE,IAAI,CAAC,IAAI,EAAE,SAAS,CAAC;QAChC,iCAAiC;QACjC,SAAS,EAAE,IAAI,CAAC,IAAI,EAAE,SAAS,CAAC;QAChC,wEAAwE;QACxE,kBAAkB,EAAE,IAAI,CACtB,IAAI,EACJ,QAAQ,EACR,OAAO,EACP,UAAU,EACV,SAAS,EACT,SAAS,CACV;QACD,iCAAiC;QACjC,WAAW,EAAE,IAAI,CAAC,IAAI,EAAE,WAAW,CAAC;KACrC,CAAC;AACJ,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@nocoo/pew",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Track token usage from your local AI coding tools",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"pew": "dist/bin.js"
|
|
8
|
+
},
|
|
9
|
+
"main": "./dist/index.js",
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"scripts": {
|
|
12
|
+
"build": "tsc",
|
|
13
|
+
"test": "vitest run",
|
|
14
|
+
"test:watch": "vitest"
|
|
15
|
+
},
|
|
16
|
+
"files": ["dist"],
|
|
17
|
+
"publishConfig": {
|
|
18
|
+
"access": "public"
|
|
19
|
+
},
|
|
20
|
+
"repository": {
|
|
21
|
+
"type": "git",
|
|
22
|
+
"url": "git+https://github.com/nocoo/pew.git",
|
|
23
|
+
"directory": "packages/cli"
|
|
24
|
+
},
|
|
25
|
+
"keywords": [
|
|
26
|
+
"ai",
|
|
27
|
+
"token-usage",
|
|
28
|
+
"claude",
|
|
29
|
+
"codex",
|
|
30
|
+
"gemini",
|
|
31
|
+
"opencode",
|
|
32
|
+
"cli"
|
|
33
|
+
],
|
|
34
|
+
"license": "MIT",
|
|
35
|
+
"dependencies": {
|
|
36
|
+
"citty": "^0.1.0",
|
|
37
|
+
"consola": "^3.4.0",
|
|
38
|
+
"picocolors": "^1.1.0"
|
|
39
|
+
},
|
|
40
|
+
"devDependencies": {
|
|
41
|
+
"@pew/core": "workspace:*",
|
|
42
|
+
"@types/node": "^22.0.0"
|
|
43
|
+
}
|
|
44
|
+
}
|