@tokenbuddy/tokenbuddy 1.0.6 → 1.0.7
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/src/buyer-store.d.ts +28 -1
- package/dist/src/buyer-store.d.ts.map +1 -1
- package/dist/src/buyer-store.js +71 -16
- package/dist/src/buyer-store.js.map +1 -1
- package/dist/src/cli.d.ts +17 -0
- package/dist/src/cli.d.ts.map +1 -1
- package/dist/src/cli.js +201 -32
- package/dist/src/cli.js.map +1 -1
- package/dist/src/daemon.d.ts +5 -0
- package/dist/src/daemon.d.ts.map +1 -1
- package/dist/src/daemon.js +279 -72
- package/dist/src/daemon.js.map +1 -1
- package/dist/src/doctor-clawtip-wallet.d.ts +14 -0
- package/dist/src/doctor-clawtip-wallet.d.ts.map +1 -0
- package/dist/src/doctor-clawtip-wallet.js +54 -0
- package/dist/src/doctor-clawtip-wallet.js.map +1 -0
- package/dist/src/doctor-diagnostics.d.ts +2 -0
- package/dist/src/doctor-diagnostics.d.ts.map +1 -1
- package/dist/src/doctor-diagnostics.js +5 -0
- package/dist/src/doctor-diagnostics.js.map +1 -1
- package/dist/src/init-clawtip-activation.d.ts +48 -0
- package/dist/src/init-clawtip-activation.d.ts.map +1 -0
- package/dist/src/init-clawtip-activation.js +395 -0
- package/dist/src/init-clawtip-activation.js.map +1 -0
- package/dist/src/init-payment-options.d.ts +23 -1
- package/dist/src/init-payment-options.d.ts.map +1 -1
- package/dist/src/init-payment-options.js +97 -22
- package/dist/src/init-payment-options.js.map +1 -1
- package/dist/src/terminal-image.d.ts +22 -0
- package/dist/src/terminal-image.d.ts.map +1 -0
- package/dist/src/terminal-image.js +135 -0
- package/dist/src/terminal-image.js.map +1 -0
- package/package.json +1 -1
- package/src/buyer-store.ts +140 -17
- package/src/cli.ts +251 -33
- package/src/daemon.ts +308 -53
- package/src/doctor-clawtip-wallet.ts +70 -0
- package/src/doctor-diagnostics.ts +11 -0
- package/src/init-clawtip-activation.ts +487 -0
- package/src/init-payment-options.ts +140 -22
- package/src/terminal-image.ts +187 -0
- package/tests/e2e.test.ts +79 -5
- package/tests/tokenbuddy.test.ts +745 -19
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { spawn } from "child_process";
|
|
2
|
+
import * as fs from "fs";
|
|
3
|
+
import * as path from "path";
|
|
4
|
+
function defaultRunCommand(command, args) {
|
|
5
|
+
return new Promise((resolve, reject) => {
|
|
6
|
+
const child = spawn(command, args, {
|
|
7
|
+
stdio: "ignore",
|
|
8
|
+
});
|
|
9
|
+
child.on("error", reject);
|
|
10
|
+
child.on("close", (code) => {
|
|
11
|
+
if (code === 0) {
|
|
12
|
+
resolve();
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
reject(new Error(`${command} exited with ${code}`));
|
|
16
|
+
});
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
function terminalSupportsInlineImages(env, stdoutIsTTY) {
|
|
20
|
+
if (!stdoutIsTTY) {
|
|
21
|
+
return undefined;
|
|
22
|
+
}
|
|
23
|
+
const term = env.TERM || "";
|
|
24
|
+
if (env.KITTY_WINDOW_ID || term.includes("xterm-kitty")) {
|
|
25
|
+
return "kitty";
|
|
26
|
+
}
|
|
27
|
+
const termProgram = env.TERM_PROGRAM || "";
|
|
28
|
+
if (termProgram === "iTerm.app" || termProgram === "WezTerm" || env.WEZTERM_EXECUTABLE) {
|
|
29
|
+
return "iterm";
|
|
30
|
+
}
|
|
31
|
+
return undefined;
|
|
32
|
+
}
|
|
33
|
+
function basenameBase64(filePath) {
|
|
34
|
+
return Buffer.from(path.basename(filePath), "utf8").toString("base64");
|
|
35
|
+
}
|
|
36
|
+
function itermInlineImageSequence(filePath, image) {
|
|
37
|
+
const payload = image.toString("base64");
|
|
38
|
+
return `\u001B]1337;File=name=${basenameBase64(filePath)};inline=1;width=60%;preserveAspectRatio=1:${payload}\u0007\n`;
|
|
39
|
+
}
|
|
40
|
+
function kittyInlineImageSequence(filePath) {
|
|
41
|
+
const payload = Buffer.from(filePath, "utf8").toString("base64");
|
|
42
|
+
return `\u001B_Ga=T,t=f,f=100,c=60;${payload}\u001B\\\n`;
|
|
43
|
+
}
|
|
44
|
+
function openCommandForPlatform(platform, filePath) {
|
|
45
|
+
if (platform === "darwin") {
|
|
46
|
+
return {
|
|
47
|
+
command: "open",
|
|
48
|
+
args: [filePath],
|
|
49
|
+
label: `open ${filePath}`,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
if (platform === "linux") {
|
|
53
|
+
return {
|
|
54
|
+
command: "xdg-open",
|
|
55
|
+
args: [filePath],
|
|
56
|
+
label: `xdg-open ${filePath}`,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
if (platform === "win32") {
|
|
60
|
+
return {
|
|
61
|
+
command: "cmd",
|
|
62
|
+
args: ["/c", "start", "", filePath],
|
|
63
|
+
label: `start ${filePath}`,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
return undefined;
|
|
67
|
+
}
|
|
68
|
+
export function detectTerminalImageDisplay(options = {}) {
|
|
69
|
+
return terminalSupportsInlineImages(options.env || process.env, options.stdoutIsTTY ?? Boolean(process.stdout.isTTY));
|
|
70
|
+
}
|
|
71
|
+
export async function displayTerminalImage(filePath, options = {}) {
|
|
72
|
+
const fileExists = options.fileExists || fs.existsSync;
|
|
73
|
+
const readFile = options.readFile || fs.readFileSync;
|
|
74
|
+
const write = options.write || ((chunk) => process.stdout.write(chunk));
|
|
75
|
+
const env = options.env || process.env;
|
|
76
|
+
const platform = options.platform || process.platform;
|
|
77
|
+
const stdoutIsTTY = options.stdoutIsTTY ?? Boolean(process.stdout.isTTY);
|
|
78
|
+
const openCommand = openCommandForPlatform(platform, filePath);
|
|
79
|
+
const fallbackCommand = openCommand?.label;
|
|
80
|
+
if (!fileExists(filePath)) {
|
|
81
|
+
return {
|
|
82
|
+
method: "manual",
|
|
83
|
+
displayed: false,
|
|
84
|
+
fallbackCommand,
|
|
85
|
+
message: `QR image file is missing: ${filePath}`,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
const inlineProtocol = terminalSupportsInlineImages(env, stdoutIsTTY);
|
|
89
|
+
if (inlineProtocol === "kitty") {
|
|
90
|
+
write(kittyInlineImageSequence(filePath));
|
|
91
|
+
return {
|
|
92
|
+
method: "inline-kitty",
|
|
93
|
+
displayed: true,
|
|
94
|
+
fallbackCommand,
|
|
95
|
+
message: "Displayed the ClawTip wallet QR image inline using the Kitty graphics protocol.",
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
if (inlineProtocol === "iterm") {
|
|
99
|
+
write(itermInlineImageSequence(filePath, readFile(filePath)));
|
|
100
|
+
return {
|
|
101
|
+
method: "inline-iterm",
|
|
102
|
+
displayed: true,
|
|
103
|
+
fallbackCommand,
|
|
104
|
+
message: "Displayed the ClawTip wallet QR image inline using the iTerm2 image protocol.",
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
if (openCommand) {
|
|
108
|
+
try {
|
|
109
|
+
const runCommand = options.runCommand || defaultRunCommand;
|
|
110
|
+
await runCommand(openCommand.command, openCommand.args);
|
|
111
|
+
return {
|
|
112
|
+
method: "system-open",
|
|
113
|
+
displayed: true,
|
|
114
|
+
fallbackCommand,
|
|
115
|
+
message: `Opened the ClawTip wallet QR image with the system image viewer: ${openCommand.label}`,
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
catch (error) {
|
|
119
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
120
|
+
return {
|
|
121
|
+
method: "manual",
|
|
122
|
+
displayed: false,
|
|
123
|
+
fallbackCommand,
|
|
124
|
+
message: `Could not open QR image automatically: ${message}`,
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
return {
|
|
129
|
+
method: "manual",
|
|
130
|
+
displayed: false,
|
|
131
|
+
fallbackCommand,
|
|
132
|
+
message: "This terminal does not advertise inline image support and no system opener is available.",
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
//# sourceMappingURL=terminal-image.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"terminal-image.js","sourceRoot":"","sources":["../../src/terminal-image.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
package/src/buyer-store.ts
CHANGED
|
@@ -11,6 +11,9 @@ const logger = createModuleLogger("tb-proxyd");
|
|
|
11
11
|
export interface CachedToken {
|
|
12
12
|
token: string;
|
|
13
13
|
balanceMicros: number;
|
|
14
|
+
reservedMicros: number;
|
|
15
|
+
spentMicros: number;
|
|
16
|
+
balanceSource?: string;
|
|
14
17
|
}
|
|
15
18
|
|
|
16
19
|
export interface PaymentConfig {
|
|
@@ -66,6 +69,12 @@ export interface InferenceLedgerInput {
|
|
|
66
69
|
promptTokens: number;
|
|
67
70
|
completionTokens: number;
|
|
68
71
|
billedMicros: number;
|
|
72
|
+
estimatedMicros?: number;
|
|
73
|
+
settledMicros?: number;
|
|
74
|
+
settledUsdMicros?: number;
|
|
75
|
+
priceVersion?: string;
|
|
76
|
+
balanceSnapshotMicros?: number;
|
|
77
|
+
balanceSource?: string;
|
|
69
78
|
prompt?: string;
|
|
70
79
|
response?: string;
|
|
71
80
|
}
|
|
@@ -79,11 +88,28 @@ export interface SafeInferenceLedgerEntry {
|
|
|
79
88
|
promptTokens: number;
|
|
80
89
|
completionTokens: number;
|
|
81
90
|
billedMicros: number;
|
|
91
|
+
estimatedMicros?: number;
|
|
92
|
+
settledMicros?: number;
|
|
93
|
+
settledUsdMicros?: number;
|
|
94
|
+
priceVersion?: string;
|
|
95
|
+
balanceSnapshotMicros?: number;
|
|
96
|
+
balanceSource?: string;
|
|
82
97
|
promptHash?: string;
|
|
83
98
|
responseHash?: string;
|
|
84
99
|
createdAt: string;
|
|
85
100
|
}
|
|
86
101
|
|
|
102
|
+
export interface TokenBalanceSnapshotInput {
|
|
103
|
+
sellerKey: string;
|
|
104
|
+
token?: string;
|
|
105
|
+
tokenClass?: string;
|
|
106
|
+
balanceMicros: number;
|
|
107
|
+
reservedMicros?: number;
|
|
108
|
+
spentMicros?: number;
|
|
109
|
+
balanceSource: string;
|
|
110
|
+
expiresAt?: string;
|
|
111
|
+
}
|
|
112
|
+
|
|
87
113
|
export interface BuyerStoreSummary {
|
|
88
114
|
journalMode: string;
|
|
89
115
|
paymentsCount: number;
|
|
@@ -295,41 +321,83 @@ export class BuyerStore {
|
|
|
295
321
|
}
|
|
296
322
|
|
|
297
323
|
public getToken(sellerKey: string): CachedToken | undefined {
|
|
298
|
-
const stmt = this.db.prepare(
|
|
299
|
-
|
|
324
|
+
const stmt = this.db.prepare(
|
|
325
|
+
"SELECT token, balance_micros, reserved_micros, spent_micros, balance_source FROM token_cache WHERE seller_key = ?"
|
|
326
|
+
);
|
|
327
|
+
const row = stmt.get(sellerKey) as {
|
|
328
|
+
token: string;
|
|
329
|
+
balance_micros: number;
|
|
330
|
+
reserved_micros: number;
|
|
331
|
+
spent_micros: number;
|
|
332
|
+
balance_source: string | null;
|
|
333
|
+
} | undefined;
|
|
300
334
|
if (!row) {
|
|
301
335
|
return undefined;
|
|
302
336
|
}
|
|
303
337
|
return {
|
|
304
338
|
token: row.token,
|
|
305
|
-
balanceMicros: row.balance_micros
|
|
339
|
+
balanceMicros: row.balance_micros,
|
|
340
|
+
reservedMicros: row.reserved_micros,
|
|
341
|
+
spentMicros: row.spent_micros,
|
|
342
|
+
balanceSource: row.balance_source || undefined
|
|
306
343
|
};
|
|
307
344
|
}
|
|
308
345
|
|
|
309
346
|
public saveToken(sellerKey: string, token: string, tokenClass: string, balanceMicros: number, expiresAt: string): void {
|
|
310
347
|
const stmt = this.db.prepare(
|
|
311
348
|
`INSERT OR REPLACE INTO token_cache (
|
|
312
|
-
seller_key, token, token_class, balance_micros,
|
|
313
|
-
|
|
349
|
+
seller_key, token, token_class, balance_micros, reserved_micros,
|
|
350
|
+
spent_micros, balance_source, expires_at, updated_at
|
|
351
|
+
) VALUES (?, ?, ?, ?, 0, 0, 'purchase_complete', ?, ?)`
|
|
314
352
|
);
|
|
315
353
|
stmt.run(sellerKey, token, tokenClass, balanceMicros, expiresAt, nowIso());
|
|
316
354
|
}
|
|
317
355
|
|
|
318
|
-
public
|
|
319
|
-
const
|
|
356
|
+
public reconcileTokenBalance(input: TokenBalanceSnapshotInput): void {
|
|
357
|
+
const current = this.db.prepare(
|
|
358
|
+
"SELECT token, token_class, expires_at FROM token_cache WHERE seller_key = ?"
|
|
359
|
+
).get(input.sellerKey) as {
|
|
360
|
+
token: string;
|
|
361
|
+
token_class: string;
|
|
362
|
+
expires_at: string;
|
|
363
|
+
} | undefined;
|
|
364
|
+
const token = input.token || current?.token;
|
|
365
|
+
const tokenClass = input.tokenClass || current?.token_class || "seller";
|
|
366
|
+
const expiresAt = input.expiresAt || current?.expires_at || new Date(Date.now() + 86400 * 1000).toISOString();
|
|
320
367
|
if (!token) {
|
|
321
368
|
return;
|
|
322
369
|
}
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
370
|
+
this.db.prepare(
|
|
371
|
+
`INSERT OR REPLACE INTO token_cache (
|
|
372
|
+
seller_key, token, token_class, balance_micros, reserved_micros,
|
|
373
|
+
spent_micros, balance_source, expires_at, updated_at
|
|
374
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`
|
|
375
|
+
).run(
|
|
376
|
+
input.sellerKey,
|
|
377
|
+
token,
|
|
378
|
+
tokenClass,
|
|
379
|
+
input.balanceMicros,
|
|
380
|
+
input.reservedMicros ?? 0,
|
|
381
|
+
input.spentMicros ?? 0,
|
|
382
|
+
input.balanceSource,
|
|
383
|
+
expiresAt,
|
|
384
|
+
nowIso()
|
|
385
|
+
);
|
|
386
|
+
logger.info("token.cache.reconciled", "token cache reconciled from seller balance snapshot", {
|
|
387
|
+
sellerKey: input.sellerKey,
|
|
388
|
+
balanceMicros: input.balanceMicros,
|
|
389
|
+
reservedMicros: input.reservedMicros ?? 0,
|
|
390
|
+
spentMicros: input.spentMicros ?? 0,
|
|
391
|
+
balanceSource: input.balanceSource
|
|
330
392
|
});
|
|
331
393
|
}
|
|
332
394
|
|
|
395
|
+
public markTokenStale(sellerKey: string): void {
|
|
396
|
+
this.db.prepare(
|
|
397
|
+
"UPDATE token_cache SET balance_micros = 0, balance_source = 'stale', updated_at = ? WHERE seller_key = ?"
|
|
398
|
+
).run(nowIso(), sellerKey);
|
|
399
|
+
}
|
|
400
|
+
|
|
333
401
|
public listPayments(): PaymentConfig[] {
|
|
334
402
|
const rows = this.db.prepare(
|
|
335
403
|
`SELECT method, enabled, is_default, config_json, updated_at
|
|
@@ -500,8 +568,10 @@ export class BuyerStore {
|
|
|
500
568
|
this.db.prepare(
|
|
501
569
|
`INSERT INTO inference_ledger (
|
|
502
570
|
request_id, seller_key, model_id, endpoint, status, prompt_tokens,
|
|
503
|
-
completion_tokens, billed_micros,
|
|
504
|
-
|
|
571
|
+
completion_tokens, billed_micros, estimated_micros, settled_micros,
|
|
572
|
+
settled_usd_micros, price_version, balance_snapshot_micros, balance_source,
|
|
573
|
+
prompt_hash, response_hash, created_at
|
|
574
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
|
|
505
575
|
).run(
|
|
506
576
|
input.requestId,
|
|
507
577
|
input.sellerKey,
|
|
@@ -511,6 +581,12 @@ export class BuyerStore {
|
|
|
511
581
|
input.promptTokens,
|
|
512
582
|
input.completionTokens,
|
|
513
583
|
input.billedMicros,
|
|
584
|
+
input.estimatedMicros ?? input.billedMicros,
|
|
585
|
+
input.settledMicros ?? null,
|
|
586
|
+
input.settledUsdMicros ?? null,
|
|
587
|
+
input.priceVersion || null,
|
|
588
|
+
input.balanceSnapshotMicros ?? null,
|
|
589
|
+
input.balanceSource || "unknown",
|
|
514
590
|
safeHash(input.prompt) || null,
|
|
515
591
|
safeHash(input.response) || null,
|
|
516
592
|
nowIso()
|
|
@@ -520,7 +596,9 @@ export class BuyerStore {
|
|
|
520
596
|
public listInferenceLedger(): SafeInferenceLedgerEntry[] {
|
|
521
597
|
const rows = this.db.prepare(
|
|
522
598
|
`SELECT request_id, seller_key, model_id, endpoint, status, prompt_tokens,
|
|
523
|
-
completion_tokens, billed_micros,
|
|
599
|
+
completion_tokens, billed_micros, estimated_micros, settled_micros,
|
|
600
|
+
settled_usd_micros, price_version, balance_snapshot_micros, balance_source,
|
|
601
|
+
prompt_hash, response_hash, created_at
|
|
524
602
|
FROM inference_ledger
|
|
525
603
|
ORDER BY id ASC`
|
|
526
604
|
).all() as Array<{
|
|
@@ -532,6 +610,12 @@ export class BuyerStore {
|
|
|
532
610
|
prompt_tokens: number;
|
|
533
611
|
completion_tokens: number;
|
|
534
612
|
billed_micros: number;
|
|
613
|
+
estimated_micros: number | null;
|
|
614
|
+
settled_micros: number | null;
|
|
615
|
+
settled_usd_micros: number | null;
|
|
616
|
+
price_version: string | null;
|
|
617
|
+
balance_snapshot_micros: number | null;
|
|
618
|
+
balance_source: string | null;
|
|
535
619
|
prompt_hash: string | null;
|
|
536
620
|
response_hash: string | null;
|
|
537
621
|
created_at: string;
|
|
@@ -546,6 +630,12 @@ export class BuyerStore {
|
|
|
546
630
|
promptTokens: row.prompt_tokens,
|
|
547
631
|
completionTokens: row.completion_tokens,
|
|
548
632
|
billedMicros: row.billed_micros,
|
|
633
|
+
estimatedMicros: row.estimated_micros ?? undefined,
|
|
634
|
+
settledMicros: row.settled_micros ?? undefined,
|
|
635
|
+
settledUsdMicros: row.settled_usd_micros ?? undefined,
|
|
636
|
+
priceVersion: row.price_version || undefined,
|
|
637
|
+
balanceSnapshotMicros: row.balance_snapshot_micros ?? undefined,
|
|
638
|
+
balanceSource: row.balance_source || undefined,
|
|
549
639
|
promptHash: row.prompt_hash || undefined,
|
|
550
640
|
responseHash: row.response_hash || undefined,
|
|
551
641
|
createdAt: row.created_at
|
|
@@ -563,6 +653,9 @@ export class BuyerStore {
|
|
|
563
653
|
token TEXT NOT NULL,
|
|
564
654
|
token_class TEXT NOT NULL,
|
|
565
655
|
balance_micros INTEGER NOT NULL,
|
|
656
|
+
reserved_micros INTEGER NOT NULL DEFAULT 0,
|
|
657
|
+
spent_micros INTEGER NOT NULL DEFAULT 0,
|
|
658
|
+
balance_source TEXT,
|
|
566
659
|
expires_at TEXT NOT NULL,
|
|
567
660
|
updated_at TEXT NOT NULL
|
|
568
661
|
);
|
|
@@ -613,6 +706,12 @@ export class BuyerStore {
|
|
|
613
706
|
prompt_tokens INTEGER NOT NULL,
|
|
614
707
|
completion_tokens INTEGER NOT NULL,
|
|
615
708
|
billed_micros INTEGER NOT NULL,
|
|
709
|
+
estimated_micros INTEGER,
|
|
710
|
+
settled_micros INTEGER,
|
|
711
|
+
settled_usd_micros INTEGER,
|
|
712
|
+
price_version TEXT,
|
|
713
|
+
balance_snapshot_micros INTEGER,
|
|
714
|
+
balance_source TEXT,
|
|
616
715
|
prompt_hash TEXT,
|
|
617
716
|
response_hash TEXT,
|
|
618
717
|
created_at TEXT NOT NULL
|
|
@@ -639,6 +738,30 @@ export class BuyerStore {
|
|
|
639
738
|
updated_at TEXT NOT NULL
|
|
640
739
|
);
|
|
641
740
|
`);
|
|
741
|
+
for (const [column, definition] of [
|
|
742
|
+
["reserved_micros", "INTEGER NOT NULL DEFAULT 0"],
|
|
743
|
+
["spent_micros", "INTEGER NOT NULL DEFAULT 0"],
|
|
744
|
+
["balance_source", "TEXT"]
|
|
745
|
+
]) {
|
|
746
|
+
this.ensureColumn("token_cache", column, definition);
|
|
747
|
+
}
|
|
748
|
+
for (const [column, definition] of [
|
|
749
|
+
["estimated_micros", "INTEGER"],
|
|
750
|
+
["settled_micros", "INTEGER"],
|
|
751
|
+
["settled_usd_micros", "INTEGER"],
|
|
752
|
+
["price_version", "TEXT"],
|
|
753
|
+
["balance_snapshot_micros", "INTEGER"],
|
|
754
|
+
["balance_source", "TEXT"]
|
|
755
|
+
]) {
|
|
756
|
+
this.ensureColumn("inference_ledger", column, definition);
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
private ensureColumn(table: string, column: string, definition: string): void {
|
|
761
|
+
const rows = this.db.prepare(`PRAGMA table_info(${table})`).all() as Array<{ name: string }>;
|
|
762
|
+
if (!rows.some((row) => row.name === column)) {
|
|
763
|
+
this.db.exec(`ALTER TABLE ${table} ADD COLUMN ${column} ${definition}`);
|
|
764
|
+
}
|
|
642
765
|
}
|
|
643
766
|
|
|
644
767
|
private countRows(tableName: string): number {
|