@axplusb/kepler 1.0.9 → 1.0.10
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/package.json +1 -1
- package/src/core/pricing.mjs +23 -1
- package/src/core/tool-executor.mjs +29 -2
- package/src/terminal/ansi.mjs +3 -5
- package/src/terminal/repl.mjs +12 -13
package/package.json
CHANGED
package/src/core/pricing.mjs
CHANGED
|
@@ -271,7 +271,7 @@ export function calculateCost(usage) {
|
|
|
271
271
|
// ── Formatting ───────────────────────────────────────────────
|
|
272
272
|
|
|
273
273
|
/**
|
|
274
|
-
* Format a cost value as a dollar string.
|
|
274
|
+
* Format a cost value as a dollar string (diagnostic/telemetry only).
|
|
275
275
|
* @param {number} cost
|
|
276
276
|
* @returns {string}
|
|
277
277
|
*/
|
|
@@ -281,6 +281,28 @@ export function formatCostValue(cost) {
|
|
|
281
281
|
return `$${cost.toFixed(2)}`;
|
|
282
282
|
}
|
|
283
283
|
|
|
284
|
+
/**
|
|
285
|
+
* Convert provider cost to Kepler credits.
|
|
286
|
+
* 100 credits = $1 of retail model usage, 2x multiplier.
|
|
287
|
+
* @param {number} costUsd
|
|
288
|
+
* @returns {number}
|
|
289
|
+
*/
|
|
290
|
+
export function costToCredits(costUsd) {
|
|
291
|
+
if (costUsd <= 0) return 0;
|
|
292
|
+
return Math.max(1, Math.ceil(costUsd * 2.0 * 100));
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* Format credits for display (e.g. "5 cr", "1.2k cr").
|
|
297
|
+
* @param {number} credits
|
|
298
|
+
* @returns {string}
|
|
299
|
+
*/
|
|
300
|
+
export function formatCredits(credits) {
|
|
301
|
+
if (credits === 0) return '0 cr';
|
|
302
|
+
if (credits >= 1000) return `${(credits / 1000).toFixed(1)}k cr`;
|
|
303
|
+
return `${credits} cr`;
|
|
304
|
+
}
|
|
305
|
+
|
|
284
306
|
/**
|
|
285
307
|
* Format a token count for display (e.g. 42100 → '42.1k').
|
|
286
308
|
* @param {number} tokens
|
|
@@ -113,6 +113,25 @@ export function createToolExecutor({
|
|
|
113
113
|
}
|
|
114
114
|
}
|
|
115
115
|
|
|
116
|
+
// ── Post-edit verification hint ──────────────────────────────
|
|
117
|
+
// Appended to edit_file/write_file results so the model knows
|
|
118
|
+
// exactly how to verify. Uses detected project commands.
|
|
119
|
+
|
|
120
|
+
function verificationHint(filePath) {
|
|
121
|
+
const project = projectRegistry.projectForPath(filePath);
|
|
122
|
+
const commands = project?.resource?.commands || {};
|
|
123
|
+
const parts = [];
|
|
124
|
+
if (commands.test) {
|
|
125
|
+
parts.push(`Run tests: ${commands.test}`);
|
|
126
|
+
}
|
|
127
|
+
if (parts.length === 0) {
|
|
128
|
+
const ext = path.extname(filePath);
|
|
129
|
+
if (ext === '.py') parts.push('Run tests: python -m pytest');
|
|
130
|
+
else if (['.js', '.ts', '.tsx', '.mjs'].includes(ext)) parts.push('Run tests: npm test');
|
|
131
|
+
}
|
|
132
|
+
return parts.length ? `\n--- Verify ---\n${parts.join('\n')}` : '';
|
|
133
|
+
}
|
|
134
|
+
|
|
116
135
|
// ── Tool mapping table ──────────────────────────────────────
|
|
117
136
|
|
|
118
137
|
const toolMap = {
|
|
@@ -275,10 +294,14 @@ export function createToolExecutor({
|
|
|
275
294
|
// Auto-lint the written file
|
|
276
295
|
const lintOutput = autoLint(filePath);
|
|
277
296
|
if (lintOutput) {
|
|
278
|
-
wrapped.output += `\n\n--- Lint
|
|
297
|
+
wrapped.output += `\n\n--- Lint ---\n${lintOutput}`;
|
|
279
298
|
wrapped.lint = lintOutput;
|
|
280
299
|
}
|
|
281
300
|
|
|
301
|
+
// Nudge: tell the model how to verify
|
|
302
|
+
const hint = verificationHint(filePath);
|
|
303
|
+
if (hint) wrapped.output += hint;
|
|
304
|
+
|
|
282
305
|
return wrapped;
|
|
283
306
|
},
|
|
284
307
|
|
|
@@ -399,10 +422,14 @@ print('OK: replaced')
|
|
|
399
422
|
// Auto-lint the edited file
|
|
400
423
|
const lintOutput = autoLint(filePath);
|
|
401
424
|
if (lintOutput) {
|
|
402
|
-
wrapped.output += `\n\n--- Lint
|
|
425
|
+
wrapped.output += `\n\n--- Lint ---\n${lintOutput}`;
|
|
403
426
|
wrapped.lint = lintOutput;
|
|
404
427
|
}
|
|
405
428
|
|
|
429
|
+
// Nudge: tell the model how to verify
|
|
430
|
+
const hint = verificationHint(filePath);
|
|
431
|
+
if (hint) wrapped.output += hint;
|
|
432
|
+
|
|
406
433
|
return wrapped;
|
|
407
434
|
},
|
|
408
435
|
|
package/src/terminal/ansi.mjs
CHANGED
|
@@ -399,7 +399,7 @@ export function formatElapsed(startMs) {
|
|
|
399
399
|
|
|
400
400
|
// ── Format Cost ──
|
|
401
401
|
|
|
402
|
-
import { calculateCost, formatCostValue } from '../core/pricing.mjs';
|
|
402
|
+
import { calculateCost, formatCostValue, costToCredits, formatCredits } from '../core/pricing.mjs';
|
|
403
403
|
|
|
404
404
|
/**
|
|
405
405
|
* Format cost from token counts.
|
|
@@ -407,15 +407,13 @@ import { calculateCost, formatCostValue } from '../core/pricing.mjs';
|
|
|
407
407
|
* or a single usage object with optional per-model breakdown.
|
|
408
408
|
*/
|
|
409
409
|
export function formatCost(inputOrUsage, outputTokens) {
|
|
410
|
-
// New API: pass a usage object directly
|
|
411
410
|
if (typeof inputOrUsage === 'object' && inputOrUsage !== null) {
|
|
412
411
|
const { total } = calculateCost(inputOrUsage);
|
|
413
|
-
return
|
|
412
|
+
return formatCredits(costToCredits(total));
|
|
414
413
|
}
|
|
415
|
-
// Legacy API: flat input/output token counts, default pricing
|
|
416
414
|
const { total } = calculateCost({
|
|
417
415
|
input_tokens: inputOrUsage || 0,
|
|
418
416
|
output_tokens: outputTokens || 0,
|
|
419
417
|
});
|
|
420
|
-
return
|
|
418
|
+
return formatCredits(costToCredits(total));
|
|
421
419
|
}
|
package/src/terminal/repl.mjs
CHANGED
|
@@ -19,7 +19,7 @@ import * as readline from 'node:readline';
|
|
|
19
19
|
import * as path from 'node:path';
|
|
20
20
|
import * as fs from 'node:fs';
|
|
21
21
|
import { c, progressBar, spinner, inPlace, renderMarkdown, renderDiff, formatElapsed, formatCost, stripAnsi } from './ansi.mjs';
|
|
22
|
-
import { calculateCost, formatCostValue, formatTokens } from '../core/pricing.mjs';
|
|
22
|
+
import { calculateCost, formatCostValue, formatTokens, costToCredits, formatCredits } from '../core/pricing.mjs';
|
|
23
23
|
import { TarangStreamClient, EVENT_TYPES } from '../core/stream-client.mjs';
|
|
24
24
|
import { JsonlWriter } from '../core/jsonl-writer.mjs';
|
|
25
25
|
import { createToolExecutor } from '../core/tool-executor.mjs';
|
|
@@ -163,13 +163,12 @@ function printBanner(auth) {
|
|
|
163
163
|
*/
|
|
164
164
|
function buildContextStrip() {
|
|
165
165
|
const totalTokens = session.inputTokens + session.outputTokens;
|
|
166
|
-
const
|
|
166
|
+
const credits = formatCredits(costToCredits(session.totalCost));
|
|
167
167
|
const elapsed = formatElapsed(session.startTime);
|
|
168
168
|
|
|
169
|
-
// Right side — always shown
|
|
170
169
|
const right = [
|
|
171
170
|
c.dim(`${formatTokens(totalTokens)} tok`),
|
|
172
|
-
c.dim(
|
|
171
|
+
c.dim(credits),
|
|
173
172
|
c.dim(elapsed),
|
|
174
173
|
].join(c.dim(' · '));
|
|
175
174
|
|
|
@@ -200,7 +199,7 @@ function printTurnSummary(toolCount, durationS, turnCost) {
|
|
|
200
199
|
const parts = [];
|
|
201
200
|
if (toolCount > 0) parts.push(`${toolCount} tools`);
|
|
202
201
|
if (durationS) parts.push(`${Number(durationS).toFixed(1)}s`);
|
|
203
|
-
if (turnCost > 0) parts.push(
|
|
202
|
+
if (turnCost > 0) parts.push(formatCredits(costToCredits(turnCost)));
|
|
204
203
|
if (parts.length > 0) {
|
|
205
204
|
process.stderr.write(`\n ${c.green('✓')} ${c.dim(parts.join(' · '))}\n`);
|
|
206
205
|
}
|
|
@@ -783,7 +782,7 @@ async function handleCommand(input, ctx) {
|
|
|
783
782
|
process.stderr.write(` ${c.dim('Turns')} ${session.turns}\n`);
|
|
784
783
|
process.stderr.write(` ${c.dim('Tools')} ${session.totalToolCalls} total, ${session.toolCalls} last turn\n`);
|
|
785
784
|
process.stderr.write(` ${c.dim('Duration')} ${formatElapsed(session.startTime)}\n`);
|
|
786
|
-
process.stderr.write(` ${c.dim('
|
|
785
|
+
process.stderr.write(` ${c.dim('Credits')} ${formatCredits(costToCredits(session.totalCost))}${session.costAccurate ? '' : c.dim(' (est)')}\n`);
|
|
787
786
|
process.stderr.write(` ${c.dim('CWD')} ${safeCwd()}\n`);
|
|
788
787
|
|
|
789
788
|
// Permissions
|
|
@@ -851,29 +850,29 @@ async function handleCommand(input, ctx) {
|
|
|
851
850
|
process.stderr.write(` ${c.gray('Turns:')} ${session.turns}\n`);
|
|
852
851
|
process.stderr.write(` ${c.gray('Tools:')} ${session.toolCalls}\n`);
|
|
853
852
|
process.stderr.write(` ${c.gray('Blocked:')} ${session.blockedOps}\n`);
|
|
854
|
-
process.stderr.write(` ${c.gray('
|
|
853
|
+
process.stderr.write(` ${c.gray('Credits:')} ${formatCredits(costToCredits(session.totalCost))}${session.costAccurate ? '' : c.dim(' (est)')}\n`);
|
|
855
854
|
process.stderr.write(` ${c.gray('Elapsed:')} ${formatElapsed(session.startTime)}\n\n`);
|
|
856
855
|
return;
|
|
857
856
|
}
|
|
858
857
|
|
|
859
858
|
case '/cost': {
|
|
860
|
-
process.stderr.write(`\n ${c.bold('Session
|
|
859
|
+
process.stderr.write(`\n ${c.bold('Session Credits')} ${c.brand(formatCredits(costToCredits(session.totalCost)))}`);
|
|
861
860
|
if (!session.costAccurate) {
|
|
862
|
-
process.stderr.write(` ${c.yellow('(estimated
|
|
861
|
+
process.stderr.write(` ${c.yellow('(estimated)')}`);
|
|
863
862
|
}
|
|
864
863
|
process.stderr.write('\n');
|
|
865
864
|
process.stderr.write(` ${c.dim('─'.repeat(70))}\n`);
|
|
866
865
|
|
|
867
866
|
if (session.costBreakdown.length > 0) {
|
|
868
867
|
// Header
|
|
869
|
-
process.stderr.write(` ${c.dim('Model'.padEnd(36))}${c.dim('Input'.padStart(10))}${c.dim('Output'.padStart(10))}${c.dim('Cache'.padStart(10))}${c.dim('
|
|
868
|
+
process.stderr.write(` ${c.dim('Model'.padEnd(36))}${c.dim('Input'.padStart(10))}${c.dim('Output'.padStart(10))}${c.dim('Cache'.padStart(10))}${c.dim('Credits'.padStart(10))}\n`);
|
|
870
869
|
process.stderr.write(` ${c.dim('─'.repeat(70))}\n`);
|
|
871
870
|
|
|
872
871
|
for (const b of session.costBreakdown) {
|
|
873
872
|
const modelLabel = b.model === 'unknown' ? c.yellow('unknown model') : b.model;
|
|
874
873
|
const roleTag = b.role && b.role !== 'unknown' ? ` ${c.dim(`(${b.role})`)}` : '';
|
|
875
874
|
const cacheTokens = (b.cache_read_tokens || 0) + (b.cache_creation_tokens || 0);
|
|
876
|
-
const costStr = b.free ? c.green('free') :
|
|
875
|
+
const costStr = b.free ? c.green('free') : formatCredits(costToCredits(b.cost));
|
|
877
876
|
|
|
878
877
|
process.stderr.write(
|
|
879
878
|
` ${(modelLabel + roleTag).padEnd(36)}` +
|
|
@@ -892,9 +891,9 @@ async function handleCommand(input, ctx) {
|
|
|
892
891
|
`${formatTokens(session.inputTokens).padStart(10)}` +
|
|
893
892
|
`${formatTokens(session.outputTokens).padStart(10)}` +
|
|
894
893
|
`${''.padStart(10)}` +
|
|
895
|
-
`${
|
|
894
|
+
`${formatCredits(costToCredits(session.totalCost)).padStart(10)}\n`
|
|
896
895
|
);
|
|
897
|
-
process.stderr.write(` ${c.dim(`Turns: ${session.turns} Duration: ${formatElapsed(session.startTime)}`)}\n\n`);
|
|
896
|
+
process.stderr.write(` ${c.dim(`Turns: ${session.turns} Duration: ${formatElapsed(session.startTime)} Provider: ${formatCostValue(session.totalCost)}`)}\n\n`);
|
|
898
897
|
return;
|
|
899
898
|
}
|
|
900
899
|
|