dskcode 0.1.27 → 0.1.29
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 +330 -39
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -772,7 +772,7 @@ async function getAllSkills(cwd) {
|
|
|
772
772
|
|
|
773
773
|
// src/cli/index.tsx
|
|
774
774
|
import { readFile as readFile11 } from "fs/promises";
|
|
775
|
-
import { join as
|
|
775
|
+
import { join as join12 } from "path";
|
|
776
776
|
|
|
777
777
|
// src/ui/RenderScope.tsx
|
|
778
778
|
import { render } from "ink";
|
|
@@ -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
|
}
|
|
@@ -2249,6 +2277,153 @@ function isENOENT(err) {
|
|
|
2249
2277
|
return typeof err === "object" && err !== null && "code" in err && err.code === "ENOENT";
|
|
2250
2278
|
}
|
|
2251
2279
|
|
|
2280
|
+
// src/logger/logger.ts
|
|
2281
|
+
import { mkdir as mkdir5, appendFile } from "fs/promises";
|
|
2282
|
+
import { join as join5, basename } from "path";
|
|
2283
|
+
var MAX_DATA_LEN = 2e3;
|
|
2284
|
+
var MAX_QUEUE = 500;
|
|
2285
|
+
function defaultLogsDir() {
|
|
2286
|
+
const home = process.env.HOME ?? process.env.USERPROFILE ?? "~";
|
|
2287
|
+
return join5(home, ".dskcode", "logs");
|
|
2288
|
+
}
|
|
2289
|
+
function projectNameFromCwd(cwd) {
|
|
2290
|
+
const name = basename(cwd);
|
|
2291
|
+
return name || "_root";
|
|
2292
|
+
}
|
|
2293
|
+
var ConversationLogger = class _ConversationLogger {
|
|
2294
|
+
/** 所有活跃的 logger 实例(用于退出时统一 flush) */
|
|
2295
|
+
static #instances = /* @__PURE__ */ new Set();
|
|
2296
|
+
/**
|
|
2297
|
+
* 刷新所有活跃 logger 实例的缓冲,确保事件落盘。
|
|
2298
|
+
* 进程退出前调用。
|
|
2299
|
+
*/
|
|
2300
|
+
static async flushAll() {
|
|
2301
|
+
const promises = [];
|
|
2302
|
+
for (const logger of _ConversationLogger.#instances) {
|
|
2303
|
+
promises.push(logger.flush());
|
|
2304
|
+
}
|
|
2305
|
+
await Promise.allSettled(promises);
|
|
2306
|
+
}
|
|
2307
|
+
#logPath;
|
|
2308
|
+
#enabled;
|
|
2309
|
+
#queue = Promise.resolve();
|
|
2310
|
+
#queueLen = 0;
|
|
2311
|
+
#closed = false;
|
|
2312
|
+
constructor(sessionId, cwd, options) {
|
|
2313
|
+
this.#enabled = options?.enabled ?? true;
|
|
2314
|
+
if (!this.#enabled) {
|
|
2315
|
+
this.#logPath = "";
|
|
2316
|
+
return;
|
|
2317
|
+
}
|
|
2318
|
+
const baseDir = options?.logsDir ?? defaultLogsDir();
|
|
2319
|
+
const projectName = projectNameFromCwd(cwd);
|
|
2320
|
+
this.#logPath = join5(baseDir, projectName, `${sessionId}.jsonl`);
|
|
2321
|
+
_ConversationLogger.#instances.add(this);
|
|
2322
|
+
}
|
|
2323
|
+
/** 日志文件完整路径(禁用时返回空字符串) */
|
|
2324
|
+
get logPath() {
|
|
2325
|
+
return this.#logPath;
|
|
2326
|
+
}
|
|
2327
|
+
/** 是否启用日志记录 */
|
|
2328
|
+
get enabled() {
|
|
2329
|
+
return this.#enabled;
|
|
2330
|
+
}
|
|
2331
|
+
/**
|
|
2332
|
+
* 记录一个事件。
|
|
2333
|
+
*
|
|
2334
|
+
* 内部将事件序列化为 JSON 并追加到日志文件。
|
|
2335
|
+
* 写入操作串行排队,保证事件顺序。调用方无需 await。
|
|
2336
|
+
*/
|
|
2337
|
+
log(event) {
|
|
2338
|
+
if (!this.#enabled || this.#closed) return;
|
|
2339
|
+
if (this.#queueLen >= MAX_QUEUE) return;
|
|
2340
|
+
this.#queueLen++;
|
|
2341
|
+
this.#queue = this.#queue.then(() => this.#writeLine(event)).catch(() => {
|
|
2342
|
+
}).finally(() => this.#queueLen--);
|
|
2343
|
+
}
|
|
2344
|
+
/**
|
|
2345
|
+
* 关闭日志记录器。
|
|
2346
|
+
*
|
|
2347
|
+
* 标记关闭,不再接受新事件,但会等待队列中已有事件写入完成。
|
|
2348
|
+
* 返回的 Promise resolve 后保证所有已 log 的事件都已落盘。
|
|
2349
|
+
*/
|
|
2350
|
+
async flush() {
|
|
2351
|
+
this.#closed = true;
|
|
2352
|
+
await this.#queue.catch(() => {
|
|
2353
|
+
});
|
|
2354
|
+
_ConversationLogger.#instances.delete(this);
|
|
2355
|
+
}
|
|
2356
|
+
/** 将一行 JSON 写入日志文件 */
|
|
2357
|
+
async #writeLine(event) {
|
|
2358
|
+
const dir = join5(this.#logPath, "..");
|
|
2359
|
+
await mkdir5(dir, { recursive: true });
|
|
2360
|
+
const line = JSON.stringify(event) + "\n";
|
|
2361
|
+
await appendFile(this.#logPath, line, "utf-8");
|
|
2362
|
+
}
|
|
2363
|
+
// -----------------------------------------------------------------------
|
|
2364
|
+
// 便捷方法 — 封装常见事件类型,减少调用方样板代码
|
|
2365
|
+
// -----------------------------------------------------------------------
|
|
2366
|
+
/** 记录会话开始 */
|
|
2367
|
+
logSessionStart(sessionId, cwd, model, mode) {
|
|
2368
|
+
this.log({ ts: Date.now(), type: "session_start", sessionId, cwd, model, mode });
|
|
2369
|
+
}
|
|
2370
|
+
/** 记录用户消息 */
|
|
2371
|
+
logUserMessage(content) {
|
|
2372
|
+
this.log({ ts: Date.now(), type: "user_message", content });
|
|
2373
|
+
}
|
|
2374
|
+
/** 记录助手文本(一轮中模型输出的完整文本) */
|
|
2375
|
+
logAssistantText(content, round) {
|
|
2376
|
+
this.log({ ts: Date.now(), type: "assistant_text", content, round });
|
|
2377
|
+
}
|
|
2378
|
+
/** 记录工具调用 */
|
|
2379
|
+
logToolCall(name, callId, args, round) {
|
|
2380
|
+
this.log({ ts: Date.now(), type: "tool_call", name, callId, arguments: args, round });
|
|
2381
|
+
}
|
|
2382
|
+
/** 记录工具结果 */
|
|
2383
|
+
logToolResult(name, callId, success, data, error, elapsed, round) {
|
|
2384
|
+
this.log({
|
|
2385
|
+
ts: Date.now(),
|
|
2386
|
+
type: "tool_result",
|
|
2387
|
+
name,
|
|
2388
|
+
callId,
|
|
2389
|
+
success,
|
|
2390
|
+
data: truncate(data),
|
|
2391
|
+
...error ? { error } : {},
|
|
2392
|
+
...elapsed !== void 0 ? { elapsed } : {},
|
|
2393
|
+
round
|
|
2394
|
+
});
|
|
2395
|
+
}
|
|
2396
|
+
/** 记录 Token 用量与费用 */
|
|
2397
|
+
logUsage(model, promptTokens, completionTokens, cachedPromptTokens, cost, round) {
|
|
2398
|
+
this.log({
|
|
2399
|
+
ts: Date.now(),
|
|
2400
|
+
type: "usage",
|
|
2401
|
+
model,
|
|
2402
|
+
promptTokens,
|
|
2403
|
+
completionTokens,
|
|
2404
|
+
...cachedPromptTokens !== void 0 ? { cachedPromptTokens } : {},
|
|
2405
|
+
cost,
|
|
2406
|
+
round
|
|
2407
|
+
});
|
|
2408
|
+
}
|
|
2409
|
+
/** 记录错误 */
|
|
2410
|
+
logError(message, stack) {
|
|
2411
|
+
this.log({ ts: Date.now(), type: "error", message, ...stack ? { stack } : {} });
|
|
2412
|
+
}
|
|
2413
|
+
/** 记录一轮对话完成 */
|
|
2414
|
+
logTurnDone(elapsed, toolRounds) {
|
|
2415
|
+
this.log({ ts: Date.now(), type: "turn_done", elapsed, toolRounds });
|
|
2416
|
+
}
|
|
2417
|
+
/** 记录会话结束 */
|
|
2418
|
+
logSessionEnd(elapsed) {
|
|
2419
|
+
this.log({ ts: Date.now(), type: "session_end", elapsed });
|
|
2420
|
+
}
|
|
2421
|
+
};
|
|
2422
|
+
function truncate(text) {
|
|
2423
|
+
if (text.length <= MAX_DATA_LEN) return text;
|
|
2424
|
+
return text.slice(0, MAX_DATA_LEN) + `...[\u5DF2\u622A\u65AD\uFF0C\u539F\u59CB\u957F\u5EA6 ${text.length}]`;
|
|
2425
|
+
}
|
|
2426
|
+
|
|
2252
2427
|
// src/agent/index.ts
|
|
2253
2428
|
var Session = class _Session {
|
|
2254
2429
|
#messages = [];
|
|
@@ -2264,6 +2439,7 @@ var Session = class _Session {
|
|
|
2264
2439
|
#checkpoints = /* @__PURE__ */ new Map();
|
|
2265
2440
|
#stormRecords = [];
|
|
2266
2441
|
#mode = "code";
|
|
2442
|
+
#logger;
|
|
2267
2443
|
constructor(provider, tools = [], costTracker, options) {
|
|
2268
2444
|
this.#provider = provider;
|
|
2269
2445
|
if (tools instanceof ToolRegistry) {
|
|
@@ -2281,11 +2457,16 @@ var Session = class _Session {
|
|
|
2281
2457
|
projectContext: options?.projectContext,
|
|
2282
2458
|
gate: options?.gate ?? new AlwaysAllowGate(),
|
|
2283
2459
|
writeRoots: options?.writeRoots ?? [options?.cwd ?? process.cwd()],
|
|
2284
|
-
enableCheckpoint: options?.enableCheckpoint ?? true
|
|
2460
|
+
enableCheckpoint: options?.enableCheckpoint ?? true,
|
|
2461
|
+
enableLog: options?.enableLog ?? true
|
|
2285
2462
|
};
|
|
2286
2463
|
this.#sessionId = options?.sessionId ?? SessionStore.newId();
|
|
2287
2464
|
this.#store = options?.store === false ? null : options?.store ?? new SessionStore();
|
|
2288
2465
|
this.#createdAt = Date.now();
|
|
2466
|
+
this.#logger = new ConversationLogger(this.#sessionId, this.#options.cwd, {
|
|
2467
|
+
enabled: this.#options.enableLog
|
|
2468
|
+
});
|
|
2469
|
+
this.#logger.logSessionStart(this.#sessionId, this.#options.cwd, this.#provider.model(), this.#mode);
|
|
2289
2470
|
}
|
|
2290
2471
|
get messages() {
|
|
2291
2472
|
return this.#messages;
|
|
@@ -2320,6 +2501,7 @@ var Session = class _Session {
|
|
|
2320
2501
|
}
|
|
2321
2502
|
async *chat(userInput, opts) {
|
|
2322
2503
|
this.#messages.push({ role: "user", content: userInput });
|
|
2504
|
+
this.#logger.logUserMessage(userInput);
|
|
2323
2505
|
const userMsgIndex = this.#messages.length - 1;
|
|
2324
2506
|
if (this.#options.enableCheckpoint) {
|
|
2325
2507
|
try {
|
|
@@ -2368,13 +2550,26 @@ var Session = class _Session {
|
|
|
2368
2550
|
if (lastUsage) {
|
|
2369
2551
|
const modelId = this.#provider.model();
|
|
2370
2552
|
this.#costTracker.record(lastUsage, modelId);
|
|
2553
|
+
const cost = calculateCost(lastUsage, modelId);
|
|
2554
|
+
this.#logger.logUsage(
|
|
2555
|
+
modelId,
|
|
2556
|
+
lastUsage.promptTokens,
|
|
2557
|
+
lastUsage.completionTokens,
|
|
2558
|
+
lastUsage.cachedPromptTokens,
|
|
2559
|
+
cost.totalCost,
|
|
2560
|
+
toolRounds
|
|
2561
|
+
);
|
|
2371
2562
|
yield { type: "usage", usage: lastUsage, model: modelId };
|
|
2372
2563
|
}
|
|
2373
2564
|
const assistantMsg = { role: "assistant", content: accumulatedText };
|
|
2374
2565
|
if (lastToolCalls && lastToolCalls.length > 0) assistantMsg.toolCalls = lastToolCalls;
|
|
2375
2566
|
this.#messages.push(assistantMsg);
|
|
2567
|
+
this.#logger.logAssistantText(accumulatedText, toolRounds);
|
|
2376
2568
|
if (lastToolCalls && lastToolCalls.length > 0) {
|
|
2377
2569
|
yield { type: "tool_calls", calls: lastToolCalls };
|
|
2570
|
+
for (const tc of lastToolCalls) {
|
|
2571
|
+
this.#logger.logToolCall(tc.name, tc.id, tc.arguments, toolRounds);
|
|
2572
|
+
}
|
|
2378
2573
|
const stormBroken = this.#checkStormBreak(lastToolCalls);
|
|
2379
2574
|
if (stormBroken) {
|
|
2380
2575
|
const stormMsg = "\n\u26A0\uFE0F \u540C\u4E00\u5DE5\u5177\u91CD\u590D\u51FA\u9519\uFF0C\u5DF2\u5F3A\u5236\u5207\u6362\u7B56\u7565\n";
|
|
@@ -2389,6 +2584,15 @@ var Session = class _Session {
|
|
|
2389
2584
|
this.#stormRecords = results.records;
|
|
2390
2585
|
for (const item of results.items) {
|
|
2391
2586
|
yield { type: "tool_result", name: item.name, result: item.result };
|
|
2587
|
+
this.#logger.logToolResult(
|
|
2588
|
+
item.name,
|
|
2589
|
+
item.callId,
|
|
2590
|
+
item.result.success,
|
|
2591
|
+
item.result.data,
|
|
2592
|
+
item.result.error,
|
|
2593
|
+
void 0,
|
|
2594
|
+
toolRounds
|
|
2595
|
+
);
|
|
2392
2596
|
let toolContent = item.result.data;
|
|
2393
2597
|
if (item.result.diff && item.result.diff.patch) toolContent += `
|
|
2394
2598
|
|
|
@@ -2408,11 +2612,18 @@ ${item.result.diff.patch}`;
|
|
|
2408
2612
|
} catch (err) {
|
|
2409
2613
|
if (err instanceof DOMException && err.name === "AbortError") return;
|
|
2410
2614
|
if (err instanceof Error && err.name === "AbortError") return;
|
|
2615
|
+
this.#logger.logError(
|
|
2616
|
+
err instanceof Error ? err.message : String(err),
|
|
2617
|
+
err instanceof Error ? err.stack : void 0
|
|
2618
|
+
);
|
|
2411
2619
|
yield { type: "error", error: err instanceof Error ? err : new Error(String(err)) };
|
|
2412
2620
|
return;
|
|
2413
2621
|
}
|
|
2414
2622
|
const elapsed = Date.now() - startTime;
|
|
2623
|
+
this.#logger.logTurnDone(elapsed, toolRounds);
|
|
2415
2624
|
void this.#persist();
|
|
2625
|
+
await this.#costTracker.flush().catch(() => {
|
|
2626
|
+
});
|
|
2416
2627
|
yield { type: "done", elapsed };
|
|
2417
2628
|
}
|
|
2418
2629
|
async #executeBatch(calls) {
|
|
@@ -2514,6 +2725,10 @@ ${item.result.diff.patch}`;
|
|
|
2514
2725
|
this.#persistTimer = null;
|
|
2515
2726
|
}
|
|
2516
2727
|
}
|
|
2728
|
+
/** 刷新日志缓冲,确保所有已记录的事件落盘 */
|
|
2729
|
+
async flushLog() {
|
|
2730
|
+
await this.#logger.flush();
|
|
2731
|
+
}
|
|
2517
2732
|
reset() {
|
|
2518
2733
|
this.#messages.length = 0;
|
|
2519
2734
|
this.#costTracker.resetSession();
|
|
@@ -2655,6 +2870,8 @@ ${item.result.diff.patch}`;
|
|
|
2655
2870
|
void discardCheckpoint(cp2);
|
|
2656
2871
|
}
|
|
2657
2872
|
this.#checkpoints.clear();
|
|
2873
|
+
this.#logger.logSessionEnd(Date.now() - this.#createdAt);
|
|
2874
|
+
await this.#logger.flush();
|
|
2658
2875
|
}
|
|
2659
2876
|
// -------------------------------------------------------------------------
|
|
2660
2877
|
// 内部方法
|
|
@@ -2750,6 +2967,10 @@ async function execCommand(command, args, cwd, timeoutMs = DEFAULT_TIMEOUT_MS, s
|
|
|
2750
2967
|
spawnCmd = command;
|
|
2751
2968
|
spawnArgs = args;
|
|
2752
2969
|
useShell = false;
|
|
2970
|
+
if (isWindows && spawnCmd === "cmd" && spawnArgs[0] === "/c") {
|
|
2971
|
+
const prefixed = `chcp 65001 >nul && ${spawnArgs[1] ?? ""}`;
|
|
2972
|
+
spawnArgs = ["/c", prefixed];
|
|
2973
|
+
}
|
|
2753
2974
|
} else {
|
|
2754
2975
|
useShell = !isWindows;
|
|
2755
2976
|
spawnCmd = isWindows ? "cmd" : command;
|
|
@@ -2764,10 +2985,10 @@ async function execCommand(command, args, cwd, timeoutMs = DEFAULT_TIMEOUT_MS, s
|
|
|
2764
2985
|
let stdout = "";
|
|
2765
2986
|
let stderr = "";
|
|
2766
2987
|
child.stdout.on("data", (data) => {
|
|
2767
|
-
stdout += data.toString();
|
|
2988
|
+
stdout += data.toString("utf8");
|
|
2768
2989
|
});
|
|
2769
2990
|
child.stderr.on("data", (data) => {
|
|
2770
|
-
stderr += data.toString();
|
|
2991
|
+
stderr += data.toString("utf8");
|
|
2771
2992
|
});
|
|
2772
2993
|
const timeout = setTimeout(() => {
|
|
2773
2994
|
child.kill("SIGTERM");
|
|
@@ -3213,8 +3434,8 @@ var readFileTool = {
|
|
|
3213
3434
|
};
|
|
3214
3435
|
|
|
3215
3436
|
// src/tool/builtins/write-file.ts
|
|
3216
|
-
import { mkdir as
|
|
3217
|
-
import { dirname, relative as relative3, basename } from "path";
|
|
3437
|
+
import { mkdir as mkdir6, readFile as readFile6 } from "fs/promises";
|
|
3438
|
+
import { dirname, relative as relative3, basename as basename2 } from "path";
|
|
3218
3439
|
var writeFileTool = {
|
|
3219
3440
|
name: "write_file",
|
|
3220
3441
|
kind: "edit" /* Edit */,
|
|
@@ -3256,7 +3477,7 @@ var writeFileTool = {
|
|
|
3256
3477
|
existedBefore = true;
|
|
3257
3478
|
} catch {
|
|
3258
3479
|
}
|
|
3259
|
-
await
|
|
3480
|
+
await mkdir6(dirname(filePath), { recursive: true });
|
|
3260
3481
|
const content = args.content;
|
|
3261
3482
|
await writeFileWithEol(filePath, oldContent, content);
|
|
3262
3483
|
const diff = computeFileDiff(oldContent, content, filePath);
|
|
@@ -3265,7 +3486,7 @@ var writeFileTool = {
|
|
|
3265
3486
|
const byteSize = Buffer.byteLength(content, "utf-8");
|
|
3266
3487
|
const action = existedBefore ? "\u5DF2\u4FEE\u6539" : "\u5DF2\u521B\u5EFA";
|
|
3267
3488
|
const diffSummary = existedBefore ? `\uFF0C+${diff.additions} -${diff.deletions}` : `\uFF0C+${diff.additions} \u884C\uFF08\u65B0\u5EFA\uFF09`;
|
|
3268
|
-
const fileName =
|
|
3489
|
+
const fileName = basename2(filePath);
|
|
3269
3490
|
const summary = existedBefore ? `\u{1F4DD} \u4FEE\u6539: ${fileName} (+${diff.additions} -${diff.deletions})` : `\u{1F4DD} \u65B0\u5EFA: ${fileName} (+${diff.additions} \u884C)`;
|
|
3270
3491
|
return {
|
|
3271
3492
|
success: true,
|
|
@@ -3286,7 +3507,7 @@ var writeFileTool = {
|
|
|
3286
3507
|
|
|
3287
3508
|
// src/tool/builtins/edit-file.ts
|
|
3288
3509
|
import { readFile as readFile7 } from "fs/promises";
|
|
3289
|
-
import { basename as
|
|
3510
|
+
import { basename as basename3 } from "path";
|
|
3290
3511
|
var editFileTool = {
|
|
3291
3512
|
name: "edit_file",
|
|
3292
3513
|
kind: "edit" /* Edit */,
|
|
@@ -3354,7 +3575,7 @@ var editFileTool = {
|
|
|
3354
3575
|
const oldLines = args.old_text.split("\n").length;
|
|
3355
3576
|
const newLines = args.new_text.split("\n").length;
|
|
3356
3577
|
const diffSummary = `+${diff.additions} -${diff.deletions}`;
|
|
3357
|
-
const summary = `\u{1F4DD} \u4FEE\u6539: ${
|
|
3578
|
+
const summary = `\u{1F4DD} \u4FEE\u6539: ${basename3(filePath)} (+${diff.additions} -${diff.deletions})`;
|
|
3358
3579
|
return {
|
|
3359
3580
|
success: true,
|
|
3360
3581
|
data: `\u6587\u4EF6\u5DF2\u7F16\u8F91\uFF1A${filePath}
|
|
@@ -3377,7 +3598,7 @@ ${oldLines} \u884C \u2192 ${newLines} \u884C
|
|
|
3377
3598
|
|
|
3378
3599
|
// src/tool/builtins/multi-edit.ts
|
|
3379
3600
|
import { readFile as readFile8 } from "fs/promises";
|
|
3380
|
-
import { basename as
|
|
3601
|
+
import { basename as basename4 } from "path";
|
|
3381
3602
|
var multiEditTool = {
|
|
3382
3603
|
name: "multi_edit",
|
|
3383
3604
|
kind: "edit" /* Edit */,
|
|
@@ -3475,7 +3696,7 @@ var multiEditTool = {
|
|
|
3475
3696
|
data: `\u6587\u4EF6\u5DF2\u7F16\u8F91\uFF1A${filePath}
|
|
3476
3697
|
\u5171\u6267\u884C ${args.edits.length} \u6B65\u66FF\u6362
|
|
3477
3698
|
\u53D8\u66F4\uFF1A+${diff.additions} -${diff.deletions}`,
|
|
3478
|
-
summary: `\u{1F4DD} \u4FEE\u6539: ${
|
|
3699
|
+
summary: `\u{1F4DD} \u4FEE\u6539: ${basename4(filePath)} (${args.edits.length} \u6B65, +${diff.additions} -${diff.deletions})`,
|
|
3479
3700
|
diff
|
|
3480
3701
|
};
|
|
3481
3702
|
} catch (err) {
|
|
@@ -3491,7 +3712,7 @@ var multiEditTool = {
|
|
|
3491
3712
|
|
|
3492
3713
|
// src/tool/builtins/delete-range.ts
|
|
3493
3714
|
import { readFile as readFile9 } from "fs/promises";
|
|
3494
|
-
import { basename as
|
|
3715
|
+
import { basename as basename5 } from "path";
|
|
3495
3716
|
function findUniqueLine(lines, anchor, label) {
|
|
3496
3717
|
const matches = [];
|
|
3497
3718
|
for (let i = 0; i < lines.length; i++) {
|
|
@@ -3587,7 +3808,7 @@ var deleteRangeTool = {
|
|
|
3587
3808
|
const diff = computeFileDiff(content, newContent, filePath);
|
|
3588
3809
|
diff.existedBefore = true;
|
|
3589
3810
|
const deletedLines = rangeEnd - rangeStart + 1;
|
|
3590
|
-
const summary = `\u{1F4DD} \u4FEE\u6539: ${
|
|
3811
|
+
const summary = `\u{1F4DD} \u4FEE\u6539: ${basename5(filePath)} (\u5220 ${deletedLines} \u884C, +${diff.additions} -${diff.deletions})`;
|
|
3591
3812
|
return {
|
|
3592
3813
|
success: true,
|
|
3593
3814
|
data: `\u6587\u4EF6\u5DF2\u7F16\u8F91\uFF1A${filePath}
|
|
@@ -3609,17 +3830,62 @@ var deleteRangeTool = {
|
|
|
3609
3830
|
|
|
3610
3831
|
// src/tool/builtins/bash.ts
|
|
3611
3832
|
import process3 from "process";
|
|
3833
|
+
import { existsSync as existsSync3 } from "fs";
|
|
3834
|
+
import { join as join6 } from "path";
|
|
3612
3835
|
var isWindows2 = process3.platform === "win32";
|
|
3836
|
+
function detectShell() {
|
|
3837
|
+
if (!isWindows2) {
|
|
3838
|
+
return {
|
|
3839
|
+
bin: "sh",
|
|
3840
|
+
args: ["-c"],
|
|
3841
|
+
label: "sh",
|
|
3842
|
+
hint: "\u5F53\u524D shell \u662F bash \u517C\u5BB9\u8BED\u6CD5\uFF0C\u652F\u6301 pwd/ls/&&/\u7BA1\u9053\u7B49\u6807\u51C6 Unix \u5DE5\u5177\u3002"
|
|
3843
|
+
};
|
|
3844
|
+
}
|
|
3845
|
+
const envBash = process3.env.DSKCODE_BASH;
|
|
3846
|
+
if (envBash && existsSync3(envBash)) {
|
|
3847
|
+
return { bin: envBash, args: ["-c"], label: "Git Bash", hint: SHELL_HINT_BASH };
|
|
3848
|
+
}
|
|
3849
|
+
const roots = [
|
|
3850
|
+
process3.env.ProgramFiles,
|
|
3851
|
+
process3.env["ProgramFiles(x86)"],
|
|
3852
|
+
process3.env.LOCALAPPDATA ? join6(process3.env.LOCALAPPDATA, "Programs") : null,
|
|
3853
|
+
// 非 C 盘安装(你机器就是 D 盘),枚举几个常见盘符根
|
|
3854
|
+
"D:\\Program Files",
|
|
3855
|
+
"E:\\Program Files"
|
|
3856
|
+
].filter((v) => Boolean(v));
|
|
3857
|
+
for (const root of roots) {
|
|
3858
|
+
for (const sub of ["Git\\bin\\bash.exe", "Git\\usr\\bin\\bash.exe"]) {
|
|
3859
|
+
const candidate = join6(root, sub);
|
|
3860
|
+
if (existsSync3(candidate)) {
|
|
3861
|
+
return { bin: candidate, args: ["-c"], label: "Git Bash", hint: SHELL_HINT_BASH };
|
|
3862
|
+
}
|
|
3863
|
+
}
|
|
3864
|
+
}
|
|
3865
|
+
return {
|
|
3866
|
+
bin: "cmd",
|
|
3867
|
+
args: ["/c"],
|
|
3868
|
+
label: "cmd.exe",
|
|
3869
|
+
hint: "\u5F53\u524D shell \u662F Windows cmd.exe\u3002\u8BF7\u4F7F\u7528 cmd \u8BED\u6CD5\uFF1A\u8DEF\u5F84\u7528\u53CD\u659C\u6760\u6216\u6B63\u659C\u6760\u5747\u53EF\u3001\u547D\u4EE4\u94FE\u7528 & \u6216\u6362\u884C\uFF0C\u907F\u514D\u4F7F\u7528 pwd/ls/&& \u7B49 Unix \u547D\u4EE4\u3002"
|
|
3870
|
+
};
|
|
3871
|
+
}
|
|
3872
|
+
var SHELL_HINT_BASH = "\u5F53\u524D shell \u662F bash \u517C\u5BB9\u8BED\u6CD5\uFF0C\u652F\u6301 pwd/ls/&&/\u7BA1\u9053\u7B49\u6807\u51C6 Unix \u5DE5\u5177\u3002";
|
|
3873
|
+
var SHELL_INFO = detectShell();
|
|
3613
3874
|
var bashTool = {
|
|
3614
3875
|
name: "bash",
|
|
3615
3876
|
kind: "other" /* Other */,
|
|
3616
|
-
description:
|
|
3877
|
+
description: [
|
|
3878
|
+
"\u5728 shell \u4E2D\u6267\u884C\u547D\u4EE4\uFF0C\u8FD4\u56DE stdout / stderr / \u9000\u51FA\u7801\u3002\u652F\u6301\u8D85\u65F6\u63A7\u5236\u548C\u4FE1\u53F7\u4E2D\u6B62\u3002",
|
|
3879
|
+
`\u5F53\u524D shell\uFF1A${SHELL_INFO.label}\u3002${SHELL_INFO.hint}`,
|
|
3880
|
+
"\u8BFB\u53D6\u6587\u4EF6\u8BF7\u4F18\u5148\u4F7F\u7528 read_file \u5DE5\u5177\uFF0C\u4E0D\u8981\u7528 cat/type/Get-Content\uFF1B",
|
|
3881
|
+
"\u641C\u7D22\u4EE3\u7801\u7528 grep / glob\uFF0C\u5217\u76EE\u5F55\u7528 ls\u2014\u2014\u8FD9\u4E9B\u4E13\u7528\u5DE5\u5177\u5728\u6240\u6709\u5E73\u53F0\u90FD\u53EF\u7528\u4E14\u65E0\u9700\u5173\u5FC3 shell \u8BED\u6CD5\u3002"
|
|
3882
|
+
].join(""),
|
|
3617
3883
|
parameters: {
|
|
3618
3884
|
type: "object",
|
|
3619
3885
|
properties: {
|
|
3620
3886
|
command: {
|
|
3621
3887
|
type: "string",
|
|
3622
|
-
description:
|
|
3888
|
+
description: `\u8981\u6267\u884C\u7684 shell \u547D\u4EE4\uFF08\u5F53\u524D shell\uFF1A${SHELL_INFO.label}\uFF09`
|
|
3623
3889
|
},
|
|
3624
3890
|
timeout: {
|
|
3625
3891
|
type: "number",
|
|
@@ -3635,8 +3901,8 @@ var bashTool = {
|
|
|
3635
3901
|
}
|
|
3636
3902
|
const timeout = args.timeout ?? ctx.timeout ?? getDefaultTimeout();
|
|
3637
3903
|
try {
|
|
3638
|
-
const shellCommand =
|
|
3639
|
-
const shellArgs =
|
|
3904
|
+
const shellCommand = SHELL_INFO.bin ?? "cmd";
|
|
3905
|
+
const shellArgs = [...SHELL_INFO.args, args.command];
|
|
3640
3906
|
const result = await execCommand(
|
|
3641
3907
|
shellCommand,
|
|
3642
3908
|
shellArgs,
|
|
@@ -3677,7 +3943,7 @@ ${truncateOutput(result.stderr)}`);
|
|
|
3677
3943
|
|
|
3678
3944
|
// src/tool/builtins/glob.ts
|
|
3679
3945
|
import { readdir as readdir3, stat as stat2 } from "fs/promises";
|
|
3680
|
-
import { join as
|
|
3946
|
+
import { join as join7, relative as relative4, isAbsolute as isAbsolute2 } from "path";
|
|
3681
3947
|
function globToRegex(pattern) {
|
|
3682
3948
|
let regexStr = pattern;
|
|
3683
3949
|
regexStr = regexStr.replace(/\*\*\//g, "<<GLOBSTAR_SLASH>>");
|
|
@@ -3702,7 +3968,7 @@ async function walkDir(dir, baseDir) {
|
|
|
3702
3968
|
if (entry.isDirectory() && (entry.name === "node_modules" || entry.name === ".git")) {
|
|
3703
3969
|
continue;
|
|
3704
3970
|
}
|
|
3705
|
-
const fullPath =
|
|
3971
|
+
const fullPath = join7(dir, entry.name);
|
|
3706
3972
|
const relPath = relative4(baseDir, fullPath);
|
|
3707
3973
|
if (entry.isDirectory()) {
|
|
3708
3974
|
results.push(...await walkDir(fullPath, baseDir));
|
|
@@ -3735,7 +4001,7 @@ var globTool = {
|
|
|
3735
4001
|
if (!args?.pattern || typeof args.pattern !== "string") {
|
|
3736
4002
|
return { success: false, data: "\u7F3A\u5C11\u5FC5\u8981\u53C2\u6570 pattern", error: "INVALID_ARGS" };
|
|
3737
4003
|
}
|
|
3738
|
-
const searchDir = args.directory ? isAbsolute2(args.directory) ? args.directory :
|
|
4004
|
+
const searchDir = args.directory ? isAbsolute2(args.directory) ? args.directory : join7(ctx.cwd, args.directory) : ctx.cwd;
|
|
3739
4005
|
const regex = globToRegex(args.pattern);
|
|
3740
4006
|
try {
|
|
3741
4007
|
const dirStat = await stat2(searchDir);
|
|
@@ -3775,7 +4041,7 @@ var globTool = {
|
|
|
3775
4041
|
|
|
3776
4042
|
// src/tool/builtins/grep.ts
|
|
3777
4043
|
import { readdir as readdir4, readFile as readFile10, stat as stat3 } from "fs/promises";
|
|
3778
|
-
import { join as
|
|
4044
|
+
import { join as join8, relative as relative5, isAbsolute as isAbsolute3 } from "path";
|
|
3779
4045
|
async function collectFiles(dir, extension, maxFiles = 200) {
|
|
3780
4046
|
const results = [];
|
|
3781
4047
|
let entries;
|
|
@@ -3789,7 +4055,7 @@ async function collectFiles(dir, extension, maxFiles = 200) {
|
|
|
3789
4055
|
if (entry.isDirectory() && (entry.name === "node_modules" || entry.name === ".git" || entry.name === "dist")) {
|
|
3790
4056
|
continue;
|
|
3791
4057
|
}
|
|
3792
|
-
const fullPath =
|
|
4058
|
+
const fullPath = join8(dir, entry.name);
|
|
3793
4059
|
if (entry.isDirectory()) {
|
|
3794
4060
|
results.push(...await collectFiles(fullPath, extension, maxFiles - results.length));
|
|
3795
4061
|
} else {
|
|
@@ -3836,7 +4102,7 @@ var grepTool = {
|
|
|
3836
4102
|
if (!args?.pattern || typeof args.pattern !== "string") {
|
|
3837
4103
|
return { success: false, data: "\u7F3A\u5C11\u5FC5\u8981\u53C2\u6570 pattern", error: "INVALID_ARGS" };
|
|
3838
4104
|
}
|
|
3839
|
-
const searchDir = args.directory ? isAbsolute3(args.directory) ? args.directory :
|
|
4105
|
+
const searchDir = args.directory ? isAbsolute3(args.directory) ? args.directory : join8(ctx.cwd, args.directory) : ctx.cwd;
|
|
3840
4106
|
const maxFiles = args.max_files ?? 200;
|
|
3841
4107
|
try {
|
|
3842
4108
|
const flags = args.case_sensitive ? "g" : "gi";
|
|
@@ -3896,7 +4162,7 @@ var grepTool = {
|
|
|
3896
4162
|
|
|
3897
4163
|
// src/tool/builtins/ls.ts
|
|
3898
4164
|
import { readdir as readdir5, stat as stat4 } from "fs/promises";
|
|
3899
|
-
import { join as
|
|
4165
|
+
import { join as join9, relative as relative6 } from "path";
|
|
3900
4166
|
var lsTool = {
|
|
3901
4167
|
name: "ls",
|
|
3902
4168
|
kind: "read" /* Read */,
|
|
@@ -3934,7 +4200,7 @@ var lsTool = {
|
|
|
3934
4200
|
let sizeStr = "";
|
|
3935
4201
|
if (typeMark === "FILE") {
|
|
3936
4202
|
try {
|
|
3937
|
-
const fileStat = await stat4(
|
|
4203
|
+
const fileStat = await stat4(join9(dirPath, entry.name));
|
|
3938
4204
|
if (fileStat.size < 1024) {
|
|
3939
4205
|
sizeStr = `${fileStat.size}B`;
|
|
3940
4206
|
} else if (fileStat.size < 1024 * 1024) {
|
|
@@ -4373,7 +4639,12 @@ function ChatSession({
|
|
|
4373
4639
|
}
|
|
4374
4640
|
}, []);
|
|
4375
4641
|
const { doubleCtrlC, handleCtrlC } = useDoubleCtrlC(() => {
|
|
4376
|
-
|
|
4642
|
+
const s = sessionRef.current;
|
|
4643
|
+
if (s) {
|
|
4644
|
+
void s.flushLog().finally(() => process.exit(0));
|
|
4645
|
+
} else {
|
|
4646
|
+
process.exit(0);
|
|
4647
|
+
}
|
|
4377
4648
|
});
|
|
4378
4649
|
useInput(
|
|
4379
4650
|
useCallback2(
|
|
@@ -4630,10 +4901,11 @@ function ChatSession({
|
|
|
4630
4901
|
const cmdLower = trimmed.toLowerCase();
|
|
4631
4902
|
if (cmdLower === "/rewind" || cmdLower.startsWith("/rewind ")) {
|
|
4632
4903
|
if (isStreaming || rewinding) {
|
|
4904
|
+
const reason = rewinding ? "\u56DE\u9000\u4E2D" : "\u751F\u6210\u4E2D";
|
|
4633
4905
|
setDisplayMessages((prev) => [
|
|
4634
4906
|
...prev,
|
|
4635
4907
|
{ role: "user", content: trimmed },
|
|
4636
|
-
{ role: "assistant", content:
|
|
4908
|
+
{ role: "assistant", content: `\u26A0 \u6B63\u5728${reason}\uFF0C\u8BF7\u7A0D\u540E\u518D\u8BD5 /rewind\u3002` }
|
|
4637
4909
|
]);
|
|
4638
4910
|
setInput("");
|
|
4639
4911
|
return;
|
|
@@ -4772,6 +5044,7 @@ function ChatSession({
|
|
|
4772
5044
|
const result = cmd.handler();
|
|
4773
5045
|
switch (result.kind) {
|
|
4774
5046
|
case "exit":
|
|
5047
|
+
await sessionRef.current?.flushLog();
|
|
4775
5048
|
process.exit(0);
|
|
4776
5049
|
return;
|
|
4777
5050
|
case "clear":
|
|
@@ -4957,7 +5230,7 @@ function ChatSession({
|
|
|
4957
5230
|
}, 2e3);
|
|
4958
5231
|
}
|
|
4959
5232
|
}
|
|
4960
|
-
}, [onLaunchGame, onLaunchStock, currentContent, currentToolCalls, skills, skillSelectIndex, getFilteredSkills, thinkingEnabled, thinkingEffort, responseFormat, toolChoice, activeModel, sessionMode]);
|
|
5233
|
+
}, [onLaunchGame, onLaunchStock, currentContent, currentToolCalls, skills, skillSelectIndex, getFilteredSkills, thinkingEnabled, thinkingEffort, responseFormat, toolChoice, activeModel, sessionMode, isStreaming, rewinding]);
|
|
4961
5234
|
useEffect3(() => {
|
|
4962
5235
|
if (!isStreaming && externalCostTracker) {
|
|
4963
5236
|
setTodayCost(externalCostTracker.todayTotalCost);
|
|
@@ -6418,9 +6691,9 @@ import { Box as Box13, Text as Text14, useInput as useInput5 } from "ink";
|
|
|
6418
6691
|
import { useState as useState7, useCallback as useCallback6, useEffect as useEffect6, useMemo as useMemo2 } from "react";
|
|
6419
6692
|
import asciichart from "asciichart";
|
|
6420
6693
|
import os from "os";
|
|
6421
|
-
import { join as
|
|
6694
|
+
import { join as join10 } from "path";
|
|
6422
6695
|
import { jsx as jsx13, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
6423
|
-
var SETTINGS_PATH =
|
|
6696
|
+
var SETTINGS_PATH = join10(os.homedir(), ".dskcode", "settings.json");
|
|
6424
6697
|
function toFileUrl(p) {
|
|
6425
6698
|
const norm = p.replace(/\\/g, "/");
|
|
6426
6699
|
if (process.platform === "win32") {
|
|
@@ -6755,7 +7028,7 @@ function renderDetail(stock, _onBack, prices, countdown = 10, currentTime) {
|
|
|
6755
7028
|
|
|
6756
7029
|
// src/utils/scan-files.ts
|
|
6757
7030
|
import { readdir as readdir6 } from "fs/promises";
|
|
6758
|
-
import { join as
|
|
7031
|
+
import { join as join11, relative as relative7 } from "path";
|
|
6759
7032
|
var IGNORE_DIRS = /* @__PURE__ */ new Set([
|
|
6760
7033
|
"node_modules",
|
|
6761
7034
|
".git",
|
|
@@ -6820,12 +7093,12 @@ async function scanProjectFiles(baseDir, dir) {
|
|
|
6820
7093
|
} else if (entry.isFile()) {
|
|
6821
7094
|
const ext = name.slice(name.lastIndexOf(".")).toLowerCase();
|
|
6822
7095
|
if (SOURCE_EXTS.has(ext)) {
|
|
6823
|
-
files.push(relative7(baseDir,
|
|
7096
|
+
files.push(relative7(baseDir, join11(currentDir, name)));
|
|
6824
7097
|
}
|
|
6825
7098
|
}
|
|
6826
7099
|
}
|
|
6827
7100
|
const nested = await Promise.all(
|
|
6828
|
-
dirs.map((d) => scanProjectFiles(baseDir,
|
|
7101
|
+
dirs.map((d) => scanProjectFiles(baseDir, join11(currentDir, d)))
|
|
6829
7102
|
);
|
|
6830
7103
|
for (const n of nested) {
|
|
6831
7104
|
files.push(...n);
|
|
@@ -6913,7 +7186,7 @@ compdef _dskcode_completion dskcode`);
|
|
|
6913
7186
|
program2.command("stock").description("\u67E5\u770B\u81EA\u9009\u80A1\u5B9E\u65F6\u884C\u60C5").argument("[codes...]", "\u80A1\u7968\u4EE3\u7801\uFF08\u7A7A\u683C\u5206\u9694\uFF09\uFF0C\u5982 513090 600519").action(async function(codes) {
|
|
6914
7187
|
const _ctx = this.dskcodeCtx;
|
|
6915
7188
|
const home = process.env.HOME ?? process.env.USERPROFILE ?? "~";
|
|
6916
|
-
const globalConfigPath =
|
|
7189
|
+
const globalConfigPath = join12(home, ".dskcode", "settings.json");
|
|
6917
7190
|
let globalConfigHasStock = false;
|
|
6918
7191
|
try {
|
|
6919
7192
|
const raw = await readFile11(globalConfigPath, "utf-8");
|
|
@@ -7081,10 +7354,21 @@ var ExitCode = {
|
|
|
7081
7354
|
// src/index.ts
|
|
7082
7355
|
var sigintCount = 0;
|
|
7083
7356
|
var sigintTimer = null;
|
|
7357
|
+
async function gracefulExit(code) {
|
|
7358
|
+
try {
|
|
7359
|
+
await Promise.all([
|
|
7360
|
+
CostTracker.flushAll(),
|
|
7361
|
+
ConversationLogger.flushAll()
|
|
7362
|
+
]);
|
|
7363
|
+
} catch {
|
|
7364
|
+
}
|
|
7365
|
+
process.exit(code);
|
|
7366
|
+
}
|
|
7084
7367
|
process.on("SIGINT", () => {
|
|
7085
7368
|
sigintCount++;
|
|
7086
7369
|
if (sigintCount >= 2) {
|
|
7087
|
-
|
|
7370
|
+
void gracefulExit(ExitCode.SIGINT);
|
|
7371
|
+
return;
|
|
7088
7372
|
}
|
|
7089
7373
|
process.stdout.write("\n \u26A0 \u518D\u6309\u4E00\u6B21 Ctrl+C \u9000\u51FA dskcode\n");
|
|
7090
7374
|
if (sigintTimer) clearTimeout(sigintTimer);
|
|
@@ -7092,18 +7376,25 @@ process.on("SIGINT", () => {
|
|
|
7092
7376
|
sigintCount = 0;
|
|
7093
7377
|
}, 1500);
|
|
7094
7378
|
});
|
|
7379
|
+
process.on("SIGTERM", () => {
|
|
7380
|
+
void gracefulExit(ExitCode.SIGINT);
|
|
7381
|
+
});
|
|
7095
7382
|
var program = createCli();
|
|
7096
7383
|
try {
|
|
7097
7384
|
await program.parseAsync(process.argv);
|
|
7385
|
+
await Promise.all([CostTracker.flushAll(), ConversationLogger.flushAll()]);
|
|
7098
7386
|
} catch (err) {
|
|
7099
7387
|
const error = err;
|
|
7100
7388
|
if (error.code === "commander.helpDisplayed" || error.code === "commander.version") {
|
|
7389
|
+
await Promise.all([CostTracker.flushAll(), ConversationLogger.flushAll()]);
|
|
7101
7390
|
process.exit(error.exitCode ?? ExitCode.SUCCESS);
|
|
7102
7391
|
}
|
|
7103
7392
|
if (typeof error.exitCode === "number") {
|
|
7393
|
+
await Promise.all([CostTracker.flushAll(), ConversationLogger.flushAll()]);
|
|
7104
7394
|
process.exit(error.exitCode);
|
|
7105
7395
|
}
|
|
7106
7396
|
console.error(String(err));
|
|
7397
|
+
await Promise.all([CostTracker.flushAll(), ConversationLogger.flushAll()]);
|
|
7107
7398
|
process.exit(ExitCode.GENERAL_ERROR);
|
|
7108
7399
|
}
|
|
7109
7400
|
//# sourceMappingURL=index.js.map
|