@yhong91/vibetime 0.1.49 → 0.1.51
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/bin/vibetime.mjs +336 -79
- package/package.json +1 -1
package/bin/vibetime.mjs
CHANGED
|
@@ -2047,7 +2047,7 @@ function claudeStyleFileMetrics(tool, input) {
|
|
|
2047
2047
|
}
|
|
2048
2048
|
|
|
2049
2049
|
// src/lib/constants.ts
|
|
2050
|
-
var PACKAGE_VERSION = true ? "0.1.
|
|
2050
|
+
var PACKAGE_VERSION = true ? "0.1.51" : "0.1.1";
|
|
2051
2051
|
var DEFAULT_API_URL = "http://121.196.224.82:3001";
|
|
2052
2052
|
var DEFAULT_BACKFILL_BATCH_SIZE = 50;
|
|
2053
2053
|
var DEFAULT_BACKFILL_BATCH_BYTES = 800 * 1024;
|
|
@@ -8094,27 +8094,98 @@ import path17 from "node:path";
|
|
|
8094
8094
|
import { access } from "node:fs/promises";
|
|
8095
8095
|
import os7 from "node:os";
|
|
8096
8096
|
import path16 from "node:path";
|
|
8097
|
+
function takeQoderDbModelCall(calls, requestId, blockStart) {
|
|
8098
|
+
if (requestId) {
|
|
8099
|
+
const call = calls.byRequestId.get(requestId)?.shift();
|
|
8100
|
+
if (call) {
|
|
8101
|
+
const index = calls.ordered.indexOf(call);
|
|
8102
|
+
if (index !== -1) {
|
|
8103
|
+
calls.ordered.splice(index, 1);
|
|
8104
|
+
}
|
|
8105
|
+
}
|
|
8106
|
+
return call;
|
|
8107
|
+
}
|
|
8108
|
+
if (!blockStart) {
|
|
8109
|
+
return void 0;
|
|
8110
|
+
}
|
|
8111
|
+
return calls.ordered.shift();
|
|
8112
|
+
}
|
|
8113
|
+
function appDataRoot(appDirName, home = os7.homedir()) {
|
|
8114
|
+
if (process.platform === "darwin") {
|
|
8115
|
+
return path16.join(home, "Library", "Application Support", appDirName);
|
|
8116
|
+
}
|
|
8117
|
+
if (process.platform === "win32") {
|
|
8118
|
+
return path16.join(process.env.APPDATA || path16.join(home, "AppData", "Roaming"), appDirName);
|
|
8119
|
+
}
|
|
8120
|
+
return path16.join(home, ".config", appDirName);
|
|
8121
|
+
}
|
|
8097
8122
|
function qoderLocalDbCandidates(appDirName) {
|
|
8098
8123
|
const candidates = [];
|
|
8099
8124
|
const envPath = process.env.QODER_LOCAL_DB_PATH;
|
|
8100
8125
|
if (envPath) {
|
|
8101
8126
|
candidates.push(envPath);
|
|
8102
8127
|
}
|
|
8103
|
-
const
|
|
8104
|
-
let configRoot;
|
|
8105
|
-
if (process.platform === "darwin") {
|
|
8106
|
-
configRoot = path16.join(home, "Library", "Application Support", appDirName);
|
|
8107
|
-
} else if (process.platform === "win32") {
|
|
8108
|
-
configRoot = path16.join(process.env.APPDATA || path16.join(home, "AppData", "Roaming"), appDirName);
|
|
8109
|
-
} else {
|
|
8110
|
-
configRoot = path16.join(home, ".config", appDirName);
|
|
8111
|
-
}
|
|
8128
|
+
const configRoot = appDataRoot(appDirName);
|
|
8112
8129
|
candidates.push(
|
|
8113
8130
|
path16.join(configRoot, "SharedClientCache", "cache", "db", "local.db"),
|
|
8114
8131
|
path16.join(configRoot, "SharedClientCache", "db", "local.db")
|
|
8115
8132
|
);
|
|
8116
8133
|
return candidates;
|
|
8117
8134
|
}
|
|
8135
|
+
async function loadQoderIdeModelCatalog(appDirName, home) {
|
|
8136
|
+
const map = {};
|
|
8137
|
+
try {
|
|
8138
|
+
const { DatabaseSync } = await import("node:sqlite");
|
|
8139
|
+
const db = new DatabaseSync(
|
|
8140
|
+
path16.join(appDataRoot(appDirName, home), "User", "globalStorage", "state.vscdb"),
|
|
8141
|
+
{ readOnly: true }
|
|
8142
|
+
);
|
|
8143
|
+
try {
|
|
8144
|
+
const rows = db.prepare(
|
|
8145
|
+
`select key, value from ItemTable where key like 'aicoding.modelConfigs.cache.%' or key = 'aicoding.customModels'`
|
|
8146
|
+
).all();
|
|
8147
|
+
const customIds = [];
|
|
8148
|
+
for (const row of rows) {
|
|
8149
|
+
const key = stringField(row, "key");
|
|
8150
|
+
const value = stringField(row, "value");
|
|
8151
|
+
if (!key || !value) {
|
|
8152
|
+
continue;
|
|
8153
|
+
}
|
|
8154
|
+
let entries;
|
|
8155
|
+
try {
|
|
8156
|
+
entries = JSON.parse(value);
|
|
8157
|
+
} catch {
|
|
8158
|
+
continue;
|
|
8159
|
+
}
|
|
8160
|
+
for (const entry of entries) {
|
|
8161
|
+
const displayName = stringField(entry, "displayName");
|
|
8162
|
+
if (key === "aicoding.customModels") {
|
|
8163
|
+
const id = stringField(entry, "id");
|
|
8164
|
+
if (id && displayName) {
|
|
8165
|
+
map[`custom:${id}`] = displayName;
|
|
8166
|
+
customIds.push(id);
|
|
8167
|
+
}
|
|
8168
|
+
} else {
|
|
8169
|
+
const name = stringField(entry, "name");
|
|
8170
|
+
if (name && displayName) {
|
|
8171
|
+
map[name] = displayName;
|
|
8172
|
+
}
|
|
8173
|
+
}
|
|
8174
|
+
}
|
|
8175
|
+
}
|
|
8176
|
+
if (customIds.length === 1) {
|
|
8177
|
+
const displayName = map[`custom:${customIds[0]}`];
|
|
8178
|
+
if (displayName) {
|
|
8179
|
+
map.custom_model = displayName;
|
|
8180
|
+
}
|
|
8181
|
+
}
|
|
8182
|
+
} finally {
|
|
8183
|
+
db.close();
|
|
8184
|
+
}
|
|
8185
|
+
} catch {
|
|
8186
|
+
}
|
|
8187
|
+
return map;
|
|
8188
|
+
}
|
|
8118
8189
|
async function loadQoderDbModelCalls(appDirName, sessionId, modelMap) {
|
|
8119
8190
|
const calls = { byRequestId: /* @__PURE__ */ new Map(), ordered: [] };
|
|
8120
8191
|
if (!sessionId) {
|
|
@@ -8266,7 +8337,7 @@ async function parseModelNamesFromDynamicTexts(dynamicTextsPath) {
|
|
|
8266
8337
|
return {};
|
|
8267
8338
|
}
|
|
8268
8339
|
}
|
|
8269
|
-
async function loadQoderCnModelNames(configDir2) {
|
|
8340
|
+
async function loadQoderCnModelNames(configDir2, home) {
|
|
8270
8341
|
const map = await parseModelNamesFromDynamicTexts(path17.join(configDir2, ".auth", "dynamic-texts.json"));
|
|
8271
8342
|
const siblingConfigDir = configDir2.replace(/\.qoder-cn$/, ".qoder");
|
|
8272
8343
|
if (siblingConfigDir !== configDir2) {
|
|
@@ -8277,6 +8348,14 @@ async function loadQoderCnModelNames(configDir2) {
|
|
|
8277
8348
|
}
|
|
8278
8349
|
}
|
|
8279
8350
|
}
|
|
8351
|
+
for (const appDirName of ["QoderCN", "Qoder"]) {
|
|
8352
|
+
const ideMap = await loadQoderIdeModelCatalog(appDirName, home);
|
|
8353
|
+
for (const [key, val] of Object.entries(ideMap)) {
|
|
8354
|
+
if (!(key in map)) {
|
|
8355
|
+
map[key] = val;
|
|
8356
|
+
}
|
|
8357
|
+
}
|
|
8358
|
+
}
|
|
8280
8359
|
return map;
|
|
8281
8360
|
}
|
|
8282
8361
|
async function loadQoderCnSegmentModelCalls(filePath, isSubagentSession, modelMap) {
|
|
@@ -8310,7 +8389,8 @@ async function loadQoderCnSegmentModelCalls(filePath, isSubagentSession, modelMa
|
|
|
8310
8389
|
inputTokens: numberField(data, "input_tokens") || 0,
|
|
8311
8390
|
outputTokens: numberField(data, "output_tokens") || 0,
|
|
8312
8391
|
cacheCreationInputTokens: numberField(data, "cache_creation_input_tokens") || 0,
|
|
8313
|
-
cacheReadInputTokens: numberField(data, "cache_read_input_tokens") || 0
|
|
8392
|
+
cacheReadInputTokens: numberField(data, "cache_read_input_tokens") || 0,
|
|
8393
|
+
stopReason: stringField(data, "stop_reason") || void 0
|
|
8314
8394
|
});
|
|
8315
8395
|
}
|
|
8316
8396
|
}
|
|
@@ -8333,20 +8413,15 @@ async function parseQoderCnSessionFile(filePath, options) {
|
|
|
8333
8413
|
let cwd;
|
|
8334
8414
|
let project = projectContext.project;
|
|
8335
8415
|
let model;
|
|
8336
|
-
const
|
|
8416
|
+
const home = path17.resolve(stringOption(options.home) || os8.homedir());
|
|
8417
|
+
const modelMap = await loadQoderCnModelNames(configDir2, home);
|
|
8337
8418
|
const isSubagentSession = filePath.includes("subagents");
|
|
8338
8419
|
const segmentModelCalls = await loadQoderCnSegmentModelCalls(filePath, isSubagentSession, modelMap);
|
|
8339
8420
|
let modelCallIndex = 0;
|
|
8340
8421
|
let dbModelCalls;
|
|
8341
8422
|
const nextDbModelCall = async (requestId, blockStart) => {
|
|
8342
8423
|
dbModelCalls ??= await loadQoderDbModelCalls("QoderCN", sessionId, modelMap);
|
|
8343
|
-
|
|
8344
|
-
return dbModelCalls.byRequestId.get(requestId)?.shift();
|
|
8345
|
-
}
|
|
8346
|
-
if (!blockStart) {
|
|
8347
|
-
return void 0;
|
|
8348
|
-
}
|
|
8349
|
-
return dbModelCalls.ordered.shift();
|
|
8424
|
+
return takeQoderDbModelCall(dbModelCalls, requestId, blockStart);
|
|
8350
8425
|
};
|
|
8351
8426
|
const state = new SessionParserState(filePath, options, (event) => baseQoderCnEvent({ ...event, cwd, project, model }));
|
|
8352
8427
|
state.sessionId = sessionId;
|
|
@@ -8458,6 +8533,41 @@ async function parseQoderCnSessionFile(filePath, options) {
|
|
|
8458
8533
|
}
|
|
8459
8534
|
pendingTools.delete(pending.id);
|
|
8460
8535
|
}
|
|
8536
|
+
const attachedPrompt = extractQoderAttachedPrompt(qoderCnToolResultItems(message)[0]?.content);
|
|
8537
|
+
if (attachedPrompt) {
|
|
8538
|
+
const prompt = qoderCnTextStats(attachedPrompt);
|
|
8539
|
+
state.closeTurn(ts, lineNumber, topType);
|
|
8540
|
+
state.currentTurnId = stringField(raw, "uuid") || stringField(raw, "promptId") || `turn_${createStableHash([sessionId, lineNumber]).slice(0, 24)}`;
|
|
8541
|
+
state.currentTurnStartedAt = ts;
|
|
8542
|
+
state.currentTurnLastEventAt = ts;
|
|
8543
|
+
push(baseQoderCnEvent({
|
|
8544
|
+
ts,
|
|
8545
|
+
type: "turn.started",
|
|
8546
|
+
sessionId,
|
|
8547
|
+
turnId: state.currentTurnId,
|
|
8548
|
+
cwd,
|
|
8549
|
+
project,
|
|
8550
|
+
model,
|
|
8551
|
+
confidence: "derived"
|
|
8552
|
+
}), lineNumber, topType, "attached_prompt");
|
|
8553
|
+
push(baseQoderCnEvent({
|
|
8554
|
+
ts,
|
|
8555
|
+
type: "prompt.submitted",
|
|
8556
|
+
sessionId,
|
|
8557
|
+
turnId: state.currentTurnId,
|
|
8558
|
+
cwd,
|
|
8559
|
+
project,
|
|
8560
|
+
model,
|
|
8561
|
+
confidence: "exact",
|
|
8562
|
+
metrics: {
|
|
8563
|
+
prompts: 1,
|
|
8564
|
+
promptChars: prompt.chars
|
|
8565
|
+
},
|
|
8566
|
+
refs: stringRefs({
|
|
8567
|
+
promptHash: prompt.hash
|
|
8568
|
+
})
|
|
8569
|
+
}), lineNumber, topType, "attached_prompt");
|
|
8570
|
+
}
|
|
8461
8571
|
continue;
|
|
8462
8572
|
}
|
|
8463
8573
|
if (topType === "user") {
|
|
@@ -8532,6 +8642,20 @@ async function parseQoderCnSessionFile(filePath, options) {
|
|
|
8532
8642
|
modelCalls: 1
|
|
8533
8643
|
};
|
|
8534
8644
|
model = call.model || model;
|
|
8645
|
+
if (!usage.tokensTotal && !usage.tokensCachedInput) {
|
|
8646
|
+
const dbCall = await nextDbModelCall(requestId, isBlockStart);
|
|
8647
|
+
if (dbCall && (dbCall.inputTokens || dbCall.outputTokens || dbCall.cachedTokens)) {
|
|
8648
|
+
usage = {
|
|
8649
|
+
tokensInput: dbCall.inputTokens || void 0,
|
|
8650
|
+
tokensCachedInput: dbCall.cachedTokens || void 0,
|
|
8651
|
+
tokensCacheReadInput: dbCall.cachedTokens || void 0,
|
|
8652
|
+
tokensOutput: dbCall.outputTokens || void 0,
|
|
8653
|
+
tokensTotal: dbCall.inputTokens + dbCall.outputTokens || void 0,
|
|
8654
|
+
modelCalls: 1
|
|
8655
|
+
};
|
|
8656
|
+
model = dbCall.model || model;
|
|
8657
|
+
}
|
|
8658
|
+
}
|
|
8535
8659
|
} else if (shouldEmitUsage) {
|
|
8536
8660
|
const dbCall = await nextDbModelCall(requestId, isBlockStart);
|
|
8537
8661
|
if (dbCall) {
|
|
@@ -8785,6 +8909,30 @@ function isNoisePrompt(text) {
|
|
|
8785
8909
|
}
|
|
8786
8910
|
return false;
|
|
8787
8911
|
}
|
|
8912
|
+
function extractQoderAttachedPrompt(content) {
|
|
8913
|
+
let text;
|
|
8914
|
+
if (typeof content === "string") {
|
|
8915
|
+
text = content;
|
|
8916
|
+
} else if (Array.isArray(content)) {
|
|
8917
|
+
text = content.filter((it) => isPlainObject(it) && it.type === "text").map((it) => stringField(it, "text")).join("\n");
|
|
8918
|
+
} else {
|
|
8919
|
+
return void 0;
|
|
8920
|
+
}
|
|
8921
|
+
if (!text.includes("aicoding-text-field") || !text.includes("text-field-content")) {
|
|
8922
|
+
return void 0;
|
|
8923
|
+
}
|
|
8924
|
+
const fenceStart = text.indexOf("```");
|
|
8925
|
+
if (fenceStart === -1) {
|
|
8926
|
+
return void 0;
|
|
8927
|
+
}
|
|
8928
|
+
const afterFence = text.slice(fenceStart + 3);
|
|
8929
|
+
const fenceEnd = afterFence.indexOf("```");
|
|
8930
|
+
if (fenceEnd === -1) {
|
|
8931
|
+
return void 0;
|
|
8932
|
+
}
|
|
8933
|
+
const inner = afterFence.slice(0, fenceEnd);
|
|
8934
|
+
return inner.split("\n").map((line) => line.replace(/^\s*\d+->/, "")).join("\n").trim() || void 0;
|
|
8935
|
+
}
|
|
8788
8936
|
async function qoderCnProjectContextFromLines(filePath, lines, options, configDir2) {
|
|
8789
8937
|
const { projectName: projectDir, sessionId } = parseQoderCnPaths(filePath);
|
|
8790
8938
|
const isSubagent = filePath.includes(`${path17.sep}subagents${path17.sep}`);
|
|
@@ -8982,6 +9130,7 @@ function createQoderCnAdapter() {
|
|
|
8982
9130
|
}
|
|
8983
9131
|
|
|
8984
9132
|
// src/adapters/qoder.ts
|
|
9133
|
+
import { existsSync } from "node:fs";
|
|
8985
9134
|
import { readdir as readdir8, readFile as readFile11, stat as stat9 } from "node:fs/promises";
|
|
8986
9135
|
import os9 from "node:os";
|
|
8987
9136
|
import path18 from "node:path";
|
|
@@ -9044,7 +9193,7 @@ async function parseModelNamesFromDynamicTexts2(dynamicTextsPath) {
|
|
|
9044
9193
|
return {};
|
|
9045
9194
|
}
|
|
9046
9195
|
}
|
|
9047
|
-
async function loadQoderModelNames(configDir2) {
|
|
9196
|
+
async function loadQoderModelNames(configDir2, home) {
|
|
9048
9197
|
const map = await parseModelNamesFromDynamicTexts2(path18.join(configDir2, ".auth", "dynamic-texts.json"));
|
|
9049
9198
|
const siblingConfigDir = configDir2.replace(/\.qoder$/, ".qoder-cn");
|
|
9050
9199
|
if (siblingConfigDir !== configDir2) {
|
|
@@ -9055,8 +9204,38 @@ async function loadQoderModelNames(configDir2) {
|
|
|
9055
9204
|
}
|
|
9056
9205
|
}
|
|
9057
9206
|
}
|
|
9207
|
+
if (isQwenworkConfigRoot(configDir2)) {
|
|
9208
|
+
for (const dir of [path18.join(home, ".qoder"), path18.join(home, ".qoder-cn")]) {
|
|
9209
|
+
if (path18.resolve(dir) === path18.resolve(configDir2)) {
|
|
9210
|
+
continue;
|
|
9211
|
+
}
|
|
9212
|
+
const fallbackMap = await parseModelNamesFromDynamicTexts2(path18.join(dir, ".auth", "dynamic-texts.json"));
|
|
9213
|
+
for (const [key, val] of Object.entries(fallbackMap)) {
|
|
9214
|
+
if (!(key in map)) {
|
|
9215
|
+
map[key] = val;
|
|
9216
|
+
}
|
|
9217
|
+
}
|
|
9218
|
+
}
|
|
9219
|
+
}
|
|
9220
|
+
for (const appDirName of ["Qoder", "QoderCN"]) {
|
|
9221
|
+
const ideMap = await loadQoderIdeModelCatalog(appDirName, home);
|
|
9222
|
+
for (const [key, val] of Object.entries(ideMap)) {
|
|
9223
|
+
if (!(key in map)) {
|
|
9224
|
+
map[key] = val;
|
|
9225
|
+
}
|
|
9226
|
+
}
|
|
9227
|
+
}
|
|
9058
9228
|
return map;
|
|
9059
9229
|
}
|
|
9230
|
+
function isQwenworkConfigRoot(configDir2) {
|
|
9231
|
+
const name = path18.basename(path18.resolve(configDir2));
|
|
9232
|
+
if (name === ".qwenworkcn" || name === ".qwenwork") {
|
|
9233
|
+
return true;
|
|
9234
|
+
}
|
|
9235
|
+
const override = process.env.QWENWORK_CONFIG_DIR;
|
|
9236
|
+
return Boolean(override && override.trim() && path18.basename(path18.resolve(override)) === name);
|
|
9237
|
+
}
|
|
9238
|
+
var FAILED_SEGMENT_STOP_REASONS = /* @__PURE__ */ new Set(["cancelled", "canceled", "error", "failed", "refusal"]);
|
|
9060
9239
|
async function loadQoderSegmentModelCalls(filePath, isSubagentSession, modelMap) {
|
|
9061
9240
|
const { configDir: configDir2, projectName, sessionId } = parseQoderPaths(filePath);
|
|
9062
9241
|
const segmentsPath = path18.join(configDir2, "logs", "sessions", projectName, sessionId, "segments");
|
|
@@ -9088,7 +9267,8 @@ async function loadQoderSegmentModelCalls(filePath, isSubagentSession, modelMap)
|
|
|
9088
9267
|
inputTokens: numberField(data, "input_tokens") || 0,
|
|
9089
9268
|
outputTokens: numberField(data, "output_tokens") || 0,
|
|
9090
9269
|
cacheCreationInputTokens: numberField(data, "cache_creation_input_tokens") || 0,
|
|
9091
|
-
cacheReadInputTokens: numberField(data, "cache_read_input_tokens") || 0
|
|
9270
|
+
cacheReadInputTokens: numberField(data, "cache_read_input_tokens") || 0,
|
|
9271
|
+
stopReason: stringField(data, "stop_reason") || void 0
|
|
9092
9272
|
});
|
|
9093
9273
|
}
|
|
9094
9274
|
}
|
|
@@ -9111,20 +9291,16 @@ async function parseQoderSessionFile(filePath, options) {
|
|
|
9111
9291
|
let cwd;
|
|
9112
9292
|
let project = projectContext.project;
|
|
9113
9293
|
let model;
|
|
9114
|
-
const
|
|
9294
|
+
const home = path18.resolve(stringOption(options.home) || os9.homedir());
|
|
9295
|
+
const modelMap = await loadQoderModelNames(configDir2, home);
|
|
9296
|
+
const qwenworkRoot = isQwenworkConfigRoot(configDir2);
|
|
9115
9297
|
const isSubagentSession = filePath.includes("subagents");
|
|
9116
9298
|
const segmentModelCalls = await loadQoderSegmentModelCalls(filePath, isSubagentSession, modelMap);
|
|
9117
9299
|
let modelCallIndex = 0;
|
|
9118
9300
|
let dbModelCalls;
|
|
9119
9301
|
const nextDbModelCall = async (requestId, blockStart) => {
|
|
9120
9302
|
dbModelCalls ??= await loadQoderDbModelCalls("Qoder", sessionId, modelMap);
|
|
9121
|
-
|
|
9122
|
-
return dbModelCalls.byRequestId.get(requestId)?.shift();
|
|
9123
|
-
}
|
|
9124
|
-
if (!blockStart) {
|
|
9125
|
-
return void 0;
|
|
9126
|
-
}
|
|
9127
|
-
return dbModelCalls.ordered.shift();
|
|
9303
|
+
return takeQoderDbModelCall(dbModelCalls, requestId, blockStart);
|
|
9128
9304
|
};
|
|
9129
9305
|
const state = new SessionParserState(filePath, options, (event) => baseQoderEvent({ ...event, cwd, project, model }));
|
|
9130
9306
|
state.sessionId = sessionId;
|
|
@@ -9236,6 +9412,41 @@ async function parseQoderSessionFile(filePath, options) {
|
|
|
9236
9412
|
}
|
|
9237
9413
|
pendingTools.delete(pending.id);
|
|
9238
9414
|
}
|
|
9415
|
+
const attachedPrompt = extractQoderAttachedPrompt(qoderToolResultItems(message)[0]?.content);
|
|
9416
|
+
if (attachedPrompt) {
|
|
9417
|
+
const prompt = qoderTextStats(attachedPrompt);
|
|
9418
|
+
state.closeTurn(ts, lineNumber, topType);
|
|
9419
|
+
state.currentTurnId = stringField(raw, "uuid") || stringField(raw, "promptId") || `turn_${createStableHash([sessionId, lineNumber]).slice(0, 24)}`;
|
|
9420
|
+
state.currentTurnStartedAt = ts;
|
|
9421
|
+
state.currentTurnLastEventAt = ts;
|
|
9422
|
+
push(baseQoderEvent({
|
|
9423
|
+
ts,
|
|
9424
|
+
type: "turn.started",
|
|
9425
|
+
sessionId,
|
|
9426
|
+
turnId: state.currentTurnId,
|
|
9427
|
+
cwd,
|
|
9428
|
+
project,
|
|
9429
|
+
model,
|
|
9430
|
+
confidence: "derived"
|
|
9431
|
+
}), lineNumber, topType, "attached_prompt");
|
|
9432
|
+
push(baseQoderEvent({
|
|
9433
|
+
ts,
|
|
9434
|
+
type: "prompt.submitted",
|
|
9435
|
+
sessionId,
|
|
9436
|
+
turnId: state.currentTurnId,
|
|
9437
|
+
cwd,
|
|
9438
|
+
project,
|
|
9439
|
+
model,
|
|
9440
|
+
confidence: "exact",
|
|
9441
|
+
metrics: {
|
|
9442
|
+
prompts: 1,
|
|
9443
|
+
promptChars: prompt.chars
|
|
9444
|
+
},
|
|
9445
|
+
refs: stringRefs({
|
|
9446
|
+
promptHash: prompt.hash
|
|
9447
|
+
})
|
|
9448
|
+
}), lineNumber, topType, "attached_prompt");
|
|
9449
|
+
}
|
|
9239
9450
|
continue;
|
|
9240
9451
|
}
|
|
9241
9452
|
if (topType === "user") {
|
|
@@ -9292,39 +9503,28 @@ async function parseQoderSessionFile(filePath, options) {
|
|
|
9292
9503
|
seenUsageKeys.add(usageKey);
|
|
9293
9504
|
}
|
|
9294
9505
|
let usage;
|
|
9506
|
+
let keepZeroTokenUsage = false;
|
|
9295
9507
|
if (shouldEmitUsage && modelCallIndex < segmentModelCalls.length) {
|
|
9296
9508
|
const call = segmentModelCalls[modelCallIndex++];
|
|
9297
|
-
|
|
9298
|
-
const cacheCreationInputTokens = call.cacheCreationInputTokens;
|
|
9299
|
-
const cacheReadInputTokens = call.cacheReadInputTokens;
|
|
9300
|
-
const outputTokens = call.outputTokens;
|
|
9301
|
-
const cachedInputTokens = cacheCreationInputTokens + cacheReadInputTokens;
|
|
9302
|
-
const totalInputTokens = inputTokens;
|
|
9303
|
-
usage = {
|
|
9304
|
-
tokensInput: totalInputTokens || void 0,
|
|
9305
|
-
tokensCachedInput: cachedInputTokens || void 0,
|
|
9306
|
-
tokensCacheCreationInput: cacheCreationInputTokens || void 0,
|
|
9307
|
-
tokensCacheReadInput: cacheReadInputTokens || void 0,
|
|
9308
|
-
tokensOutput: outputTokens || void 0,
|
|
9309
|
-
tokensTotal: totalInputTokens + outputTokens || void 0,
|
|
9310
|
-
modelCalls: 1
|
|
9311
|
-
};
|
|
9509
|
+
usage = qoderSegmentUsage(call);
|
|
9312
9510
|
model = call.model || model;
|
|
9511
|
+
if (!usage.tokensTotal && !usage.tokensCachedInput) {
|
|
9512
|
+
const dbCall = await nextDbModelCall(requestId, isBlockStart);
|
|
9513
|
+
if (dbCall && (dbCall.inputTokens || dbCall.outputTokens || dbCall.cachedTokens)) {
|
|
9514
|
+
usage = qoderDbUsage(dbCall);
|
|
9515
|
+
model = dbCall.model || model;
|
|
9516
|
+
} else if (qwenworkRoot && (!call.stopReason || !FAILED_SEGMENT_STOP_REASONS.has(call.stopReason))) {
|
|
9517
|
+
keepZeroTokenUsage = true;
|
|
9518
|
+
}
|
|
9519
|
+
}
|
|
9313
9520
|
} else if (shouldEmitUsage) {
|
|
9314
9521
|
const dbCall = await nextDbModelCall(requestId, isBlockStart);
|
|
9315
9522
|
if (dbCall) {
|
|
9316
|
-
usage =
|
|
9317
|
-
tokensInput: dbCall.inputTokens || void 0,
|
|
9318
|
-
tokensCachedInput: dbCall.cachedTokens || void 0,
|
|
9319
|
-
tokensCacheReadInput: dbCall.cachedTokens || void 0,
|
|
9320
|
-
tokensOutput: dbCall.outputTokens || void 0,
|
|
9321
|
-
tokensTotal: dbCall.inputTokens + dbCall.outputTokens || void 0,
|
|
9322
|
-
modelCalls: 1
|
|
9323
|
-
};
|
|
9523
|
+
usage = qoderDbUsage(dbCall);
|
|
9324
9524
|
model = dbCall.model || model;
|
|
9325
9525
|
}
|
|
9326
9526
|
}
|
|
9327
|
-
if (usage && (usage.tokensTotal || usage.tokensCachedInput)) {
|
|
9527
|
+
if (usage && (usage.tokensTotal || usage.tokensCachedInput || keepZeroTokenUsage)) {
|
|
9328
9528
|
const speed = stringField(objectField(message, "usage"), "speed");
|
|
9329
9529
|
const usageModel = speed === "fast" && model ? `${model}-fast` : model;
|
|
9330
9530
|
push(baseQoderEvent({
|
|
@@ -9451,6 +9651,28 @@ async function parseQoderSessionFile(filePath, options) {
|
|
|
9451
9651
|
}
|
|
9452
9652
|
return validEvents;
|
|
9453
9653
|
}
|
|
9654
|
+
function qoderSegmentUsage(call) {
|
|
9655
|
+
const cachedInputTokens = call.cacheCreationInputTokens + call.cacheReadInputTokens;
|
|
9656
|
+
return {
|
|
9657
|
+
tokensInput: call.inputTokens || void 0,
|
|
9658
|
+
tokensCachedInput: cachedInputTokens || void 0,
|
|
9659
|
+
tokensCacheCreationInput: call.cacheCreationInputTokens || void 0,
|
|
9660
|
+
tokensCacheReadInput: call.cacheReadInputTokens || void 0,
|
|
9661
|
+
tokensOutput: call.outputTokens || void 0,
|
|
9662
|
+
tokensTotal: call.inputTokens + call.outputTokens || void 0,
|
|
9663
|
+
modelCalls: 1
|
|
9664
|
+
};
|
|
9665
|
+
}
|
|
9666
|
+
function qoderDbUsage(dbCall) {
|
|
9667
|
+
return {
|
|
9668
|
+
tokensInput: dbCall.inputTokens || void 0,
|
|
9669
|
+
tokensCachedInput: dbCall.cachedTokens || void 0,
|
|
9670
|
+
tokensCacheReadInput: dbCall.cachedTokens || void 0,
|
|
9671
|
+
tokensOutput: dbCall.outputTokens || void 0,
|
|
9672
|
+
tokensTotal: dbCall.inputTokens + dbCall.outputTokens || void 0,
|
|
9673
|
+
modelCalls: 1
|
|
9674
|
+
};
|
|
9675
|
+
}
|
|
9454
9676
|
function baseQoderEvent(event) {
|
|
9455
9677
|
return {
|
|
9456
9678
|
schemaVersion: AGENT_TIME_SCHEMA_VERSION,
|
|
@@ -9534,12 +9756,20 @@ async function qoderProjectContextFromLines(filePath, lines, options, configDir2
|
|
|
9534
9756
|
const isSubagent = filePath.includes(`${path18.sep}subagents${path18.sep}`);
|
|
9535
9757
|
const inherited = isSubagent ? await readPersistedSessionContextFromOptions(options, sessionId) : void 0;
|
|
9536
9758
|
let cwds = [];
|
|
9759
|
+
const workspaceDirs = [];
|
|
9537
9760
|
for (const line of lines) {
|
|
9538
9761
|
const raw = parseJsonLine(line);
|
|
9539
9762
|
const cwd = raw ? stringField(raw, "cwd") : void 0;
|
|
9540
9763
|
if (cwd && path18.isAbsolute(cwd)) {
|
|
9541
9764
|
cwds.push(cwd);
|
|
9542
9765
|
}
|
|
9766
|
+
if (raw && stringField(raw, "type") === "workspace-directories") {
|
|
9767
|
+
for (const dir of arrayField5(raw, "directories")) {
|
|
9768
|
+
if (typeof dir === "string" && path18.isAbsolute(dir)) {
|
|
9769
|
+
workspaceDirs.push(dir);
|
|
9770
|
+
}
|
|
9771
|
+
}
|
|
9772
|
+
}
|
|
9543
9773
|
}
|
|
9544
9774
|
if (isSubagent) {
|
|
9545
9775
|
if (inherited?.cwd && path18.isAbsolute(inherited.cwd)) {
|
|
@@ -9555,6 +9785,13 @@ async function qoderProjectContextFromLines(filePath, lines, options, configDir2
|
|
|
9555
9785
|
if (cwd && path18.isAbsolute(cwd)) {
|
|
9556
9786
|
parentCwds.push(cwd);
|
|
9557
9787
|
}
|
|
9788
|
+
if (workspaceDirs.length === 0 && raw && stringField(raw, "type") === "workspace-directories") {
|
|
9789
|
+
for (const dir of arrayField5(raw, "directories")) {
|
|
9790
|
+
if (typeof dir === "string" && path18.isAbsolute(dir)) {
|
|
9791
|
+
workspaceDirs.push(dir);
|
|
9792
|
+
}
|
|
9793
|
+
}
|
|
9794
|
+
}
|
|
9558
9795
|
}
|
|
9559
9796
|
if (parentCwds.length > 0) {
|
|
9560
9797
|
cwds = parentCwds;
|
|
@@ -9563,6 +9800,12 @@ async function qoderProjectContextFromLines(filePath, lines, options, configDir2
|
|
|
9563
9800
|
}
|
|
9564
9801
|
}
|
|
9565
9802
|
}
|
|
9803
|
+
if (cwds.length > 0 && cwds.every((cwd) => pathInsideDir(cwd, configDir2))) {
|
|
9804
|
+
const external = workspaceDirs.filter((dir) => !pathInsideDir(dir, configDir2));
|
|
9805
|
+
if (external.length > 0) {
|
|
9806
|
+
cwds = external;
|
|
9807
|
+
}
|
|
9808
|
+
}
|
|
9566
9809
|
const root = await gitRootFromCwds3(cwds) || qoderProjectRootFromCwds(projectDir, cwds);
|
|
9567
9810
|
const project = inherited?.project || (cwds.length > 0 ? path18.basename(cwds[0]) : root ? path18.basename(root) : await qoderProjectFromFilePath(filePath, options));
|
|
9568
9811
|
return {
|
|
@@ -9570,6 +9813,11 @@ async function qoderProjectContextFromLines(filePath, lines, options, configDir2
|
|
|
9570
9813
|
workspaceId: createWorkspaceId({ projectName: project, repoRoot: root })
|
|
9571
9814
|
};
|
|
9572
9815
|
}
|
|
9816
|
+
function pathInsideDir(candidate, dir) {
|
|
9817
|
+
const resolvedDir = path18.resolve(dir);
|
|
9818
|
+
const resolved = path18.resolve(candidate);
|
|
9819
|
+
return resolved === resolvedDir || resolved.startsWith(`${resolvedDir}${path18.sep}`);
|
|
9820
|
+
}
|
|
9573
9821
|
async function gitRootFromCwds3(cwds) {
|
|
9574
9822
|
const seen = /* @__PURE__ */ new Set();
|
|
9575
9823
|
for (const cwd of cwds) {
|
|
@@ -9594,7 +9842,7 @@ function qoderProjectRootFromCwds(projectDir, cwds) {
|
|
|
9594
9842
|
for (const cwd of cwds) {
|
|
9595
9843
|
let current = path18.resolve(cwd);
|
|
9596
9844
|
while (true) {
|
|
9597
|
-
if (
|
|
9845
|
+
if (qoderEncodedVariants(current).includes(projectDir)) {
|
|
9598
9846
|
return current;
|
|
9599
9847
|
}
|
|
9600
9848
|
const parent = path18.dirname(current);
|
|
@@ -9606,21 +9854,23 @@ function qoderProjectRootFromCwds(projectDir, cwds) {
|
|
|
9606
9854
|
}
|
|
9607
9855
|
return void 0;
|
|
9608
9856
|
}
|
|
9609
|
-
function encodeQoderProjectPath(value) {
|
|
9610
|
-
return path18.resolve(value).split(path18.sep).join("-").replaceAll("_", "-");
|
|
9611
|
-
}
|
|
9612
9857
|
function rawQoderProjectPath(value) {
|
|
9613
9858
|
return path18.resolve(value).split(path18.sep).join("-");
|
|
9614
9859
|
}
|
|
9615
9860
|
function qoderEncodedVariants(value) {
|
|
9616
9861
|
const raw = rawQoderProjectPath(value);
|
|
9617
|
-
const
|
|
9618
|
-
|
|
9862
|
+
const variants = /* @__PURE__ */ new Set([
|
|
9863
|
+
raw,
|
|
9864
|
+
raw.replaceAll("_", "-"),
|
|
9865
|
+
raw.replaceAll(".", "-"),
|
|
9866
|
+
raw.replaceAll("_", "-").replaceAll(".", "-")
|
|
9867
|
+
]);
|
|
9868
|
+
return [...variants];
|
|
9619
9869
|
}
|
|
9620
9870
|
function qoderEncodedProjectSuffix(projectDir, home) {
|
|
9621
9871
|
for (const prefix of qoderEncodedVariants(home).map((value) => `${value}-`)) {
|
|
9622
9872
|
if (projectDir.startsWith(prefix)) {
|
|
9623
|
-
return projectDir.slice(prefix.length) || void 0;
|
|
9873
|
+
return projectDir.slice(prefix.length).replace(/^-+/, "") || void 0;
|
|
9624
9874
|
}
|
|
9625
9875
|
}
|
|
9626
9876
|
return void 0;
|
|
@@ -9705,6 +9955,16 @@ function qoderConfigDir(home, env) {
|
|
|
9705
9955
|
}
|
|
9706
9956
|
return path18.join(home, ".qoder");
|
|
9707
9957
|
}
|
|
9958
|
+
function qwenworkConfigDir(home, env) {
|
|
9959
|
+
const override = env?.QWENWORK_CONFIG_DIR;
|
|
9960
|
+
if (override && override.trim()) {
|
|
9961
|
+
return path18.resolve(override);
|
|
9962
|
+
}
|
|
9963
|
+
return path18.join(home, ".qwenworkcn");
|
|
9964
|
+
}
|
|
9965
|
+
function qoderConfigDirs(home, env) {
|
|
9966
|
+
return [...new Set([qoderConfigDir(home, env), qwenworkConfigDir(home, env)].map((dir) => path18.resolve(dir)))];
|
|
9967
|
+
}
|
|
9708
9968
|
function createQoderAdapter() {
|
|
9709
9969
|
return {
|
|
9710
9970
|
id: "qoder",
|
|
@@ -9724,19 +9984,22 @@ function createQoderAdapter() {
|
|
|
9724
9984
|
);
|
|
9725
9985
|
},
|
|
9726
9986
|
installEntries(home, env) {
|
|
9727
|
-
|
|
9987
|
+
const [primary, ...variants] = qoderConfigDirs(home, env);
|
|
9988
|
+
const targets = [primary, ...variants.filter((dir) => existsSync(dir))];
|
|
9989
|
+
return targets.map((base) => ({
|
|
9728
9990
|
kind: "hooks-json",
|
|
9729
|
-
path: path18.join(
|
|
9991
|
+
path: path18.join(base, "settings.json"),
|
|
9730
9992
|
content: hookConfig7()
|
|
9731
|
-
}
|
|
9993
|
+
}));
|
|
9732
9994
|
},
|
|
9733
9995
|
sourcePaths(home, env) {
|
|
9996
|
+
const paths = qoderConfigDirs(home, env).map((base2) => path18.join(base2, "projects"));
|
|
9734
9997
|
const base = qoderConfigDir(home, env);
|
|
9735
|
-
|
|
9736
|
-
path18.join(base, "projects"),
|
|
9998
|
+
paths.push(
|
|
9737
9999
|
path18.join(base, ".qoder.json"),
|
|
9738
10000
|
path18.join(home, ".qoder.json")
|
|
9739
|
-
|
|
10001
|
+
);
|
|
10002
|
+
return paths;
|
|
9740
10003
|
},
|
|
9741
10004
|
parseSessionFile: parseQoderSessionFile
|
|
9742
10005
|
};
|
|
@@ -11171,13 +11434,7 @@ function createZedAdapter() {
|
|
|
11171
11434
|
}
|
|
11172
11435
|
|
|
11173
11436
|
// src/lib/pricing.ts
|
|
11174
|
-
function estimateEventCostUsd(
|
|
11175
|
-
if (event.type !== "model.usage") {
|
|
11176
|
-
return 0;
|
|
11177
|
-
}
|
|
11178
|
-
if (typeof event.metrics?.costUsd === "number" && event.metrics.costUsd > 0) {
|
|
11179
|
-
return event.metrics.costUsd;
|
|
11180
|
-
}
|
|
11437
|
+
function estimateEventCostUsd(_event) {
|
|
11181
11438
|
return 0;
|
|
11182
11439
|
}
|
|
11183
11440
|
|
|
@@ -11640,7 +11897,7 @@ function hookCommandFromGroup(group) {
|
|
|
11640
11897
|
|
|
11641
11898
|
// src/lib/config.ts
|
|
11642
11899
|
import { randomUUID } from "node:crypto";
|
|
11643
|
-
import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
11900
|
+
import { existsSync as existsSync2, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
11644
11901
|
import { homedir, hostname } from "node:os";
|
|
11645
11902
|
import path22 from "node:path";
|
|
11646
11903
|
function configDir(home = homedir()) {
|
|
@@ -11654,7 +11911,7 @@ function machineIdPath(home = homedir()) {
|
|
|
11654
11911
|
}
|
|
11655
11912
|
function readConfig(home = homedir()) {
|
|
11656
11913
|
const file = configPath(home);
|
|
11657
|
-
if (!
|
|
11914
|
+
if (!existsSync2(file)) {
|
|
11658
11915
|
return {};
|
|
11659
11916
|
}
|
|
11660
11917
|
try {
|
|
@@ -11666,7 +11923,7 @@ function readConfig(home = homedir()) {
|
|
|
11666
11923
|
}
|
|
11667
11924
|
function writeConfig(config, home = homedir()) {
|
|
11668
11925
|
const dir = configDir(home);
|
|
11669
|
-
if (!
|
|
11926
|
+
if (!existsSync2(dir)) {
|
|
11670
11927
|
mkdirSync(dir, { recursive: true });
|
|
11671
11928
|
}
|
|
11672
11929
|
writeFileSync(configPath(home), `${JSON.stringify(config, null, 2)}
|
|
@@ -11674,7 +11931,7 @@ function writeConfig(config, home = homedir()) {
|
|
|
11674
11931
|
}
|
|
11675
11932
|
function ensureLocalMachineId(home = homedir()) {
|
|
11676
11933
|
const file = machineIdPath(home);
|
|
11677
|
-
if (
|
|
11934
|
+
if (existsSync2(file)) {
|
|
11678
11935
|
const value = readFileSync(file, "utf8").trim();
|
|
11679
11936
|
if (value.length > 0) {
|
|
11680
11937
|
return value;
|
|
@@ -11682,7 +11939,7 @@ function ensureLocalMachineId(home = homedir()) {
|
|
|
11682
11939
|
}
|
|
11683
11940
|
const id = randomUUID();
|
|
11684
11941
|
const dir = configDir(home);
|
|
11685
|
-
if (!
|
|
11942
|
+
if (!existsSync2(dir)) {
|
|
11686
11943
|
mkdirSync(dir, { recursive: true });
|
|
11687
11944
|
}
|
|
11688
11945
|
writeFileSync(file, `${id}
|
package/package.json
CHANGED