dskcode 0.1.27 → 0.1.28
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/index.js +51 -5
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -846,7 +846,15 @@ import { Box as Box5, Text as Text6 } from "ink";
|
|
|
846
846
|
// src/provider/cost-tracker.ts
|
|
847
847
|
import { mkdir as mkdir3, readFile as readFile3, writeFile as writeFile2 } from "fs/promises";
|
|
848
848
|
import { join as join3 } from "path";
|
|
849
|
-
var CostTracker = class {
|
|
849
|
+
var CostTracker = class _CostTracker {
|
|
850
|
+
/** 全局活跃实例表,用于进程退出时统一 flush。 */
|
|
851
|
+
static #instances = /* @__PURE__ */ new Set();
|
|
852
|
+
/** 进程退出兜底:统一 flush 所有活跃 CostTracker。 */
|
|
853
|
+
static async flushAll() {
|
|
854
|
+
await Promise.allSettled(
|
|
855
|
+
[..._CostTracker.#instances].map((t) => t.flush())
|
|
856
|
+
);
|
|
857
|
+
}
|
|
850
858
|
#costDir;
|
|
851
859
|
#budgetLimit;
|
|
852
860
|
#tokenBudgetLimit;
|
|
@@ -872,6 +880,7 @@ var CostTracker = class {
|
|
|
872
880
|
const today = getTodayStr();
|
|
873
881
|
this.#todayDate = today;
|
|
874
882
|
this.#todaySummary = createEmptyDailySummary(today);
|
|
883
|
+
_CostTracker.#instances.add(this);
|
|
875
884
|
}
|
|
876
885
|
// -----------------------------------------------------------------------
|
|
877
886
|
// 核心方法
|
|
@@ -879,6 +888,7 @@ var CostTracker = class {
|
|
|
879
888
|
/**
|
|
880
889
|
* 记录一次 API 调用的 Token 使用量和成本。
|
|
881
890
|
* 自动计算费用,累加到会话级和日级统计。
|
|
891
|
+
* 标记脏数据(dirty),由上层在合适的时机调用 flush() 持久化到磁盘。
|
|
882
892
|
*/
|
|
883
893
|
record(usage, model) {
|
|
884
894
|
const cost = calculateCost(usage, model);
|
|
@@ -987,8 +997,10 @@ var CostTracker = class {
|
|
|
987
997
|
/**
|
|
988
998
|
* 从磁盘加载历史成本数据。
|
|
989
999
|
* 启动时调用,用于恢复今日和历史数据。
|
|
1000
|
+
* 副作用:确保成本目录存在(首次运行时创建 ~/.dskcode/costs)。
|
|
990
1001
|
*/
|
|
991
1002
|
async load() {
|
|
1003
|
+
await this.#ensureCostDir();
|
|
992
1004
|
const store = await this.#loadStore();
|
|
993
1005
|
const today = getTodayStr();
|
|
994
1006
|
if (store.daily[today]) {
|
|
@@ -998,14 +1010,19 @@ var CostTracker = class {
|
|
|
998
1010
|
}
|
|
999
1011
|
/**
|
|
1000
1012
|
* 将脏数据持久化到磁盘。
|
|
1001
|
-
*
|
|
1013
|
+
* 会话结束/进程退出时主动调用一次即可。需要每日数据精确时也可在每次
|
|
1014
|
+
* record() 后调用。并发安全:靠 #flushInProgress 互斥。
|
|
1002
1015
|
*/
|
|
1003
1016
|
async flush() {
|
|
1004
1017
|
if (!this.#dirty || this.#flushInProgress) return;
|
|
1005
1018
|
this.#flushInProgress = true;
|
|
1006
1019
|
this.#dirty = false;
|
|
1007
1020
|
try {
|
|
1021
|
+
await this.#ensureCostDir();
|
|
1008
1022
|
await this.#saveStore();
|
|
1023
|
+
} catch (err) {
|
|
1024
|
+
this.#dirty = true;
|
|
1025
|
+
console.error("[CostTracker] flush \u5931\u8D25:", err);
|
|
1009
1026
|
} finally {
|
|
1010
1027
|
this.#flushInProgress = false;
|
|
1011
1028
|
}
|
|
@@ -1033,6 +1050,14 @@ var CostTracker = class {
|
|
|
1033
1050
|
this.#sessionStartedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1034
1051
|
this.#sessionRecords.length = 0;
|
|
1035
1052
|
}
|
|
1053
|
+
/**
|
|
1054
|
+
* 销毁实例:从全局实例表移除并 flush。
|
|
1055
|
+
* 一般用不到,仅在测试或显式生命周期管理时使用。
|
|
1056
|
+
*/
|
|
1057
|
+
async dispose() {
|
|
1058
|
+
_CostTracker.#instances.delete(this);
|
|
1059
|
+
await this.flush();
|
|
1060
|
+
}
|
|
1036
1061
|
// -----------------------------------------------------------------------
|
|
1037
1062
|
// 内部方法
|
|
1038
1063
|
// -----------------------------------------------------------------------
|
|
@@ -1085,6 +1110,10 @@ var CostTracker = class {
|
|
|
1085
1110
|
return { version: 1, daily: {} };
|
|
1086
1111
|
}
|
|
1087
1112
|
}
|
|
1113
|
+
/** 确保成本目录存在(首次运行时创建 ~/.dskcode/costs) */
|
|
1114
|
+
async #ensureCostDir() {
|
|
1115
|
+
await mkdir3(this.#costDir, { recursive: true });
|
|
1116
|
+
}
|
|
1088
1117
|
/** 保存成本存储到磁盘 */
|
|
1089
1118
|
async #saveStore() {
|
|
1090
1119
|
const store = await this.#loadStore();
|
|
@@ -1096,7 +1125,6 @@ var CostTracker = class {
|
|
|
1096
1125
|
for (const date of datesToRemove) {
|
|
1097
1126
|
delete store.daily[date];
|
|
1098
1127
|
}
|
|
1099
|
-
await mkdir3(this.#costDir, { recursive: true });
|
|
1100
1128
|
const filePath = join3(this.#costDir, "history.json");
|
|
1101
1129
|
await writeFile2(filePath, JSON.stringify(store, null, 2), "utf-8");
|
|
1102
1130
|
}
|
|
@@ -2413,6 +2441,8 @@ ${item.result.diff.patch}`;
|
|
|
2413
2441
|
}
|
|
2414
2442
|
const elapsed = Date.now() - startTime;
|
|
2415
2443
|
void this.#persist();
|
|
2444
|
+
await this.#costTracker.flush().catch(() => {
|
|
2445
|
+
});
|
|
2416
2446
|
yield { type: "done", elapsed };
|
|
2417
2447
|
}
|
|
2418
2448
|
async #executeBatch(calls) {
|
|
@@ -4630,10 +4660,11 @@ function ChatSession({
|
|
|
4630
4660
|
const cmdLower = trimmed.toLowerCase();
|
|
4631
4661
|
if (cmdLower === "/rewind" || cmdLower.startsWith("/rewind ")) {
|
|
4632
4662
|
if (isStreaming || rewinding) {
|
|
4663
|
+
const reason = rewinding ? "\u56DE\u9000\u4E2D" : "\u751F\u6210\u4E2D";
|
|
4633
4664
|
setDisplayMessages((prev) => [
|
|
4634
4665
|
...prev,
|
|
4635
4666
|
{ role: "user", content: trimmed },
|
|
4636
|
-
{ role: "assistant", content:
|
|
4667
|
+
{ role: "assistant", content: `\u26A0 \u6B63\u5728${reason}\uFF0C\u8BF7\u7A0D\u540E\u518D\u8BD5 /rewind\u3002` }
|
|
4637
4668
|
]);
|
|
4638
4669
|
setInput("");
|
|
4639
4670
|
return;
|
|
@@ -7081,10 +7112,18 @@ var ExitCode = {
|
|
|
7081
7112
|
// src/index.ts
|
|
7082
7113
|
var sigintCount = 0;
|
|
7083
7114
|
var sigintTimer = null;
|
|
7115
|
+
async function gracefulExit(code) {
|
|
7116
|
+
try {
|
|
7117
|
+
await CostTracker.flushAll();
|
|
7118
|
+
} catch {
|
|
7119
|
+
}
|
|
7120
|
+
process.exit(code);
|
|
7121
|
+
}
|
|
7084
7122
|
process.on("SIGINT", () => {
|
|
7085
7123
|
sigintCount++;
|
|
7086
7124
|
if (sigintCount >= 2) {
|
|
7087
|
-
|
|
7125
|
+
void gracefulExit(ExitCode.SIGINT);
|
|
7126
|
+
return;
|
|
7088
7127
|
}
|
|
7089
7128
|
process.stdout.write("\n \u26A0 \u518D\u6309\u4E00\u6B21 Ctrl+C \u9000\u51FA dskcode\n");
|
|
7090
7129
|
if (sigintTimer) clearTimeout(sigintTimer);
|
|
@@ -7092,18 +7131,25 @@ process.on("SIGINT", () => {
|
|
|
7092
7131
|
sigintCount = 0;
|
|
7093
7132
|
}, 1500);
|
|
7094
7133
|
});
|
|
7134
|
+
process.on("SIGTERM", () => {
|
|
7135
|
+
void gracefulExit(ExitCode.SIGINT);
|
|
7136
|
+
});
|
|
7095
7137
|
var program = createCli();
|
|
7096
7138
|
try {
|
|
7097
7139
|
await program.parseAsync(process.argv);
|
|
7140
|
+
await CostTracker.flushAll();
|
|
7098
7141
|
} catch (err) {
|
|
7099
7142
|
const error = err;
|
|
7100
7143
|
if (error.code === "commander.helpDisplayed" || error.code === "commander.version") {
|
|
7144
|
+
await CostTracker.flushAll();
|
|
7101
7145
|
process.exit(error.exitCode ?? ExitCode.SUCCESS);
|
|
7102
7146
|
}
|
|
7103
7147
|
if (typeof error.exitCode === "number") {
|
|
7148
|
+
await CostTracker.flushAll();
|
|
7104
7149
|
process.exit(error.exitCode);
|
|
7105
7150
|
}
|
|
7106
7151
|
console.error(String(err));
|
|
7152
|
+
await CostTracker.flushAll();
|
|
7107
7153
|
process.exit(ExitCode.GENERAL_ERROR);
|
|
7108
7154
|
}
|
|
7109
7155
|
//# sourceMappingURL=index.js.map
|