dskcode 0.1.28 → 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 +284 -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";
|
|
@@ -2277,6 +2277,153 @@ function isENOENT(err) {
|
|
|
2277
2277
|
return typeof err === "object" && err !== null && "code" in err && err.code === "ENOENT";
|
|
2278
2278
|
}
|
|
2279
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
|
+
|
|
2280
2427
|
// src/agent/index.ts
|
|
2281
2428
|
var Session = class _Session {
|
|
2282
2429
|
#messages = [];
|
|
@@ -2292,6 +2439,7 @@ var Session = class _Session {
|
|
|
2292
2439
|
#checkpoints = /* @__PURE__ */ new Map();
|
|
2293
2440
|
#stormRecords = [];
|
|
2294
2441
|
#mode = "code";
|
|
2442
|
+
#logger;
|
|
2295
2443
|
constructor(provider, tools = [], costTracker, options) {
|
|
2296
2444
|
this.#provider = provider;
|
|
2297
2445
|
if (tools instanceof ToolRegistry) {
|
|
@@ -2309,11 +2457,16 @@ var Session = class _Session {
|
|
|
2309
2457
|
projectContext: options?.projectContext,
|
|
2310
2458
|
gate: options?.gate ?? new AlwaysAllowGate(),
|
|
2311
2459
|
writeRoots: options?.writeRoots ?? [options?.cwd ?? process.cwd()],
|
|
2312
|
-
enableCheckpoint: options?.enableCheckpoint ?? true
|
|
2460
|
+
enableCheckpoint: options?.enableCheckpoint ?? true,
|
|
2461
|
+
enableLog: options?.enableLog ?? true
|
|
2313
2462
|
};
|
|
2314
2463
|
this.#sessionId = options?.sessionId ?? SessionStore.newId();
|
|
2315
2464
|
this.#store = options?.store === false ? null : options?.store ?? new SessionStore();
|
|
2316
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);
|
|
2317
2470
|
}
|
|
2318
2471
|
get messages() {
|
|
2319
2472
|
return this.#messages;
|
|
@@ -2348,6 +2501,7 @@ var Session = class _Session {
|
|
|
2348
2501
|
}
|
|
2349
2502
|
async *chat(userInput, opts) {
|
|
2350
2503
|
this.#messages.push({ role: "user", content: userInput });
|
|
2504
|
+
this.#logger.logUserMessage(userInput);
|
|
2351
2505
|
const userMsgIndex = this.#messages.length - 1;
|
|
2352
2506
|
if (this.#options.enableCheckpoint) {
|
|
2353
2507
|
try {
|
|
@@ -2396,13 +2550,26 @@ var Session = class _Session {
|
|
|
2396
2550
|
if (lastUsage) {
|
|
2397
2551
|
const modelId = this.#provider.model();
|
|
2398
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
|
+
);
|
|
2399
2562
|
yield { type: "usage", usage: lastUsage, model: modelId };
|
|
2400
2563
|
}
|
|
2401
2564
|
const assistantMsg = { role: "assistant", content: accumulatedText };
|
|
2402
2565
|
if (lastToolCalls && lastToolCalls.length > 0) assistantMsg.toolCalls = lastToolCalls;
|
|
2403
2566
|
this.#messages.push(assistantMsg);
|
|
2567
|
+
this.#logger.logAssistantText(accumulatedText, toolRounds);
|
|
2404
2568
|
if (lastToolCalls && lastToolCalls.length > 0) {
|
|
2405
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
|
+
}
|
|
2406
2573
|
const stormBroken = this.#checkStormBreak(lastToolCalls);
|
|
2407
2574
|
if (stormBroken) {
|
|
2408
2575
|
const stormMsg = "\n\u26A0\uFE0F \u540C\u4E00\u5DE5\u5177\u91CD\u590D\u51FA\u9519\uFF0C\u5DF2\u5F3A\u5236\u5207\u6362\u7B56\u7565\n";
|
|
@@ -2417,6 +2584,15 @@ var Session = class _Session {
|
|
|
2417
2584
|
this.#stormRecords = results.records;
|
|
2418
2585
|
for (const item of results.items) {
|
|
2419
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
|
+
);
|
|
2420
2596
|
let toolContent = item.result.data;
|
|
2421
2597
|
if (item.result.diff && item.result.diff.patch) toolContent += `
|
|
2422
2598
|
|
|
@@ -2436,10 +2612,15 @@ ${item.result.diff.patch}`;
|
|
|
2436
2612
|
} catch (err) {
|
|
2437
2613
|
if (err instanceof DOMException && err.name === "AbortError") return;
|
|
2438
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
|
+
);
|
|
2439
2619
|
yield { type: "error", error: err instanceof Error ? err : new Error(String(err)) };
|
|
2440
2620
|
return;
|
|
2441
2621
|
}
|
|
2442
2622
|
const elapsed = Date.now() - startTime;
|
|
2623
|
+
this.#logger.logTurnDone(elapsed, toolRounds);
|
|
2443
2624
|
void this.#persist();
|
|
2444
2625
|
await this.#costTracker.flush().catch(() => {
|
|
2445
2626
|
});
|
|
@@ -2544,6 +2725,10 @@ ${item.result.diff.patch}`;
|
|
|
2544
2725
|
this.#persistTimer = null;
|
|
2545
2726
|
}
|
|
2546
2727
|
}
|
|
2728
|
+
/** 刷新日志缓冲,确保所有已记录的事件落盘 */
|
|
2729
|
+
async flushLog() {
|
|
2730
|
+
await this.#logger.flush();
|
|
2731
|
+
}
|
|
2547
2732
|
reset() {
|
|
2548
2733
|
this.#messages.length = 0;
|
|
2549
2734
|
this.#costTracker.resetSession();
|
|
@@ -2685,6 +2870,8 @@ ${item.result.diff.patch}`;
|
|
|
2685
2870
|
void discardCheckpoint(cp2);
|
|
2686
2871
|
}
|
|
2687
2872
|
this.#checkpoints.clear();
|
|
2873
|
+
this.#logger.logSessionEnd(Date.now() - this.#createdAt);
|
|
2874
|
+
await this.#logger.flush();
|
|
2688
2875
|
}
|
|
2689
2876
|
// -------------------------------------------------------------------------
|
|
2690
2877
|
// 内部方法
|
|
@@ -2780,6 +2967,10 @@ async function execCommand(command, args, cwd, timeoutMs = DEFAULT_TIMEOUT_MS, s
|
|
|
2780
2967
|
spawnCmd = command;
|
|
2781
2968
|
spawnArgs = args;
|
|
2782
2969
|
useShell = false;
|
|
2970
|
+
if (isWindows && spawnCmd === "cmd" && spawnArgs[0] === "/c") {
|
|
2971
|
+
const prefixed = `chcp 65001 >nul && ${spawnArgs[1] ?? ""}`;
|
|
2972
|
+
spawnArgs = ["/c", prefixed];
|
|
2973
|
+
}
|
|
2783
2974
|
} else {
|
|
2784
2975
|
useShell = !isWindows;
|
|
2785
2976
|
spawnCmd = isWindows ? "cmd" : command;
|
|
@@ -2794,10 +2985,10 @@ async function execCommand(command, args, cwd, timeoutMs = DEFAULT_TIMEOUT_MS, s
|
|
|
2794
2985
|
let stdout = "";
|
|
2795
2986
|
let stderr = "";
|
|
2796
2987
|
child.stdout.on("data", (data) => {
|
|
2797
|
-
stdout += data.toString();
|
|
2988
|
+
stdout += data.toString("utf8");
|
|
2798
2989
|
});
|
|
2799
2990
|
child.stderr.on("data", (data) => {
|
|
2800
|
-
stderr += data.toString();
|
|
2991
|
+
stderr += data.toString("utf8");
|
|
2801
2992
|
});
|
|
2802
2993
|
const timeout = setTimeout(() => {
|
|
2803
2994
|
child.kill("SIGTERM");
|
|
@@ -3243,8 +3434,8 @@ var readFileTool = {
|
|
|
3243
3434
|
};
|
|
3244
3435
|
|
|
3245
3436
|
// src/tool/builtins/write-file.ts
|
|
3246
|
-
import { mkdir as
|
|
3247
|
-
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";
|
|
3248
3439
|
var writeFileTool = {
|
|
3249
3440
|
name: "write_file",
|
|
3250
3441
|
kind: "edit" /* Edit */,
|
|
@@ -3286,7 +3477,7 @@ var writeFileTool = {
|
|
|
3286
3477
|
existedBefore = true;
|
|
3287
3478
|
} catch {
|
|
3288
3479
|
}
|
|
3289
|
-
await
|
|
3480
|
+
await mkdir6(dirname(filePath), { recursive: true });
|
|
3290
3481
|
const content = args.content;
|
|
3291
3482
|
await writeFileWithEol(filePath, oldContent, content);
|
|
3292
3483
|
const diff = computeFileDiff(oldContent, content, filePath);
|
|
@@ -3295,7 +3486,7 @@ var writeFileTool = {
|
|
|
3295
3486
|
const byteSize = Buffer.byteLength(content, "utf-8");
|
|
3296
3487
|
const action = existedBefore ? "\u5DF2\u4FEE\u6539" : "\u5DF2\u521B\u5EFA";
|
|
3297
3488
|
const diffSummary = existedBefore ? `\uFF0C+${diff.additions} -${diff.deletions}` : `\uFF0C+${diff.additions} \u884C\uFF08\u65B0\u5EFA\uFF09`;
|
|
3298
|
-
const fileName =
|
|
3489
|
+
const fileName = basename2(filePath);
|
|
3299
3490
|
const summary = existedBefore ? `\u{1F4DD} \u4FEE\u6539: ${fileName} (+${diff.additions} -${diff.deletions})` : `\u{1F4DD} \u65B0\u5EFA: ${fileName} (+${diff.additions} \u884C)`;
|
|
3300
3491
|
return {
|
|
3301
3492
|
success: true,
|
|
@@ -3316,7 +3507,7 @@ var writeFileTool = {
|
|
|
3316
3507
|
|
|
3317
3508
|
// src/tool/builtins/edit-file.ts
|
|
3318
3509
|
import { readFile as readFile7 } from "fs/promises";
|
|
3319
|
-
import { basename as
|
|
3510
|
+
import { basename as basename3 } from "path";
|
|
3320
3511
|
var editFileTool = {
|
|
3321
3512
|
name: "edit_file",
|
|
3322
3513
|
kind: "edit" /* Edit */,
|
|
@@ -3384,7 +3575,7 @@ var editFileTool = {
|
|
|
3384
3575
|
const oldLines = args.old_text.split("\n").length;
|
|
3385
3576
|
const newLines = args.new_text.split("\n").length;
|
|
3386
3577
|
const diffSummary = `+${diff.additions} -${diff.deletions}`;
|
|
3387
|
-
const summary = `\u{1F4DD} \u4FEE\u6539: ${
|
|
3578
|
+
const summary = `\u{1F4DD} \u4FEE\u6539: ${basename3(filePath)} (+${diff.additions} -${diff.deletions})`;
|
|
3388
3579
|
return {
|
|
3389
3580
|
success: true,
|
|
3390
3581
|
data: `\u6587\u4EF6\u5DF2\u7F16\u8F91\uFF1A${filePath}
|
|
@@ -3407,7 +3598,7 @@ ${oldLines} \u884C \u2192 ${newLines} \u884C
|
|
|
3407
3598
|
|
|
3408
3599
|
// src/tool/builtins/multi-edit.ts
|
|
3409
3600
|
import { readFile as readFile8 } from "fs/promises";
|
|
3410
|
-
import { basename as
|
|
3601
|
+
import { basename as basename4 } from "path";
|
|
3411
3602
|
var multiEditTool = {
|
|
3412
3603
|
name: "multi_edit",
|
|
3413
3604
|
kind: "edit" /* Edit */,
|
|
@@ -3505,7 +3696,7 @@ var multiEditTool = {
|
|
|
3505
3696
|
data: `\u6587\u4EF6\u5DF2\u7F16\u8F91\uFF1A${filePath}
|
|
3506
3697
|
\u5171\u6267\u884C ${args.edits.length} \u6B65\u66FF\u6362
|
|
3507
3698
|
\u53D8\u66F4\uFF1A+${diff.additions} -${diff.deletions}`,
|
|
3508
|
-
summary: `\u{1F4DD} \u4FEE\u6539: ${
|
|
3699
|
+
summary: `\u{1F4DD} \u4FEE\u6539: ${basename4(filePath)} (${args.edits.length} \u6B65, +${diff.additions} -${diff.deletions})`,
|
|
3509
3700
|
diff
|
|
3510
3701
|
};
|
|
3511
3702
|
} catch (err) {
|
|
@@ -3521,7 +3712,7 @@ var multiEditTool = {
|
|
|
3521
3712
|
|
|
3522
3713
|
// src/tool/builtins/delete-range.ts
|
|
3523
3714
|
import { readFile as readFile9 } from "fs/promises";
|
|
3524
|
-
import { basename as
|
|
3715
|
+
import { basename as basename5 } from "path";
|
|
3525
3716
|
function findUniqueLine(lines, anchor, label) {
|
|
3526
3717
|
const matches = [];
|
|
3527
3718
|
for (let i = 0; i < lines.length; i++) {
|
|
@@ -3617,7 +3808,7 @@ var deleteRangeTool = {
|
|
|
3617
3808
|
const diff = computeFileDiff(content, newContent, filePath);
|
|
3618
3809
|
diff.existedBefore = true;
|
|
3619
3810
|
const deletedLines = rangeEnd - rangeStart + 1;
|
|
3620
|
-
const summary = `\u{1F4DD} \u4FEE\u6539: ${
|
|
3811
|
+
const summary = `\u{1F4DD} \u4FEE\u6539: ${basename5(filePath)} (\u5220 ${deletedLines} \u884C, +${diff.additions} -${diff.deletions})`;
|
|
3621
3812
|
return {
|
|
3622
3813
|
success: true,
|
|
3623
3814
|
data: `\u6587\u4EF6\u5DF2\u7F16\u8F91\uFF1A${filePath}
|
|
@@ -3639,17 +3830,62 @@ var deleteRangeTool = {
|
|
|
3639
3830
|
|
|
3640
3831
|
// src/tool/builtins/bash.ts
|
|
3641
3832
|
import process3 from "process";
|
|
3833
|
+
import { existsSync as existsSync3 } from "fs";
|
|
3834
|
+
import { join as join6 } from "path";
|
|
3642
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();
|
|
3643
3874
|
var bashTool = {
|
|
3644
3875
|
name: "bash",
|
|
3645
3876
|
kind: "other" /* Other */,
|
|
3646
|
-
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(""),
|
|
3647
3883
|
parameters: {
|
|
3648
3884
|
type: "object",
|
|
3649
3885
|
properties: {
|
|
3650
3886
|
command: {
|
|
3651
3887
|
type: "string",
|
|
3652
|
-
description:
|
|
3888
|
+
description: `\u8981\u6267\u884C\u7684 shell \u547D\u4EE4\uFF08\u5F53\u524D shell\uFF1A${SHELL_INFO.label}\uFF09`
|
|
3653
3889
|
},
|
|
3654
3890
|
timeout: {
|
|
3655
3891
|
type: "number",
|
|
@@ -3665,8 +3901,8 @@ var bashTool = {
|
|
|
3665
3901
|
}
|
|
3666
3902
|
const timeout = args.timeout ?? ctx.timeout ?? getDefaultTimeout();
|
|
3667
3903
|
try {
|
|
3668
|
-
const shellCommand =
|
|
3669
|
-
const shellArgs =
|
|
3904
|
+
const shellCommand = SHELL_INFO.bin ?? "cmd";
|
|
3905
|
+
const shellArgs = [...SHELL_INFO.args, args.command];
|
|
3670
3906
|
const result = await execCommand(
|
|
3671
3907
|
shellCommand,
|
|
3672
3908
|
shellArgs,
|
|
@@ -3707,7 +3943,7 @@ ${truncateOutput(result.stderr)}`);
|
|
|
3707
3943
|
|
|
3708
3944
|
// src/tool/builtins/glob.ts
|
|
3709
3945
|
import { readdir as readdir3, stat as stat2 } from "fs/promises";
|
|
3710
|
-
import { join as
|
|
3946
|
+
import { join as join7, relative as relative4, isAbsolute as isAbsolute2 } from "path";
|
|
3711
3947
|
function globToRegex(pattern) {
|
|
3712
3948
|
let regexStr = pattern;
|
|
3713
3949
|
regexStr = regexStr.replace(/\*\*\//g, "<<GLOBSTAR_SLASH>>");
|
|
@@ -3732,7 +3968,7 @@ async function walkDir(dir, baseDir) {
|
|
|
3732
3968
|
if (entry.isDirectory() && (entry.name === "node_modules" || entry.name === ".git")) {
|
|
3733
3969
|
continue;
|
|
3734
3970
|
}
|
|
3735
|
-
const fullPath =
|
|
3971
|
+
const fullPath = join7(dir, entry.name);
|
|
3736
3972
|
const relPath = relative4(baseDir, fullPath);
|
|
3737
3973
|
if (entry.isDirectory()) {
|
|
3738
3974
|
results.push(...await walkDir(fullPath, baseDir));
|
|
@@ -3765,7 +4001,7 @@ var globTool = {
|
|
|
3765
4001
|
if (!args?.pattern || typeof args.pattern !== "string") {
|
|
3766
4002
|
return { success: false, data: "\u7F3A\u5C11\u5FC5\u8981\u53C2\u6570 pattern", error: "INVALID_ARGS" };
|
|
3767
4003
|
}
|
|
3768
|
-
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;
|
|
3769
4005
|
const regex = globToRegex(args.pattern);
|
|
3770
4006
|
try {
|
|
3771
4007
|
const dirStat = await stat2(searchDir);
|
|
@@ -3805,7 +4041,7 @@ var globTool = {
|
|
|
3805
4041
|
|
|
3806
4042
|
// src/tool/builtins/grep.ts
|
|
3807
4043
|
import { readdir as readdir4, readFile as readFile10, stat as stat3 } from "fs/promises";
|
|
3808
|
-
import { join as
|
|
4044
|
+
import { join as join8, relative as relative5, isAbsolute as isAbsolute3 } from "path";
|
|
3809
4045
|
async function collectFiles(dir, extension, maxFiles = 200) {
|
|
3810
4046
|
const results = [];
|
|
3811
4047
|
let entries;
|
|
@@ -3819,7 +4055,7 @@ async function collectFiles(dir, extension, maxFiles = 200) {
|
|
|
3819
4055
|
if (entry.isDirectory() && (entry.name === "node_modules" || entry.name === ".git" || entry.name === "dist")) {
|
|
3820
4056
|
continue;
|
|
3821
4057
|
}
|
|
3822
|
-
const fullPath =
|
|
4058
|
+
const fullPath = join8(dir, entry.name);
|
|
3823
4059
|
if (entry.isDirectory()) {
|
|
3824
4060
|
results.push(...await collectFiles(fullPath, extension, maxFiles - results.length));
|
|
3825
4061
|
} else {
|
|
@@ -3866,7 +4102,7 @@ var grepTool = {
|
|
|
3866
4102
|
if (!args?.pattern || typeof args.pattern !== "string") {
|
|
3867
4103
|
return { success: false, data: "\u7F3A\u5C11\u5FC5\u8981\u53C2\u6570 pattern", error: "INVALID_ARGS" };
|
|
3868
4104
|
}
|
|
3869
|
-
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;
|
|
3870
4106
|
const maxFiles = args.max_files ?? 200;
|
|
3871
4107
|
try {
|
|
3872
4108
|
const flags = args.case_sensitive ? "g" : "gi";
|
|
@@ -3926,7 +4162,7 @@ var grepTool = {
|
|
|
3926
4162
|
|
|
3927
4163
|
// src/tool/builtins/ls.ts
|
|
3928
4164
|
import { readdir as readdir5, stat as stat4 } from "fs/promises";
|
|
3929
|
-
import { join as
|
|
4165
|
+
import { join as join9, relative as relative6 } from "path";
|
|
3930
4166
|
var lsTool = {
|
|
3931
4167
|
name: "ls",
|
|
3932
4168
|
kind: "read" /* Read */,
|
|
@@ -3964,7 +4200,7 @@ var lsTool = {
|
|
|
3964
4200
|
let sizeStr = "";
|
|
3965
4201
|
if (typeMark === "FILE") {
|
|
3966
4202
|
try {
|
|
3967
|
-
const fileStat = await stat4(
|
|
4203
|
+
const fileStat = await stat4(join9(dirPath, entry.name));
|
|
3968
4204
|
if (fileStat.size < 1024) {
|
|
3969
4205
|
sizeStr = `${fileStat.size}B`;
|
|
3970
4206
|
} else if (fileStat.size < 1024 * 1024) {
|
|
@@ -4403,7 +4639,12 @@ function ChatSession({
|
|
|
4403
4639
|
}
|
|
4404
4640
|
}, []);
|
|
4405
4641
|
const { doubleCtrlC, handleCtrlC } = useDoubleCtrlC(() => {
|
|
4406
|
-
|
|
4642
|
+
const s = sessionRef.current;
|
|
4643
|
+
if (s) {
|
|
4644
|
+
void s.flushLog().finally(() => process.exit(0));
|
|
4645
|
+
} else {
|
|
4646
|
+
process.exit(0);
|
|
4647
|
+
}
|
|
4407
4648
|
});
|
|
4408
4649
|
useInput(
|
|
4409
4650
|
useCallback2(
|
|
@@ -4803,6 +5044,7 @@ function ChatSession({
|
|
|
4803
5044
|
const result = cmd.handler();
|
|
4804
5045
|
switch (result.kind) {
|
|
4805
5046
|
case "exit":
|
|
5047
|
+
await sessionRef.current?.flushLog();
|
|
4806
5048
|
process.exit(0);
|
|
4807
5049
|
return;
|
|
4808
5050
|
case "clear":
|
|
@@ -4988,7 +5230,7 @@ function ChatSession({
|
|
|
4988
5230
|
}, 2e3);
|
|
4989
5231
|
}
|
|
4990
5232
|
}
|
|
4991
|
-
}, [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]);
|
|
4992
5234
|
useEffect3(() => {
|
|
4993
5235
|
if (!isStreaming && externalCostTracker) {
|
|
4994
5236
|
setTodayCost(externalCostTracker.todayTotalCost);
|
|
@@ -6449,9 +6691,9 @@ import { Box as Box13, Text as Text14, useInput as useInput5 } from "ink";
|
|
|
6449
6691
|
import { useState as useState7, useCallback as useCallback6, useEffect as useEffect6, useMemo as useMemo2 } from "react";
|
|
6450
6692
|
import asciichart from "asciichart";
|
|
6451
6693
|
import os from "os";
|
|
6452
|
-
import { join as
|
|
6694
|
+
import { join as join10 } from "path";
|
|
6453
6695
|
import { jsx as jsx13, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
6454
|
-
var SETTINGS_PATH =
|
|
6696
|
+
var SETTINGS_PATH = join10(os.homedir(), ".dskcode", "settings.json");
|
|
6455
6697
|
function toFileUrl(p) {
|
|
6456
6698
|
const norm = p.replace(/\\/g, "/");
|
|
6457
6699
|
if (process.platform === "win32") {
|
|
@@ -6786,7 +7028,7 @@ function renderDetail(stock, _onBack, prices, countdown = 10, currentTime) {
|
|
|
6786
7028
|
|
|
6787
7029
|
// src/utils/scan-files.ts
|
|
6788
7030
|
import { readdir as readdir6 } from "fs/promises";
|
|
6789
|
-
import { join as
|
|
7031
|
+
import { join as join11, relative as relative7 } from "path";
|
|
6790
7032
|
var IGNORE_DIRS = /* @__PURE__ */ new Set([
|
|
6791
7033
|
"node_modules",
|
|
6792
7034
|
".git",
|
|
@@ -6851,12 +7093,12 @@ async function scanProjectFiles(baseDir, dir) {
|
|
|
6851
7093
|
} else if (entry.isFile()) {
|
|
6852
7094
|
const ext = name.slice(name.lastIndexOf(".")).toLowerCase();
|
|
6853
7095
|
if (SOURCE_EXTS.has(ext)) {
|
|
6854
|
-
files.push(relative7(baseDir,
|
|
7096
|
+
files.push(relative7(baseDir, join11(currentDir, name)));
|
|
6855
7097
|
}
|
|
6856
7098
|
}
|
|
6857
7099
|
}
|
|
6858
7100
|
const nested = await Promise.all(
|
|
6859
|
-
dirs.map((d) => scanProjectFiles(baseDir,
|
|
7101
|
+
dirs.map((d) => scanProjectFiles(baseDir, join11(currentDir, d)))
|
|
6860
7102
|
);
|
|
6861
7103
|
for (const n of nested) {
|
|
6862
7104
|
files.push(...n);
|
|
@@ -6944,7 +7186,7 @@ compdef _dskcode_completion dskcode`);
|
|
|
6944
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) {
|
|
6945
7187
|
const _ctx = this.dskcodeCtx;
|
|
6946
7188
|
const home = process.env.HOME ?? process.env.USERPROFILE ?? "~";
|
|
6947
|
-
const globalConfigPath =
|
|
7189
|
+
const globalConfigPath = join12(home, ".dskcode", "settings.json");
|
|
6948
7190
|
let globalConfigHasStock = false;
|
|
6949
7191
|
try {
|
|
6950
7192
|
const raw = await readFile11(globalConfigPath, "utf-8");
|
|
@@ -7114,7 +7356,10 @@ var sigintCount = 0;
|
|
|
7114
7356
|
var sigintTimer = null;
|
|
7115
7357
|
async function gracefulExit(code) {
|
|
7116
7358
|
try {
|
|
7117
|
-
await
|
|
7359
|
+
await Promise.all([
|
|
7360
|
+
CostTracker.flushAll(),
|
|
7361
|
+
ConversationLogger.flushAll()
|
|
7362
|
+
]);
|
|
7118
7363
|
} catch {
|
|
7119
7364
|
}
|
|
7120
7365
|
process.exit(code);
|
|
@@ -7137,19 +7382,19 @@ process.on("SIGTERM", () => {
|
|
|
7137
7382
|
var program = createCli();
|
|
7138
7383
|
try {
|
|
7139
7384
|
await program.parseAsync(process.argv);
|
|
7140
|
-
await CostTracker.flushAll();
|
|
7385
|
+
await Promise.all([CostTracker.flushAll(), ConversationLogger.flushAll()]);
|
|
7141
7386
|
} catch (err) {
|
|
7142
7387
|
const error = err;
|
|
7143
7388
|
if (error.code === "commander.helpDisplayed" || error.code === "commander.version") {
|
|
7144
|
-
await CostTracker.flushAll();
|
|
7389
|
+
await Promise.all([CostTracker.flushAll(), ConversationLogger.flushAll()]);
|
|
7145
7390
|
process.exit(error.exitCode ?? ExitCode.SUCCESS);
|
|
7146
7391
|
}
|
|
7147
7392
|
if (typeof error.exitCode === "number") {
|
|
7148
|
-
await CostTracker.flushAll();
|
|
7393
|
+
await Promise.all([CostTracker.flushAll(), ConversationLogger.flushAll()]);
|
|
7149
7394
|
process.exit(error.exitCode);
|
|
7150
7395
|
}
|
|
7151
7396
|
console.error(String(err));
|
|
7152
|
-
await CostTracker.flushAll();
|
|
7397
|
+
await Promise.all([CostTracker.flushAll(), ConversationLogger.flushAll()]);
|
|
7153
7398
|
process.exit(ExitCode.GENERAL_ERROR);
|
|
7154
7399
|
}
|
|
7155
7400
|
//# sourceMappingURL=index.js.map
|