@rasensio/aidlc 1.2.0 → 1.3.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/LICENSE +21 -0
- package/dist/cli.js +4 -0
- package/dist/cli.js.map +1 -1
- package/dist/commands/claim.d.ts +15 -0
- package/dist/commands/claim.d.ts.map +1 -0
- package/dist/commands/claim.js +122 -0
- package/dist/commands/claim.js.map +1 -0
- package/dist/commands/cost.d.ts +12 -0
- package/dist/commands/cost.d.ts.map +1 -0
- package/dist/commands/cost.js +409 -0
- package/dist/commands/cost.js.map +1 -0
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +38 -2
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/status.d.ts.map +1 -1
- package/dist/commands/status.js +30 -1
- package/dist/commands/status.js.map +1 -1
- package/dist/commands/transition.js +9 -0
- package/dist/commands/transition.js.map +1 -1
- package/dist/core/types.d.ts +8 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/cost/config.d.ts +8 -0
- package/dist/cost/config.d.ts.map +1 -0
- package/dist/cost/config.js +37 -0
- package/dist/cost/config.js.map +1 -0
- package/dist/cost/hooks/claude-code.d.ts +29 -0
- package/dist/cost/hooks/claude-code.d.ts.map +1 -0
- package/dist/cost/hooks/claude-code.js +101 -0
- package/dist/cost/hooks/claude-code.js.map +1 -0
- package/dist/cost/hooks/kiro.d.ts +17 -0
- package/dist/cost/hooks/kiro.d.ts.map +1 -0
- package/dist/cost/hooks/kiro.js +66 -0
- package/dist/cost/hooks/kiro.js.map +1 -0
- package/dist/cost/ledger.d.ts +70 -0
- package/dist/cost/ledger.d.ts.map +1 -0
- package/dist/cost/ledger.js +314 -0
- package/dist/cost/ledger.js.map +1 -0
- package/dist/cost/providers/claude-code.d.ts +20 -0
- package/dist/cost/providers/claude-code.d.ts.map +1 -0
- package/dist/cost/providers/claude-code.js +116 -0
- package/dist/cost/providers/claude-code.js.map +1 -0
- package/dist/cost/providers/kiro.d.ts +19 -0
- package/dist/cost/providers/kiro.d.ts.map +1 -0
- package/dist/cost/providers/kiro.js +147 -0
- package/dist/cost/providers/kiro.js.map +1 -0
- package/dist/cost/providers/provider.d.ts +36 -0
- package/dist/cost/providers/provider.d.ts.map +1 -0
- package/dist/cost/providers/provider.js +21 -0
- package/dist/cost/providers/provider.js.map +1 -0
- package/dist/cost/rates-bundled.d.ts +11 -0
- package/dist/cost/rates-bundled.d.ts.map +1 -0
- package/dist/cost/rates-bundled.js +23 -0
- package/dist/cost/rates-bundled.js.map +1 -0
- package/dist/cost/rates.d.ts +42 -0
- package/dist/cost/rates.d.ts.map +1 -0
- package/dist/cost/rates.js +190 -0
- package/dist/cost/rates.js.map +1 -0
- package/dist/cost/recorder.d.ts +57 -0
- package/dist/cost/recorder.d.ts.map +1 -0
- package/dist/cost/recorder.js +339 -0
- package/dist/cost/recorder.js.map +1 -0
- package/dist/cost/report.d.ts +44 -0
- package/dist/cost/report.d.ts.map +1 -0
- package/dist/cost/report.js +164 -0
- package/dist/cost/report.js.map +1 -0
- package/dist/cost/sessions.d.ts +40 -0
- package/dist/cost/sessions.d.ts.map +1 -0
- package/dist/cost/sessions.js +200 -0
- package/dist/cost/sessions.js.map +1 -0
- package/dist/cost/sync.d.ts +24 -0
- package/dist/cost/sync.d.ts.map +1 -0
- package/dist/cost/sync.js +191 -0
- package/dist/cost/sync.js.map +1 -0
- package/dist/cost/types.d.ts +116 -0
- package/dist/cost/types.d.ts.map +1 -0
- package/dist/cost/types.js +13 -0
- package/dist/cost/types.js.map +1 -0
- package/dist/setup/setup-compute.d.ts +6 -0
- package/dist/setup/setup-compute.d.ts.map +1 -1
- package/dist/setup/setup-compute.js +3 -0
- package/dist/setup/setup-compute.js.map +1 -1
- package/package.json +11 -14
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* kiro hook installer: own-file v1 hooks in .kiro/hooks/aidlc-cost.json.
|
|
3
|
+
*
|
|
4
|
+
* Kiro v1 hooks (IDE 1.0 / CLI 3.0+) are standalone JSON files, so install
|
|
5
|
+
* is a file write and remove is a delete — no merge risk. `Stop` fires per
|
|
6
|
+
* response (no session-end event exists); the recorder's cursor + lock make
|
|
7
|
+
* repeated firings cheap no-ops (design §4.2, D2).
|
|
8
|
+
*
|
|
9
|
+
* Kiro CLI 2.x uses a different hook format and is out of scope for v1.
|
|
10
|
+
*/
|
|
11
|
+
import { readFileSync, writeFileSync, existsSync, unlinkSync, mkdirSync } from 'node:fs';
|
|
12
|
+
import { dirname, join } from 'node:path';
|
|
13
|
+
import { HOOK_MARKER } from './claude-code.js';
|
|
14
|
+
function hookFilePath(projectRoot) {
|
|
15
|
+
return join(projectRoot, '.kiro', 'hooks', 'aidlc-cost.json');
|
|
16
|
+
}
|
|
17
|
+
function quote(p) {
|
|
18
|
+
return `"${p.replace(/"/g, '\\"')}"`;
|
|
19
|
+
}
|
|
20
|
+
export function installKiroHooks(projectRoot, nodePath, cliPath) {
|
|
21
|
+
const base = `${quote(nodePath)} ${quote(cliPath)}`;
|
|
22
|
+
const file = {
|
|
23
|
+
version: 'v1',
|
|
24
|
+
hooks: [
|
|
25
|
+
{
|
|
26
|
+
name: 'aidlc-cost-record',
|
|
27
|
+
trigger: 'Stop',
|
|
28
|
+
action: {
|
|
29
|
+
type: 'command',
|
|
30
|
+
command: `${base} cost record --provider kiro --detach ${HOOK_MARKER}`,
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
name: 'aidlc-cost-session-start',
|
|
35
|
+
trigger: 'SessionStart',
|
|
36
|
+
action: {
|
|
37
|
+
type: 'command',
|
|
38
|
+
command: `${base} cost session-start --provider kiro ${HOOK_MARKER}`,
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
],
|
|
42
|
+
};
|
|
43
|
+
const path = hookFilePath(projectRoot);
|
|
44
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
45
|
+
writeFileSync(path, JSON.stringify(file, null, 2) + '\n');
|
|
46
|
+
}
|
|
47
|
+
export function removeKiroHooks(projectRoot) {
|
|
48
|
+
const path = hookFilePath(projectRoot);
|
|
49
|
+
if (existsSync(path))
|
|
50
|
+
unlinkSync(path);
|
|
51
|
+
}
|
|
52
|
+
export function kiroHookStatus(projectRoot) {
|
|
53
|
+
const path = hookFilePath(projectRoot);
|
|
54
|
+
if (!existsSync(path))
|
|
55
|
+
return { installed: false, commandResolvable: false };
|
|
56
|
+
try {
|
|
57
|
+
const parsed = JSON.parse(readFileSync(path, 'utf8'));
|
|
58
|
+
const cmd = parsed.hooks?.find((h) => h.action?.command?.includes(HOOK_MARKER))?.action?.command;
|
|
59
|
+
const quoted = cmd?.match(/"([^"]+)"\s+"([^"]+)"/);
|
|
60
|
+
return { installed: true, commandResolvable: quoted ? existsSync(quoted[2]) : false };
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
return { installed: false, commandResolvable: false };
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
//# sourceMappingURL=kiro.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"kiro.js","sourceRoot":"","sources":["../../../src/cost/hooks/kiro.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,UAAU,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACzF,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAE/C,SAAS,YAAY,CAAC,WAAmB;IACvC,OAAO,IAAI,CAAC,WAAW,EAAE,OAAO,EAAE,OAAO,EAAE,iBAAiB,CAAC,CAAC;AAChE,CAAC;AAED,SAAS,KAAK,CAAC,CAAS;IACtB,OAAO,IAAI,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,GAAG,CAAC;AACvC,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,WAAmB,EAAE,QAAgB,EAAE,OAAe;IACrF,MAAM,IAAI,GAAG,GAAG,KAAK,CAAC,QAAQ,CAAC,IAAI,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC;IACpD,MAAM,IAAI,GAAG;QACX,OAAO,EAAE,IAAI;QACb,KAAK,EAAE;YACL;gBACE,IAAI,EAAE,mBAAmB;gBACzB,OAAO,EAAE,MAAM;gBACf,MAAM,EAAE;oBACN,IAAI,EAAE,SAAS;oBACf,OAAO,EAAE,GAAG,IAAI,yCAAyC,WAAW,EAAE;iBACvE;aACF;YACD;gBACE,IAAI,EAAE,0BAA0B;gBAChC,OAAO,EAAE,cAAc;gBACvB,MAAM,EAAE;oBACN,IAAI,EAAE,SAAS;oBACf,OAAO,EAAE,GAAG,IAAI,uCAAuC,WAAW,EAAE;iBACrE;aACF;SACF;KACF,CAAC;IACF,MAAM,IAAI,GAAG,YAAY,CAAC,WAAW,CAAC,CAAC;IACvC,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC9C,aAAa,CAAC,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;AAC5D,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,WAAmB;IACjD,MAAM,IAAI,GAAG,YAAY,CAAC,WAAW,CAAC,CAAC;IACvC,IAAI,UAAU,CAAC,IAAI,CAAC;QAAE,UAAU,CAAC,IAAI,CAAC,CAAC;AACzC,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,WAAmB;IAChD,MAAM,IAAI,GAAG,YAAY,CAAC,WAAW,CAAC,CAAC;IACvC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,iBAAiB,EAAE,KAAK,EAAE,CAAC;IAC7E,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAEnD,CAAC;QACF,MAAM,GAAG,GAAG,MAAM,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,EAAE,OAAO,EAAE,QAAQ,CAAC,WAAW,CAAC,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC;QACjG,MAAM,MAAM,GAAG,GAAG,EAAE,KAAK,CAAC,uBAAuB,CAAC,CAAC;QACnD,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,iBAAiB,EAAE,MAAM,CAAC,CAAC,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC;IACxF,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,iBAAiB,EAAE,KAAK,EAAE,CAAC;IACxD,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cost ledger: append-only NDJSON with closed-schema validation.
|
|
3
|
+
*
|
|
4
|
+
* Write semantics mirror transition-log.ts: O_APPEND single-line writes,
|
|
5
|
+
* whole-line atomicity under concurrent writers. Readers skip torn or
|
|
6
|
+
* malformed lines with a stderr warning.
|
|
7
|
+
*
|
|
8
|
+
* Privacy (SR1/FR2.4): validateEntry enforces a closed key set with
|
|
9
|
+
* numeric/enum/identifier values only — free-form text cannot enter the
|
|
10
|
+
* ledger regardless of origin (provider, sync, or --manual).
|
|
11
|
+
*
|
|
12
|
+
* Design: design.md §2.1, §4.5
|
|
13
|
+
*/
|
|
14
|
+
import type { CostEntry, CostFidelity } from './types.js';
|
|
15
|
+
/**
|
|
16
|
+
* Validate a candidate ledger entry against the closed schema.
|
|
17
|
+
*
|
|
18
|
+
* @returns null when valid, otherwise a reason string (safe to log).
|
|
19
|
+
*/
|
|
20
|
+
export declare function validateEntry(entry: unknown): string | null;
|
|
21
|
+
/**
|
|
22
|
+
* Append one validated entry to a ledger file. Throws on invalid entries —
|
|
23
|
+
* callers decide whether that is a logged rejection (hook path) or an error
|
|
24
|
+
* surfaced to the user (manual path).
|
|
25
|
+
*/
|
|
26
|
+
export declare function appendEntry(ledgerPath: string, entry: CostEntry): void;
|
|
27
|
+
/**
|
|
28
|
+
* Read all valid entries from a ledger file. Missing file → empty array.
|
|
29
|
+
* Malformed or schema-invalid lines are skipped with a stderr warning
|
|
30
|
+
* (line number only — never line content, per SR1).
|
|
31
|
+
*/
|
|
32
|
+
export declare function readEntries(ledgerPath: string): CostEntry[];
|
|
33
|
+
/** Ledger path for an instance. */
|
|
34
|
+
export declare function ledgerPath(projectRoot: string, instance: string): string;
|
|
35
|
+
/** Path of the unattributed ledger. */
|
|
36
|
+
export declare function unattributedPath(projectRoot: string): string;
|
|
37
|
+
/**
|
|
38
|
+
* Newest cursor recorded for a session across the given ledger files
|
|
39
|
+
* (instance ledgers plus unattributed — design §2.4). "Newest" is by entry
|
|
40
|
+
* timestamp; ties resolved by later coverage.to.
|
|
41
|
+
*/
|
|
42
|
+
export declare function newestCursor(session_id: string, ledgerPaths: string[]): string | null;
|
|
43
|
+
/** A cost entry with its counted weight after precedence reduction. */
|
|
44
|
+
export interface WeightedEntry {
|
|
45
|
+
entry: CostEntry;
|
|
46
|
+
/** 0..1 — fraction of this entry's units that count toward totals. */
|
|
47
|
+
weight: number;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Apply read-time fidelity precedence: each entry's weight is the fraction
|
|
51
|
+
* of its coverage window not overlapped by higher-fidelity coverage for the
|
|
52
|
+
* same session. `session_id: null` entries always count in full. `unknown`
|
|
53
|
+
* entries carry no units and get weight 0.
|
|
54
|
+
*
|
|
55
|
+
* Linear sweep over per-session sorted window lists — O(n log n).
|
|
56
|
+
*/
|
|
57
|
+
export declare function applyPrecedence(entries: CostEntry[]): WeightedEntry[];
|
|
58
|
+
/** Sum weighted units per model (tokens) and total credits. */
|
|
59
|
+
export interface UsageTotals {
|
|
60
|
+
tokens: Map<string, {
|
|
61
|
+
input: number;
|
|
62
|
+
output: number;
|
|
63
|
+
cacheRead: number;
|
|
64
|
+
cacheWrite: number;
|
|
65
|
+
}>;
|
|
66
|
+
credits: number;
|
|
67
|
+
fidelityWeights: Record<CostFidelity, number>;
|
|
68
|
+
}
|
|
69
|
+
export declare function totalUsage(weighted: WeightedEntry[]): UsageTotals;
|
|
70
|
+
//# sourceMappingURL=ledger.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ledger.d.ts","sourceRoot":"","sources":["../../src/cost/ledger.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAWH,OAAO,KAAK,EAAE,SAAS,EAAE,YAAY,EAAa,MAAM,YAAY,CAAC;AAmDrE;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAmC3D;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,GAAG,IAAI,CAUtE;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,UAAU,EAAE,MAAM,GAAG,SAAS,EAAE,CA6B3D;AAED,mCAAmC;AACnC,wBAAgB,UAAU,CAAC,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAExE;AAED,uCAAuC;AACvC,wBAAgB,gBAAgB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAE5D;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,UAAU,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,GAAG,MAAM,GAAG,IAAI,CAerF;AAiDD,uEAAuE;AACvE,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,SAAS,CAAC;IACjB,sEAAsE;IACtE,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,SAAS,EAAE,GAAG,aAAa,EAAE,CA8CrE;AAED,+DAA+D;AAC/D,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC9F,OAAO,EAAE,MAAM,CAAC;IAChB,eAAe,EAAE,MAAM,CAAC,YAAY,EAAE,MAAM,CAAC,CAAC;CAC/C;AAED,wBAAgB,UAAU,CAAC,QAAQ,EAAE,aAAa,EAAE,GAAG,WAAW,CAsBjE"}
|
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cost ledger: append-only NDJSON with closed-schema validation.
|
|
3
|
+
*
|
|
4
|
+
* Write semantics mirror transition-log.ts: O_APPEND single-line writes,
|
|
5
|
+
* whole-line atomicity under concurrent writers. Readers skip torn or
|
|
6
|
+
* malformed lines with a stderr warning.
|
|
7
|
+
*
|
|
8
|
+
* Privacy (SR1/FR2.4): validateEntry enforces a closed key set with
|
|
9
|
+
* numeric/enum/identifier values only — free-form text cannot enter the
|
|
10
|
+
* ledger regardless of origin (provider, sync, or --manual).
|
|
11
|
+
*
|
|
12
|
+
* Design: design.md §2.1, §4.5
|
|
13
|
+
*/
|
|
14
|
+
import { readFileSync, openSync, writeSync, closeSync, constants, mkdirSync, } from 'node:fs';
|
|
15
|
+
import { dirname, join } from 'node:path';
|
|
16
|
+
import { SESSION_ID_RE, MODEL_ID_RE } from './types.js';
|
|
17
|
+
const FIDELITIES = ['metered', 'reconciled', 'estimated', 'unknown'];
|
|
18
|
+
const PROVIDER_RE = /^[a-z][a-z0-9-]{0,31}$/;
|
|
19
|
+
const PHASE_RE = /^[a-z][a-z0-9-]{0,31}$/;
|
|
20
|
+
const CURSOR_RE = /^[A-Za-z0-9_.:/@-]{1,256}$/;
|
|
21
|
+
const ENTRY_KEYS = new Set([
|
|
22
|
+
'timestamp', 'session_id', 'phase', 'provider', 'fidelity', 'coverage', 'cursor', 'units',
|
|
23
|
+
]);
|
|
24
|
+
const TOKEN_KEYS = new Set([
|
|
25
|
+
'kind', 'model', 'input_tokens', 'output_tokens', 'cache_read_tokens', 'cache_write_tokens',
|
|
26
|
+
]);
|
|
27
|
+
const CREDIT_KEYS = new Set(['kind', 'credits', 'model']);
|
|
28
|
+
function isIso8601(v) {
|
|
29
|
+
return typeof v === 'string' && !Number.isNaN(Date.parse(v)) && /^\d{4}-\d{2}-\d{2}T/.test(v);
|
|
30
|
+
}
|
|
31
|
+
function isCount(v) {
|
|
32
|
+
return typeof v === 'number' && Number.isFinite(v) && v >= 0;
|
|
33
|
+
}
|
|
34
|
+
function keysWithin(obj, allowed) {
|
|
35
|
+
return Object.keys(obj).every((k) => allowed.has(k));
|
|
36
|
+
}
|
|
37
|
+
function validateUnits(units) {
|
|
38
|
+
if (units === null)
|
|
39
|
+
return null;
|
|
40
|
+
if (typeof units !== 'object' || Array.isArray(units))
|
|
41
|
+
return 'units must be an object or null';
|
|
42
|
+
const u = units;
|
|
43
|
+
if (u.kind === 'tokens') {
|
|
44
|
+
if (!keysWithin(u, TOKEN_KEYS))
|
|
45
|
+
return 'unknown key in token units';
|
|
46
|
+
if (typeof u.model !== 'string' || !MODEL_ID_RE.test(u.model))
|
|
47
|
+
return 'invalid model id';
|
|
48
|
+
for (const k of ['input_tokens', 'output_tokens', 'cache_read_tokens', 'cache_write_tokens']) {
|
|
49
|
+
if (!isCount(u[k]))
|
|
50
|
+
return `invalid ${k}`;
|
|
51
|
+
}
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
if (u.kind === 'credits') {
|
|
55
|
+
if (!keysWithin(u, CREDIT_KEYS))
|
|
56
|
+
return 'unknown key in credit units';
|
|
57
|
+
if (!isCount(u.credits))
|
|
58
|
+
return 'invalid credits';
|
|
59
|
+
if (u.model !== null && (typeof u.model !== 'string' || !MODEL_ID_RE.test(u.model))) {
|
|
60
|
+
return 'invalid model id';
|
|
61
|
+
}
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
return 'units.kind must be tokens or credits';
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Validate a candidate ledger entry against the closed schema.
|
|
68
|
+
*
|
|
69
|
+
* @returns null when valid, otherwise a reason string (safe to log).
|
|
70
|
+
*/
|
|
71
|
+
export function validateEntry(entry) {
|
|
72
|
+
if (typeof entry !== 'object' || entry === null || Array.isArray(entry)) {
|
|
73
|
+
return 'entry must be an object';
|
|
74
|
+
}
|
|
75
|
+
const e = entry;
|
|
76
|
+
if (!keysWithin(e, ENTRY_KEYS))
|
|
77
|
+
return 'unknown key in entry';
|
|
78
|
+
for (const k of ENTRY_KEYS) {
|
|
79
|
+
if (!(k in e))
|
|
80
|
+
return `missing key ${k}`;
|
|
81
|
+
}
|
|
82
|
+
if (!isIso8601(e.timestamp))
|
|
83
|
+
return 'invalid timestamp';
|
|
84
|
+
if (e.session_id !== null && (typeof e.session_id !== 'string' || !SESSION_ID_RE.test(e.session_id))) {
|
|
85
|
+
return 'invalid session_id';
|
|
86
|
+
}
|
|
87
|
+
if (e.phase !== null && (typeof e.phase !== 'string' || !PHASE_RE.test(e.phase))) {
|
|
88
|
+
return 'invalid phase';
|
|
89
|
+
}
|
|
90
|
+
if (typeof e.provider !== 'string' || !PROVIDER_RE.test(e.provider))
|
|
91
|
+
return 'invalid provider';
|
|
92
|
+
if (!FIDELITIES.includes(e.fidelity))
|
|
93
|
+
return 'invalid fidelity';
|
|
94
|
+
const cov = e.coverage;
|
|
95
|
+
if (typeof cov !== 'object' || cov === null || !keysWithin(cov, new Set(['from', 'to']))) {
|
|
96
|
+
return 'invalid coverage';
|
|
97
|
+
}
|
|
98
|
+
if (!isIso8601(cov.from) || !isIso8601(cov.to))
|
|
99
|
+
return 'invalid coverage timestamps';
|
|
100
|
+
if (Date.parse(cov.from) > Date.parse(cov.to)) {
|
|
101
|
+
return 'coverage.from after coverage.to';
|
|
102
|
+
}
|
|
103
|
+
if (e.cursor !== null && (typeof e.cursor !== 'string' || !CURSOR_RE.test(e.cursor))) {
|
|
104
|
+
return 'invalid cursor';
|
|
105
|
+
}
|
|
106
|
+
const unitsErr = validateUnits(e.units);
|
|
107
|
+
if (unitsErr)
|
|
108
|
+
return unitsErr;
|
|
109
|
+
if ((e.units === null) !== (e.fidelity === 'unknown')) {
|
|
110
|
+
return 'units null iff fidelity unknown';
|
|
111
|
+
}
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Append one validated entry to a ledger file. Throws on invalid entries —
|
|
116
|
+
* callers decide whether that is a logged rejection (hook path) or an error
|
|
117
|
+
* surfaced to the user (manual path).
|
|
118
|
+
*/
|
|
119
|
+
export function appendEntry(ledgerPath, entry) {
|
|
120
|
+
const err = validateEntry(entry);
|
|
121
|
+
if (err)
|
|
122
|
+
throw new Error(`invalid cost entry: ${err}`);
|
|
123
|
+
mkdirSync(dirname(ledgerPath), { recursive: true });
|
|
124
|
+
const fd = openSync(ledgerPath, constants.O_WRONLY | constants.O_APPEND | constants.O_CREAT);
|
|
125
|
+
try {
|
|
126
|
+
writeSync(fd, JSON.stringify(entry) + '\n');
|
|
127
|
+
}
|
|
128
|
+
finally {
|
|
129
|
+
closeSync(fd);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Read all valid entries from a ledger file. Missing file → empty array.
|
|
134
|
+
* Malformed or schema-invalid lines are skipped with a stderr warning
|
|
135
|
+
* (line number only — never line content, per SR1).
|
|
136
|
+
*/
|
|
137
|
+
export function readEntries(ledgerPath) {
|
|
138
|
+
let raw;
|
|
139
|
+
try {
|
|
140
|
+
raw = readFileSync(ledgerPath, 'utf8');
|
|
141
|
+
}
|
|
142
|
+
catch (err) {
|
|
143
|
+
if (err.code === 'ENOENT')
|
|
144
|
+
return [];
|
|
145
|
+
throw err;
|
|
146
|
+
}
|
|
147
|
+
const entries = [];
|
|
148
|
+
const lines = raw.split('\n');
|
|
149
|
+
let skipped = 0;
|
|
150
|
+
for (let i = 0; i < lines.length; i++) {
|
|
151
|
+
const line = lines[i].trim();
|
|
152
|
+
if (line === '')
|
|
153
|
+
continue;
|
|
154
|
+
try {
|
|
155
|
+
const parsed = JSON.parse(line);
|
|
156
|
+
if (validateEntry(parsed) === null) {
|
|
157
|
+
entries.push(parsed);
|
|
158
|
+
}
|
|
159
|
+
else {
|
|
160
|
+
skipped++;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
catch {
|
|
164
|
+
skipped++;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
if (skipped > 0) {
|
|
168
|
+
process.stderr.write(`${ledgerPath}: skipped ${skipped} invalid line(s)\n`);
|
|
169
|
+
}
|
|
170
|
+
return entries;
|
|
171
|
+
}
|
|
172
|
+
/** Ledger path for an instance. */
|
|
173
|
+
export function ledgerPath(projectRoot, instance) {
|
|
174
|
+
return join(projectRoot, '.aidlc', 'state', instance, 'costs.ndjson');
|
|
175
|
+
}
|
|
176
|
+
/** Path of the unattributed ledger. */
|
|
177
|
+
export function unattributedPath(projectRoot) {
|
|
178
|
+
return join(projectRoot, '.aidlc', 'cost', 'unattributed.ndjson');
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Newest cursor recorded for a session across the given ledger files
|
|
182
|
+
* (instance ledgers plus unattributed — design §2.4). "Newest" is by entry
|
|
183
|
+
* timestamp; ties resolved by later coverage.to.
|
|
184
|
+
*/
|
|
185
|
+
export function newestCursor(session_id, ledgerPaths) {
|
|
186
|
+
let best = null;
|
|
187
|
+
for (const p of ledgerPaths) {
|
|
188
|
+
for (const e of readEntries(p)) {
|
|
189
|
+
if (e.session_id !== session_id || e.cursor === null)
|
|
190
|
+
continue;
|
|
191
|
+
if (best === null ||
|
|
192
|
+
Date.parse(e.timestamp) > Date.parse(best.timestamp) ||
|
|
193
|
+
(e.timestamp === best.timestamp && Date.parse(e.coverage.to) > Date.parse(best.coverage.to))) {
|
|
194
|
+
best = e;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
return best?.cursor ?? null;
|
|
199
|
+
}
|
|
200
|
+
// ---------------------------------------------------------------------------
|
|
201
|
+
// Read-time fidelity precedence (design §4.5)
|
|
202
|
+
// ---------------------------------------------------------------------------
|
|
203
|
+
const FIDELITY_RANK = {
|
|
204
|
+
metered: 3,
|
|
205
|
+
reconciled: 2,
|
|
206
|
+
estimated: 1,
|
|
207
|
+
unknown: 0,
|
|
208
|
+
};
|
|
209
|
+
/** Merge windows into a sorted, non-overlapping list. */
|
|
210
|
+
function mergeWindows(windows) {
|
|
211
|
+
const sorted = [...windows].sort((a, b) => a.from - b.from);
|
|
212
|
+
const out = [];
|
|
213
|
+
for (const w of sorted) {
|
|
214
|
+
const last = out[out.length - 1];
|
|
215
|
+
if (last && w.from <= last.to) {
|
|
216
|
+
last.to = Math.max(last.to, w.to);
|
|
217
|
+
}
|
|
218
|
+
else {
|
|
219
|
+
out.push({ ...w });
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
return out;
|
|
223
|
+
}
|
|
224
|
+
/** Fraction of [from, to] NOT overlapped by any window in `covered`. */
|
|
225
|
+
function uncoveredFraction(from, to, covered) {
|
|
226
|
+
const span = to - from;
|
|
227
|
+
if (span <= 0) {
|
|
228
|
+
// Zero-length coverage: counted fully unless a window contains the point.
|
|
229
|
+
return covered.some((w) => w.from <= from && from <= w.to) ? 0 : 1;
|
|
230
|
+
}
|
|
231
|
+
let overlapped = 0;
|
|
232
|
+
for (const w of covered) {
|
|
233
|
+
const lo = Math.max(from, w.from);
|
|
234
|
+
const hi = Math.min(to, w.to);
|
|
235
|
+
if (hi > lo)
|
|
236
|
+
overlapped += hi - lo;
|
|
237
|
+
}
|
|
238
|
+
return Math.max(0, (span - overlapped) / span);
|
|
239
|
+
}
|
|
240
|
+
/**
|
|
241
|
+
* Apply read-time fidelity precedence: each entry's weight is the fraction
|
|
242
|
+
* of its coverage window not overlapped by higher-fidelity coverage for the
|
|
243
|
+
* same session. `session_id: null` entries always count in full. `unknown`
|
|
244
|
+
* entries carry no units and get weight 0.
|
|
245
|
+
*
|
|
246
|
+
* Linear sweep over per-session sorted window lists — O(n log n).
|
|
247
|
+
*/
|
|
248
|
+
export function applyPrecedence(entries) {
|
|
249
|
+
const bySession = new Map();
|
|
250
|
+
const out = [];
|
|
251
|
+
for (const e of entries) {
|
|
252
|
+
if (e.units === null) {
|
|
253
|
+
out.push({ entry: e, weight: 0 });
|
|
254
|
+
continue;
|
|
255
|
+
}
|
|
256
|
+
if (e.session_id === null) {
|
|
257
|
+
out.push({ entry: e, weight: 1 });
|
|
258
|
+
continue;
|
|
259
|
+
}
|
|
260
|
+
const list = bySession.get(e.session_id) ?? [];
|
|
261
|
+
list.push(e);
|
|
262
|
+
bySession.set(e.session_id, list);
|
|
263
|
+
}
|
|
264
|
+
for (const list of bySession.values()) {
|
|
265
|
+
// Pre-merge windows per fidelity rank for this session.
|
|
266
|
+
const windowsByRank = new Map();
|
|
267
|
+
for (const e of list) {
|
|
268
|
+
const rank = FIDELITY_RANK[e.fidelity];
|
|
269
|
+
const w = windowsByRank.get(rank) ?? [];
|
|
270
|
+
w.push({ from: Date.parse(e.coverage.from), to: Date.parse(e.coverage.to) });
|
|
271
|
+
windowsByRank.set(rank, w);
|
|
272
|
+
}
|
|
273
|
+
const mergedByRank = new Map();
|
|
274
|
+
for (const [rank, ws] of windowsByRank)
|
|
275
|
+
mergedByRank.set(rank, mergeWindows(ws));
|
|
276
|
+
for (const e of list) {
|
|
277
|
+
const rank = FIDELITY_RANK[e.fidelity];
|
|
278
|
+
const higher = [];
|
|
279
|
+
for (const [r, ws] of mergedByRank) {
|
|
280
|
+
if (r > rank)
|
|
281
|
+
higher.push(...ws);
|
|
282
|
+
}
|
|
283
|
+
const weight = uncoveredFraction(Date.parse(e.coverage.from), Date.parse(e.coverage.to), mergeWindows(higher));
|
|
284
|
+
out.push({ entry: e, weight });
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
return out;
|
|
288
|
+
}
|
|
289
|
+
export function totalUsage(weighted) {
|
|
290
|
+
const totals = {
|
|
291
|
+
tokens: new Map(),
|
|
292
|
+
credits: 0,
|
|
293
|
+
fidelityWeights: { metered: 0, reconciled: 0, estimated: 0, unknown: 0 },
|
|
294
|
+
};
|
|
295
|
+
for (const { entry, weight } of weighted) {
|
|
296
|
+
if (weight === 0 || entry.units === null)
|
|
297
|
+
continue;
|
|
298
|
+
totals.fidelityWeights[entry.fidelity] += weight;
|
|
299
|
+
const u = entry.units;
|
|
300
|
+
if (u.kind === 'tokens') {
|
|
301
|
+
const t = totals.tokens.get(u.model) ?? { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 };
|
|
302
|
+
t.input += u.input_tokens * weight;
|
|
303
|
+
t.output += u.output_tokens * weight;
|
|
304
|
+
t.cacheRead += u.cache_read_tokens * weight;
|
|
305
|
+
t.cacheWrite += u.cache_write_tokens * weight;
|
|
306
|
+
totals.tokens.set(u.model, t);
|
|
307
|
+
}
|
|
308
|
+
else {
|
|
309
|
+
totals.credits += u.credits * weight;
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
return totals;
|
|
313
|
+
}
|
|
314
|
+
//# sourceMappingURL=ledger.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ledger.js","sourceRoot":"","sources":["../../src/cost/ledger.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* claude-code cost provider: transcript JSONL parsing since a cursor.
|
|
3
|
+
*
|
|
4
|
+
* Transcripts live at ~/.claude/projects/<project-slug>/<session-id>.jsonl
|
|
5
|
+
* (CLAUDE_CONFIG_DIR respected). The entry format is internal to Claude Code
|
|
6
|
+
* and may change between versions — extraction is fail-soft: anything that
|
|
7
|
+
* doesn't match the expected shape is skipped and counted, never fatal (D1).
|
|
8
|
+
*
|
|
9
|
+
* Cursor: last processed entry uuid. Resume/compaction append to the same
|
|
10
|
+
* file, so uuid-based resumption is safe; if the cursor uuid is absent
|
|
11
|
+
* (e.g. /branch copied the file), extraction falls back to the beginning
|
|
12
|
+
* and the recorder's coverage dedup prevents double counting.
|
|
13
|
+
*
|
|
14
|
+
* Design: design.md §3
|
|
15
|
+
*/
|
|
16
|
+
import type { CostProvider, ProviderEnv } from './provider.js';
|
|
17
|
+
/** Root directory that transcripts must live under (FR3.9). */
|
|
18
|
+
export declare function transcriptRoot(env: ProviderEnv): string;
|
|
19
|
+
export declare const claudeCodeProvider: CostProvider;
|
|
20
|
+
//# sourceMappingURL=claude-code.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claude-code.d.ts","sourceRoot":"","sources":["../../../src/cost/providers/claude-code.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAQH,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAE/D,+DAA+D;AAC/D,wBAAgB,cAAc,CAAC,GAAG,EAAE,WAAW,GAAG,MAAM,CAIvD;AAMD,eAAO,MAAM,kBAAkB,EAAE,YAmFhC,CAAC"}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* claude-code cost provider: transcript JSONL parsing since a cursor.
|
|
3
|
+
*
|
|
4
|
+
* Transcripts live at ~/.claude/projects/<project-slug>/<session-id>.jsonl
|
|
5
|
+
* (CLAUDE_CONFIG_DIR respected). The entry format is internal to Claude Code
|
|
6
|
+
* and may change between versions — extraction is fail-soft: anything that
|
|
7
|
+
* doesn't match the expected shape is skipped and counted, never fatal (D1).
|
|
8
|
+
*
|
|
9
|
+
* Cursor: last processed entry uuid. Resume/compaction append to the same
|
|
10
|
+
* file, so uuid-based resumption is safe; if the cursor uuid is absent
|
|
11
|
+
* (e.g. /branch copied the file), extraction falls back to the beginning
|
|
12
|
+
* and the recorder's coverage dedup prevents double counting.
|
|
13
|
+
*
|
|
14
|
+
* Design: design.md §3
|
|
15
|
+
*/
|
|
16
|
+
import { createReadStream, existsSync, realpathSync } from 'node:fs';
|
|
17
|
+
import { createInterface } from 'node:readline';
|
|
18
|
+
import { homedir } from 'node:os';
|
|
19
|
+
import { join, resolve, sep } from 'node:path';
|
|
20
|
+
import { SESSION_ID_RE, MODEL_ID_RE } from '../types.js';
|
|
21
|
+
/** Root directory that transcripts must live under (FR3.9). */
|
|
22
|
+
export function transcriptRoot(env) {
|
|
23
|
+
const configDir = process.env.CLAUDE_CONFIG_DIR;
|
|
24
|
+
const base = configDir && configDir.trim() !== '' ? configDir : join(env.home ?? homedir(), '.claude');
|
|
25
|
+
return join(base, 'projects');
|
|
26
|
+
}
|
|
27
|
+
function isCount(v) {
|
|
28
|
+
return typeof v === 'number' && Number.isFinite(v) && v >= 0;
|
|
29
|
+
}
|
|
30
|
+
export const claudeCodeProvider = {
|
|
31
|
+
name: 'claude-code',
|
|
32
|
+
detect(env) {
|
|
33
|
+
return existsSync(transcriptRoot(env));
|
|
34
|
+
},
|
|
35
|
+
resolveSource(input, env) {
|
|
36
|
+
if (!SESSION_ID_RE.test(input.session_id))
|
|
37
|
+
return null;
|
|
38
|
+
if (!input.transcript_path)
|
|
39
|
+
return null;
|
|
40
|
+
let real;
|
|
41
|
+
try {
|
|
42
|
+
real = realpathSync(resolve(input.transcript_path));
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
let root;
|
|
48
|
+
try {
|
|
49
|
+
root = realpathSync(transcriptRoot(env));
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
if (real !== root && !real.startsWith(root + sep))
|
|
55
|
+
return null;
|
|
56
|
+
if (!real.endsWith('.jsonl'))
|
|
57
|
+
return null;
|
|
58
|
+
return { provider: 'claude-code', path: real };
|
|
59
|
+
},
|
|
60
|
+
async extract(source, since) {
|
|
61
|
+
const segments = [];
|
|
62
|
+
const rl = createInterface({
|
|
63
|
+
input: createReadStream(source.path, { encoding: 'utf8' }),
|
|
64
|
+
crlfDelay: Infinity,
|
|
65
|
+
});
|
|
66
|
+
// When a cursor is set, skip lines until we pass the entry carrying it.
|
|
67
|
+
// Cursor uuid absent from the file → seenCursor stays false and we emit
|
|
68
|
+
// everything; the recorder's coverage dedup handles the overlap.
|
|
69
|
+
let skipping = since !== null;
|
|
70
|
+
const all = [];
|
|
71
|
+
for await (const line of rl) {
|
|
72
|
+
const t = line.trim();
|
|
73
|
+
if (t === '')
|
|
74
|
+
continue;
|
|
75
|
+
let entry;
|
|
76
|
+
try {
|
|
77
|
+
entry = JSON.parse(t);
|
|
78
|
+
}
|
|
79
|
+
catch {
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
const uuid = typeof entry.uuid === 'string' ? entry.uuid : null;
|
|
83
|
+
const message = entry.message;
|
|
84
|
+
const usage = message?.usage;
|
|
85
|
+
const model = typeof message?.model === 'string' ? message.model : null;
|
|
86
|
+
const at = typeof entry.timestamp === 'string' && !Number.isNaN(Date.parse(entry.timestamp))
|
|
87
|
+
? entry.timestamp
|
|
88
|
+
: null;
|
|
89
|
+
if (usage && model && MODEL_ID_RE.test(model) && at && uuid) {
|
|
90
|
+
const units = {
|
|
91
|
+
kind: 'tokens',
|
|
92
|
+
model,
|
|
93
|
+
input_tokens: isCount(usage.input_tokens) ? usage.input_tokens : 0,
|
|
94
|
+
output_tokens: isCount(usage.output_tokens) ? usage.output_tokens : 0,
|
|
95
|
+
cache_read_tokens: isCount(usage.cache_read_input_tokens) ? usage.cache_read_input_tokens : 0,
|
|
96
|
+
cache_write_tokens: isCount(usage.cache_creation_input_tokens) ? usage.cache_creation_input_tokens : 0,
|
|
97
|
+
};
|
|
98
|
+
const seg = { at, units, cursor: uuid };
|
|
99
|
+
if (skipping)
|
|
100
|
+
all.push(seg);
|
|
101
|
+
else
|
|
102
|
+
segments.push(seg);
|
|
103
|
+
}
|
|
104
|
+
if (skipping && uuid === since) {
|
|
105
|
+
// Found the cursor: everything collected so far precedes it — drop it.
|
|
106
|
+
skipping = false;
|
|
107
|
+
all.length = 0;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
// Cursor never found: fall back to emitting everything (recorder dedups).
|
|
111
|
+
if (skipping)
|
|
112
|
+
return all;
|
|
113
|
+
return segments;
|
|
114
|
+
},
|
|
115
|
+
};
|
|
116
|
+
//# sourceMappingURL=claude-code.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claude-code.js","sourceRoot":"","sources":["../../../src/cost/providers/claude-code.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* kiro cost provider: per-turn credit usage from local session files.
|
|
3
|
+
*
|
|
4
|
+
* Two on-disk shapes (both undocumented — fail-soft everywhere, D2):
|
|
5
|
+
* - IDE: ~/.kiro/sessions/<workspace-hash>/sess_<uuid>/messages.jsonl with
|
|
6
|
+
* usage_summary events: promptTurnSummaries[].usage (credits, fractional).
|
|
7
|
+
* session.json alongside carries workspacePaths for project matching.
|
|
8
|
+
* - CLI: ~/.kiro/sessions/cli/<uuid>.json with
|
|
9
|
+
* session_state.conversation_metadata.user_turn_metadatas[].metering_usage.
|
|
10
|
+
*
|
|
11
|
+
* Cursor: "<n>" — count of usage events already consumed from the file
|
|
12
|
+
* (files are append-only per session).
|
|
13
|
+
*
|
|
14
|
+
* Design: design.md §3, D2
|
|
15
|
+
*/
|
|
16
|
+
import type { CostProvider, ProviderEnv } from './provider.js';
|
|
17
|
+
export declare function kiroSessionsRoot(env: ProviderEnv): string;
|
|
18
|
+
export declare const kiroProvider: CostProvider;
|
|
19
|
+
//# sourceMappingURL=kiro.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"kiro.d.ts","sourceRoot":"","sources":["../../../src/cost/providers/kiro.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAOH,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAE/D,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,WAAW,GAAG,MAAM,CAEzD;AA4ED,eAAO,MAAM,YAAY,EAAE,YAoD1B,CAAC"}
|