@adecore/agents 0.21.1 → 0.21.2
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/async.js +3 -8
- package/dist/chat/chat-core.d.ts +2 -2
- package/dist/chat/chat-core.js +4 -4
- package/dist/chat/chat-log.js +2 -2
- package/dist/chat/chat-store.js +4 -7
- package/dist/chat/chat-title.js +4 -8
- package/dist/chat/claude-backend.d.ts +1 -0
- package/dist/chat/claude-backend.js +8 -6
- package/dist/chat/claude-title.js +1 -1
- package/dist/chat/codex-backend.d.ts +2 -2
- package/dist/chat/codex-backend.js +5 -8
- package/dist/chat/codex-protocol.js +1 -1
- package/dist/chat/codex-thread.js +4 -5
- package/dist/chat/composer-preferences.js +2 -3
- package/dist/chat/limit-resume.js +2 -7
- package/dist/chat/visual-store.js +8 -6
- package/dist/context/refusal.js +3 -7
- package/dist/context/verb.js +1 -5
- package/dist/host/agent-host.js +2 -2
- package/dist/host/handlers.js +0 -1
- package/dist/host/wiring.js +1 -1
- package/dist/lineage.js +2 -3
- package/dist/messages/deliver-message.js +6 -11
- package/dist/messages/deliver-notice.js +8 -12
- package/dist/messages/notice-store.js +3 -5
- package/dist/outbox/outbox-worker.js +5 -2
- package/dist/providers/accounts/service.d.ts +2 -1
- package/dist/providers/accounts/service.js +11 -13
- package/dist/providers/claude.js +0 -1
- package/dist/providers/detect.js +2 -10
- package/dist/record-directory.js +0 -1
- package/dist/tasks/task-coordinator.d.ts +1 -0
- package/dist/tasks/task-coordinator.js +34 -34
- package/dist/tasks/task-store.js +1 -1
- package/dist/tasks/task-work.js +2 -2
- package/dist/tasks/waiting-child.js +5 -3
- package/dist/tasks/wake-parent.js +15 -10
- package/dist/tasks/wiring.js +32 -37
- package/dist/usage/aggregate.js +62 -46
- package/dist/usage/exchange.js +1 -1
- package/dist/usage/limits/monitor.d.ts +2 -2
- package/dist/usage/limits/monitor.js +2 -2
- package/dist/usage/limits/probe.js +1 -1
- package/dist/usage/pricing.js +1 -1
- package/dist/usage/roots.js +1 -2
- package/dist/usage/scanner.js +7 -9
- package/dist/usage/usage-service.d.ts +1 -2
- package/dist/usage/usage-service.js +3 -6
- package/package.json +3 -3
- package/src/async.ts +3 -9
- package/src/chat/chat-core.ts +4 -4
- package/src/chat/chat-log.ts +2 -2
- package/src/chat/chat-store.ts +4 -8
- package/src/chat/chat-title.ts +4 -8
- package/src/chat/claude-backend.ts +9 -6
- package/src/chat/claude-title.ts +1 -1
- package/src/chat/claude-transcript.ts +1 -1
- package/src/chat/codex-backend.ts +5 -8
- package/src/chat/codex-protocol.ts +1 -1
- package/src/chat/codex-thread.ts +6 -5
- package/src/chat/composer-preferences.ts +2 -3
- package/src/chat/limit-resume.ts +2 -7
- package/src/chat/thread.ts +1 -1
- package/src/chat/visual-store.ts +9 -6
- package/src/chat/wake-chat.ts +4 -1
- package/src/context/refusal.ts +3 -7
- package/src/context/verb.ts +1 -5
- package/src/host/agent-host.ts +2 -2
- package/src/host/handlers.ts +0 -1
- package/src/host/wiring.ts +1 -1
- package/src/lineage.ts +2 -3
- package/src/messages/deliver-message.ts +6 -11
- package/src/messages/deliver-notice.ts +8 -12
- package/src/messages/notice-store.ts +3 -5
- package/src/outbox/outbox-worker.ts +5 -2
- package/src/providers/accounts/service.ts +12 -13
- package/src/providers/accounts/variables.ts +1 -0
- package/src/providers/claude.ts +0 -1
- package/src/providers/detect.ts +2 -11
- package/src/record-directory.ts +0 -1
- package/src/skills.ts +0 -1
- package/src/tasks/task-coordinator.ts +35 -33
- package/src/tasks/task-store.ts +1 -1
- package/src/tasks/task-work.ts +2 -2
- package/src/tasks/waiting-child.ts +6 -3
- package/src/tasks/wake-parent.ts +21 -16
- package/src/tasks/wiring.ts +33 -41
- package/src/usage/aggregate.ts +80 -50
- package/src/usage/exchange.ts +1 -1
- package/src/usage/limits/monitor.ts +4 -4
- package/src/usage/limits/probe.ts +2 -2
- package/src/usage/pricing.ts +1 -1
- package/src/usage/roots.ts +1 -2
- package/src/usage/scanner.ts +8 -10
- package/src/usage/usage-service.ts +5 -9
package/src/usage/aggregate.ts
CHANGED
|
@@ -9,7 +9,7 @@ import type {
|
|
|
9
9
|
UsageSummaryPayload,
|
|
10
10
|
UsageTotals
|
|
11
11
|
} from '@adecore/agent-contracts';
|
|
12
|
-
import { cacheSavingsOf, costOf, type PriceBook } from './pricing.ts';
|
|
12
|
+
import { cacheSavingsOf, costOf, type ModelPrice, type PriceBook, type PriceLookup } from './pricing.ts';
|
|
13
13
|
import { ProjectResolver, type KnownProject } from './projects.ts';
|
|
14
14
|
import type { UsageRecord } from './record.ts';
|
|
15
15
|
|
|
@@ -80,6 +80,75 @@ interface ProjectAccumulator {
|
|
|
80
80
|
costUsd: number;
|
|
81
81
|
}
|
|
82
82
|
|
|
83
|
+
interface ModelRow extends ModelAccumulator {
|
|
84
|
+
/* The basis of the first record of a model prices the whole row: a model has one price. */
|
|
85
|
+
basis: UsagePriceBasis;
|
|
86
|
+
pricedAs: string | null;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function foldBucket(
|
|
90
|
+
buckets: Map<string, BucketAccumulator>,
|
|
91
|
+
slot: string,
|
|
92
|
+
record: UsageRecord,
|
|
93
|
+
account: string | null,
|
|
94
|
+
price: ModelPrice | null,
|
|
95
|
+
cost: number
|
|
96
|
+
): void {
|
|
97
|
+
const key = `${slot}\0${record.provider}\0${record.model}\0${account ?? ''}`;
|
|
98
|
+
let bucket = buckets.get(key);
|
|
99
|
+
if (bucket === undefined) {
|
|
100
|
+
bucket = {
|
|
101
|
+
slot,
|
|
102
|
+
provider: record.provider,
|
|
103
|
+
model: record.model,
|
|
104
|
+
account,
|
|
105
|
+
totals: emptyTotals(),
|
|
106
|
+
costUsd: 0,
|
|
107
|
+
priced: false,
|
|
108
|
+
cacheSavingsUsd: 0,
|
|
109
|
+
sessions: new Set()
|
|
110
|
+
};
|
|
111
|
+
buckets.set(key, bucket);
|
|
112
|
+
}
|
|
113
|
+
bucket.totals = addTotals(bucket.totals, record.totals);
|
|
114
|
+
bucket.costUsd += cost;
|
|
115
|
+
bucket.priced ||= price !== null;
|
|
116
|
+
bucket.cacheSavingsUsd += price === null ? 0 : cacheSavingsOf(record.totals, price);
|
|
117
|
+
if (record.sessionId !== '') {
|
|
118
|
+
bucket.sessions.add(record.sessionId);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function foldModel(models: Map<string, ModelRow>, record: UsageRecord, account: string | null, lookup: PriceLookup, cost: number): void {
|
|
123
|
+
const key = `${record.provider}\0${record.model}\0${account ?? ''}`;
|
|
124
|
+
let model = models.get(key);
|
|
125
|
+
if (model === undefined) {
|
|
126
|
+
model = {
|
|
127
|
+
provider: record.provider,
|
|
128
|
+
model: record.model,
|
|
129
|
+
account,
|
|
130
|
+
totals: emptyTotals(),
|
|
131
|
+
costUsd: 0,
|
|
132
|
+
priced: false,
|
|
133
|
+
basis: lookup.basis,
|
|
134
|
+
pricedAs: lookup.pricedAs
|
|
135
|
+
};
|
|
136
|
+
models.set(key, model);
|
|
137
|
+
}
|
|
138
|
+
model.totals = addTotals(model.totals, record.totals);
|
|
139
|
+
model.costUsd += cost;
|
|
140
|
+
model.priced ||= lookup.price !== null;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function foldProject(project: ProjectAccumulator, record: UsageRecord, cost: number): void {
|
|
144
|
+
project.totals = addTotals(project.totals, record.totals);
|
|
145
|
+
project.costUsd += cost;
|
|
146
|
+
const share = project.byProvider.get(record.provider) ?? { costUsd: 0, tokens: 0 };
|
|
147
|
+
share.costUsd += cost;
|
|
148
|
+
share.tokens += totalTokensOf(record.totals);
|
|
149
|
+
project.byProvider.set(record.provider, share);
|
|
150
|
+
}
|
|
151
|
+
|
|
83
152
|
/* The account a record was made under, the default one under its CLI's kind. */
|
|
84
153
|
export function accountOfRecord(record: UsageRecord): string {
|
|
85
154
|
return record.account ?? record.provider;
|
|
@@ -98,9 +167,7 @@ export async function aggregate(
|
|
|
98
167
|
): Promise<Aggregation> {
|
|
99
168
|
const format = slotFormatter(payload.timeZone, payload.resolution);
|
|
100
169
|
const buckets = new Map<string, BucketAccumulator>();
|
|
101
|
-
const models = new Map<string,
|
|
102
|
-
/* The basis of the first record of a model prices the whole row: a model has one price. */
|
|
103
|
-
const modelBasis = new Map<string, { basis: UsagePriceBasis; pricedAs: string | null }>();
|
|
170
|
+
const models = new Map<string, ModelRow>();
|
|
104
171
|
const projects = new Map<string, ProjectAccumulator>();
|
|
105
172
|
const sessions = new Set<string>();
|
|
106
173
|
const resolver = new ProjectResolver(known);
|
|
@@ -118,45 +185,14 @@ export async function aggregate(
|
|
|
118
185
|
if (day < payload.from || day > payload.to) {
|
|
119
186
|
continue;
|
|
120
187
|
}
|
|
121
|
-
const
|
|
122
|
-
const cost = price === null ? 0 : costOf(record.totals, price);
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
let bucket = buckets.get(bucketKey);
|
|
126
|
-
if (bucket === undefined) {
|
|
127
|
-
bucket = {
|
|
128
|
-
slot,
|
|
129
|
-
provider: record.provider,
|
|
130
|
-
model: record.model,
|
|
131
|
-
account,
|
|
132
|
-
totals: emptyTotals(),
|
|
133
|
-
costUsd: 0,
|
|
134
|
-
priced: false,
|
|
135
|
-
cacheSavingsUsd: 0,
|
|
136
|
-
sessions: new Set()
|
|
137
|
-
};
|
|
138
|
-
buckets.set(bucketKey, bucket);
|
|
139
|
-
}
|
|
140
|
-
bucket.totals = addTotals(bucket.totals, record.totals);
|
|
141
|
-
bucket.costUsd += cost;
|
|
142
|
-
bucket.priced ||= price !== null;
|
|
143
|
-
bucket.cacheSavingsUsd += price === null ? 0 : cacheSavingsOf(record.totals, price);
|
|
188
|
+
const lookup = prices.look(record.model);
|
|
189
|
+
const cost = lookup.price === null ? 0 : costOf(record.totals, lookup.price);
|
|
190
|
+
foldBucket(buckets, slot, record, account, lookup.price, cost);
|
|
191
|
+
foldModel(models, record, account, lookup, cost);
|
|
144
192
|
if (record.sessionId !== '') {
|
|
145
|
-
bucket.sessions.add(record.sessionId);
|
|
146
193
|
sessions.add(`${record.provider}\0${record.sessionId}`);
|
|
147
194
|
}
|
|
148
195
|
|
|
149
|
-
const modelKey = `${record.provider}\0${record.model}\0${account ?? ''}`;
|
|
150
|
-
let model = models.get(modelKey);
|
|
151
|
-
if (model === undefined) {
|
|
152
|
-
model = { provider: record.provider, model: record.model, account, totals: emptyTotals(), costUsd: 0, priced: false };
|
|
153
|
-
models.set(modelKey, model);
|
|
154
|
-
modelBasis.set(modelKey, { basis, pricedAs });
|
|
155
|
-
}
|
|
156
|
-
model.totals = addTotals(model.totals, record.totals);
|
|
157
|
-
model.costUsd += cost;
|
|
158
|
-
model.priced ||= price !== null;
|
|
159
|
-
|
|
160
196
|
let folder = folders.get(record.cwd);
|
|
161
197
|
if (folder === undefined) {
|
|
162
198
|
const resolved = await resolver.resolve(record.cwd);
|
|
@@ -166,13 +202,7 @@ export async function aggregate(
|
|
|
166
202
|
projects.set(folder, { folder, name: resolved.name, projectId: resolved.projectId, byProvider: new Map(), totals: emptyTotals(), costUsd: 0 });
|
|
167
203
|
}
|
|
168
204
|
}
|
|
169
|
-
|
|
170
|
-
project.totals = addTotals(project.totals, record.totals);
|
|
171
|
-
project.costUsd += cost;
|
|
172
|
-
const share = project.byProvider.get(record.provider) ?? { costUsd: 0, tokens: 0 };
|
|
173
|
-
share.costUsd += cost;
|
|
174
|
-
share.tokens += totalTokensOf(record.totals);
|
|
175
|
-
project.byProvider.set(record.provider, share);
|
|
205
|
+
foldProject(projects.get(folder)!, record, cost);
|
|
176
206
|
}
|
|
177
207
|
|
|
178
208
|
return {
|
|
@@ -194,15 +224,15 @@ export async function aggregate(
|
|
|
194
224
|
a.model.localeCompare(b.model) ||
|
|
195
225
|
(a.account ?? '').localeCompare(b.account ?? '')
|
|
196
226
|
),
|
|
197
|
-
models: [...models.
|
|
198
|
-
.map((
|
|
227
|
+
models: [...models.values()]
|
|
228
|
+
.map((model) => ({
|
|
199
229
|
provider: model.provider,
|
|
200
230
|
model: model.model,
|
|
201
231
|
...(model.account === null ? {} : { account: model.account }),
|
|
202
232
|
totals: model.totals,
|
|
203
233
|
costUsd: model.priced ? model.costUsd : null,
|
|
204
|
-
priceBasis:
|
|
205
|
-
pricedAs:
|
|
234
|
+
priceBasis: model.basis,
|
|
235
|
+
pricedAs: model.pricedAs
|
|
206
236
|
}))
|
|
207
237
|
.sort((a, b) => (b.costUsd ?? -1) - (a.costUsd ?? -1)),
|
|
208
238
|
projects: [...projects.values()]
|
package/src/usage/exchange.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { mkdir, readFile } from 'node:fs/promises';
|
|
2
2
|
import { join } from 'node:path';
|
|
3
3
|
import type { UsageRate } from '@adecore/agent-contracts';
|
|
4
|
-
import { isNotFound, writeAtomic } from '../fs.ts';
|
|
5
4
|
import { errorText } from '../error-text.ts';
|
|
5
|
+
import { isNotFound, writeAtomic } from '../fs.ts';
|
|
6
6
|
|
|
7
7
|
export const FRANKFURTER_URL = 'https://api.frankfurter.app/latest';
|
|
8
8
|
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import type { UsageLimitsProvider, UsageLimitsSnapshot, UsageProvider } from '@adecore/agent-contracts';
|
|
2
|
+
import type { CodexClientInfo } from '../../chat/codex-transport.ts';
|
|
3
|
+
import { ClientSinks } from '../../client-sinks.ts';
|
|
4
|
+
import { errorText } from '../../error-text.ts';
|
|
5
|
+
import type { AgentEvent, AgentSink } from '../../events.ts';
|
|
2
6
|
import { definedEnv } from '../../providers/accounts/launch.ts';
|
|
3
7
|
import type { ProviderRegistry } from '../../providers/registry.ts';
|
|
4
|
-
import type { AgentEvent, AgentSink } from '../../events.ts';
|
|
5
8
|
import { mergeWindows, type LimitsUpdate } from './normalize.ts';
|
|
6
|
-
import type { CodexClientInfo } from '../../chat/codex-transport.ts';
|
|
7
9
|
import { probeClaude, probeCodex, type ProbeResult } from './probe.ts';
|
|
8
|
-
import { ClientSinks } from '../../client-sinks.ts';
|
|
9
|
-
import { errorText } from '../../error-text.ts';
|
|
10
10
|
|
|
11
11
|
/* Often enough that a bar is never far behind, rarely enough that two CLIs are not started all day. */
|
|
12
12
|
const PROBE_INTERVAL_MS = 5 * 60_000;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { withTimeout } from '../../async.ts';
|
|
2
1
|
import type { UsageLimitsProvider } from '@adecore/agent-contracts';
|
|
2
|
+
import { withTimeout } from '../../async.ts';
|
|
3
3
|
import { spawnChatProcess, type ChatProcess, type SpawnChatProcess } from '../../chat/chat-process.ts';
|
|
4
4
|
import { CodexTransport, DEFAULT_CODEX_CLIENT, type CodexClientInfo } from '../../chat/codex-transport.ts';
|
|
5
|
-
import { readClaudeUsage, readCodexLimits, type ProviderReading } from './normalize.ts';
|
|
6
5
|
import { errorText } from '../../error-text.ts';
|
|
6
|
+
import { readClaudeUsage, readCodexLimits, type ProviderReading } from './normalize.ts';
|
|
7
7
|
|
|
8
8
|
/* A CLI that has not answered by now is not going to; the next pass tries again. */
|
|
9
9
|
const PROBE_TIMEOUT_MS = 20_000;
|
package/src/usage/pricing.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { mkdir, readFile } from 'node:fs/promises';
|
|
2
2
|
import { join } from 'node:path';
|
|
3
3
|
import type { UsagePriceBasis, UsagePricing, UsageTotals } from '@adecore/agent-contracts';
|
|
4
|
-
import { isNotFound, writeAtomic } from '../fs.ts';
|
|
5
4
|
import { errorText } from '../error-text.ts';
|
|
5
|
+
import { isNotFound, writeAtomic } from '../fs.ts';
|
|
6
6
|
import bundled from './prices-snapshot.json' with { type: 'json' };
|
|
7
7
|
|
|
8
8
|
export const LITELLM_URL = 'https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json';
|
package/src/usage/roots.ts
CHANGED
|
@@ -12,8 +12,7 @@ export interface UsageRootPath {
|
|
|
12
12
|
/* Where in its config folder each CLI leaves its transcripts. */
|
|
13
13
|
const TRANSCRIPTS: Record<UsageProvider, string> = { claude: 'projects', codex: 'sessions' };
|
|
14
14
|
|
|
15
|
-
/*
|
|
16
|
-
so the scanner and the installer never disagree about where a CLI keeps its things. */
|
|
15
|
+
/* The transcripts of the default accounts, found through the variables the CLIs themselves read. */
|
|
17
16
|
export function usageRoots(env: Record<string, string | undefined> = process.env): UsageRootPath[] {
|
|
18
17
|
const home = env.HOME ?? homedir();
|
|
19
18
|
return [
|
package/src/usage/scanner.ts
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { mkdir, open, readFile, readdir, stat } from 'node:fs/promises';
|
|
2
2
|
import { join } from 'node:path';
|
|
3
3
|
import type { UsageRoot } from '@adecore/agent-contracts';
|
|
4
|
+
import { errorText } from '../error-text.ts';
|
|
4
5
|
import { isNotFound, writeAtomic } from '../fs.ts';
|
|
5
6
|
import { decodeIndex, encodeIndex, type IndexedFile, type UsageIndex } from './index-file.ts';
|
|
6
7
|
import { foldByKey, type UsageRecord } from './record.ts';
|
|
7
8
|
import { claudeMightCarryUsage, parseClaudeLine } from './readers/claude.ts';
|
|
8
9
|
import { cloneCodexState, codexMightCarryUsage, createCodexState, parseCodexLine, type CodexParserState } from './readers/codex.ts';
|
|
9
10
|
import { usageRoots, type UsageRootPath } from './roots.ts';
|
|
10
|
-
import { errorText } from '../error-text.ts';
|
|
11
11
|
|
|
12
12
|
/* What a file holds from `from` to its end, as it is at the read. */
|
|
13
13
|
async function readFrom(path: string, from: number): Promise<string> {
|
|
@@ -69,11 +69,11 @@ async function listJsonl(dir: string, out: string[] = []): Promise<string[]> {
|
|
|
69
69
|
* read again next time. The Codex state is snapshotted before that tail for the same reason.
|
|
70
70
|
*/
|
|
71
71
|
function parseChunk(text: string, provider: 'claude' | 'codex', from: number, state: CodexParserState | null): ParsedFile {
|
|
72
|
-
let records: UsageRecord[] = [];
|
|
73
72
|
const end = text.lastIndexOf('\n');
|
|
74
73
|
const whole = end === -1 ? '' : text.slice(0, end);
|
|
75
74
|
const tail = text.slice(end + 1);
|
|
76
|
-
const read = (chunk: string, codex: CodexParserState | null):
|
|
75
|
+
const read = (chunk: string, codex: CodexParserState | null): UsageRecord[] => {
|
|
76
|
+
const records: UsageRecord[] = [];
|
|
77
77
|
for (const raw of chunk.split('\n')) {
|
|
78
78
|
const line = raw.endsWith('\r') ? raw.slice(0, -1) : raw;
|
|
79
79
|
if (line === '') {
|
|
@@ -95,14 +95,12 @@ function parseChunk(text: string, provider: 'claude' | 'codex', from: number, st
|
|
|
95
95
|
}
|
|
96
96
|
}
|
|
97
97
|
}
|
|
98
|
+
return records;
|
|
98
99
|
};
|
|
99
100
|
const codex = provider === 'codex' ? (state ?? createCodexState()) : null;
|
|
100
|
-
read(whole, codex);
|
|
101
|
+
const counted = read(whole, codex);
|
|
101
102
|
const committed = codex === null ? null : cloneCodexState(codex);
|
|
102
|
-
|
|
103
|
-
records = [];
|
|
104
|
-
read(tail, codex);
|
|
105
|
-
return { records: counted, tail: records, offset: from + (end === -1 ? 0 : Buffer.byteLength(whole, 'utf8') + 1), codex: committed };
|
|
103
|
+
return { records: counted, tail: read(tail, codex), offset: from + (end === -1 ? 0 : Buffer.byteLength(whole, 'utf8') + 1), codex: committed };
|
|
106
104
|
}
|
|
107
105
|
|
|
108
106
|
export interface UsageScannerOptions {
|
|
@@ -139,8 +137,8 @@ function withAccount(records: UsageRecord[], accountOf: AccountOf): UsageRecord[
|
|
|
139
137
|
}
|
|
140
138
|
|
|
141
139
|
/*
|
|
142
|
-
*
|
|
143
|
-
*
|
|
140
|
+
* Reads only what a transcript appended since the last scan, and starts a file over once it was
|
|
141
|
+
* rewritten or cut. A deleted transcript keeps its totals, since its usage still happened.
|
|
144
142
|
*/
|
|
145
143
|
export class UsageScanner {
|
|
146
144
|
private readonly home: string;
|
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
import type { UsageAccount, UsageSummaryPayload, UsageSummaryResult } from '@adecore/agent-contracts';
|
|
2
|
+
import { ClientSinks } from '../client-sinks.ts';
|
|
3
|
+
import { errorText } from '../error-text.ts';
|
|
2
4
|
import type { AgentEvent, AgentSink } from '../events.ts';
|
|
3
5
|
import { accountOfRecord, aggregate } from './aggregate.ts';
|
|
4
6
|
import { ExchangeRates } from './exchange.ts';
|
|
5
7
|
import { PriceBook } from './pricing.ts';
|
|
6
8
|
import type { KnownProject } from './projects.ts';
|
|
7
9
|
import type { UsageRecord } from './record.ts';
|
|
8
|
-
import { UsageScanner, type ScanReport, type UsageScannerOptions } from './scanner.ts';
|
|
9
10
|
import type { UsageRootPath } from './roots.ts';
|
|
10
|
-
import {
|
|
11
|
-
import { ClientSinks } from '../client-sinks.ts';
|
|
11
|
+
import { UsageScanner, type ScanReport, type UsageScannerOptions } from './scanner.ts';
|
|
12
12
|
|
|
13
13
|
/* A scan this fresh answers the question the page is asking, so nothing is opened for it. */
|
|
14
14
|
const SCAN_TTL_MS = 60_000;
|
|
15
15
|
|
|
16
16
|
export interface UsageServiceOptions {
|
|
17
17
|
home: string;
|
|
18
|
-
/* Off
|
|
18
|
+
/* Off, the bundled table prices everything and no rate is asked for. */
|
|
19
19
|
allowPriceFetch?: boolean;
|
|
20
20
|
/* The projects the host knows, so a folder can wear the name it has in the app. */
|
|
21
21
|
knownProjects(): Promise<KnownProject[]>;
|
|
@@ -151,11 +151,7 @@ export class UsageService {
|
|
|
151
151
|
}
|
|
152
152
|
this.report = report;
|
|
153
153
|
if (report.changedFiles > 0) {
|
|
154
|
-
this.emit({ event: 'usage.changed', payload: { scannedAt: report.at } });
|
|
154
|
+
this.sinks.emit({ event: 'usage.changed', payload: { scannedAt: report.at } });
|
|
155
155
|
}
|
|
156
156
|
}
|
|
157
|
-
|
|
158
|
-
private emit(event: AgentEvent): void {
|
|
159
|
-
this.sinks.emit(event);
|
|
160
|
-
}
|
|
161
157
|
}
|