@yhong91/vibetime 0.1.48 → 0.1.50
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 +334 -85
- 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.50" : "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;
|
|
@@ -7501,6 +7501,79 @@ function createOpenCodeAdapter() {
|
|
|
7501
7501
|
// src/adapters/pi.ts
|
|
7502
7502
|
import { readFile as readFile9 } from "node:fs/promises";
|
|
7503
7503
|
import path15 from "node:path";
|
|
7504
|
+
function parsePiSubagentLink(filePath, headerParentSession) {
|
|
7505
|
+
if (headerParentSession) {
|
|
7506
|
+
const parentFile = path15.isAbsolute(headerParentSession) ? headerParentSession : void 0;
|
|
7507
|
+
const parentSessionId2 = parentFile ? path15.basename(parentFile, ".jsonl") : headerParentSession;
|
|
7508
|
+
return { parentSessionId: parentSessionId2, parentSessionFile: parentFile, explicit: true };
|
|
7509
|
+
}
|
|
7510
|
+
const normalized = filePath.replaceAll("\\", "/");
|
|
7511
|
+
const match = normalized.match(/([^/]+)\/[^/]+\/run-\d+\/session\.jsonl$/);
|
|
7512
|
+
if (!match) {
|
|
7513
|
+
return void 0;
|
|
7514
|
+
}
|
|
7515
|
+
const parentBasename = match[1];
|
|
7516
|
+
if (!parentBasename || parentBasename === "session") {
|
|
7517
|
+
return void 0;
|
|
7518
|
+
}
|
|
7519
|
+
const parentSessionId = parentBasename.endsWith(".jsonl") ? parentBasename.slice(0, -".jsonl".length) : parentBasename;
|
|
7520
|
+
const parentDir = path15.dirname(path15.dirname(path15.dirname(filePath)));
|
|
7521
|
+
const parentSessionFile = path15.join(path15.dirname(parentDir), `${parentBasename}.jsonl`);
|
|
7522
|
+
return { parentSessionId, parentSessionFile, explicit: false };
|
|
7523
|
+
}
|
|
7524
|
+
async function resolveParentContext(link, options) {
|
|
7525
|
+
if (link.parentSessionFile) {
|
|
7526
|
+
try {
|
|
7527
|
+
const text = await readFile9(link.parentSessionFile, "utf8");
|
|
7528
|
+
const firstLine = text.split("\n").find((line) => line.trim().length > 0);
|
|
7529
|
+
const raw = firstLine ? parseJsonLine(firstLine) : void 0;
|
|
7530
|
+
if (raw) {
|
|
7531
|
+
const id = stringField(raw, "id");
|
|
7532
|
+
const cwd = stringField(raw, "cwd");
|
|
7533
|
+
const project = cwd ? path15.basename(cwd) : void 0;
|
|
7534
|
+
if (id || cwd || project) {
|
|
7535
|
+
return { sessionId: id, cwd, project };
|
|
7536
|
+
}
|
|
7537
|
+
}
|
|
7538
|
+
} catch {
|
|
7539
|
+
}
|
|
7540
|
+
}
|
|
7541
|
+
const persisted = await readPersistedSessionContextFromOptions(options, link.parentSessionId);
|
|
7542
|
+
if (persisted) {
|
|
7543
|
+
return { cwd: persisted.cwd, project: persisted.project };
|
|
7544
|
+
}
|
|
7545
|
+
return void 0;
|
|
7546
|
+
}
|
|
7547
|
+
function foldEventsIntoParent(events, link) {
|
|
7548
|
+
const childSessionId = events.find((event) => event.sessionId)?.sessionId;
|
|
7549
|
+
return events.map((event) => {
|
|
7550
|
+
const rewritten = {
|
|
7551
|
+
...event,
|
|
7552
|
+
sessionId: link.parentSessionId
|
|
7553
|
+
};
|
|
7554
|
+
if (childSessionId && childSessionId !== link.parentSessionId) {
|
|
7555
|
+
rewritten.refs = {
|
|
7556
|
+
...event.refs,
|
|
7557
|
+
supersededSessionId: childSessionId
|
|
7558
|
+
};
|
|
7559
|
+
}
|
|
7560
|
+
return rebuildEventIdentity2(rewritten);
|
|
7561
|
+
});
|
|
7562
|
+
}
|
|
7563
|
+
function rebuildEventIdentity2(event) {
|
|
7564
|
+
const importKey = createImportKey([
|
|
7565
|
+
event.source,
|
|
7566
|
+
event.refs?.sourcePathHash,
|
|
7567
|
+
event.refs?.sourceLine,
|
|
7568
|
+
event.type,
|
|
7569
|
+
event.refs?.sourceId
|
|
7570
|
+
]);
|
|
7571
|
+
return {
|
|
7572
|
+
...event,
|
|
7573
|
+
id: createStableEventId(importKey),
|
|
7574
|
+
refs: { ...event.refs, importKey }
|
|
7575
|
+
};
|
|
7576
|
+
}
|
|
7504
7577
|
async function parsePiSessionFile(filePath, options) {
|
|
7505
7578
|
const text = await readFile9(filePath, "utf8");
|
|
7506
7579
|
const lines = text.split("\n").filter(Boolean);
|
|
@@ -7511,6 +7584,7 @@ async function parsePiSessionFile(filePath, options) {
|
|
|
7511
7584
|
let provider;
|
|
7512
7585
|
let turnStartedAt;
|
|
7513
7586
|
let reasoningEffort;
|
|
7587
|
+
let headerParentSession;
|
|
7514
7588
|
const pendingToolCalls = /* @__PURE__ */ new Map();
|
|
7515
7589
|
const state = new SessionParserState(filePath, options, (event) => basePiEvent({ ...event, cwd, project, model }));
|
|
7516
7590
|
const push = (event, ln) => {
|
|
@@ -7534,6 +7608,7 @@ async function parsePiSessionFile(filePath, options) {
|
|
|
7534
7608
|
state.sessionId = sessionId || state.sessionId;
|
|
7535
7609
|
cwd = stringField(raw, "cwd") || cwd;
|
|
7536
7610
|
project = cwd ? path15.basename(cwd) : project;
|
|
7611
|
+
headerParentSession = stringField(raw, "parentSession") || headerParentSession;
|
|
7537
7612
|
continue;
|
|
7538
7613
|
}
|
|
7539
7614
|
if (entryType === "model_change") {
|
|
@@ -7717,6 +7792,17 @@ async function parsePiSessionFile(filePath, options) {
|
|
|
7717
7792
|
if (isTurnIdle(state.currentTurnLastEventAt)) {
|
|
7718
7793
|
state.closeTurn(state.currentTurnLastEventAt, lines.length);
|
|
7719
7794
|
}
|
|
7795
|
+
const link = parsePiSubagentLink(filePath, headerParentSession);
|
|
7796
|
+
if (link) {
|
|
7797
|
+
const parentContext = await resolveParentContext(link, options);
|
|
7798
|
+
const effectiveLink = parentContext?.sessionId ? { ...link, parentSessionId: parentContext.sessionId } : link;
|
|
7799
|
+
if (parentContext) {
|
|
7800
|
+
cwd = cwd || parentContext.cwd;
|
|
7801
|
+
project = project || parentContext.project;
|
|
7802
|
+
}
|
|
7803
|
+
const events = state.events.filter((event) => matchesBackfillFilters(event, options));
|
|
7804
|
+
return foldEventsIntoParent(events, effectiveLink);
|
|
7805
|
+
}
|
|
7720
7806
|
return state.events.filter((event) => matchesBackfillFilters(event, options));
|
|
7721
7807
|
}
|
|
7722
7808
|
function basePiEvent(event) {
|
|
@@ -8008,27 +8094,98 @@ import path17 from "node:path";
|
|
|
8008
8094
|
import { access } from "node:fs/promises";
|
|
8009
8095
|
import os7 from "node:os";
|
|
8010
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
|
+
}
|
|
8011
8122
|
function qoderLocalDbCandidates(appDirName) {
|
|
8012
8123
|
const candidates = [];
|
|
8013
8124
|
const envPath = process.env.QODER_LOCAL_DB_PATH;
|
|
8014
8125
|
if (envPath) {
|
|
8015
8126
|
candidates.push(envPath);
|
|
8016
8127
|
}
|
|
8017
|
-
const
|
|
8018
|
-
let configRoot;
|
|
8019
|
-
if (process.platform === "darwin") {
|
|
8020
|
-
configRoot = path16.join(home, "Library", "Application Support", appDirName);
|
|
8021
|
-
} else if (process.platform === "win32") {
|
|
8022
|
-
configRoot = path16.join(process.env.APPDATA || path16.join(home, "AppData", "Roaming"), appDirName);
|
|
8023
|
-
} else {
|
|
8024
|
-
configRoot = path16.join(home, ".config", appDirName);
|
|
8025
|
-
}
|
|
8128
|
+
const configRoot = appDataRoot(appDirName);
|
|
8026
8129
|
candidates.push(
|
|
8027
8130
|
path16.join(configRoot, "SharedClientCache", "cache", "db", "local.db"),
|
|
8028
8131
|
path16.join(configRoot, "SharedClientCache", "db", "local.db")
|
|
8029
8132
|
);
|
|
8030
8133
|
return candidates;
|
|
8031
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
|
+
}
|
|
8032
8189
|
async function loadQoderDbModelCalls(appDirName, sessionId, modelMap) {
|
|
8033
8190
|
const calls = { byRequestId: /* @__PURE__ */ new Map(), ordered: [] };
|
|
8034
8191
|
if (!sessionId) {
|
|
@@ -8146,7 +8303,7 @@ function parseQoderCnPaths(filePath) {
|
|
|
8146
8303
|
}
|
|
8147
8304
|
return { configDir: configDir2, projectName, sessionId, mainTranscriptPath };
|
|
8148
8305
|
}
|
|
8149
|
-
function
|
|
8306
|
+
function rebuildEventIdentity3(event) {
|
|
8150
8307
|
const importKey = createImportKey([
|
|
8151
8308
|
event.source,
|
|
8152
8309
|
event.refs?.sourcePathHash,
|
|
@@ -8180,7 +8337,7 @@ async function parseModelNamesFromDynamicTexts(dynamicTextsPath) {
|
|
|
8180
8337
|
return {};
|
|
8181
8338
|
}
|
|
8182
8339
|
}
|
|
8183
|
-
async function loadQoderCnModelNames(configDir2) {
|
|
8340
|
+
async function loadQoderCnModelNames(configDir2, home) {
|
|
8184
8341
|
const map = await parseModelNamesFromDynamicTexts(path17.join(configDir2, ".auth", "dynamic-texts.json"));
|
|
8185
8342
|
const siblingConfigDir = configDir2.replace(/\.qoder-cn$/, ".qoder");
|
|
8186
8343
|
if (siblingConfigDir !== configDir2) {
|
|
@@ -8191,6 +8348,14 @@ async function loadQoderCnModelNames(configDir2) {
|
|
|
8191
8348
|
}
|
|
8192
8349
|
}
|
|
8193
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
|
+
}
|
|
8194
8359
|
return map;
|
|
8195
8360
|
}
|
|
8196
8361
|
async function loadQoderCnSegmentModelCalls(filePath, isSubagentSession, modelMap) {
|
|
@@ -8224,7 +8389,8 @@ async function loadQoderCnSegmentModelCalls(filePath, isSubagentSession, modelMa
|
|
|
8224
8389
|
inputTokens: numberField(data, "input_tokens") || 0,
|
|
8225
8390
|
outputTokens: numberField(data, "output_tokens") || 0,
|
|
8226
8391
|
cacheCreationInputTokens: numberField(data, "cache_creation_input_tokens") || 0,
|
|
8227
|
-
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
|
|
8228
8394
|
});
|
|
8229
8395
|
}
|
|
8230
8396
|
}
|
|
@@ -8247,20 +8413,15 @@ async function parseQoderCnSessionFile(filePath, options) {
|
|
|
8247
8413
|
let cwd;
|
|
8248
8414
|
let project = projectContext.project;
|
|
8249
8415
|
let model;
|
|
8250
|
-
const
|
|
8416
|
+
const home = path17.resolve(stringOption(options.home) || os8.homedir());
|
|
8417
|
+
const modelMap = await loadQoderCnModelNames(configDir2, home);
|
|
8251
8418
|
const isSubagentSession = filePath.includes("subagents");
|
|
8252
8419
|
const segmentModelCalls = await loadQoderCnSegmentModelCalls(filePath, isSubagentSession, modelMap);
|
|
8253
8420
|
let modelCallIndex = 0;
|
|
8254
8421
|
let dbModelCalls;
|
|
8255
8422
|
const nextDbModelCall = async (requestId, blockStart) => {
|
|
8256
8423
|
dbModelCalls ??= await loadQoderDbModelCalls("QoderCN", sessionId, modelMap);
|
|
8257
|
-
|
|
8258
|
-
return dbModelCalls.byRequestId.get(requestId)?.shift();
|
|
8259
|
-
}
|
|
8260
|
-
if (!blockStart) {
|
|
8261
|
-
return void 0;
|
|
8262
|
-
}
|
|
8263
|
-
return dbModelCalls.ordered.shift();
|
|
8424
|
+
return takeQoderDbModelCall(dbModelCalls, requestId, blockStart);
|
|
8264
8425
|
};
|
|
8265
8426
|
const state = new SessionParserState(filePath, options, (event) => baseQoderCnEvent({ ...event, cwd, project, model }));
|
|
8266
8427
|
state.sessionId = sessionId;
|
|
@@ -8446,6 +8607,20 @@ async function parseQoderCnSessionFile(filePath, options) {
|
|
|
8446
8607
|
modelCalls: 1
|
|
8447
8608
|
};
|
|
8448
8609
|
model = call.model || model;
|
|
8610
|
+
if (!usage.tokensTotal && !usage.tokensCachedInput) {
|
|
8611
|
+
const dbCall = await nextDbModelCall(requestId, isBlockStart);
|
|
8612
|
+
if (dbCall && (dbCall.inputTokens || dbCall.outputTokens || dbCall.cachedTokens)) {
|
|
8613
|
+
usage = {
|
|
8614
|
+
tokensInput: dbCall.inputTokens || void 0,
|
|
8615
|
+
tokensCachedInput: dbCall.cachedTokens || void 0,
|
|
8616
|
+
tokensCacheReadInput: dbCall.cachedTokens || void 0,
|
|
8617
|
+
tokensOutput: dbCall.outputTokens || void 0,
|
|
8618
|
+
tokensTotal: dbCall.inputTokens + dbCall.outputTokens || void 0,
|
|
8619
|
+
modelCalls: 1
|
|
8620
|
+
};
|
|
8621
|
+
model = dbCall.model || model;
|
|
8622
|
+
}
|
|
8623
|
+
}
|
|
8449
8624
|
} else if (shouldEmitUsage) {
|
|
8450
8625
|
const dbCall = await nextDbModelCall(requestId, isBlockStart);
|
|
8451
8626
|
if (dbCall) {
|
|
@@ -8567,7 +8742,7 @@ async function parseQoderCnSessionFile(filePath, options) {
|
|
|
8567
8742
|
...event.sessionId && event.sessionId !== parentSessionId ? { supersededSessionId: event.sessionId } : {}
|
|
8568
8743
|
}
|
|
8569
8744
|
};
|
|
8570
|
-
return
|
|
8745
|
+
return rebuildEventIdentity3(mapped);
|
|
8571
8746
|
});
|
|
8572
8747
|
}
|
|
8573
8748
|
}
|
|
@@ -8575,7 +8750,7 @@ async function parseQoderCnSessionFile(filePath, options) {
|
|
|
8575
8750
|
if (dbModelCalls.rootSessionId) {
|
|
8576
8751
|
const parentPath = path17.join(path17.dirname(filePath), `${dbModelCalls.rootSessionId}.jsonl`);
|
|
8577
8752
|
const parentSourcePathHash = `sha256:${createStableHash(parentPath)}`;
|
|
8578
|
-
return validEvents.map((event) =>
|
|
8753
|
+
return validEvents.map((event) => rebuildEventIdentity3({
|
|
8579
8754
|
...event,
|
|
8580
8755
|
sessionId: dbModelCalls.rootSessionId,
|
|
8581
8756
|
refs: {
|
|
@@ -8896,6 +9071,7 @@ function createQoderCnAdapter() {
|
|
|
8896
9071
|
}
|
|
8897
9072
|
|
|
8898
9073
|
// src/adapters/qoder.ts
|
|
9074
|
+
import { existsSync } from "node:fs";
|
|
8899
9075
|
import { readdir as readdir8, readFile as readFile11, stat as stat9 } from "node:fs/promises";
|
|
8900
9076
|
import os9 from "node:os";
|
|
8901
9077
|
import path18 from "node:path";
|
|
@@ -8924,7 +9100,7 @@ function parseQoderPaths(filePath) {
|
|
|
8924
9100
|
}
|
|
8925
9101
|
return { configDir: configDir2, projectName, sessionId, mainTranscriptPath };
|
|
8926
9102
|
}
|
|
8927
|
-
function
|
|
9103
|
+
function rebuildEventIdentity4(event) {
|
|
8928
9104
|
const importKey = createImportKey([
|
|
8929
9105
|
event.source,
|
|
8930
9106
|
event.refs?.sourcePathHash,
|
|
@@ -8958,7 +9134,7 @@ async function parseModelNamesFromDynamicTexts2(dynamicTextsPath) {
|
|
|
8958
9134
|
return {};
|
|
8959
9135
|
}
|
|
8960
9136
|
}
|
|
8961
|
-
async function loadQoderModelNames(configDir2) {
|
|
9137
|
+
async function loadQoderModelNames(configDir2, home) {
|
|
8962
9138
|
const map = await parseModelNamesFromDynamicTexts2(path18.join(configDir2, ".auth", "dynamic-texts.json"));
|
|
8963
9139
|
const siblingConfigDir = configDir2.replace(/\.qoder$/, ".qoder-cn");
|
|
8964
9140
|
if (siblingConfigDir !== configDir2) {
|
|
@@ -8969,8 +9145,38 @@ async function loadQoderModelNames(configDir2) {
|
|
|
8969
9145
|
}
|
|
8970
9146
|
}
|
|
8971
9147
|
}
|
|
9148
|
+
if (isQwenworkConfigRoot(configDir2)) {
|
|
9149
|
+
for (const dir of [path18.join(home, ".qoder"), path18.join(home, ".qoder-cn")]) {
|
|
9150
|
+
if (path18.resolve(dir) === path18.resolve(configDir2)) {
|
|
9151
|
+
continue;
|
|
9152
|
+
}
|
|
9153
|
+
const fallbackMap = await parseModelNamesFromDynamicTexts2(path18.join(dir, ".auth", "dynamic-texts.json"));
|
|
9154
|
+
for (const [key, val] of Object.entries(fallbackMap)) {
|
|
9155
|
+
if (!(key in map)) {
|
|
9156
|
+
map[key] = val;
|
|
9157
|
+
}
|
|
9158
|
+
}
|
|
9159
|
+
}
|
|
9160
|
+
}
|
|
9161
|
+
for (const appDirName of ["Qoder", "QoderCN"]) {
|
|
9162
|
+
const ideMap = await loadQoderIdeModelCatalog(appDirName, home);
|
|
9163
|
+
for (const [key, val] of Object.entries(ideMap)) {
|
|
9164
|
+
if (!(key in map)) {
|
|
9165
|
+
map[key] = val;
|
|
9166
|
+
}
|
|
9167
|
+
}
|
|
9168
|
+
}
|
|
8972
9169
|
return map;
|
|
8973
9170
|
}
|
|
9171
|
+
function isQwenworkConfigRoot(configDir2) {
|
|
9172
|
+
const name = path18.basename(path18.resolve(configDir2));
|
|
9173
|
+
if (name === ".qwenworkcn" || name === ".qwenwork") {
|
|
9174
|
+
return true;
|
|
9175
|
+
}
|
|
9176
|
+
const override = process.env.QWENWORK_CONFIG_DIR;
|
|
9177
|
+
return Boolean(override && override.trim() && path18.basename(path18.resolve(override)) === name);
|
|
9178
|
+
}
|
|
9179
|
+
var FAILED_SEGMENT_STOP_REASONS = /* @__PURE__ */ new Set(["cancelled", "canceled", "error", "failed", "refusal"]);
|
|
8974
9180
|
async function loadQoderSegmentModelCalls(filePath, isSubagentSession, modelMap) {
|
|
8975
9181
|
const { configDir: configDir2, projectName, sessionId } = parseQoderPaths(filePath);
|
|
8976
9182
|
const segmentsPath = path18.join(configDir2, "logs", "sessions", projectName, sessionId, "segments");
|
|
@@ -9002,7 +9208,8 @@ async function loadQoderSegmentModelCalls(filePath, isSubagentSession, modelMap)
|
|
|
9002
9208
|
inputTokens: numberField(data, "input_tokens") || 0,
|
|
9003
9209
|
outputTokens: numberField(data, "output_tokens") || 0,
|
|
9004
9210
|
cacheCreationInputTokens: numberField(data, "cache_creation_input_tokens") || 0,
|
|
9005
|
-
cacheReadInputTokens: numberField(data, "cache_read_input_tokens") || 0
|
|
9211
|
+
cacheReadInputTokens: numberField(data, "cache_read_input_tokens") || 0,
|
|
9212
|
+
stopReason: stringField(data, "stop_reason") || void 0
|
|
9006
9213
|
});
|
|
9007
9214
|
}
|
|
9008
9215
|
}
|
|
@@ -9025,20 +9232,16 @@ async function parseQoderSessionFile(filePath, options) {
|
|
|
9025
9232
|
let cwd;
|
|
9026
9233
|
let project = projectContext.project;
|
|
9027
9234
|
let model;
|
|
9028
|
-
const
|
|
9235
|
+
const home = path18.resolve(stringOption(options.home) || os9.homedir());
|
|
9236
|
+
const modelMap = await loadQoderModelNames(configDir2, home);
|
|
9237
|
+
const qwenworkRoot = isQwenworkConfigRoot(configDir2);
|
|
9029
9238
|
const isSubagentSession = filePath.includes("subagents");
|
|
9030
9239
|
const segmentModelCalls = await loadQoderSegmentModelCalls(filePath, isSubagentSession, modelMap);
|
|
9031
9240
|
let modelCallIndex = 0;
|
|
9032
9241
|
let dbModelCalls;
|
|
9033
9242
|
const nextDbModelCall = async (requestId, blockStart) => {
|
|
9034
9243
|
dbModelCalls ??= await loadQoderDbModelCalls("Qoder", sessionId, modelMap);
|
|
9035
|
-
|
|
9036
|
-
return dbModelCalls.byRequestId.get(requestId)?.shift();
|
|
9037
|
-
}
|
|
9038
|
-
if (!blockStart) {
|
|
9039
|
-
return void 0;
|
|
9040
|
-
}
|
|
9041
|
-
return dbModelCalls.ordered.shift();
|
|
9244
|
+
return takeQoderDbModelCall(dbModelCalls, requestId, blockStart);
|
|
9042
9245
|
};
|
|
9043
9246
|
const state = new SessionParserState(filePath, options, (event) => baseQoderEvent({ ...event, cwd, project, model }));
|
|
9044
9247
|
state.sessionId = sessionId;
|
|
@@ -9206,39 +9409,28 @@ async function parseQoderSessionFile(filePath, options) {
|
|
|
9206
9409
|
seenUsageKeys.add(usageKey);
|
|
9207
9410
|
}
|
|
9208
9411
|
let usage;
|
|
9412
|
+
let keepZeroTokenUsage = false;
|
|
9209
9413
|
if (shouldEmitUsage && modelCallIndex < segmentModelCalls.length) {
|
|
9210
9414
|
const call = segmentModelCalls[modelCallIndex++];
|
|
9211
|
-
|
|
9212
|
-
const cacheCreationInputTokens = call.cacheCreationInputTokens;
|
|
9213
|
-
const cacheReadInputTokens = call.cacheReadInputTokens;
|
|
9214
|
-
const outputTokens = call.outputTokens;
|
|
9215
|
-
const cachedInputTokens = cacheCreationInputTokens + cacheReadInputTokens;
|
|
9216
|
-
const totalInputTokens = inputTokens;
|
|
9217
|
-
usage = {
|
|
9218
|
-
tokensInput: totalInputTokens || void 0,
|
|
9219
|
-
tokensCachedInput: cachedInputTokens || void 0,
|
|
9220
|
-
tokensCacheCreationInput: cacheCreationInputTokens || void 0,
|
|
9221
|
-
tokensCacheReadInput: cacheReadInputTokens || void 0,
|
|
9222
|
-
tokensOutput: outputTokens || void 0,
|
|
9223
|
-
tokensTotal: totalInputTokens + outputTokens || void 0,
|
|
9224
|
-
modelCalls: 1
|
|
9225
|
-
};
|
|
9415
|
+
usage = qoderSegmentUsage(call);
|
|
9226
9416
|
model = call.model || model;
|
|
9417
|
+
if (!usage.tokensTotal && !usage.tokensCachedInput) {
|
|
9418
|
+
const dbCall = await nextDbModelCall(requestId, isBlockStart);
|
|
9419
|
+
if (dbCall && (dbCall.inputTokens || dbCall.outputTokens || dbCall.cachedTokens)) {
|
|
9420
|
+
usage = qoderDbUsage(dbCall);
|
|
9421
|
+
model = dbCall.model || model;
|
|
9422
|
+
} else if (qwenworkRoot && (!call.stopReason || !FAILED_SEGMENT_STOP_REASONS.has(call.stopReason))) {
|
|
9423
|
+
keepZeroTokenUsage = true;
|
|
9424
|
+
}
|
|
9425
|
+
}
|
|
9227
9426
|
} else if (shouldEmitUsage) {
|
|
9228
9427
|
const dbCall = await nextDbModelCall(requestId, isBlockStart);
|
|
9229
9428
|
if (dbCall) {
|
|
9230
|
-
usage =
|
|
9231
|
-
tokensInput: dbCall.inputTokens || void 0,
|
|
9232
|
-
tokensCachedInput: dbCall.cachedTokens || void 0,
|
|
9233
|
-
tokensCacheReadInput: dbCall.cachedTokens || void 0,
|
|
9234
|
-
tokensOutput: dbCall.outputTokens || void 0,
|
|
9235
|
-
tokensTotal: dbCall.inputTokens + dbCall.outputTokens || void 0,
|
|
9236
|
-
modelCalls: 1
|
|
9237
|
-
};
|
|
9429
|
+
usage = qoderDbUsage(dbCall);
|
|
9238
9430
|
model = dbCall.model || model;
|
|
9239
9431
|
}
|
|
9240
9432
|
}
|
|
9241
|
-
if (usage && (usage.tokensTotal || usage.tokensCachedInput)) {
|
|
9433
|
+
if (usage && (usage.tokensTotal || usage.tokensCachedInput || keepZeroTokenUsage)) {
|
|
9242
9434
|
const speed = stringField(objectField(message, "usage"), "speed");
|
|
9243
9435
|
const usageModel = speed === "fast" && model ? `${model}-fast` : model;
|
|
9244
9436
|
push(baseQoderEvent({
|
|
@@ -9345,7 +9537,7 @@ async function parseQoderSessionFile(filePath, options) {
|
|
|
9345
9537
|
...event.sessionId && event.sessionId !== parentSessionId ? { supersededSessionId: event.sessionId } : {}
|
|
9346
9538
|
}
|
|
9347
9539
|
};
|
|
9348
|
-
return
|
|
9540
|
+
return rebuildEventIdentity4(mapped);
|
|
9349
9541
|
});
|
|
9350
9542
|
}
|
|
9351
9543
|
}
|
|
@@ -9353,7 +9545,7 @@ async function parseQoderSessionFile(filePath, options) {
|
|
|
9353
9545
|
if (dbModelCalls.rootSessionId) {
|
|
9354
9546
|
const parentPath = path18.join(path18.dirname(filePath), `${dbModelCalls.rootSessionId}.jsonl`);
|
|
9355
9547
|
const parentSourcePathHash = `sha256:${createStableHash(parentPath)}`;
|
|
9356
|
-
return validEvents.map((event) =>
|
|
9548
|
+
return validEvents.map((event) => rebuildEventIdentity4({
|
|
9357
9549
|
...event,
|
|
9358
9550
|
sessionId: dbModelCalls.rootSessionId,
|
|
9359
9551
|
refs: {
|
|
@@ -9365,6 +9557,28 @@ async function parseQoderSessionFile(filePath, options) {
|
|
|
9365
9557
|
}
|
|
9366
9558
|
return validEvents;
|
|
9367
9559
|
}
|
|
9560
|
+
function qoderSegmentUsage(call) {
|
|
9561
|
+
const cachedInputTokens = call.cacheCreationInputTokens + call.cacheReadInputTokens;
|
|
9562
|
+
return {
|
|
9563
|
+
tokensInput: call.inputTokens || void 0,
|
|
9564
|
+
tokensCachedInput: cachedInputTokens || void 0,
|
|
9565
|
+
tokensCacheCreationInput: call.cacheCreationInputTokens || void 0,
|
|
9566
|
+
tokensCacheReadInput: call.cacheReadInputTokens || void 0,
|
|
9567
|
+
tokensOutput: call.outputTokens || void 0,
|
|
9568
|
+
tokensTotal: call.inputTokens + call.outputTokens || void 0,
|
|
9569
|
+
modelCalls: 1
|
|
9570
|
+
};
|
|
9571
|
+
}
|
|
9572
|
+
function qoderDbUsage(dbCall) {
|
|
9573
|
+
return {
|
|
9574
|
+
tokensInput: dbCall.inputTokens || void 0,
|
|
9575
|
+
tokensCachedInput: dbCall.cachedTokens || void 0,
|
|
9576
|
+
tokensCacheReadInput: dbCall.cachedTokens || void 0,
|
|
9577
|
+
tokensOutput: dbCall.outputTokens || void 0,
|
|
9578
|
+
tokensTotal: dbCall.inputTokens + dbCall.outputTokens || void 0,
|
|
9579
|
+
modelCalls: 1
|
|
9580
|
+
};
|
|
9581
|
+
}
|
|
9368
9582
|
function baseQoderEvent(event) {
|
|
9369
9583
|
return {
|
|
9370
9584
|
schemaVersion: AGENT_TIME_SCHEMA_VERSION,
|
|
@@ -9448,12 +9662,20 @@ async function qoderProjectContextFromLines(filePath, lines, options, configDir2
|
|
|
9448
9662
|
const isSubagent = filePath.includes(`${path18.sep}subagents${path18.sep}`);
|
|
9449
9663
|
const inherited = isSubagent ? await readPersistedSessionContextFromOptions(options, sessionId) : void 0;
|
|
9450
9664
|
let cwds = [];
|
|
9665
|
+
const workspaceDirs = [];
|
|
9451
9666
|
for (const line of lines) {
|
|
9452
9667
|
const raw = parseJsonLine(line);
|
|
9453
9668
|
const cwd = raw ? stringField(raw, "cwd") : void 0;
|
|
9454
9669
|
if (cwd && path18.isAbsolute(cwd)) {
|
|
9455
9670
|
cwds.push(cwd);
|
|
9456
9671
|
}
|
|
9672
|
+
if (raw && stringField(raw, "type") === "workspace-directories") {
|
|
9673
|
+
for (const dir of arrayField5(raw, "directories")) {
|
|
9674
|
+
if (typeof dir === "string" && path18.isAbsolute(dir)) {
|
|
9675
|
+
workspaceDirs.push(dir);
|
|
9676
|
+
}
|
|
9677
|
+
}
|
|
9678
|
+
}
|
|
9457
9679
|
}
|
|
9458
9680
|
if (isSubagent) {
|
|
9459
9681
|
if (inherited?.cwd && path18.isAbsolute(inherited.cwd)) {
|
|
@@ -9469,6 +9691,13 @@ async function qoderProjectContextFromLines(filePath, lines, options, configDir2
|
|
|
9469
9691
|
if (cwd && path18.isAbsolute(cwd)) {
|
|
9470
9692
|
parentCwds.push(cwd);
|
|
9471
9693
|
}
|
|
9694
|
+
if (workspaceDirs.length === 0 && raw && stringField(raw, "type") === "workspace-directories") {
|
|
9695
|
+
for (const dir of arrayField5(raw, "directories")) {
|
|
9696
|
+
if (typeof dir === "string" && path18.isAbsolute(dir)) {
|
|
9697
|
+
workspaceDirs.push(dir);
|
|
9698
|
+
}
|
|
9699
|
+
}
|
|
9700
|
+
}
|
|
9472
9701
|
}
|
|
9473
9702
|
if (parentCwds.length > 0) {
|
|
9474
9703
|
cwds = parentCwds;
|
|
@@ -9477,6 +9706,12 @@ async function qoderProjectContextFromLines(filePath, lines, options, configDir2
|
|
|
9477
9706
|
}
|
|
9478
9707
|
}
|
|
9479
9708
|
}
|
|
9709
|
+
if (cwds.length > 0 && cwds.every((cwd) => pathInsideDir(cwd, configDir2))) {
|
|
9710
|
+
const external = workspaceDirs.filter((dir) => !pathInsideDir(dir, configDir2));
|
|
9711
|
+
if (external.length > 0) {
|
|
9712
|
+
cwds = external;
|
|
9713
|
+
}
|
|
9714
|
+
}
|
|
9480
9715
|
const root = await gitRootFromCwds3(cwds) || qoderProjectRootFromCwds(projectDir, cwds);
|
|
9481
9716
|
const project = inherited?.project || (cwds.length > 0 ? path18.basename(cwds[0]) : root ? path18.basename(root) : await qoderProjectFromFilePath(filePath, options));
|
|
9482
9717
|
return {
|
|
@@ -9484,6 +9719,11 @@ async function qoderProjectContextFromLines(filePath, lines, options, configDir2
|
|
|
9484
9719
|
workspaceId: createWorkspaceId({ projectName: project, repoRoot: root })
|
|
9485
9720
|
};
|
|
9486
9721
|
}
|
|
9722
|
+
function pathInsideDir(candidate, dir) {
|
|
9723
|
+
const resolvedDir = path18.resolve(dir);
|
|
9724
|
+
const resolved = path18.resolve(candidate);
|
|
9725
|
+
return resolved === resolvedDir || resolved.startsWith(`${resolvedDir}${path18.sep}`);
|
|
9726
|
+
}
|
|
9487
9727
|
async function gitRootFromCwds3(cwds) {
|
|
9488
9728
|
const seen = /* @__PURE__ */ new Set();
|
|
9489
9729
|
for (const cwd of cwds) {
|
|
@@ -9508,7 +9748,7 @@ function qoderProjectRootFromCwds(projectDir, cwds) {
|
|
|
9508
9748
|
for (const cwd of cwds) {
|
|
9509
9749
|
let current = path18.resolve(cwd);
|
|
9510
9750
|
while (true) {
|
|
9511
|
-
if (
|
|
9751
|
+
if (qoderEncodedVariants(current).includes(projectDir)) {
|
|
9512
9752
|
return current;
|
|
9513
9753
|
}
|
|
9514
9754
|
const parent = path18.dirname(current);
|
|
@@ -9520,21 +9760,23 @@ function qoderProjectRootFromCwds(projectDir, cwds) {
|
|
|
9520
9760
|
}
|
|
9521
9761
|
return void 0;
|
|
9522
9762
|
}
|
|
9523
|
-
function encodeQoderProjectPath(value) {
|
|
9524
|
-
return path18.resolve(value).split(path18.sep).join("-").replaceAll("_", "-");
|
|
9525
|
-
}
|
|
9526
9763
|
function rawQoderProjectPath(value) {
|
|
9527
9764
|
return path18.resolve(value).split(path18.sep).join("-");
|
|
9528
9765
|
}
|
|
9529
9766
|
function qoderEncodedVariants(value) {
|
|
9530
9767
|
const raw = rawQoderProjectPath(value);
|
|
9531
|
-
const
|
|
9532
|
-
|
|
9768
|
+
const variants = /* @__PURE__ */ new Set([
|
|
9769
|
+
raw,
|
|
9770
|
+
raw.replaceAll("_", "-"),
|
|
9771
|
+
raw.replaceAll(".", "-"),
|
|
9772
|
+
raw.replaceAll("_", "-").replaceAll(".", "-")
|
|
9773
|
+
]);
|
|
9774
|
+
return [...variants];
|
|
9533
9775
|
}
|
|
9534
9776
|
function qoderEncodedProjectSuffix(projectDir, home) {
|
|
9535
9777
|
for (const prefix of qoderEncodedVariants(home).map((value) => `${value}-`)) {
|
|
9536
9778
|
if (projectDir.startsWith(prefix)) {
|
|
9537
|
-
return projectDir.slice(prefix.length) || void 0;
|
|
9779
|
+
return projectDir.slice(prefix.length).replace(/^-+/, "") || void 0;
|
|
9538
9780
|
}
|
|
9539
9781
|
}
|
|
9540
9782
|
return void 0;
|
|
@@ -9619,6 +9861,16 @@ function qoderConfigDir(home, env) {
|
|
|
9619
9861
|
}
|
|
9620
9862
|
return path18.join(home, ".qoder");
|
|
9621
9863
|
}
|
|
9864
|
+
function qwenworkConfigDir(home, env) {
|
|
9865
|
+
const override = env?.QWENWORK_CONFIG_DIR;
|
|
9866
|
+
if (override && override.trim()) {
|
|
9867
|
+
return path18.resolve(override);
|
|
9868
|
+
}
|
|
9869
|
+
return path18.join(home, ".qwenworkcn");
|
|
9870
|
+
}
|
|
9871
|
+
function qoderConfigDirs(home, env) {
|
|
9872
|
+
return [...new Set([qoderConfigDir(home, env), qwenworkConfigDir(home, env)].map((dir) => path18.resolve(dir)))];
|
|
9873
|
+
}
|
|
9622
9874
|
function createQoderAdapter() {
|
|
9623
9875
|
return {
|
|
9624
9876
|
id: "qoder",
|
|
@@ -9638,19 +9890,22 @@ function createQoderAdapter() {
|
|
|
9638
9890
|
);
|
|
9639
9891
|
},
|
|
9640
9892
|
installEntries(home, env) {
|
|
9641
|
-
|
|
9893
|
+
const [primary, ...variants] = qoderConfigDirs(home, env);
|
|
9894
|
+
const targets = [primary, ...variants.filter((dir) => existsSync(dir))];
|
|
9895
|
+
return targets.map((base) => ({
|
|
9642
9896
|
kind: "hooks-json",
|
|
9643
|
-
path: path18.join(
|
|
9897
|
+
path: path18.join(base, "settings.json"),
|
|
9644
9898
|
content: hookConfig7()
|
|
9645
|
-
}
|
|
9899
|
+
}));
|
|
9646
9900
|
},
|
|
9647
9901
|
sourcePaths(home, env) {
|
|
9902
|
+
const paths = qoderConfigDirs(home, env).map((base2) => path18.join(base2, "projects"));
|
|
9648
9903
|
const base = qoderConfigDir(home, env);
|
|
9649
|
-
|
|
9650
|
-
path18.join(base, "projects"),
|
|
9904
|
+
paths.push(
|
|
9651
9905
|
path18.join(base, ".qoder.json"),
|
|
9652
9906
|
path18.join(home, ".qoder.json")
|
|
9653
|
-
|
|
9907
|
+
);
|
|
9908
|
+
return paths;
|
|
9654
9909
|
},
|
|
9655
9910
|
parseSessionFile: parseQoderSessionFile
|
|
9656
9911
|
};
|
|
@@ -11085,13 +11340,7 @@ function createZedAdapter() {
|
|
|
11085
11340
|
}
|
|
11086
11341
|
|
|
11087
11342
|
// src/lib/pricing.ts
|
|
11088
|
-
function estimateEventCostUsd(
|
|
11089
|
-
if (event.type !== "model.usage") {
|
|
11090
|
-
return 0;
|
|
11091
|
-
}
|
|
11092
|
-
if (typeof event.metrics?.costUsd === "number" && event.metrics.costUsd > 0) {
|
|
11093
|
-
return event.metrics.costUsd;
|
|
11094
|
-
}
|
|
11343
|
+
function estimateEventCostUsd(_event) {
|
|
11095
11344
|
return 0;
|
|
11096
11345
|
}
|
|
11097
11346
|
|
|
@@ -11554,7 +11803,7 @@ function hookCommandFromGroup(group) {
|
|
|
11554
11803
|
|
|
11555
11804
|
// src/lib/config.ts
|
|
11556
11805
|
import { randomUUID } from "node:crypto";
|
|
11557
|
-
import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
11806
|
+
import { existsSync as existsSync2, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
11558
11807
|
import { homedir, hostname } from "node:os";
|
|
11559
11808
|
import path22 from "node:path";
|
|
11560
11809
|
function configDir(home = homedir()) {
|
|
@@ -11568,7 +11817,7 @@ function machineIdPath(home = homedir()) {
|
|
|
11568
11817
|
}
|
|
11569
11818
|
function readConfig(home = homedir()) {
|
|
11570
11819
|
const file = configPath(home);
|
|
11571
|
-
if (!
|
|
11820
|
+
if (!existsSync2(file)) {
|
|
11572
11821
|
return {};
|
|
11573
11822
|
}
|
|
11574
11823
|
try {
|
|
@@ -11580,7 +11829,7 @@ function readConfig(home = homedir()) {
|
|
|
11580
11829
|
}
|
|
11581
11830
|
function writeConfig(config, home = homedir()) {
|
|
11582
11831
|
const dir = configDir(home);
|
|
11583
|
-
if (!
|
|
11832
|
+
if (!existsSync2(dir)) {
|
|
11584
11833
|
mkdirSync(dir, { recursive: true });
|
|
11585
11834
|
}
|
|
11586
11835
|
writeFileSync(configPath(home), `${JSON.stringify(config, null, 2)}
|
|
@@ -11588,7 +11837,7 @@ function writeConfig(config, home = homedir()) {
|
|
|
11588
11837
|
}
|
|
11589
11838
|
function ensureLocalMachineId(home = homedir()) {
|
|
11590
11839
|
const file = machineIdPath(home);
|
|
11591
|
-
if (
|
|
11840
|
+
if (existsSync2(file)) {
|
|
11592
11841
|
const value = readFileSync(file, "utf8").trim();
|
|
11593
11842
|
if (value.length > 0) {
|
|
11594
11843
|
return value;
|
|
@@ -11596,7 +11845,7 @@ function ensureLocalMachineId(home = homedir()) {
|
|
|
11596
11845
|
}
|
|
11597
11846
|
const id = randomUUID();
|
|
11598
11847
|
const dir = configDir(home);
|
|
11599
|
-
if (!
|
|
11848
|
+
if (!existsSync2(dir)) {
|
|
11600
11849
|
mkdirSync(dir, { recursive: true });
|
|
11601
11850
|
}
|
|
11602
11851
|
writeFileSync(file, `${id}
|
package/package.json
CHANGED