@yhong91/vibetime 0.1.51 → 0.1.53
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 +1202 -291
- package/package.json +1 -1
package/bin/vibetime.mjs
CHANGED
|
@@ -18,7 +18,7 @@ var __export = (target, all) => {
|
|
|
18
18
|
// src/lib/fs.ts
|
|
19
19
|
var fs_exports = {};
|
|
20
20
|
__export(fs_exports, {
|
|
21
|
-
GENERATED_MARKER: () =>
|
|
21
|
+
GENERATED_MARKER: () => GENERATED_MARKER2,
|
|
22
22
|
countDirectoryEntries: () => countDirectoryEntries,
|
|
23
23
|
listFilesByExtensions: () => listFilesByExtensions,
|
|
24
24
|
listJsonlFiles: () => listJsonlFiles,
|
|
@@ -72,7 +72,7 @@ async function writeGeneratedFile(filePath, content, { dryRun, force, onWrite })
|
|
|
72
72
|
onWrite(`Already installed ${filePath}`);
|
|
73
73
|
return;
|
|
74
74
|
}
|
|
75
|
-
if (existing !== null && !existing.includes(
|
|
75
|
+
if (existing !== null && !existing.includes(GENERATED_MARKER2) && !existing.includes(LEGACY_GENERATED_MARKER) && !force) {
|
|
76
76
|
throw new Error(
|
|
77
77
|
`Refusing to overwrite non-vibetime file: ${filePath}. Re-run with --force if this is intentional.`
|
|
78
78
|
);
|
|
@@ -148,11 +148,11 @@ async function countDirectoryEntries(candidatePath) {
|
|
|
148
148
|
throw error;
|
|
149
149
|
}
|
|
150
150
|
}
|
|
151
|
-
var
|
|
151
|
+
var GENERATED_MARKER2, LEGACY_GENERATED_MARKER;
|
|
152
152
|
var init_fs = __esm({
|
|
153
153
|
"src/lib/fs.ts"() {
|
|
154
154
|
"use strict";
|
|
155
|
-
|
|
155
|
+
GENERATED_MARKER2 = "Generated by vibetime.";
|
|
156
156
|
LEGACY_GENERATED_MARKER = "Generated by codetime.";
|
|
157
157
|
}
|
|
158
158
|
});
|
|
@@ -886,7 +886,7 @@ var init_esm = __esm({
|
|
|
886
886
|
import { spawn as spawn2, spawnSync } from "node:child_process";
|
|
887
887
|
import { mkdir as mkdir5, open, rm, stat as stat13, writeFile as writeFile4 } from "node:fs/promises";
|
|
888
888
|
import os11 from "node:os";
|
|
889
|
-
import
|
|
889
|
+
import path25 from "node:path";
|
|
890
890
|
import { fileURLToPath } from "node:url";
|
|
891
891
|
|
|
892
892
|
// ../shared/src/index.ts
|
|
@@ -924,7 +924,7 @@ var TELEMETRY_EVENT_TYPES = [
|
|
|
924
924
|
"agent.operation"
|
|
925
925
|
];
|
|
926
926
|
var FILE_ACTIVITY_OPERATIONS = ["read", "search", "create", "write", "edit", "delete"];
|
|
927
|
-
var BACKFILL_SOURCE_IDS = ["codex", "claude-code", "claude-cowork", "copilot", "opencode", "pi", "agy", "codebuddy", "qoder", "qoder-cn", "workbuddy", "zcode", "grok-build", "zed"];
|
|
927
|
+
var BACKFILL_SOURCE_IDS = ["codex", "claude-code", "claude-cowork", "copilot", "opencode", "pi", "agy", "codebuddy", "qoder", "qoder-cn", "workbuddy", "zcode", "grok-build", "zed", "kimi-code"];
|
|
928
928
|
function createWorkspaceId(input) {
|
|
929
929
|
const basis = input.repoUrl || input.repoRoot || input.projectName || "unknown";
|
|
930
930
|
return `workspace_${fnv1a(basis)}`;
|
|
@@ -2047,7 +2047,8 @@ 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.53" : "0.1.1";
|
|
2051
|
+
var GENERATED_MARKER = "Generated by vibetime.";
|
|
2051
2052
|
var DEFAULT_API_URL = "http://121.196.224.82:3001";
|
|
2052
2053
|
var DEFAULT_BACKFILL_BATCH_SIZE = 50;
|
|
2053
2054
|
var DEFAULT_BACKFILL_BATCH_BYTES = 800 * 1024;
|
|
@@ -6935,120 +6936,761 @@ function createGrokBuildAdapter() {
|
|
|
6935
6936
|
};
|
|
6936
6937
|
}
|
|
6937
6938
|
|
|
6938
|
-
// src/adapters/
|
|
6939
|
-
import
|
|
6939
|
+
// src/adapters/kimi-code.ts
|
|
6940
|
+
import { readFile as readFile9 } from "node:fs/promises";
|
|
6940
6941
|
import path14 from "node:path";
|
|
6941
|
-
|
|
6942
|
-
|
|
6943
|
-
|
|
6944
|
-
|
|
6942
|
+
|
|
6943
|
+
// src/lib/toml-hooks.ts
|
|
6944
|
+
init_fs();
|
|
6945
|
+
async function hasTomlHookCommand(filePath, command) {
|
|
6946
|
+
const text = await readTextIfExists(filePath);
|
|
6947
|
+
if (!text) {
|
|
6948
|
+
return false;
|
|
6945
6949
|
}
|
|
6946
|
-
|
|
6947
|
-
|
|
6950
|
+
return parseTomlHookRules(text).some((rule) => rule.command === command);
|
|
6951
|
+
}
|
|
6952
|
+
function parseTomlHookRules(text) {
|
|
6953
|
+
const rules = [];
|
|
6954
|
+
const chunks = text.split(/^\[\[hooks\]\][ \t]*\r?\n?/m);
|
|
6955
|
+
for (const chunk of chunks.slice(1)) {
|
|
6956
|
+
const body = chunk.split(/^\[[^\]]+\]/m)[0] ?? "";
|
|
6957
|
+
const event = tomlStringField(body, "event");
|
|
6958
|
+
const command = tomlStringField(body, "command");
|
|
6959
|
+
if (!event || !command) {
|
|
6960
|
+
continue;
|
|
6961
|
+
}
|
|
6962
|
+
const matcher = tomlStringField(body, "matcher");
|
|
6963
|
+
const timeout = tomlNumberField(body, "timeout");
|
|
6964
|
+
const rule = { event, command };
|
|
6965
|
+
if (matcher !== void 0) {
|
|
6966
|
+
rule.matcher = matcher;
|
|
6967
|
+
}
|
|
6968
|
+
if (timeout !== void 0) {
|
|
6969
|
+
rule.timeout = timeout;
|
|
6970
|
+
}
|
|
6971
|
+
rules.push(rule);
|
|
6972
|
+
}
|
|
6973
|
+
return rules;
|
|
6974
|
+
}
|
|
6975
|
+
function mergeTomlHookRules(existingText, desired) {
|
|
6976
|
+
const existingKeys = new Set(parseTomlHookRules(existingText).map(ruleKey));
|
|
6977
|
+
const toAppend = desired.filter((rule) => !existingKeys.has(ruleKey(rule)));
|
|
6978
|
+
if (toAppend.length === 0) {
|
|
6979
|
+
return existingText;
|
|
6980
|
+
}
|
|
6981
|
+
let base = existingText;
|
|
6982
|
+
if (base.length > 0 && !base.endsWith("\n")) {
|
|
6983
|
+
base += "\n";
|
|
6984
|
+
}
|
|
6985
|
+
if (base.length > 0 && !base.endsWith("\n\n") && base.trim().length > 0) {
|
|
6986
|
+
base += "\n";
|
|
6987
|
+
}
|
|
6988
|
+
const blocks = toAppend.map(serializeTomlHookRule).join("\n\n");
|
|
6989
|
+
return `${base}${blocks}
|
|
6990
|
+
`;
|
|
6991
|
+
}
|
|
6992
|
+
function removeTomlHookRulesByCommand(existingText, commands) {
|
|
6993
|
+
const commandSet = new Set(commands.filter(Boolean));
|
|
6994
|
+
if (commandSet.size === 0 || !existingText) {
|
|
6995
|
+
return existingText;
|
|
6996
|
+
}
|
|
6997
|
+
const parts = [];
|
|
6998
|
+
let cursor = 0;
|
|
6999
|
+
const headerRe = /^\[\[hooks\]\][ \t]*\r?\n?/gm;
|
|
7000
|
+
let match = headerRe.exec(existingText);
|
|
7001
|
+
let removed = 0;
|
|
7002
|
+
while (match) {
|
|
7003
|
+
const headerStart = match.index;
|
|
7004
|
+
const bodyStart = headerRe.lastIndex;
|
|
7005
|
+
const rest = existingText.slice(bodyStart);
|
|
7006
|
+
const nextHeader = rest.search(/^\[[^\]]+\]/m);
|
|
7007
|
+
const bodyEnd = nextHeader === -1 ? existingText.length : bodyStart + nextHeader;
|
|
7008
|
+
const body = existingText.slice(bodyStart, bodyEnd);
|
|
7009
|
+
const command = tomlStringField(body, "command");
|
|
7010
|
+
parts.push(existingText.slice(cursor, headerStart));
|
|
7011
|
+
if (command && commandSet.has(command)) {
|
|
7012
|
+
removed += 1;
|
|
7013
|
+
} else {
|
|
7014
|
+
parts.push(existingText.slice(headerStart, bodyEnd));
|
|
7015
|
+
}
|
|
7016
|
+
cursor = bodyEnd;
|
|
7017
|
+
match = headerRe.exec(existingText);
|
|
7018
|
+
}
|
|
7019
|
+
if (removed === 0) {
|
|
7020
|
+
return existingText;
|
|
7021
|
+
}
|
|
7022
|
+
parts.push(existingText.slice(cursor));
|
|
7023
|
+
let next = parts.join("");
|
|
7024
|
+
next = next.replace(/\n{3,}/g, "\n\n");
|
|
7025
|
+
if (next.length > 0 && !next.endsWith("\n")) {
|
|
7026
|
+
next += "\n";
|
|
7027
|
+
}
|
|
7028
|
+
return next;
|
|
7029
|
+
}
|
|
7030
|
+
function looksLikeToml(text) {
|
|
7031
|
+
const sample = text.trim();
|
|
7032
|
+
if (!sample) {
|
|
7033
|
+
return true;
|
|
7034
|
+
}
|
|
7035
|
+
if (sample.startsWith("{")) {
|
|
7036
|
+
return false;
|
|
7037
|
+
}
|
|
7038
|
+
if (/^\s*\[\s*\{/.test(sample)) {
|
|
7039
|
+
return false;
|
|
7040
|
+
}
|
|
7041
|
+
return true;
|
|
7042
|
+
}
|
|
7043
|
+
function ruleKey(rule) {
|
|
7044
|
+
return `${rule.event}\0${rule.command}`;
|
|
7045
|
+
}
|
|
7046
|
+
function serializeTomlHookRule(rule) {
|
|
7047
|
+
const lines = [
|
|
7048
|
+
"[[hooks]]",
|
|
7049
|
+
`event = "${escapeTomlString(rule.event)}"`
|
|
7050
|
+
];
|
|
7051
|
+
if (rule.matcher !== void 0 && rule.matcher !== "") {
|
|
7052
|
+
lines.push(`matcher = "${escapeTomlString(rule.matcher)}"`);
|
|
7053
|
+
}
|
|
7054
|
+
lines.push(`command = "${escapeTomlString(rule.command)}"`);
|
|
7055
|
+
if (rule.timeout !== void 0) {
|
|
7056
|
+
lines.push(`timeout = ${Math.floor(rule.timeout)}`);
|
|
7057
|
+
}
|
|
7058
|
+
return lines.join("\n");
|
|
7059
|
+
}
|
|
7060
|
+
function escapeTomlString(value) {
|
|
7061
|
+
return value.replaceAll("\\", "\\\\").replaceAll('"', '\\"').replaceAll("\n", "\\n").replaceAll(" ", "\\t");
|
|
7062
|
+
}
|
|
7063
|
+
function tomlStringField(body, key) {
|
|
7064
|
+
const re = new RegExp(
|
|
7065
|
+
`^\\s*${key}\\s*=\\s*(?:"((?:\\\\.|[^"\\\\])*)"|'([^']*)')\\s*$`,
|
|
7066
|
+
"m"
|
|
7067
|
+
);
|
|
7068
|
+
const match = body.match(re);
|
|
7069
|
+
if (!match) {
|
|
7070
|
+
return void 0;
|
|
7071
|
+
}
|
|
7072
|
+
if (match[1] !== void 0) {
|
|
7073
|
+
return match[1].replaceAll("\\n", "\n").replaceAll("\\t", " ").replaceAll('\\"', '"').replaceAll("\\\\", "\\");
|
|
7074
|
+
}
|
|
7075
|
+
return match[2];
|
|
7076
|
+
}
|
|
7077
|
+
function tomlNumberField(body, key) {
|
|
7078
|
+
const re = new RegExp(`^\\s*${key}\\s*=\\s*(-?\\d+)\\s*$`, "m");
|
|
7079
|
+
const match = body.match(re);
|
|
7080
|
+
if (!match?.[1]) {
|
|
7081
|
+
return void 0;
|
|
7082
|
+
}
|
|
7083
|
+
const n = Number.parseInt(match[1], 10);
|
|
7084
|
+
return Number.isFinite(n) ? n : void 0;
|
|
7085
|
+
}
|
|
7086
|
+
|
|
7087
|
+
// src/adapters/kimi-code.ts
|
|
7088
|
+
var HOOK_COMMAND2 = "vibetime hook --agent kimi-code";
|
|
7089
|
+
var HOOK_TIMEOUT_SECONDS = 10;
|
|
7090
|
+
function kimiCodeHome(home, env) {
|
|
7091
|
+
const override = env?.KIMI_CODE_HOME || env?.KIMI_HOME;
|
|
7092
|
+
if (override && override.trim()) {
|
|
7093
|
+
return path14.resolve(override);
|
|
7094
|
+
}
|
|
7095
|
+
return path14.join(home, ".kimi-code");
|
|
7096
|
+
}
|
|
7097
|
+
function kimiCodeSessionsDir(home, env) {
|
|
7098
|
+
return path14.join(kimiCodeHome(home, env), "sessions");
|
|
7099
|
+
}
|
|
7100
|
+
function kimiCodeConfigPath(home, env) {
|
|
7101
|
+
return path14.join(kimiCodeHome(home, env), "config.toml");
|
|
7102
|
+
}
|
|
7103
|
+
function kimiHookRules() {
|
|
7104
|
+
const events = [
|
|
7105
|
+
"SessionStart",
|
|
7106
|
+
"SessionEnd",
|
|
7107
|
+
"UserPromptSubmit",
|
|
7108
|
+
"TurnStarted",
|
|
7109
|
+
"PostToolUse",
|
|
7110
|
+
"PostToolUseFailure",
|
|
7111
|
+
"Stop",
|
|
7112
|
+
"StopFailure",
|
|
7113
|
+
"PermissionResult",
|
|
7114
|
+
"SubagentStop"
|
|
7115
|
+
];
|
|
7116
|
+
return events.map((event) => ({
|
|
7117
|
+
event,
|
|
7118
|
+
command: HOOK_COMMAND2,
|
|
7119
|
+
timeout: HOOK_TIMEOUT_SECONDS
|
|
7120
|
+
}));
|
|
7121
|
+
}
|
|
7122
|
+
function baseKimiEvent(event) {
|
|
7123
|
+
return {
|
|
7124
|
+
schemaVersion: AGENT_TIME_SCHEMA_VERSION,
|
|
7125
|
+
source: "kimi-code",
|
|
7126
|
+
agent: "kimi-code",
|
|
7127
|
+
workspaceId: createWorkspaceId({ projectName: event.project, repoRoot: event.cwd }),
|
|
7128
|
+
...event
|
|
7129
|
+
};
|
|
7130
|
+
}
|
|
7131
|
+
function extractTextParts(input) {
|
|
7132
|
+
if (typeof input === "string") {
|
|
7133
|
+
return input;
|
|
7134
|
+
}
|
|
7135
|
+
if (!Array.isArray(input)) {
|
|
7136
|
+
return "";
|
|
7137
|
+
}
|
|
7138
|
+
return input.filter((item) => isPlainObject(item) && item.type === "text").map((item) => stringField(item, "text") || "").join("");
|
|
7139
|
+
}
|
|
7140
|
+
function kimiUsageFromRecord(usage) {
|
|
7141
|
+
const inputOther = numberField(usage, "inputOther") || 0;
|
|
7142
|
+
const output = numberField(usage, "output") || 0;
|
|
7143
|
+
const cacheRead = numberField(usage, "inputCacheRead") || 0;
|
|
7144
|
+
const cacheWrite = numberField(usage, "inputCacheCreation") || 0;
|
|
7145
|
+
const inputTokens = numberField(usage, "input_tokens") || numberField(usage, "input") || 0;
|
|
7146
|
+
const outputTokens = numberField(usage, "output_tokens") || 0;
|
|
7147
|
+
const nonCached = inputOther || inputTokens;
|
|
7148
|
+
const out = output || outputTokens;
|
|
7149
|
+
const total = nonCached + out + cacheRead + cacheWrite;
|
|
7150
|
+
if (total <= 0) {
|
|
7151
|
+
return void 0;
|
|
7152
|
+
}
|
|
7153
|
+
return {
|
|
7154
|
+
tokensInput: nonCached + cacheRead + cacheWrite || void 0,
|
|
7155
|
+
tokensOutput: out || void 0,
|
|
7156
|
+
tokensCacheReadInput: cacheRead || void 0,
|
|
7157
|
+
tokensCacheCreationInput: cacheWrite || void 0,
|
|
7158
|
+
tokensCachedInput: cacheRead + cacheWrite || void 0,
|
|
7159
|
+
tokensTotal: total,
|
|
7160
|
+
modelCalls: 1
|
|
7161
|
+
};
|
|
7162
|
+
}
|
|
7163
|
+
function normalizeTurnId(raw) {
|
|
7164
|
+
if (typeof raw === "number" && Number.isFinite(raw)) {
|
|
7165
|
+
return `turn_${raw}`;
|
|
7166
|
+
}
|
|
7167
|
+
if (typeof raw === "string" && raw.trim()) {
|
|
7168
|
+
return raw.startsWith("turn_") ? raw : `turn_${raw}`;
|
|
7169
|
+
}
|
|
7170
|
+
return void 0;
|
|
7171
|
+
}
|
|
7172
|
+
function sessionIdFromWirePath(filePath) {
|
|
7173
|
+
const normalized = filePath.replaceAll("\\", "/");
|
|
7174
|
+
const match = normalized.match(/\/(session_[^/]+)\/agents\/[^/]+\/wire\.jsonl$/);
|
|
7175
|
+
return match?.[1];
|
|
7176
|
+
}
|
|
7177
|
+
async function readSessionState(filePath) {
|
|
7178
|
+
const sessionDir = path14.dirname(path14.dirname(path14.dirname(filePath)));
|
|
7179
|
+
const statePath = path14.join(sessionDir, "state.json");
|
|
6948
7180
|
try {
|
|
6949
|
-
const
|
|
6950
|
-
|
|
6951
|
-
)
|
|
6952
|
-
|
|
6953
|
-
const hasPath = sessionCols.has("path");
|
|
6954
|
-
const hasArchived = sessionCols.has("time_archived");
|
|
6955
|
-
const hasParentId = sessionCols.has("parent_id");
|
|
6956
|
-
const selectCols = ["id", "title", "time_created"];
|
|
6957
|
-
if (hasDirectory) {
|
|
6958
|
-
selectCols.push("directory");
|
|
7181
|
+
const text = await readFile9(statePath, "utf8");
|
|
7182
|
+
const raw = JSON.parse(text);
|
|
7183
|
+
if (!isPlainObject(raw)) {
|
|
7184
|
+
return {};
|
|
6959
7185
|
}
|
|
6960
|
-
|
|
6961
|
-
|
|
7186
|
+
return {
|
|
7187
|
+
sessionId: stringField(raw, "id"),
|
|
7188
|
+
cwd: stringField(raw, "cwd"),
|
|
7189
|
+
title: stringField(raw, "title")
|
|
7190
|
+
};
|
|
7191
|
+
} catch {
|
|
7192
|
+
return {};
|
|
7193
|
+
}
|
|
7194
|
+
}
|
|
7195
|
+
async function parseKimiCodeSessionFile(filePath, options) {
|
|
7196
|
+
if (path14.basename(filePath) !== "wire.jsonl") {
|
|
7197
|
+
return [];
|
|
7198
|
+
}
|
|
7199
|
+
const text = await readFile9(filePath, "utf8");
|
|
7200
|
+
const lines = text.split("\n").filter(Boolean);
|
|
7201
|
+
const stateMeta = await readSessionState(filePath);
|
|
7202
|
+
let sessionId = stateMeta.sessionId || sessionIdFromWirePath(filePath);
|
|
7203
|
+
let cwd = stateMeta.cwd;
|
|
7204
|
+
let project = cwd ? path14.basename(cwd) : void 0;
|
|
7205
|
+
let model;
|
|
7206
|
+
let provider;
|
|
7207
|
+
let reasoningEffort;
|
|
7208
|
+
const pendingToolCalls = /* @__PURE__ */ new Map();
|
|
7209
|
+
const pendingPermissions = /* @__PURE__ */ new Map();
|
|
7210
|
+
let sawUsageRecord = false;
|
|
7211
|
+
const state = new SessionParserState(filePath, options, (event) => baseKimiEvent({ ...event, cwd, project, model, provider }));
|
|
7212
|
+
if (sessionId) {
|
|
7213
|
+
state.sessionId = sessionId;
|
|
7214
|
+
}
|
|
7215
|
+
const push = (event, ln, topType) => {
|
|
7216
|
+
state.push(
|
|
7217
|
+
{
|
|
7218
|
+
...event,
|
|
7219
|
+
sessionId: event.sessionId || sessionId,
|
|
7220
|
+
workspaceId: event.workspaceId || createWorkspaceId({ projectName: project, repoRoot: cwd })
|
|
7221
|
+
},
|
|
7222
|
+
ln,
|
|
7223
|
+
topType || event.type,
|
|
7224
|
+
event.type
|
|
7225
|
+
);
|
|
7226
|
+
};
|
|
7227
|
+
for (const [index, line] of lines.entries()) {
|
|
7228
|
+
const lineNumber = index + 1;
|
|
7229
|
+
const raw = parseJsonLine(line);
|
|
7230
|
+
if (!raw) {
|
|
7231
|
+
continue;
|
|
6962
7232
|
}
|
|
6963
|
-
|
|
6964
|
-
|
|
7233
|
+
const entryType = stringField(raw, "type");
|
|
7234
|
+
const ts = timestampFrom(raw.time) || timestampFrom(raw.created_at) || timestampFrom(raw.timestamp);
|
|
7235
|
+
if (!ts || !entryType) {
|
|
7236
|
+
continue;
|
|
6965
7237
|
}
|
|
6966
|
-
if (
|
|
6967
|
-
|
|
7238
|
+
if (entryType === "metadata") {
|
|
7239
|
+
state.ensureSessionStarted(ts, lineNumber, entryType);
|
|
7240
|
+
continue;
|
|
6968
7241
|
}
|
|
6969
|
-
|
|
6970
|
-
|
|
6971
|
-
|
|
6972
|
-
|
|
6973
|
-
|
|
6974
|
-
const
|
|
6975
|
-
|
|
6976
|
-
|
|
6977
|
-
|
|
6978
|
-
while (true) {
|
|
6979
|
-
if (visited.has(current)) {
|
|
6980
|
-
break;
|
|
6981
|
-
}
|
|
6982
|
-
visited.add(current);
|
|
6983
|
-
const node = byId.get(current);
|
|
6984
|
-
const parentId = node?.parent_id || void 0;
|
|
6985
|
-
if (!parentId || !byId.has(parentId)) {
|
|
6986
|
-
break;
|
|
6987
|
-
}
|
|
6988
|
-
current = parentId;
|
|
6989
|
-
}
|
|
6990
|
-
rootIdByRawId.set(session.id, current);
|
|
7242
|
+
if (entryType === "profile.bind") {
|
|
7243
|
+
state.ensureSessionStarted(ts, lineNumber, entryType);
|
|
7244
|
+
model = stringField(raw, "modelAlias") || stringField(raw, "model") || model;
|
|
7245
|
+
reasoningEffort = stringField(raw, "thinkingEffort") || reasoningEffort;
|
|
7246
|
+
const disclosure = objectField(raw, "environmentDisclosure");
|
|
7247
|
+
const disclosedCwd = stringField(disclosure, "cwd");
|
|
7248
|
+
if (disclosedCwd) {
|
|
7249
|
+
cwd = disclosedCwd;
|
|
7250
|
+
project = path14.basename(disclosedCwd);
|
|
6991
7251
|
}
|
|
7252
|
+
continue;
|
|
6992
7253
|
}
|
|
6993
|
-
|
|
6994
|
-
|
|
6995
|
-
|
|
6996
|
-
|
|
6997
|
-
|
|
6998
|
-
const
|
|
6999
|
-
|
|
7000
|
-
|
|
7001
|
-
|
|
7254
|
+
if (entryType === "turn.prompt") {
|
|
7255
|
+
state.ensureSessionStarted(ts, lineNumber, entryType);
|
|
7256
|
+
if (isTurnIdle(state.currentTurnLastEventAt) || state.currentTurnId) {
|
|
7257
|
+
state.closeTurn(ts, lineNumber, entryType);
|
|
7258
|
+
}
|
|
7259
|
+
const nextOrdinal = state.currentTurnId ? Number.parseInt(state.currentTurnId.replace(/^turn_/, ""), 10) : Number.NaN;
|
|
7260
|
+
const turnOrdinal = Number.isFinite(nextOrdinal) ? nextOrdinal + 1 : 0;
|
|
7261
|
+
const turnId = `turn_${turnOrdinal}`;
|
|
7262
|
+
state.startTurn(turnId, ts);
|
|
7263
|
+
push(baseKimiEvent({
|
|
7264
|
+
ts,
|
|
7265
|
+
type: "turn.started",
|
|
7002
7266
|
sessionId,
|
|
7267
|
+
turnId,
|
|
7003
7268
|
cwd,
|
|
7004
7269
|
project,
|
|
7005
|
-
|
|
7006
|
-
|
|
7007
|
-
|
|
7008
|
-
|
|
7009
|
-
|
|
7010
|
-
|
|
7011
|
-
|
|
7012
|
-
|
|
7013
|
-
|
|
7014
|
-
|
|
7015
|
-
|
|
7016
|
-
|
|
7017
|
-
|
|
7018
|
-
|
|
7019
|
-
|
|
7020
|
-
|
|
7021
|
-
|
|
7022
|
-
|
|
7023
|
-
|
|
7024
|
-
|
|
7270
|
+
model,
|
|
7271
|
+
provider,
|
|
7272
|
+
confidence: "exact"
|
|
7273
|
+
}), lineNumber, entryType);
|
|
7274
|
+
const promptText = extractTextParts(raw.input);
|
|
7275
|
+
push(baseKimiEvent({
|
|
7276
|
+
ts,
|
|
7277
|
+
type: "prompt.submitted",
|
|
7278
|
+
sessionId,
|
|
7279
|
+
turnId,
|
|
7280
|
+
cwd,
|
|
7281
|
+
project,
|
|
7282
|
+
model,
|
|
7283
|
+
provider,
|
|
7284
|
+
confidence: "exact",
|
|
7285
|
+
metrics: {
|
|
7286
|
+
prompts: 1,
|
|
7287
|
+
promptChars: promptText.length || void 0
|
|
7288
|
+
},
|
|
7289
|
+
refs: stringRefs({
|
|
7290
|
+
promptHash: promptText ? `sha256:${createStableHash(promptText)}` : void 0
|
|
7291
|
+
})
|
|
7292
|
+
}), lineNumber, entryType);
|
|
7293
|
+
continue;
|
|
7294
|
+
}
|
|
7295
|
+
if (entryType === "llm.request") {
|
|
7296
|
+
state.ensureSessionStarted(ts, lineNumber, entryType);
|
|
7297
|
+
model = stringField(raw, "modelAlias") || stringField(raw, "model") || model;
|
|
7298
|
+
provider = stringField(raw, "provider") || provider;
|
|
7299
|
+
reasoningEffort = stringField(raw, "thinkingEffort") || reasoningEffort;
|
|
7300
|
+
const turnStep = stringField(raw, "turnStep");
|
|
7301
|
+
if (turnStep && !state.currentTurnId) {
|
|
7302
|
+
const turnPart = turnStep.split(".")[0];
|
|
7303
|
+
const turnId = normalizeTurnId(turnPart);
|
|
7304
|
+
if (turnId) {
|
|
7305
|
+
state.startTurn(turnId, ts);
|
|
7025
7306
|
}
|
|
7026
7307
|
}
|
|
7027
|
-
|
|
7028
|
-
|
|
7029
|
-
|
|
7030
|
-
|
|
7031
|
-
|
|
7032
|
-
|
|
7308
|
+
continue;
|
|
7309
|
+
}
|
|
7310
|
+
if (entryType === "usage.record") {
|
|
7311
|
+
state.ensureSessionStarted(ts, lineNumber, entryType);
|
|
7312
|
+
sawUsageRecord = true;
|
|
7313
|
+
model = stringField(raw, "model") || model;
|
|
7314
|
+
const usage = objectField(raw, "usage");
|
|
7315
|
+
const metrics = kimiUsageFromRecord(usage);
|
|
7316
|
+
if (metrics) {
|
|
7317
|
+
if (reasoningEffort) {
|
|
7318
|
+
metrics.reasoningEffort = reasoningEffort;
|
|
7033
7319
|
}
|
|
7034
|
-
|
|
7035
|
-
|
|
7320
|
+
push(baseKimiEvent({
|
|
7321
|
+
ts,
|
|
7322
|
+
type: "model.usage",
|
|
7323
|
+
sessionId,
|
|
7324
|
+
turnId: state.currentTurnId,
|
|
7325
|
+
cwd,
|
|
7326
|
+
project,
|
|
7327
|
+
model,
|
|
7328
|
+
provider,
|
|
7329
|
+
confidence: "exact",
|
|
7330
|
+
metrics
|
|
7331
|
+
}), lineNumber, entryType);
|
|
7332
|
+
}
|
|
7333
|
+
continue;
|
|
7334
|
+
}
|
|
7335
|
+
if (entryType === "context.append_loop_event") {
|
|
7336
|
+
state.ensureSessionStarted(ts, lineNumber, entryType);
|
|
7337
|
+
const loopEvent = objectField(raw, "event");
|
|
7338
|
+
const loopType = stringField(loopEvent, "type");
|
|
7339
|
+
const turnId = normalizeTurnId(loopEvent.turnId) || state.currentTurnId;
|
|
7340
|
+
if (turnId && turnId !== state.currentTurnId) {
|
|
7341
|
+
if (state.currentTurnId) {
|
|
7342
|
+
state.closeTurn(ts, lineNumber, entryType);
|
|
7036
7343
|
}
|
|
7037
|
-
|
|
7038
|
-
|
|
7039
|
-
|
|
7040
|
-
|
|
7041
|
-
|
|
7344
|
+
state.startTurn(turnId, ts);
|
|
7345
|
+
}
|
|
7346
|
+
if (loopType === "tool.call") {
|
|
7347
|
+
const toolCallId = stringField(loopEvent, "toolCallId") || stringField(loopEvent, "uuid");
|
|
7348
|
+
const toolName = stringField(loopEvent, "name") || "tool";
|
|
7349
|
+
const toolInput = isPlainObject(loopEvent.args) ? loopEvent.args : {};
|
|
7350
|
+
if (toolCallId) {
|
|
7351
|
+
pendingToolCalls.set(toolCallId, {
|
|
7352
|
+
toolName,
|
|
7353
|
+
startedAt: ts,
|
|
7354
|
+
turnId: turnId || state.currentTurnId,
|
|
7355
|
+
input: toolInput
|
|
7356
|
+
});
|
|
7042
7357
|
}
|
|
7043
|
-
|
|
7044
|
-
|
|
7045
|
-
|
|
7046
|
-
|
|
7047
|
-
|
|
7048
|
-
|
|
7049
|
-
|
|
7050
|
-
|
|
7051
|
-
|
|
7358
|
+
push(baseKimiEvent({
|
|
7359
|
+
ts,
|
|
7360
|
+
type: "tool.started",
|
|
7361
|
+
operation: `${toolName} started`,
|
|
7362
|
+
sessionId,
|
|
7363
|
+
turnId: turnId || state.currentTurnId,
|
|
7364
|
+
cwd,
|
|
7365
|
+
project,
|
|
7366
|
+
model,
|
|
7367
|
+
provider,
|
|
7368
|
+
tool: toolName,
|
|
7369
|
+
confidence: "exact",
|
|
7370
|
+
metrics: { toolCalls: 1 },
|
|
7371
|
+
refs: stringRefs({
|
|
7372
|
+
sourceId: toolCallId,
|
|
7373
|
+
commandHash: toolName.toLowerCase() === "bash" && stringField(toolInput, "command") ? createStableHash(stringField(toolInput, "command")) : void 0
|
|
7374
|
+
})
|
|
7375
|
+
}), lineNumber, entryType);
|
|
7376
|
+
const fileActivities = claudeStyleToolFileActivities(
|
|
7377
|
+
toolName,
|
|
7378
|
+
toolInput,
|
|
7379
|
+
ts,
|
|
7380
|
+
cwd
|
|
7381
|
+
);
|
|
7382
|
+
if (fileActivities.length > 0) {
|
|
7383
|
+
push(baseKimiEvent({
|
|
7384
|
+
ts,
|
|
7385
|
+
type: eventTypeFromFileActivities(fileActivities),
|
|
7386
|
+
operation: `${toolName} file activity`,
|
|
7387
|
+
sessionId,
|
|
7388
|
+
turnId: turnId || state.currentTurnId,
|
|
7389
|
+
cwd,
|
|
7390
|
+
project,
|
|
7391
|
+
model,
|
|
7392
|
+
provider,
|
|
7393
|
+
tool: toolName,
|
|
7394
|
+
confidence: "derived",
|
|
7395
|
+
fileActivities,
|
|
7396
|
+
metrics: summarizeFileActivities(fileActivities),
|
|
7397
|
+
refs: stringRefs({ sourceId: toolCallId })
|
|
7398
|
+
}), lineNumber, entryType);
|
|
7399
|
+
}
|
|
7400
|
+
continue;
|
|
7401
|
+
}
|
|
7402
|
+
if (loopType === "tool.result") {
|
|
7403
|
+
const toolCallId = stringField(loopEvent, "toolCallId");
|
|
7404
|
+
const pending = toolCallId ? pendingToolCalls.get(toolCallId) : void 0;
|
|
7405
|
+
if (toolCallId) {
|
|
7406
|
+
pendingToolCalls.delete(toolCallId);
|
|
7407
|
+
}
|
|
7408
|
+
const result = objectField(loopEvent, "result");
|
|
7409
|
+
const isError = Boolean(result.isError);
|
|
7410
|
+
const toolName = pending?.toolName || "tool";
|
|
7411
|
+
const durationMs = pending ? durationMsBetween(pending.startedAt, ts) : void 0;
|
|
7412
|
+
push(baseKimiEvent({
|
|
7413
|
+
ts,
|
|
7414
|
+
type: isError ? "tool.failed" : "tool.completed",
|
|
7415
|
+
operation: isError ? `${toolName} failed` : `${toolName} completed`,
|
|
7416
|
+
sessionId,
|
|
7417
|
+
turnId: pending?.turnId || turnId || state.currentTurnId,
|
|
7418
|
+
cwd,
|
|
7419
|
+
project,
|
|
7420
|
+
model,
|
|
7421
|
+
provider,
|
|
7422
|
+
tool: toolName,
|
|
7423
|
+
success: !isError,
|
|
7424
|
+
confidence: "exact",
|
|
7425
|
+
metrics: {
|
|
7426
|
+
toolDurationMs: durationMs,
|
|
7427
|
+
durationMs
|
|
7428
|
+
},
|
|
7429
|
+
refs: stringRefs({ sourceId: toolCallId })
|
|
7430
|
+
}), lineNumber, entryType);
|
|
7431
|
+
if (toolName.toLowerCase() === "bash") {
|
|
7432
|
+
push(baseKimiEvent({
|
|
7433
|
+
ts,
|
|
7434
|
+
type: isError ? "command.failed" : "command.completed",
|
|
7435
|
+
operation: "command completed",
|
|
7436
|
+
sessionId,
|
|
7437
|
+
turnId: pending?.turnId || turnId || state.currentTurnId,
|
|
7438
|
+
cwd,
|
|
7439
|
+
project,
|
|
7440
|
+
model,
|
|
7441
|
+
provider,
|
|
7442
|
+
tool: "Bash",
|
|
7443
|
+
success: !isError,
|
|
7444
|
+
confidence: "derived",
|
|
7445
|
+
metrics: {
|
|
7446
|
+
commandCalls: 1,
|
|
7447
|
+
commandDurationMs: durationMs,
|
|
7448
|
+
durationMs
|
|
7449
|
+
},
|
|
7450
|
+
refs: stringRefs({
|
|
7451
|
+
sourceId: toolCallId,
|
|
7452
|
+
commandHash: pending?.input.command ? `sha256:${createStableHash(String(pending.input.command))}` : void 0
|
|
7453
|
+
})
|
|
7454
|
+
}), lineNumber, entryType);
|
|
7455
|
+
}
|
|
7456
|
+
continue;
|
|
7457
|
+
}
|
|
7458
|
+
if (loopType === "step.end") {
|
|
7459
|
+
if (!sawUsageRecord) {
|
|
7460
|
+
const streamMs = numberField(loopEvent, "llmStreamDurationMs") || numberField(loopEvent, "llmServerDecodeMs");
|
|
7461
|
+
const usage = objectField(loopEvent, "usage");
|
|
7462
|
+
const metrics = kimiUsageFromRecord(usage);
|
|
7463
|
+
if (metrics) {
|
|
7464
|
+
if (streamMs) {
|
|
7465
|
+
metrics.modelDurationMs = streamMs;
|
|
7466
|
+
}
|
|
7467
|
+
if (reasoningEffort) {
|
|
7468
|
+
metrics.reasoningEffort = reasoningEffort;
|
|
7469
|
+
}
|
|
7470
|
+
push(baseKimiEvent({
|
|
7471
|
+
ts,
|
|
7472
|
+
type: "model.usage",
|
|
7473
|
+
sessionId,
|
|
7474
|
+
turnId: turnId || state.currentTurnId,
|
|
7475
|
+
cwd,
|
|
7476
|
+
project,
|
|
7477
|
+
model,
|
|
7478
|
+
provider,
|
|
7479
|
+
confidence: "exact",
|
|
7480
|
+
metrics
|
|
7481
|
+
}), lineNumber, entryType);
|
|
7482
|
+
}
|
|
7483
|
+
}
|
|
7484
|
+
continue;
|
|
7485
|
+
}
|
|
7486
|
+
continue;
|
|
7487
|
+
}
|
|
7488
|
+
if (entryType === "interaction.request") {
|
|
7489
|
+
state.ensureSessionStarted(ts, lineNumber, entryType);
|
|
7490
|
+
const requestId = stringField(raw, "id") || stringField(raw, "toolCallId");
|
|
7491
|
+
const request2 = objectField(raw, "request");
|
|
7492
|
+
const toolName = stringField(request2, "toolName") || stringField(raw, "toolName");
|
|
7493
|
+
const turnId = normalizeTurnId(request2.turnId) || state.currentTurnId;
|
|
7494
|
+
if (requestId) {
|
|
7495
|
+
pendingPermissions.set(requestId, { toolName, startedAt: ts, turnId });
|
|
7496
|
+
}
|
|
7497
|
+
push(baseKimiEvent({
|
|
7498
|
+
ts,
|
|
7499
|
+
type: "permission.requested",
|
|
7500
|
+
operation: toolName ? `${toolName} permission` : "permission requested",
|
|
7501
|
+
sessionId,
|
|
7502
|
+
turnId,
|
|
7503
|
+
cwd,
|
|
7504
|
+
project,
|
|
7505
|
+
model,
|
|
7506
|
+
provider,
|
|
7507
|
+
tool: toolName,
|
|
7508
|
+
confidence: "exact",
|
|
7509
|
+
refs: stringRefs({ sourceId: requestId })
|
|
7510
|
+
}), lineNumber, entryType);
|
|
7511
|
+
continue;
|
|
7512
|
+
}
|
|
7513
|
+
if (entryType === "interaction.resolved") {
|
|
7514
|
+
state.ensureSessionStarted(ts, lineNumber, entryType);
|
|
7515
|
+
const requestId = stringField(raw, "id");
|
|
7516
|
+
const pending = requestId ? pendingPermissions.get(requestId) : void 0;
|
|
7517
|
+
if (requestId) {
|
|
7518
|
+
pendingPermissions.delete(requestId);
|
|
7519
|
+
}
|
|
7520
|
+
const response = objectField(raw, "response");
|
|
7521
|
+
const decision = stringField(response, "decision") || stringField(raw, "decision");
|
|
7522
|
+
const granted = decision === "approved" || decision === "allow" || decision === "granted";
|
|
7523
|
+
const denied = decision === "denied" || decision === "reject" || decision === "rejected";
|
|
7524
|
+
push(baseKimiEvent({
|
|
7525
|
+
ts,
|
|
7526
|
+
type: granted ? "permission.granted" : denied ? "permission.denied" : "permission.resolved",
|
|
7527
|
+
operation: decision ? `permission ${decision}` : "permission resolved",
|
|
7528
|
+
sessionId,
|
|
7529
|
+
turnId: pending?.turnId || state.currentTurnId,
|
|
7530
|
+
cwd,
|
|
7531
|
+
project,
|
|
7532
|
+
model,
|
|
7533
|
+
provider,
|
|
7534
|
+
tool: pending?.toolName,
|
|
7535
|
+
success: granted ? true : denied ? false : void 0,
|
|
7536
|
+
confidence: "exact",
|
|
7537
|
+
metrics: {
|
|
7538
|
+
durationMs: pending ? durationMsBetween(pending.startedAt, ts) : void 0,
|
|
7539
|
+
approvalWaitMs: pending ? durationMsBetween(pending.startedAt, ts) : void 0
|
|
7540
|
+
},
|
|
7541
|
+
refs: stringRefs({ sourceId: requestId })
|
|
7542
|
+
}), lineNumber, entryType);
|
|
7543
|
+
continue;
|
|
7544
|
+
}
|
|
7545
|
+
}
|
|
7546
|
+
if (isTurnIdle(state.currentTurnLastEventAt)) {
|
|
7547
|
+
state.closeTurn(state.currentTurnLastEventAt, lines.length);
|
|
7548
|
+
}
|
|
7549
|
+
return state.events.filter((event) => matchesBackfillFilters(event, options));
|
|
7550
|
+
}
|
|
7551
|
+
function createKimiCodeAdapter() {
|
|
7552
|
+
return {
|
|
7553
|
+
id: "kimi-code",
|
|
7554
|
+
label: "Kimi Code",
|
|
7555
|
+
agentName: "kimi-code",
|
|
7556
|
+
kind: "agent",
|
|
7557
|
+
detectPath(home, env) {
|
|
7558
|
+
return kimiCodeHome(home, env);
|
|
7559
|
+
},
|
|
7560
|
+
installedPath(home, env) {
|
|
7561
|
+
return kimiCodeConfigPath(home, env);
|
|
7562
|
+
},
|
|
7563
|
+
async isInstalled(home, env) {
|
|
7564
|
+
return hasTomlHookCommand(kimiCodeConfigPath(home, env), HOOK_COMMAND2);
|
|
7565
|
+
},
|
|
7566
|
+
installEntries(home, env) {
|
|
7567
|
+
return [{
|
|
7568
|
+
kind: "hooks-toml",
|
|
7569
|
+
path: kimiCodeConfigPath(home, env),
|
|
7570
|
+
content: { hooks: kimiHookRules() }
|
|
7571
|
+
}];
|
|
7572
|
+
},
|
|
7573
|
+
sourcePaths(home, env) {
|
|
7574
|
+
return [kimiCodeSessionsDir(home, env)];
|
|
7575
|
+
},
|
|
7576
|
+
parseSessionFile: parseKimiCodeSessionFile
|
|
7577
|
+
};
|
|
7578
|
+
}
|
|
7579
|
+
|
|
7580
|
+
// src/adapters/opencode.ts
|
|
7581
|
+
import os6 from "node:os";
|
|
7582
|
+
import path15 from "node:path";
|
|
7583
|
+
async function parseOpenCodeSessionFile(dbPath, options) {
|
|
7584
|
+
const { DatabaseSync } = await import("node:sqlite");
|
|
7585
|
+
if (!dbPath.endsWith(".db")) {
|
|
7586
|
+
return [];
|
|
7587
|
+
}
|
|
7588
|
+
const db = new DatabaseSync(dbPath, { readOnly: true });
|
|
7589
|
+
const events = [];
|
|
7590
|
+
try {
|
|
7591
|
+
const sessionCols = new Set(
|
|
7592
|
+
db.prepare("PRAGMA table_info(session)").all().map((row) => row.name)
|
|
7593
|
+
);
|
|
7594
|
+
const hasDirectory = sessionCols.has("directory");
|
|
7595
|
+
const hasPath = sessionCols.has("path");
|
|
7596
|
+
const hasArchived = sessionCols.has("time_archived");
|
|
7597
|
+
const hasParentId = sessionCols.has("parent_id");
|
|
7598
|
+
const selectCols = ["id", "title", "time_created"];
|
|
7599
|
+
if (hasDirectory) {
|
|
7600
|
+
selectCols.push("directory");
|
|
7601
|
+
}
|
|
7602
|
+
if (hasPath) {
|
|
7603
|
+
selectCols.push("path");
|
|
7604
|
+
}
|
|
7605
|
+
if (hasArchived) {
|
|
7606
|
+
selectCols.push("time_archived");
|
|
7607
|
+
}
|
|
7608
|
+
if (hasParentId) {
|
|
7609
|
+
selectCols.push("parent_id");
|
|
7610
|
+
}
|
|
7611
|
+
const sessions = db.prepare(
|
|
7612
|
+
`SELECT ${selectCols.join(", ")} FROM session WHERE time_created IS NOT NULL ORDER BY time_created`
|
|
7613
|
+
).all();
|
|
7614
|
+
const rootIdByRawId = /* @__PURE__ */ new Map();
|
|
7615
|
+
if (hasParentId) {
|
|
7616
|
+
const byId = new Map(sessions.map((s) => [s.id, s]));
|
|
7617
|
+
for (const session of sessions) {
|
|
7618
|
+
let current = session.id;
|
|
7619
|
+
const visited = /* @__PURE__ */ new Set();
|
|
7620
|
+
while (true) {
|
|
7621
|
+
if (visited.has(current)) {
|
|
7622
|
+
break;
|
|
7623
|
+
}
|
|
7624
|
+
visited.add(current);
|
|
7625
|
+
const node = byId.get(current);
|
|
7626
|
+
const parentId = node?.parent_id || void 0;
|
|
7627
|
+
if (!parentId || !byId.has(parentId)) {
|
|
7628
|
+
break;
|
|
7629
|
+
}
|
|
7630
|
+
current = parentId;
|
|
7631
|
+
}
|
|
7632
|
+
rootIdByRawId.set(session.id, current);
|
|
7633
|
+
}
|
|
7634
|
+
}
|
|
7635
|
+
for (const session of sessions) {
|
|
7636
|
+
const rawSessionId = session.id;
|
|
7637
|
+
const sessionId = rootIdByRawId.get(rawSessionId) || rawSessionId;
|
|
7638
|
+
const cwd = session.directory || session.path || void 0;
|
|
7639
|
+
const project = cwd ? path15.basename(cwd) : void 0;
|
|
7640
|
+
const sessionTs = msToIso(session.time_created);
|
|
7641
|
+
events.push(baseOpenCodeEvent({
|
|
7642
|
+
ts: sessionTs,
|
|
7643
|
+
type: "session.started",
|
|
7644
|
+
sessionId,
|
|
7645
|
+
cwd,
|
|
7646
|
+
project,
|
|
7647
|
+
operation: "session start"
|
|
7648
|
+
}));
|
|
7649
|
+
const messages = db.prepare(
|
|
7650
|
+
"SELECT id, data FROM message WHERE session_id = ? ORDER BY time_created"
|
|
7651
|
+
).all(rawSessionId);
|
|
7652
|
+
let currentTurnId;
|
|
7653
|
+
let currentProvider;
|
|
7654
|
+
let turnTs;
|
|
7655
|
+
let reasoningEffort;
|
|
7656
|
+
const modelSwitchedEvents = db.prepare(
|
|
7657
|
+
"SELECT data FROM event WHERE aggregate_id = ? AND type = 'session.next.model.switched.1' ORDER BY seq"
|
|
7658
|
+
).all(rawSessionId);
|
|
7659
|
+
for (const evt of modelSwitchedEvents) {
|
|
7660
|
+
try {
|
|
7661
|
+
const evtData = JSON.parse(evt.data);
|
|
7662
|
+
const variant = isPlainObject(evtData) ? stringField(evtData.model || evtData, "variant") : void 0;
|
|
7663
|
+
if (variant && variant !== "default") {
|
|
7664
|
+
reasoningEffort = variant;
|
|
7665
|
+
}
|
|
7666
|
+
} catch {
|
|
7667
|
+
}
|
|
7668
|
+
}
|
|
7669
|
+
for (const msg of messages) {
|
|
7670
|
+
let info;
|
|
7671
|
+
try {
|
|
7672
|
+
info = JSON.parse(msg.data);
|
|
7673
|
+
} catch {
|
|
7674
|
+
continue;
|
|
7675
|
+
}
|
|
7676
|
+
if (!isPlainObject(info)) {
|
|
7677
|
+
continue;
|
|
7678
|
+
}
|
|
7679
|
+
const role = stringField(info, "role");
|
|
7680
|
+
const timeObj = objectField(info, "time");
|
|
7681
|
+
const timeCreated = numberField(timeObj, "created");
|
|
7682
|
+
if (!role || !timeCreated) {
|
|
7683
|
+
continue;
|
|
7684
|
+
}
|
|
7685
|
+
if (role === "user") {
|
|
7686
|
+
currentTurnId = `turn_${createStableHash([sessionId, msg.id]).slice(0, 24)}`;
|
|
7687
|
+
turnTs = msToIso(timeCreated);
|
|
7688
|
+
const userAgent = stringField(info, "agent") || "opencode";
|
|
7689
|
+
const modelObj = objectField(info, "model");
|
|
7690
|
+
const model = modelObj ? stringField(modelObj, "modelID") : void 0;
|
|
7691
|
+
currentProvider = modelObj ? stringField(modelObj, "providerID") : void 0;
|
|
7692
|
+
events.push(baseOpenCodeEvent({
|
|
7693
|
+
ts: turnTs,
|
|
7052
7694
|
type: "turn.started",
|
|
7053
7695
|
sessionId,
|
|
7054
7696
|
turnId: currentTurnId,
|
|
@@ -7102,7 +7744,7 @@ async function parseOpenCodeSessionFile(dbPath, options) {
|
|
|
7102
7744
|
const provider = currentProvider;
|
|
7103
7745
|
const pathObj = objectField(info, "path");
|
|
7104
7746
|
const assistantCwd = stringField(pathObj, "cwd") || cwd;
|
|
7105
|
-
const assistantProject = assistantCwd ?
|
|
7747
|
+
const assistantProject = assistantCwd ? path15.basename(assistantCwd) : project;
|
|
7106
7748
|
const completedTs = numberField(objectField(info, "time"), "completed");
|
|
7107
7749
|
const createdTs = timeCreated;
|
|
7108
7750
|
const tokens = opencodeUsageFromInfo(info);
|
|
@@ -7373,18 +8015,18 @@ function opencodeUsageFromInfo(info) {
|
|
|
7373
8015
|
function opencodeConfigDir(home, env) {
|
|
7374
8016
|
const override = env?.OPENCODE_CONFIG_DIR;
|
|
7375
8017
|
if (override && override.trim()) {
|
|
7376
|
-
return
|
|
8018
|
+
return path15.resolve(override);
|
|
7377
8019
|
}
|
|
7378
8020
|
const xdgConfig = env?.XDG_CONFIG_HOME;
|
|
7379
8021
|
if (xdgConfig && xdgConfig.trim()) {
|
|
7380
|
-
return
|
|
8022
|
+
return path15.join(path15.resolve(xdgConfig), "opencode");
|
|
7381
8023
|
}
|
|
7382
|
-
return
|
|
8024
|
+
return path15.join(home, ".config", "opencode");
|
|
7383
8025
|
}
|
|
7384
8026
|
function opencodeDataCandidates(home, env) {
|
|
7385
8027
|
const xdgData = env?.XDG_DATA_HOME;
|
|
7386
|
-
const primary = xdgData && xdgData.trim() ?
|
|
7387
|
-
return [primary,
|
|
8028
|
+
const primary = xdgData && xdgData.trim() ? path15.join(path15.resolve(xdgData), "opencode", "opencode.db") : path15.join(home, ".local", "share", "opencode", "opencode.db");
|
|
8029
|
+
return [primary, path15.join(home, ".opencode", "opencode.db")];
|
|
7388
8030
|
}
|
|
7389
8031
|
async function opencodeBackfillFiles(sourceRoot, home = os6.homedir(), env) {
|
|
7390
8032
|
const { stat: stat14 } = await import("node:fs/promises");
|
|
@@ -7474,12 +8116,12 @@ function createOpenCodeAdapter() {
|
|
|
7474
8116
|
return opencodeConfigDir(home, env);
|
|
7475
8117
|
},
|
|
7476
8118
|
installedPath(home, env) {
|
|
7477
|
-
return
|
|
8119
|
+
return path15.join(opencodeConfigDir(home, env), PLUGIN_PATH);
|
|
7478
8120
|
},
|
|
7479
8121
|
async isInstalled(home, env) {
|
|
7480
8122
|
try {
|
|
7481
8123
|
const { pathExists: pathExists2 } = await Promise.resolve().then(() => (init_fs(), fs_exports));
|
|
7482
|
-
return await pathExists2(
|
|
8124
|
+
return await pathExists2(path15.join(opencodeConfigDir(home, env), PLUGIN_PATH)) || await pathExists2(path15.join(".opencode", PLUGIN_PATH));
|
|
7483
8125
|
} catch {
|
|
7484
8126
|
return false;
|
|
7485
8127
|
}
|
|
@@ -7487,7 +8129,7 @@ function createOpenCodeAdapter() {
|
|
|
7487
8129
|
installEntries(home, env) {
|
|
7488
8130
|
return [{
|
|
7489
8131
|
kind: "file",
|
|
7490
|
-
path:
|
|
8132
|
+
path: path15.join(opencodeConfigDir(home, env), PLUGIN_PATH),
|
|
7491
8133
|
content: opencodePluginContent()
|
|
7492
8134
|
}];
|
|
7493
8135
|
},
|
|
@@ -7499,12 +8141,12 @@ function createOpenCodeAdapter() {
|
|
|
7499
8141
|
}
|
|
7500
8142
|
|
|
7501
8143
|
// src/adapters/pi.ts
|
|
7502
|
-
import { readFile as
|
|
7503
|
-
import
|
|
8144
|
+
import { readFile as readFile10 } from "node:fs/promises";
|
|
8145
|
+
import path16 from "node:path";
|
|
7504
8146
|
function parsePiSubagentLink(filePath, headerParentSession) {
|
|
7505
8147
|
if (headerParentSession) {
|
|
7506
|
-
const parentFile =
|
|
7507
|
-
const parentSessionId2 = parentFile ?
|
|
8148
|
+
const parentFile = path16.isAbsolute(headerParentSession) ? headerParentSession : void 0;
|
|
8149
|
+
const parentSessionId2 = parentFile ? path16.basename(parentFile, ".jsonl") : headerParentSession;
|
|
7508
8150
|
return { parentSessionId: parentSessionId2, parentSessionFile: parentFile, explicit: true };
|
|
7509
8151
|
}
|
|
7510
8152
|
const normalized = filePath.replaceAll("\\", "/");
|
|
@@ -7517,20 +8159,20 @@ function parsePiSubagentLink(filePath, headerParentSession) {
|
|
|
7517
8159
|
return void 0;
|
|
7518
8160
|
}
|
|
7519
8161
|
const parentSessionId = parentBasename.endsWith(".jsonl") ? parentBasename.slice(0, -".jsonl".length) : parentBasename;
|
|
7520
|
-
const parentDir =
|
|
7521
|
-
const parentSessionFile =
|
|
8162
|
+
const parentDir = path16.dirname(path16.dirname(path16.dirname(filePath)));
|
|
8163
|
+
const parentSessionFile = path16.join(path16.dirname(parentDir), `${parentBasename}.jsonl`);
|
|
7522
8164
|
return { parentSessionId, parentSessionFile, explicit: false };
|
|
7523
8165
|
}
|
|
7524
8166
|
async function resolveParentContext(link, options) {
|
|
7525
8167
|
if (link.parentSessionFile) {
|
|
7526
8168
|
try {
|
|
7527
|
-
const text = await
|
|
8169
|
+
const text = await readFile10(link.parentSessionFile, "utf8");
|
|
7528
8170
|
const firstLine = text.split("\n").find((line) => line.trim().length > 0);
|
|
7529
8171
|
const raw = firstLine ? parseJsonLine(firstLine) : void 0;
|
|
7530
8172
|
if (raw) {
|
|
7531
8173
|
const id = stringField(raw, "id");
|
|
7532
8174
|
const cwd = stringField(raw, "cwd");
|
|
7533
|
-
const project = cwd ?
|
|
8175
|
+
const project = cwd ? path16.basename(cwd) : void 0;
|
|
7534
8176
|
if (id || cwd || project) {
|
|
7535
8177
|
return { sessionId: id, cwd, project };
|
|
7536
8178
|
}
|
|
@@ -7575,7 +8217,7 @@ function rebuildEventIdentity2(event) {
|
|
|
7575
8217
|
};
|
|
7576
8218
|
}
|
|
7577
8219
|
async function parsePiSessionFile(filePath, options) {
|
|
7578
|
-
const text = await
|
|
8220
|
+
const text = await readFile10(filePath, "utf8");
|
|
7579
8221
|
const lines = text.split("\n").filter(Boolean);
|
|
7580
8222
|
let sessionId;
|
|
7581
8223
|
let cwd;
|
|
@@ -7607,7 +8249,7 @@ async function parsePiSessionFile(filePath, options) {
|
|
|
7607
8249
|
sessionId = stringField(raw, "id") || state.sessionId;
|
|
7608
8250
|
state.sessionId = sessionId || state.sessionId;
|
|
7609
8251
|
cwd = stringField(raw, "cwd") || cwd;
|
|
7610
|
-
project = cwd ?
|
|
8252
|
+
project = cwd ? path16.basename(cwd) : project;
|
|
7611
8253
|
headerParentSession = stringField(raw, "parentSession") || headerParentSession;
|
|
7612
8254
|
continue;
|
|
7613
8255
|
}
|
|
@@ -8040,16 +8682,16 @@ export default function (pi: ExtensionAPI) {
|
|
|
8040
8682
|
function piAgentDir(home, env) {
|
|
8041
8683
|
const override = env?.PI_CODING_AGENT_DIR;
|
|
8042
8684
|
if (override && override.trim()) {
|
|
8043
|
-
return
|
|
8685
|
+
return path16.resolve(override);
|
|
8044
8686
|
}
|
|
8045
|
-
return
|
|
8687
|
+
return path16.join(home, ".pi", "agent");
|
|
8046
8688
|
}
|
|
8047
8689
|
function piSessionDir(home, env) {
|
|
8048
8690
|
const override = env?.PI_CODING_AGENT_SESSION_DIR;
|
|
8049
8691
|
if (override && override.trim()) {
|
|
8050
|
-
return
|
|
8692
|
+
return path16.resolve(override);
|
|
8051
8693
|
}
|
|
8052
|
-
return
|
|
8694
|
+
return path16.join(piAgentDir(home, env), "sessions");
|
|
8053
8695
|
}
|
|
8054
8696
|
function createPiAdapter() {
|
|
8055
8697
|
return {
|
|
@@ -8061,12 +8703,12 @@ function createPiAdapter() {
|
|
|
8061
8703
|
return piAgentDir(home, env);
|
|
8062
8704
|
},
|
|
8063
8705
|
installedPath(home, env) {
|
|
8064
|
-
return
|
|
8706
|
+
return path16.join(piAgentDir(home, env), "extensions", "vibetime.ts");
|
|
8065
8707
|
},
|
|
8066
8708
|
async isInstalled(home, env) {
|
|
8067
8709
|
try {
|
|
8068
8710
|
const { pathExists: pathExists2 } = await Promise.resolve().then(() => (init_fs(), fs_exports));
|
|
8069
|
-
return await pathExists2(
|
|
8711
|
+
return await pathExists2(path16.join(piAgentDir(home, env), "extensions", "vibetime.ts"));
|
|
8070
8712
|
} catch {
|
|
8071
8713
|
return false;
|
|
8072
8714
|
}
|
|
@@ -8074,7 +8716,7 @@ function createPiAdapter() {
|
|
|
8074
8716
|
installEntries(home, env) {
|
|
8075
8717
|
return [{
|
|
8076
8718
|
kind: "file",
|
|
8077
|
-
path:
|
|
8719
|
+
path: path16.join(piAgentDir(home, env), "extensions", "vibetime.ts"),
|
|
8078
8720
|
content: piExtensionContent()
|
|
8079
8721
|
}];
|
|
8080
8722
|
},
|
|
@@ -8086,14 +8728,14 @@ function createPiAdapter() {
|
|
|
8086
8728
|
}
|
|
8087
8729
|
|
|
8088
8730
|
// src/adapters/qoder-cn.ts
|
|
8089
|
-
import { readdir as readdir7, readFile as
|
|
8731
|
+
import { readdir as readdir7, readFile as readFile11, stat as stat8 } from "node:fs/promises";
|
|
8090
8732
|
import os8 from "node:os";
|
|
8091
|
-
import
|
|
8733
|
+
import path18 from "node:path";
|
|
8092
8734
|
|
|
8093
8735
|
// src/adapters/qoder-local-db.ts
|
|
8094
8736
|
import { access } from "node:fs/promises";
|
|
8095
8737
|
import os7 from "node:os";
|
|
8096
|
-
import
|
|
8738
|
+
import path17 from "node:path";
|
|
8097
8739
|
function takeQoderDbModelCall(calls, requestId, blockStart) {
|
|
8098
8740
|
if (requestId) {
|
|
8099
8741
|
const call = calls.byRequestId.get(requestId)?.shift();
|
|
@@ -8112,12 +8754,12 @@ function takeQoderDbModelCall(calls, requestId, blockStart) {
|
|
|
8112
8754
|
}
|
|
8113
8755
|
function appDataRoot(appDirName, home = os7.homedir()) {
|
|
8114
8756
|
if (process.platform === "darwin") {
|
|
8115
|
-
return
|
|
8757
|
+
return path17.join(home, "Library", "Application Support", appDirName);
|
|
8116
8758
|
}
|
|
8117
8759
|
if (process.platform === "win32") {
|
|
8118
|
-
return
|
|
8760
|
+
return path17.join(process.env.APPDATA || path17.join(home, "AppData", "Roaming"), appDirName);
|
|
8119
8761
|
}
|
|
8120
|
-
return
|
|
8762
|
+
return path17.join(home, ".config", appDirName);
|
|
8121
8763
|
}
|
|
8122
8764
|
function qoderLocalDbCandidates(appDirName) {
|
|
8123
8765
|
const candidates = [];
|
|
@@ -8127,8 +8769,8 @@ function qoderLocalDbCandidates(appDirName) {
|
|
|
8127
8769
|
}
|
|
8128
8770
|
const configRoot = appDataRoot(appDirName);
|
|
8129
8771
|
candidates.push(
|
|
8130
|
-
|
|
8131
|
-
|
|
8772
|
+
path17.join(configRoot, "SharedClientCache", "cache", "db", "local.db"),
|
|
8773
|
+
path17.join(configRoot, "SharedClientCache", "db", "local.db")
|
|
8132
8774
|
);
|
|
8133
8775
|
return candidates;
|
|
8134
8776
|
}
|
|
@@ -8137,7 +8779,7 @@ async function loadQoderIdeModelCatalog(appDirName, home) {
|
|
|
8137
8779
|
try {
|
|
8138
8780
|
const { DatabaseSync } = await import("node:sqlite");
|
|
8139
8781
|
const db = new DatabaseSync(
|
|
8140
|
-
|
|
8782
|
+
path17.join(appDataRoot(appDirName, home), "User", "globalStorage", "state.vscdb"),
|
|
8141
8783
|
{ readOnly: true }
|
|
8142
8784
|
);
|
|
8143
8785
|
try {
|
|
@@ -8279,7 +8921,7 @@ function resolveSessionPreferredModel(db, sessionId, modelMap) {
|
|
|
8279
8921
|
|
|
8280
8922
|
// src/adapters/qoder-cn.ts
|
|
8281
8923
|
function parseQoderCnPaths(filePath) {
|
|
8282
|
-
const parts = filePath.split(
|
|
8924
|
+
const parts = filePath.split(path18.sep);
|
|
8283
8925
|
const subagentsIdx = parts.lastIndexOf("subagents");
|
|
8284
8926
|
let sessionId = "";
|
|
8285
8927
|
let projectName = "";
|
|
@@ -8289,17 +8931,17 @@ function parseQoderCnPaths(filePath) {
|
|
|
8289
8931
|
sessionId = parts[subagentsIdx - 1];
|
|
8290
8932
|
projectName = parts[subagentsIdx - 2];
|
|
8291
8933
|
const projectsIdx = parts.lastIndexOf("projects");
|
|
8292
|
-
configDir2 = parts.slice(0, projectsIdx).join(
|
|
8293
|
-
mainTranscriptPath = [...parts.slice(0, subagentsIdx - 1), `${sessionId}.jsonl`].join(
|
|
8934
|
+
configDir2 = parts.slice(0, projectsIdx).join(path18.sep);
|
|
8935
|
+
mainTranscriptPath = [...parts.slice(0, subagentsIdx - 1), `${sessionId}.jsonl`].join(path18.sep);
|
|
8294
8936
|
} else {
|
|
8295
8937
|
const filename = parts.at(-1) || "";
|
|
8296
|
-
sessionId =
|
|
8938
|
+
sessionId = path18.basename(filename, ".jsonl");
|
|
8297
8939
|
projectName = parts.at(-2) || "";
|
|
8298
8940
|
if (projectName === "transcript") {
|
|
8299
8941
|
projectName = parts.at(-3) || "";
|
|
8300
8942
|
}
|
|
8301
8943
|
const projectsIdx = parts.lastIndexOf("projects");
|
|
8302
|
-
configDir2 = parts.slice(0, projectsIdx).join(
|
|
8944
|
+
configDir2 = parts.slice(0, projectsIdx).join(path18.sep);
|
|
8303
8945
|
}
|
|
8304
8946
|
return { configDir: configDir2, projectName, sessionId, mainTranscriptPath };
|
|
8305
8947
|
}
|
|
@@ -8322,7 +8964,7 @@ function rebuildEventIdentity3(event) {
|
|
|
8322
8964
|
}
|
|
8323
8965
|
async function parseModelNamesFromDynamicTexts(dynamicTextsPath) {
|
|
8324
8966
|
try {
|
|
8325
|
-
const content = await
|
|
8967
|
+
const content = await readFile11(dynamicTextsPath, "utf8");
|
|
8326
8968
|
const json = JSON.parse(content);
|
|
8327
8969
|
const texts = json.texts || {};
|
|
8328
8970
|
const map = {};
|
|
@@ -8338,10 +8980,10 @@ async function parseModelNamesFromDynamicTexts(dynamicTextsPath) {
|
|
|
8338
8980
|
}
|
|
8339
8981
|
}
|
|
8340
8982
|
async function loadQoderCnModelNames(configDir2, home) {
|
|
8341
|
-
const map = await parseModelNamesFromDynamicTexts(
|
|
8983
|
+
const map = await parseModelNamesFromDynamicTexts(path18.join(configDir2, ".auth", "dynamic-texts.json"));
|
|
8342
8984
|
const siblingConfigDir = configDir2.replace(/\.qoder-cn$/, ".qoder");
|
|
8343
8985
|
if (siblingConfigDir !== configDir2) {
|
|
8344
|
-
const siblingMap = await parseModelNamesFromDynamicTexts(
|
|
8986
|
+
const siblingMap = await parseModelNamesFromDynamicTexts(path18.join(siblingConfigDir, ".auth", "dynamic-texts.json"));
|
|
8345
8987
|
for (const [key, val] of Object.entries(siblingMap)) {
|
|
8346
8988
|
if (!(key in map)) {
|
|
8347
8989
|
map[key] = val;
|
|
@@ -8360,7 +9002,7 @@ async function loadQoderCnModelNames(configDir2, home) {
|
|
|
8360
9002
|
}
|
|
8361
9003
|
async function loadQoderCnSegmentModelCalls(filePath, isSubagentSession, modelMap) {
|
|
8362
9004
|
const { configDir: configDir2, projectName, sessionId } = parseQoderCnPaths(filePath);
|
|
8363
|
-
const segmentsPath =
|
|
9005
|
+
const segmentsPath = path18.join(configDir2, "logs", "sessions", projectName, sessionId, "segments");
|
|
8364
9006
|
const modelCalls = [];
|
|
8365
9007
|
try {
|
|
8366
9008
|
const files = await readdir7(segmentsPath);
|
|
@@ -8368,7 +9010,7 @@ async function loadQoderCnSegmentModelCalls(filePath, isSubagentSession, modelMa
|
|
|
8368
9010
|
if (!file.endsWith(".jsonl")) {
|
|
8369
9011
|
continue;
|
|
8370
9012
|
}
|
|
8371
|
-
const content = await
|
|
9013
|
+
const content = await readFile11(path18.join(segmentsPath, file), "utf8");
|
|
8372
9014
|
let currentTurnIsSubagent = false;
|
|
8373
9015
|
for (const line of content.split("\n").filter(Boolean)) {
|
|
8374
9016
|
const raw = parseJsonLine(line);
|
|
@@ -8402,7 +9044,7 @@ async function loadQoderCnSegmentModelCalls(filePath, isSubagentSession, modelMa
|
|
|
8402
9044
|
return modelCalls.filter((call) => call.isSubagent === isSubagentSession);
|
|
8403
9045
|
}
|
|
8404
9046
|
async function parseQoderCnSessionFile(filePath, options) {
|
|
8405
|
-
const text = await
|
|
9047
|
+
const text = await readFile11(filePath, "utf8");
|
|
8406
9048
|
const lines = text.split("\n").filter(Boolean);
|
|
8407
9049
|
const parsedPaths = parseQoderCnPaths(filePath);
|
|
8408
9050
|
const { configDir: configDir2 } = parsedPaths;
|
|
@@ -8413,7 +9055,7 @@ async function parseQoderCnSessionFile(filePath, options) {
|
|
|
8413
9055
|
let cwd;
|
|
8414
9056
|
let project = projectContext.project;
|
|
8415
9057
|
let model;
|
|
8416
|
-
const home =
|
|
9058
|
+
const home = path18.resolve(stringOption(options.home) || os8.homedir());
|
|
8417
9059
|
const modelMap = await loadQoderCnModelNames(configDir2, home);
|
|
8418
9060
|
const isSubagentSession = filePath.includes("subagents");
|
|
8419
9061
|
const segmentModelCalls = await loadQoderCnSegmentModelCalls(filePath, isSubagentSession, modelMap);
|
|
@@ -8448,7 +9090,7 @@ async function parseQoderCnSessionFile(filePath, options) {
|
|
|
8448
9090
|
sessionId = stringField(raw, "sessionId") || sessionId;
|
|
8449
9091
|
state.sessionId = sessionId;
|
|
8450
9092
|
cwd = stringField(raw, "cwd") || cwd;
|
|
8451
|
-
project = projectContext.project || (cwd ?
|
|
9093
|
+
project = projectContext.project || (cwd ? path18.basename(cwd) : project || await qoderCnProjectFromFilePath(filePath, options));
|
|
8452
9094
|
if (!ts) {
|
|
8453
9095
|
continue;
|
|
8454
9096
|
}
|
|
@@ -8783,7 +9425,7 @@ async function parseQoderCnSessionFile(filePath, options) {
|
|
|
8783
9425
|
}
|
|
8784
9426
|
dbModelCalls ??= await loadQoderDbModelCalls("QoderCN", parsedPaths.sessionId, modelMap);
|
|
8785
9427
|
if (dbModelCalls.rootSessionId) {
|
|
8786
|
-
const parentPath =
|
|
9428
|
+
const parentPath = path18.join(path18.dirname(filePath), `${dbModelCalls.rootSessionId}.jsonl`);
|
|
8787
9429
|
const parentSourcePathHash = `sha256:${createStableHash(parentPath)}`;
|
|
8788
9430
|
return validEvents.map((event) => rebuildEventIdentity3({
|
|
8789
9431
|
...event,
|
|
@@ -8935,28 +9577,28 @@ function extractQoderAttachedPrompt(content) {
|
|
|
8935
9577
|
}
|
|
8936
9578
|
async function qoderCnProjectContextFromLines(filePath, lines, options, configDir2) {
|
|
8937
9579
|
const { projectName: projectDir, sessionId } = parseQoderCnPaths(filePath);
|
|
8938
|
-
const isSubagent = filePath.includes(`${
|
|
9580
|
+
const isSubagent = filePath.includes(`${path18.sep}subagents${path18.sep}`);
|
|
8939
9581
|
const inherited = isSubagent ? await readPersistedSessionContextFromOptions(options, sessionId) : void 0;
|
|
8940
9582
|
let cwds = [];
|
|
8941
9583
|
for (const line of lines) {
|
|
8942
9584
|
const raw = parseJsonLine(line);
|
|
8943
9585
|
const cwd = raw ? stringField(raw, "cwd") : void 0;
|
|
8944
|
-
if (cwd &&
|
|
9586
|
+
if (cwd && path18.isAbsolute(cwd)) {
|
|
8945
9587
|
cwds.push(cwd);
|
|
8946
9588
|
}
|
|
8947
9589
|
}
|
|
8948
9590
|
if (isSubagent) {
|
|
8949
|
-
if (inherited?.cwd &&
|
|
9591
|
+
if (inherited?.cwd && path18.isAbsolute(inherited.cwd)) {
|
|
8950
9592
|
cwds = [inherited.cwd];
|
|
8951
9593
|
} else {
|
|
8952
|
-
const parentSessionPath =
|
|
9594
|
+
const parentSessionPath = path18.join(configDir2, "projects", projectDir, `${sessionId}.jsonl`);
|
|
8953
9595
|
try {
|
|
8954
|
-
const parentText = await
|
|
9596
|
+
const parentText = await readFile11(parentSessionPath, "utf8");
|
|
8955
9597
|
const parentCwds = [];
|
|
8956
9598
|
for (const line of parentText.split("\n").filter(Boolean)) {
|
|
8957
9599
|
const raw = parseJsonLine(line);
|
|
8958
9600
|
const cwd = raw ? stringField(raw, "cwd") : void 0;
|
|
8959
|
-
if (cwd &&
|
|
9601
|
+
if (cwd && path18.isAbsolute(cwd)) {
|
|
8960
9602
|
parentCwds.push(cwd);
|
|
8961
9603
|
}
|
|
8962
9604
|
}
|
|
@@ -8968,7 +9610,7 @@ async function qoderCnProjectContextFromLines(filePath, lines, options, configDi
|
|
|
8968
9610
|
}
|
|
8969
9611
|
}
|
|
8970
9612
|
const root = await gitRootFromCwds2(cwds) || qoderCnProjectRootFromCwds(projectDir, cwds);
|
|
8971
|
-
const project = inherited?.project || (cwds.length > 0 ?
|
|
9613
|
+
const project = inherited?.project || (cwds.length > 0 ? path18.basename(cwds[0]) : root ? path18.basename(root) : await qoderCnProjectFromFilePath(filePath, options));
|
|
8972
9614
|
return {
|
|
8973
9615
|
project,
|
|
8974
9616
|
workspaceId: createWorkspaceId({ projectName: project, repoRoot: root })
|
|
@@ -8977,15 +9619,15 @@ async function qoderCnProjectContextFromLines(filePath, lines, options, configDi
|
|
|
8977
9619
|
async function gitRootFromCwds2(cwds) {
|
|
8978
9620
|
const seen = /* @__PURE__ */ new Set();
|
|
8979
9621
|
for (const cwd of cwds) {
|
|
8980
|
-
let current =
|
|
9622
|
+
let current = path18.resolve(cwd);
|
|
8981
9623
|
while (!seen.has(current)) {
|
|
8982
9624
|
seen.add(current);
|
|
8983
9625
|
try {
|
|
8984
|
-
await stat8(
|
|
9626
|
+
await stat8(path18.join(current, ".git"));
|
|
8985
9627
|
return current;
|
|
8986
9628
|
} catch {
|
|
8987
9629
|
}
|
|
8988
|
-
const parent =
|
|
9630
|
+
const parent = path18.dirname(current);
|
|
8989
9631
|
if (parent === current) {
|
|
8990
9632
|
break;
|
|
8991
9633
|
}
|
|
@@ -8996,12 +9638,12 @@ async function gitRootFromCwds2(cwds) {
|
|
|
8996
9638
|
}
|
|
8997
9639
|
function qoderCnProjectRootFromCwds(projectDir, cwds) {
|
|
8998
9640
|
for (const cwd of cwds) {
|
|
8999
|
-
let current =
|
|
9641
|
+
let current = path18.resolve(cwd);
|
|
9000
9642
|
while (true) {
|
|
9001
9643
|
if (encodeQoderCnProjectPath(current) === projectDir) {
|
|
9002
9644
|
return current;
|
|
9003
9645
|
}
|
|
9004
|
-
const parent =
|
|
9646
|
+
const parent = path18.dirname(current);
|
|
9005
9647
|
if (parent === current) {
|
|
9006
9648
|
break;
|
|
9007
9649
|
}
|
|
@@ -9011,14 +9653,14 @@ function qoderCnProjectRootFromCwds(projectDir, cwds) {
|
|
|
9011
9653
|
return void 0;
|
|
9012
9654
|
}
|
|
9013
9655
|
function encodeQoderCnProjectPath(value) {
|
|
9014
|
-
return
|
|
9656
|
+
return path18.resolve(value).split(path18.sep).join("-").replaceAll("_", "-");
|
|
9015
9657
|
}
|
|
9016
9658
|
async function qoderCnProjectFromFilePath(filePath, options) {
|
|
9017
|
-
const projectDir =
|
|
9018
|
-
const home = options ?
|
|
9659
|
+
const projectDir = path18.basename(path18.dirname(filePath));
|
|
9660
|
+
const home = options ? path18.resolve(stringOption(options.home) || os8.homedir()) : os8.homedir();
|
|
9019
9661
|
const resolved = await resolveQoderCnProjectPath(projectDir, home);
|
|
9020
9662
|
if (resolved) {
|
|
9021
|
-
return
|
|
9663
|
+
return path18.basename(resolved);
|
|
9022
9664
|
}
|
|
9023
9665
|
const homePrefix = `${encodeQoderCnProjectPath(home)}-`;
|
|
9024
9666
|
if (projectDir.startsWith(homePrefix)) {
|
|
@@ -9043,7 +9685,7 @@ async function resolveQoderCnProjectPath(projectDir, home) {
|
|
|
9043
9685
|
if (!entry.isDirectory()) {
|
|
9044
9686
|
continue;
|
|
9045
9687
|
}
|
|
9046
|
-
const candidate =
|
|
9688
|
+
const candidate = path18.join(current, entry.name);
|
|
9047
9689
|
const encoded = encodeQoderCnProjectPath(candidate);
|
|
9048
9690
|
if (encoded === projectDir) {
|
|
9049
9691
|
return candidate;
|
|
@@ -9088,9 +9730,9 @@ function hookConfig6() {
|
|
|
9088
9730
|
function qoderCnConfigDir(home, env) {
|
|
9089
9731
|
const override = env?.QODER_CN_CONFIG_DIR;
|
|
9090
9732
|
if (override && override.trim()) {
|
|
9091
|
-
return
|
|
9733
|
+
return path18.resolve(override);
|
|
9092
9734
|
}
|
|
9093
|
-
return
|
|
9735
|
+
return path18.join(home, ".qoder-cn");
|
|
9094
9736
|
}
|
|
9095
9737
|
function createQoderCnAdapter() {
|
|
9096
9738
|
return {
|
|
@@ -9102,27 +9744,27 @@ function createQoderCnAdapter() {
|
|
|
9102
9744
|
return qoderCnConfigDir(home, env);
|
|
9103
9745
|
},
|
|
9104
9746
|
installedPath(home, env) {
|
|
9105
|
-
return
|
|
9747
|
+
return path18.join(qoderCnConfigDir(home, env), "settings.json");
|
|
9106
9748
|
},
|
|
9107
9749
|
async isInstalled(home, env) {
|
|
9108
9750
|
return isHooksJsonInstalled(
|
|
9109
|
-
|
|
9751
|
+
path18.join(qoderCnConfigDir(home, env), "settings.json"),
|
|
9110
9752
|
"vibetime hook --agent qoder-cn"
|
|
9111
9753
|
);
|
|
9112
9754
|
},
|
|
9113
9755
|
installEntries(home, env) {
|
|
9114
9756
|
return [{
|
|
9115
9757
|
kind: "hooks-json",
|
|
9116
|
-
path:
|
|
9758
|
+
path: path18.join(qoderCnConfigDir(home, env), "settings.json"),
|
|
9117
9759
|
content: hookConfig6()
|
|
9118
9760
|
}];
|
|
9119
9761
|
},
|
|
9120
9762
|
sourcePaths(home, env) {
|
|
9121
9763
|
const base = qoderCnConfigDir(home, env);
|
|
9122
9764
|
return [
|
|
9123
|
-
|
|
9124
|
-
|
|
9125
|
-
|
|
9765
|
+
path18.join(base, "projects"),
|
|
9766
|
+
path18.join(base, ".qoder.json"),
|
|
9767
|
+
path18.join(home, ".qoder.json")
|
|
9126
9768
|
];
|
|
9127
9769
|
},
|
|
9128
9770
|
parseSessionFile: parseQoderCnSessionFile
|
|
@@ -9131,11 +9773,11 @@ function createQoderCnAdapter() {
|
|
|
9131
9773
|
|
|
9132
9774
|
// src/adapters/qoder.ts
|
|
9133
9775
|
import { existsSync } from "node:fs";
|
|
9134
|
-
import { readdir as readdir8, readFile as
|
|
9776
|
+
import { readdir as readdir8, readFile as readFile12, stat as stat9 } from "node:fs/promises";
|
|
9135
9777
|
import os9 from "node:os";
|
|
9136
|
-
import
|
|
9778
|
+
import path19 from "node:path";
|
|
9137
9779
|
function parseQoderPaths(filePath) {
|
|
9138
|
-
const parts = filePath.split(
|
|
9780
|
+
const parts = filePath.split(path19.sep);
|
|
9139
9781
|
const subagentsIdx = parts.lastIndexOf("subagents");
|
|
9140
9782
|
let sessionId = "";
|
|
9141
9783
|
let projectName = "";
|
|
@@ -9145,17 +9787,17 @@ function parseQoderPaths(filePath) {
|
|
|
9145
9787
|
sessionId = parts[subagentsIdx - 1];
|
|
9146
9788
|
projectName = parts[subagentsIdx - 2];
|
|
9147
9789
|
const projectsIdx = parts.lastIndexOf("projects");
|
|
9148
|
-
configDir2 = parts.slice(0, projectsIdx).join(
|
|
9149
|
-
mainTranscriptPath = [...parts.slice(0, subagentsIdx - 1), `${sessionId}.jsonl`].join(
|
|
9790
|
+
configDir2 = parts.slice(0, projectsIdx).join(path19.sep);
|
|
9791
|
+
mainTranscriptPath = [...parts.slice(0, subagentsIdx - 1), `${sessionId}.jsonl`].join(path19.sep);
|
|
9150
9792
|
} else {
|
|
9151
9793
|
const filename = parts.at(-1) || "";
|
|
9152
|
-
sessionId =
|
|
9794
|
+
sessionId = path19.basename(filename, ".jsonl");
|
|
9153
9795
|
projectName = parts.at(-2) || "";
|
|
9154
9796
|
if (projectName === "transcript") {
|
|
9155
9797
|
projectName = parts.at(-3) || "";
|
|
9156
9798
|
}
|
|
9157
9799
|
const projectsIdx = parts.lastIndexOf("projects");
|
|
9158
|
-
configDir2 = parts.slice(0, projectsIdx).join(
|
|
9800
|
+
configDir2 = parts.slice(0, projectsIdx).join(path19.sep);
|
|
9159
9801
|
}
|
|
9160
9802
|
return { configDir: configDir2, projectName, sessionId, mainTranscriptPath };
|
|
9161
9803
|
}
|
|
@@ -9178,7 +9820,7 @@ function rebuildEventIdentity4(event) {
|
|
|
9178
9820
|
}
|
|
9179
9821
|
async function parseModelNamesFromDynamicTexts2(dynamicTextsPath) {
|
|
9180
9822
|
try {
|
|
9181
|
-
const content = await
|
|
9823
|
+
const content = await readFile12(dynamicTextsPath, "utf8");
|
|
9182
9824
|
const json = JSON.parse(content);
|
|
9183
9825
|
const texts = json.texts || {};
|
|
9184
9826
|
const map = {};
|
|
@@ -9194,10 +9836,10 @@ async function parseModelNamesFromDynamicTexts2(dynamicTextsPath) {
|
|
|
9194
9836
|
}
|
|
9195
9837
|
}
|
|
9196
9838
|
async function loadQoderModelNames(configDir2, home) {
|
|
9197
|
-
const map = await parseModelNamesFromDynamicTexts2(
|
|
9839
|
+
const map = await parseModelNamesFromDynamicTexts2(path19.join(configDir2, ".auth", "dynamic-texts.json"));
|
|
9198
9840
|
const siblingConfigDir = configDir2.replace(/\.qoder$/, ".qoder-cn");
|
|
9199
9841
|
if (siblingConfigDir !== configDir2) {
|
|
9200
|
-
const siblingMap = await parseModelNamesFromDynamicTexts2(
|
|
9842
|
+
const siblingMap = await parseModelNamesFromDynamicTexts2(path19.join(siblingConfigDir, ".auth", "dynamic-texts.json"));
|
|
9201
9843
|
for (const [key, val] of Object.entries(siblingMap)) {
|
|
9202
9844
|
if (!(key in map)) {
|
|
9203
9845
|
map[key] = val;
|
|
@@ -9205,11 +9847,11 @@ async function loadQoderModelNames(configDir2, home) {
|
|
|
9205
9847
|
}
|
|
9206
9848
|
}
|
|
9207
9849
|
if (isQwenworkConfigRoot(configDir2)) {
|
|
9208
|
-
for (const dir of [
|
|
9209
|
-
if (
|
|
9850
|
+
for (const dir of [path19.join(home, ".qoder"), path19.join(home, ".qoder-cn")]) {
|
|
9851
|
+
if (path19.resolve(dir) === path19.resolve(configDir2)) {
|
|
9210
9852
|
continue;
|
|
9211
9853
|
}
|
|
9212
|
-
const fallbackMap = await parseModelNamesFromDynamicTexts2(
|
|
9854
|
+
const fallbackMap = await parseModelNamesFromDynamicTexts2(path19.join(dir, ".auth", "dynamic-texts.json"));
|
|
9213
9855
|
for (const [key, val] of Object.entries(fallbackMap)) {
|
|
9214
9856
|
if (!(key in map)) {
|
|
9215
9857
|
map[key] = val;
|
|
@@ -9228,17 +9870,17 @@ async function loadQoderModelNames(configDir2, home) {
|
|
|
9228
9870
|
return map;
|
|
9229
9871
|
}
|
|
9230
9872
|
function isQwenworkConfigRoot(configDir2) {
|
|
9231
|
-
const name =
|
|
9873
|
+
const name = path19.basename(path19.resolve(configDir2));
|
|
9232
9874
|
if (name === ".qwenworkcn" || name === ".qwenwork") {
|
|
9233
9875
|
return true;
|
|
9234
9876
|
}
|
|
9235
9877
|
const override = process.env.QWENWORK_CONFIG_DIR;
|
|
9236
|
-
return Boolean(override && override.trim() &&
|
|
9878
|
+
return Boolean(override && override.trim() && path19.basename(path19.resolve(override)) === name);
|
|
9237
9879
|
}
|
|
9238
9880
|
var FAILED_SEGMENT_STOP_REASONS = /* @__PURE__ */ new Set(["cancelled", "canceled", "error", "failed", "refusal"]);
|
|
9239
9881
|
async function loadQoderSegmentModelCalls(filePath, isSubagentSession, modelMap) {
|
|
9240
9882
|
const { configDir: configDir2, projectName, sessionId } = parseQoderPaths(filePath);
|
|
9241
|
-
const segmentsPath =
|
|
9883
|
+
const segmentsPath = path19.join(configDir2, "logs", "sessions", projectName, sessionId, "segments");
|
|
9242
9884
|
const modelCalls = [];
|
|
9243
9885
|
try {
|
|
9244
9886
|
const files = await readdir8(segmentsPath);
|
|
@@ -9246,7 +9888,7 @@ async function loadQoderSegmentModelCalls(filePath, isSubagentSession, modelMap)
|
|
|
9246
9888
|
if (!file.endsWith(".jsonl")) {
|
|
9247
9889
|
continue;
|
|
9248
9890
|
}
|
|
9249
|
-
const content = await
|
|
9891
|
+
const content = await readFile12(path19.join(segmentsPath, file), "utf8");
|
|
9250
9892
|
let currentTurnIsSubagent = false;
|
|
9251
9893
|
for (const line of content.split("\n").filter(Boolean)) {
|
|
9252
9894
|
const raw = parseJsonLine(line);
|
|
@@ -9280,7 +9922,7 @@ async function loadQoderSegmentModelCalls(filePath, isSubagentSession, modelMap)
|
|
|
9280
9922
|
return modelCalls.filter((call) => call.isSubagent === isSubagentSession);
|
|
9281
9923
|
}
|
|
9282
9924
|
async function parseQoderSessionFile(filePath, options) {
|
|
9283
|
-
const text = await
|
|
9925
|
+
const text = await readFile12(filePath, "utf8");
|
|
9284
9926
|
const lines = text.split("\n").filter(Boolean);
|
|
9285
9927
|
const parsedPaths = parseQoderPaths(filePath);
|
|
9286
9928
|
const { configDir: configDir2 } = parsedPaths;
|
|
@@ -9291,7 +9933,7 @@ async function parseQoderSessionFile(filePath, options) {
|
|
|
9291
9933
|
let cwd;
|
|
9292
9934
|
let project = projectContext.project;
|
|
9293
9935
|
let model;
|
|
9294
|
-
const home =
|
|
9936
|
+
const home = path19.resolve(stringOption(options.home) || os9.homedir());
|
|
9295
9937
|
const modelMap = await loadQoderModelNames(configDir2, home);
|
|
9296
9938
|
const qwenworkRoot = isQwenworkConfigRoot(configDir2);
|
|
9297
9939
|
const isSubagentSession = filePath.includes("subagents");
|
|
@@ -9327,7 +9969,7 @@ async function parseQoderSessionFile(filePath, options) {
|
|
|
9327
9969
|
sessionId = stringField(raw, "sessionId") || sessionId;
|
|
9328
9970
|
state.sessionId = sessionId;
|
|
9329
9971
|
cwd = stringField(raw, "cwd") || cwd;
|
|
9330
|
-
project = projectContext.project || (cwd ?
|
|
9972
|
+
project = projectContext.project || (cwd ? path19.basename(cwd) : project || await qoderProjectFromFilePath(filePath, options));
|
|
9331
9973
|
if (!ts) {
|
|
9332
9974
|
continue;
|
|
9333
9975
|
}
|
|
@@ -9637,7 +10279,7 @@ async function parseQoderSessionFile(filePath, options) {
|
|
|
9637
10279
|
}
|
|
9638
10280
|
dbModelCalls ??= await loadQoderDbModelCalls("Qoder", parsedPaths.sessionId, modelMap);
|
|
9639
10281
|
if (dbModelCalls.rootSessionId) {
|
|
9640
|
-
const parentPath =
|
|
10282
|
+
const parentPath = path19.join(path19.dirname(filePath), `${dbModelCalls.rootSessionId}.jsonl`);
|
|
9641
10283
|
const parentSourcePathHash = `sha256:${createStableHash(parentPath)}`;
|
|
9642
10284
|
return validEvents.map((event) => rebuildEventIdentity4({
|
|
9643
10285
|
...event,
|
|
@@ -9753,41 +10395,41 @@ function qoderExtractText(value) {
|
|
|
9753
10395
|
}
|
|
9754
10396
|
async function qoderProjectContextFromLines(filePath, lines, options, configDir2) {
|
|
9755
10397
|
const { projectName: projectDir, sessionId } = parseQoderPaths(filePath);
|
|
9756
|
-
const isSubagent = filePath.includes(`${
|
|
10398
|
+
const isSubagent = filePath.includes(`${path19.sep}subagents${path19.sep}`);
|
|
9757
10399
|
const inherited = isSubagent ? await readPersistedSessionContextFromOptions(options, sessionId) : void 0;
|
|
9758
10400
|
let cwds = [];
|
|
9759
10401
|
const workspaceDirs = [];
|
|
9760
10402
|
for (const line of lines) {
|
|
9761
10403
|
const raw = parseJsonLine(line);
|
|
9762
10404
|
const cwd = raw ? stringField(raw, "cwd") : void 0;
|
|
9763
|
-
if (cwd &&
|
|
10405
|
+
if (cwd && path19.isAbsolute(cwd)) {
|
|
9764
10406
|
cwds.push(cwd);
|
|
9765
10407
|
}
|
|
9766
10408
|
if (raw && stringField(raw, "type") === "workspace-directories") {
|
|
9767
10409
|
for (const dir of arrayField5(raw, "directories")) {
|
|
9768
|
-
if (typeof dir === "string" &&
|
|
10410
|
+
if (typeof dir === "string" && path19.isAbsolute(dir)) {
|
|
9769
10411
|
workspaceDirs.push(dir);
|
|
9770
10412
|
}
|
|
9771
10413
|
}
|
|
9772
10414
|
}
|
|
9773
10415
|
}
|
|
9774
10416
|
if (isSubagent) {
|
|
9775
|
-
if (inherited?.cwd &&
|
|
10417
|
+
if (inherited?.cwd && path19.isAbsolute(inherited.cwd)) {
|
|
9776
10418
|
cwds = [inherited.cwd];
|
|
9777
10419
|
} else {
|
|
9778
|
-
const parentSessionPath =
|
|
10420
|
+
const parentSessionPath = path19.join(configDir2, "projects", projectDir, `${sessionId}.jsonl`);
|
|
9779
10421
|
try {
|
|
9780
|
-
const parentText = await
|
|
10422
|
+
const parentText = await readFile12(parentSessionPath, "utf8");
|
|
9781
10423
|
const parentCwds = [];
|
|
9782
10424
|
for (const line of parentText.split("\n").filter(Boolean)) {
|
|
9783
10425
|
const raw = parseJsonLine(line);
|
|
9784
10426
|
const cwd = raw ? stringField(raw, "cwd") : void 0;
|
|
9785
|
-
if (cwd &&
|
|
10427
|
+
if (cwd && path19.isAbsolute(cwd)) {
|
|
9786
10428
|
parentCwds.push(cwd);
|
|
9787
10429
|
}
|
|
9788
10430
|
if (workspaceDirs.length === 0 && raw && stringField(raw, "type") === "workspace-directories") {
|
|
9789
10431
|
for (const dir of arrayField5(raw, "directories")) {
|
|
9790
|
-
if (typeof dir === "string" &&
|
|
10432
|
+
if (typeof dir === "string" && path19.isAbsolute(dir)) {
|
|
9791
10433
|
workspaceDirs.push(dir);
|
|
9792
10434
|
}
|
|
9793
10435
|
}
|
|
@@ -9807,29 +10449,29 @@ async function qoderProjectContextFromLines(filePath, lines, options, configDir2
|
|
|
9807
10449
|
}
|
|
9808
10450
|
}
|
|
9809
10451
|
const root = await gitRootFromCwds3(cwds) || qoderProjectRootFromCwds(projectDir, cwds);
|
|
9810
|
-
const project = inherited?.project || (cwds.length > 0 ?
|
|
10452
|
+
const project = inherited?.project || (cwds.length > 0 ? path19.basename(cwds[0]) : root ? path19.basename(root) : await qoderProjectFromFilePath(filePath, options));
|
|
9811
10453
|
return {
|
|
9812
10454
|
project,
|
|
9813
10455
|
workspaceId: createWorkspaceId({ projectName: project, repoRoot: root })
|
|
9814
10456
|
};
|
|
9815
10457
|
}
|
|
9816
10458
|
function pathInsideDir(candidate, dir) {
|
|
9817
|
-
const resolvedDir =
|
|
9818
|
-
const resolved =
|
|
9819
|
-
return resolved === resolvedDir || resolved.startsWith(`${resolvedDir}${
|
|
10459
|
+
const resolvedDir = path19.resolve(dir);
|
|
10460
|
+
const resolved = path19.resolve(candidate);
|
|
10461
|
+
return resolved === resolvedDir || resolved.startsWith(`${resolvedDir}${path19.sep}`);
|
|
9820
10462
|
}
|
|
9821
10463
|
async function gitRootFromCwds3(cwds) {
|
|
9822
10464
|
const seen = /* @__PURE__ */ new Set();
|
|
9823
10465
|
for (const cwd of cwds) {
|
|
9824
|
-
let current =
|
|
10466
|
+
let current = path19.resolve(cwd);
|
|
9825
10467
|
while (!seen.has(current)) {
|
|
9826
10468
|
seen.add(current);
|
|
9827
10469
|
try {
|
|
9828
|
-
await stat9(
|
|
10470
|
+
await stat9(path19.join(current, ".git"));
|
|
9829
10471
|
return current;
|
|
9830
10472
|
} catch {
|
|
9831
10473
|
}
|
|
9832
|
-
const parent =
|
|
10474
|
+
const parent = path19.dirname(current);
|
|
9833
10475
|
if (parent === current) {
|
|
9834
10476
|
break;
|
|
9835
10477
|
}
|
|
@@ -9840,12 +10482,12 @@ async function gitRootFromCwds3(cwds) {
|
|
|
9840
10482
|
}
|
|
9841
10483
|
function qoderProjectRootFromCwds(projectDir, cwds) {
|
|
9842
10484
|
for (const cwd of cwds) {
|
|
9843
|
-
let current =
|
|
10485
|
+
let current = path19.resolve(cwd);
|
|
9844
10486
|
while (true) {
|
|
9845
10487
|
if (qoderEncodedVariants(current).includes(projectDir)) {
|
|
9846
10488
|
return current;
|
|
9847
10489
|
}
|
|
9848
|
-
const parent =
|
|
10490
|
+
const parent = path19.dirname(current);
|
|
9849
10491
|
if (parent === current) {
|
|
9850
10492
|
break;
|
|
9851
10493
|
}
|
|
@@ -9855,7 +10497,7 @@ function qoderProjectRootFromCwds(projectDir, cwds) {
|
|
|
9855
10497
|
return void 0;
|
|
9856
10498
|
}
|
|
9857
10499
|
function rawQoderProjectPath(value) {
|
|
9858
|
-
return
|
|
10500
|
+
return path19.resolve(value).split(path19.sep).join("-");
|
|
9859
10501
|
}
|
|
9860
10502
|
function qoderEncodedVariants(value) {
|
|
9861
10503
|
const raw = rawQoderProjectPath(value);
|
|
@@ -9876,11 +10518,11 @@ function qoderEncodedProjectSuffix(projectDir, home) {
|
|
|
9876
10518
|
return void 0;
|
|
9877
10519
|
}
|
|
9878
10520
|
async function qoderProjectFromFilePath(filePath, options) {
|
|
9879
|
-
const projectDir =
|
|
9880
|
-
const home = options ?
|
|
10521
|
+
const projectDir = path19.basename(path19.dirname(filePath));
|
|
10522
|
+
const home = options ? path19.resolve(stringOption(options.home) || os9.homedir()) : os9.homedir();
|
|
9881
10523
|
const resolved = await resolveQoderProjectPath(projectDir, home);
|
|
9882
10524
|
if (resolved) {
|
|
9883
|
-
return
|
|
10525
|
+
return path19.basename(resolved);
|
|
9884
10526
|
}
|
|
9885
10527
|
const suffix = qoderEncodedProjectSuffix(projectDir, home);
|
|
9886
10528
|
if (suffix) {
|
|
@@ -9906,7 +10548,7 @@ async function resolveQoderProjectPath(projectDir, home) {
|
|
|
9906
10548
|
if (!entry.isDirectory()) {
|
|
9907
10549
|
continue;
|
|
9908
10550
|
}
|
|
9909
|
-
const candidate =
|
|
10551
|
+
const candidate = path19.join(current, entry.name);
|
|
9910
10552
|
const candidateVariants = qoderEncodedVariants(candidate);
|
|
9911
10553
|
if (candidateVariants.includes(projectDir)) {
|
|
9912
10554
|
return candidate;
|
|
@@ -9951,19 +10593,19 @@ function hookConfig7() {
|
|
|
9951
10593
|
function qoderConfigDir(home, env) {
|
|
9952
10594
|
const override = env?.QODER_CONFIG_DIR;
|
|
9953
10595
|
if (override && override.trim()) {
|
|
9954
|
-
return
|
|
10596
|
+
return path19.resolve(override);
|
|
9955
10597
|
}
|
|
9956
|
-
return
|
|
10598
|
+
return path19.join(home, ".qoder");
|
|
9957
10599
|
}
|
|
9958
10600
|
function qwenworkConfigDir(home, env) {
|
|
9959
10601
|
const override = env?.QWENWORK_CONFIG_DIR;
|
|
9960
10602
|
if (override && override.trim()) {
|
|
9961
|
-
return
|
|
10603
|
+
return path19.resolve(override);
|
|
9962
10604
|
}
|
|
9963
|
-
return
|
|
10605
|
+
return path19.join(home, ".qwenworkcn");
|
|
9964
10606
|
}
|
|
9965
10607
|
function qoderConfigDirs(home, env) {
|
|
9966
|
-
return [...new Set([qoderConfigDir(home, env), qwenworkConfigDir(home, env)].map((dir) =>
|
|
10608
|
+
return [...new Set([qoderConfigDir(home, env), qwenworkConfigDir(home, env)].map((dir) => path19.resolve(dir)))];
|
|
9967
10609
|
}
|
|
9968
10610
|
function createQoderAdapter() {
|
|
9969
10611
|
return {
|
|
@@ -9975,11 +10617,11 @@ function createQoderAdapter() {
|
|
|
9975
10617
|
return qoderConfigDir(home, env);
|
|
9976
10618
|
},
|
|
9977
10619
|
installedPath(home, env) {
|
|
9978
|
-
return
|
|
10620
|
+
return path19.join(qoderConfigDir(home, env), "settings.json");
|
|
9979
10621
|
},
|
|
9980
10622
|
async isInstalled(home, env) {
|
|
9981
10623
|
return isHooksJsonInstalled(
|
|
9982
|
-
|
|
10624
|
+
path19.join(qoderConfigDir(home, env), "settings.json"),
|
|
9983
10625
|
"vibetime hook --agent qoder"
|
|
9984
10626
|
);
|
|
9985
10627
|
},
|
|
@@ -9988,16 +10630,16 @@ function createQoderAdapter() {
|
|
|
9988
10630
|
const targets = [primary, ...variants.filter((dir) => existsSync(dir))];
|
|
9989
10631
|
return targets.map((base) => ({
|
|
9990
10632
|
kind: "hooks-json",
|
|
9991
|
-
path:
|
|
10633
|
+
path: path19.join(base, "settings.json"),
|
|
9992
10634
|
content: hookConfig7()
|
|
9993
10635
|
}));
|
|
9994
10636
|
},
|
|
9995
10637
|
sourcePaths(home, env) {
|
|
9996
|
-
const paths = qoderConfigDirs(home, env).map((base2) =>
|
|
10638
|
+
const paths = qoderConfigDirs(home, env).map((base2) => path19.join(base2, "projects"));
|
|
9997
10639
|
const base = qoderConfigDir(home, env);
|
|
9998
10640
|
paths.push(
|
|
9999
|
-
|
|
10000
|
-
|
|
10641
|
+
path19.join(base, ".qoder.json"),
|
|
10642
|
+
path19.join(home, ".qoder.json")
|
|
10001
10643
|
);
|
|
10002
10644
|
return paths;
|
|
10003
10645
|
},
|
|
@@ -10033,27 +10675,27 @@ function normalizeId(id) {
|
|
|
10033
10675
|
}
|
|
10034
10676
|
|
|
10035
10677
|
// src/adapters/workbuddy.ts
|
|
10036
|
-
import { readdir as readdir9, readFile as
|
|
10037
|
-
import
|
|
10678
|
+
import { readdir as readdir9, readFile as readFile13, stat as stat10 } from "node:fs/promises";
|
|
10679
|
+
import path20 from "node:path";
|
|
10038
10680
|
function workbuddyProjectsDir(home, env) {
|
|
10039
10681
|
const override = env?.WORKBUDDY_PROJECTS_DIR || env?.WORKBUDDY_HOME;
|
|
10040
10682
|
if (override && override.trim()) {
|
|
10041
|
-
return
|
|
10683
|
+
return path20.resolve(override, override.endsWith("projects") ? "" : "projects");
|
|
10042
10684
|
}
|
|
10043
|
-
return
|
|
10685
|
+
return path20.join(home, ".workbuddy", "projects");
|
|
10044
10686
|
}
|
|
10045
10687
|
function workbuddyBaseDir(home, env) {
|
|
10046
10688
|
const override = env?.WORKBUDDY_HOME;
|
|
10047
10689
|
if (override && override.trim()) {
|
|
10048
|
-
return
|
|
10690
|
+
return path20.resolve(override);
|
|
10049
10691
|
}
|
|
10050
|
-
return
|
|
10692
|
+
return path20.join(home, ".workbuddy");
|
|
10051
10693
|
}
|
|
10052
10694
|
function projectFromCwd(cwd, fallback) {
|
|
10053
10695
|
if (!cwd) {
|
|
10054
10696
|
return fallback;
|
|
10055
10697
|
}
|
|
10056
|
-
return
|
|
10698
|
+
return path20.basename(cwd) || fallback;
|
|
10057
10699
|
}
|
|
10058
10700
|
function sourceHash(filePath) {
|
|
10059
10701
|
return `sha256:${createStableHash(filePath)}`;
|
|
@@ -10171,7 +10813,7 @@ function toolCallFailed(record) {
|
|
|
10171
10813
|
return status === "failed" || status === "incomplete" || record.is_error === true || providerData.error != null || providerData.isError === true;
|
|
10172
10814
|
}
|
|
10173
10815
|
async function readWorkbuddyLines(filePath) {
|
|
10174
|
-
const text = await
|
|
10816
|
+
const text = await readFile13(filePath, "utf8");
|
|
10175
10817
|
return text.split(/\r?\n/).map((line, index) => {
|
|
10176
10818
|
if (!line.trim()) {
|
|
10177
10819
|
return void 0;
|
|
@@ -10191,8 +10833,8 @@ async function parseWorkbuddySessionFile(filePath, options) {
|
|
|
10191
10833
|
}
|
|
10192
10834
|
const events = [];
|
|
10193
10835
|
const first = lines[0].record;
|
|
10194
|
-
const sessionId = stringField(first, "sessionId") ||
|
|
10195
|
-
const fallbackProject =
|
|
10836
|
+
const sessionId = stringField(first, "sessionId") || path20.basename(filePath, ".jsonl");
|
|
10837
|
+
const fallbackProject = path20.basename(path20.dirname(filePath));
|
|
10196
10838
|
const cwd = lines.map((line) => stringField(line.record, "cwd")).find(Boolean);
|
|
10197
10839
|
const project = projectFromCwd(cwd, fallbackProject);
|
|
10198
10840
|
const workspaceId = createWorkspaceId({ projectName: project, repoRoot: cwd });
|
|
@@ -10481,11 +11123,11 @@ async function workbuddyBackfillFiles(sourceRoot, home, env) {
|
|
|
10481
11123
|
if (!project.isDirectory()) {
|
|
10482
11124
|
continue;
|
|
10483
11125
|
}
|
|
10484
|
-
const projectDir =
|
|
11126
|
+
const projectDir = path20.join(base, project.name);
|
|
10485
11127
|
const entries = await readdir9(projectDir, { withFileTypes: true });
|
|
10486
11128
|
for (const entry of entries) {
|
|
10487
11129
|
if (entry.isFile() && entry.name.endsWith(".jsonl")) {
|
|
10488
|
-
const filePath =
|
|
11130
|
+
const filePath = path20.join(projectDir, entry.name);
|
|
10489
11131
|
const info = await stat10(filePath);
|
|
10490
11132
|
files.push({ path: filePath, modifiedAt: info.mtime.toISOString() });
|
|
10491
11133
|
}
|
|
@@ -10523,18 +11165,18 @@ function createWorkbuddyAdapter() {
|
|
|
10523
11165
|
return workbuddyProjectsDir(home, env);
|
|
10524
11166
|
},
|
|
10525
11167
|
installedPath(home, env) {
|
|
10526
|
-
return
|
|
11168
|
+
return path20.join(workbuddyBaseDir(home, env), "settings.json");
|
|
10527
11169
|
},
|
|
10528
11170
|
async isInstalled(home, env) {
|
|
10529
11171
|
return isHooksJsonInstalled(
|
|
10530
|
-
|
|
11172
|
+
path20.join(workbuddyBaseDir(home, env), "settings.json"),
|
|
10531
11173
|
"vibetime hook --agent workbuddy"
|
|
10532
11174
|
);
|
|
10533
11175
|
},
|
|
10534
11176
|
installEntries(home, env) {
|
|
10535
11177
|
return [{
|
|
10536
11178
|
kind: "hooks-json",
|
|
10537
|
-
path:
|
|
11179
|
+
path: path20.join(workbuddyBaseDir(home, env), "settings.json"),
|
|
10538
11180
|
content: hookConfig8()
|
|
10539
11181
|
}];
|
|
10540
11182
|
},
|
|
@@ -10547,20 +11189,20 @@ function createWorkbuddyAdapter() {
|
|
|
10547
11189
|
|
|
10548
11190
|
// src/adapters/zcode.ts
|
|
10549
11191
|
import { execFile } from "node:child_process";
|
|
10550
|
-
import { readFile as
|
|
10551
|
-
import
|
|
11192
|
+
import { readFile as readFile14, stat as stat11 } from "node:fs/promises";
|
|
11193
|
+
import path21 from "node:path";
|
|
10552
11194
|
import { promisify as promisify2 } from "node:util";
|
|
10553
11195
|
init_fs();
|
|
10554
11196
|
var execFileAsync = promisify2(execFile);
|
|
10555
11197
|
function zcodeCliDir(home, env) {
|
|
10556
11198
|
const override = env?.ZCODE_CLI_DIR || env?.ZCODE_HOME;
|
|
10557
11199
|
if (override && override.trim()) {
|
|
10558
|
-
return
|
|
11200
|
+
return path21.resolve(override, override.endsWith("cli") ? "" : "cli");
|
|
10559
11201
|
}
|
|
10560
|
-
return
|
|
11202
|
+
return path21.join(home, ".zcode", "cli");
|
|
10561
11203
|
}
|
|
10562
11204
|
function zcodeDbPath(home, env) {
|
|
10563
|
-
return
|
|
11205
|
+
return path21.join(zcodeCliDir(home, env), "db", "db.sqlite");
|
|
10564
11206
|
}
|
|
10565
11207
|
var providerNameCache = null;
|
|
10566
11208
|
async function loadProviderNames(configPath2) {
|
|
@@ -10576,7 +11218,7 @@ async function loadProviderNames(configPath2) {
|
|
|
10576
11218
|
}
|
|
10577
11219
|
const map = /* @__PURE__ */ new Map();
|
|
10578
11220
|
try {
|
|
10579
|
-
const raw = await
|
|
11221
|
+
const raw = await readFile14(configPath2, "utf-8");
|
|
10580
11222
|
const config = JSON.parse(raw);
|
|
10581
11223
|
const providers = config?.provider;
|
|
10582
11224
|
if (isPlainObject(providers)) {
|
|
@@ -10594,7 +11236,7 @@ function sourceHash2(filePath) {
|
|
|
10594
11236
|
return `sha256:${createStableHash(filePath)}`;
|
|
10595
11237
|
}
|
|
10596
11238
|
function projectFromDirectory(directory) {
|
|
10597
|
-
return directory ?
|
|
11239
|
+
return directory ? path21.basename(directory) || "zcode" : "zcode";
|
|
10598
11240
|
}
|
|
10599
11241
|
function isoFromMs(value) {
|
|
10600
11242
|
return timestampFrom(typeof value === "number" ? value : Number(value));
|
|
@@ -10777,16 +11419,16 @@ async function parseZCodeDb(filePath, options) {
|
|
|
10777
11419
|
if (rows.length === 0) {
|
|
10778
11420
|
return [];
|
|
10779
11421
|
}
|
|
10780
|
-
let candidate =
|
|
11422
|
+
let candidate = path21.resolve(filePath);
|
|
10781
11423
|
let configPath2 = "";
|
|
10782
11424
|
for (let i = 0; i < 12; i++) {
|
|
10783
|
-
const probe =
|
|
11425
|
+
const probe = path21.join(candidate, ".zcode", "v2", "config.json");
|
|
10784
11426
|
try {
|
|
10785
11427
|
await stat11(probe);
|
|
10786
11428
|
configPath2 = probe;
|
|
10787
11429
|
break;
|
|
10788
11430
|
} catch {
|
|
10789
|
-
const parent =
|
|
11431
|
+
const parent = path21.dirname(candidate);
|
|
10790
11432
|
if (parent === candidate) break;
|
|
10791
11433
|
candidate = parent;
|
|
10792
11434
|
}
|
|
@@ -11002,7 +11644,7 @@ async function parseZCodeDb(filePath, options) {
|
|
|
11002
11644
|
}
|
|
11003
11645
|
async function zcodeBackfillFiles(sourceRoot, home, env) {
|
|
11004
11646
|
const candidate = sourceRoot || zcodeDbPath(home, env);
|
|
11005
|
-
const filePath = candidate.endsWith(".sqlite") ? candidate :
|
|
11647
|
+
const filePath = candidate.endsWith(".sqlite") ? candidate : path21.join(candidate, "db", "db.sqlite");
|
|
11006
11648
|
try {
|
|
11007
11649
|
const info = await stat11(filePath);
|
|
11008
11650
|
return [{ path: filePath, modifiedAt: info.mtime.toISOString() }];
|
|
@@ -11037,49 +11679,49 @@ function createZCodeAdapter() {
|
|
|
11037
11679
|
|
|
11038
11680
|
// src/adapters/zed.ts
|
|
11039
11681
|
import os10 from "node:os";
|
|
11040
|
-
import
|
|
11682
|
+
import path22 from "node:path";
|
|
11041
11683
|
function zedThreadsCandidates(home, env) {
|
|
11042
11684
|
const candidates = [];
|
|
11043
11685
|
const platform2 = process.platform;
|
|
11044
11686
|
if (platform2 === "darwin") {
|
|
11045
|
-
candidates.push(
|
|
11687
|
+
candidates.push(path22.join(home, "Library", "Application Support", "Zed", "threads", "threads.db"));
|
|
11046
11688
|
} else if (platform2 === "win32") {
|
|
11047
11689
|
const appdata = env?.APPDATA;
|
|
11048
11690
|
if (appdata && appdata.trim()) {
|
|
11049
|
-
candidates.push(
|
|
11691
|
+
candidates.push(path22.join(path22.resolve(appdata), "Zed", "threads", "threads.db"));
|
|
11050
11692
|
}
|
|
11051
|
-
candidates.push(
|
|
11693
|
+
candidates.push(path22.join(home, "AppData", "Roaming", "Zed", "threads", "threads.db"));
|
|
11052
11694
|
} else {
|
|
11053
11695
|
const xdgData = env?.XDG_DATA_HOME;
|
|
11054
11696
|
if (xdgData && xdgData.trim()) {
|
|
11055
|
-
candidates.push(
|
|
11697
|
+
candidates.push(path22.join(path22.resolve(xdgData), "zed", "threads", "threads.db"));
|
|
11056
11698
|
}
|
|
11057
|
-
candidates.push(
|
|
11699
|
+
candidates.push(path22.join(home, ".local", "share", "zed", "threads", "threads.db"));
|
|
11058
11700
|
const xdgConfig = env?.XDG_CONFIG_HOME;
|
|
11059
11701
|
if (xdgConfig && xdgConfig.trim()) {
|
|
11060
|
-
candidates.push(
|
|
11702
|
+
candidates.push(path22.join(path22.resolve(xdgConfig), "zed", "threads", "threads.db"));
|
|
11061
11703
|
}
|
|
11062
|
-
candidates.push(
|
|
11704
|
+
candidates.push(path22.join(home, ".config", "zed", "threads", "threads.db"));
|
|
11063
11705
|
}
|
|
11064
11706
|
return candidates;
|
|
11065
11707
|
}
|
|
11066
11708
|
function zedConfigDir(home, env) {
|
|
11067
11709
|
const platform2 = process.platform;
|
|
11068
11710
|
if (platform2 === "darwin") {
|
|
11069
|
-
return
|
|
11711
|
+
return path22.join(home, "Library", "Application Support", "Zed");
|
|
11070
11712
|
}
|
|
11071
11713
|
if (platform2 === "win32") {
|
|
11072
11714
|
const appdata = env?.APPDATA;
|
|
11073
11715
|
if (appdata && appdata.trim()) {
|
|
11074
|
-
return
|
|
11716
|
+
return path22.join(path22.resolve(appdata), "Zed");
|
|
11075
11717
|
}
|
|
11076
|
-
return
|
|
11718
|
+
return path22.join(home, "AppData", "Roaming", "Zed");
|
|
11077
11719
|
}
|
|
11078
11720
|
const xdgConfig = env?.XDG_CONFIG_HOME;
|
|
11079
11721
|
if (xdgConfig && xdgConfig.trim()) {
|
|
11080
|
-
return
|
|
11722
|
+
return path22.join(path22.resolve(xdgConfig), "zed");
|
|
11081
11723
|
}
|
|
11082
|
-
return
|
|
11724
|
+
return path22.join(home, ".config", "zed");
|
|
11083
11725
|
}
|
|
11084
11726
|
function baseZedEvent(event) {
|
|
11085
11727
|
return {
|
|
@@ -11143,7 +11785,7 @@ async function parseZedSessionFile(dbPath, options) {
|
|
|
11143
11785
|
const folderRaw = row.folder_paths || "";
|
|
11144
11786
|
const folder = folderRaw.split(/[\n,]/).map((s) => s.trim()).find(Boolean);
|
|
11145
11787
|
const cwd = folder || void 0;
|
|
11146
|
-
const project = cwd ?
|
|
11788
|
+
const project = cwd ? path22.basename(cwd) : row.summary ? row.summary.slice(0, 40) : void 0;
|
|
11147
11789
|
let json;
|
|
11148
11790
|
try {
|
|
11149
11791
|
const bytes = row.data_type === "zstd" ? decompress2(new Uint8Array(row.data)) : new Uint8Array(row.data);
|
|
@@ -11418,7 +12060,7 @@ function createZedAdapter() {
|
|
|
11418
12060
|
return zedConfigDir(home, env);
|
|
11419
12061
|
},
|
|
11420
12062
|
installedPath(home, env) {
|
|
11421
|
-
return
|
|
12063
|
+
return path22.join(zedConfigDir(home, env), "vibetime-marker");
|
|
11422
12064
|
},
|
|
11423
12065
|
async isInstalled() {
|
|
11424
12066
|
return false;
|
|
@@ -11793,11 +12435,26 @@ async function installEntry(entry, options) {
|
|
|
11793
12435
|
await mergeHooksJson(entry.path, entry.content, options);
|
|
11794
12436
|
return;
|
|
11795
12437
|
}
|
|
12438
|
+
if (entry.kind === "hooks-toml" && typeof entry.content === "object") {
|
|
12439
|
+
await mergeHooksToml(entry.path, entry.content, options);
|
|
12440
|
+
return;
|
|
12441
|
+
}
|
|
11796
12442
|
await writeGeneratedFile(entry.path, String(entry.content), {
|
|
11797
12443
|
...options,
|
|
11798
12444
|
onWrite: options.onWrite
|
|
11799
12445
|
});
|
|
11800
12446
|
}
|
|
12447
|
+
async function uninstallEntry(entry, options) {
|
|
12448
|
+
if (entry.kind === "hooks-toml" && typeof entry.content === "object") {
|
|
12449
|
+
await uninstallHooksToml(entry.path, entry.content, options);
|
|
12450
|
+
return;
|
|
12451
|
+
}
|
|
12452
|
+
if (entry.kind === "hooks-json" && typeof entry.content === "object") {
|
|
12453
|
+
await uninstallHooksJson(entry.path, entry.content, options);
|
|
12454
|
+
return;
|
|
12455
|
+
}
|
|
12456
|
+
await uninstallGeneratedFile(entry.path, options);
|
|
12457
|
+
}
|
|
11801
12458
|
async function mergeHooksJson(filePath, content, { dryRun, force, onWrite }) {
|
|
11802
12459
|
const { mkdir: mkdir6, writeFile: writeFile5 } = await import("node:fs/promises");
|
|
11803
12460
|
const pathMod = await import("node:path");
|
|
@@ -11894,20 +12551,232 @@ function hookCommandFromGroup(group) {
|
|
|
11894
12551
|
const hook = group.hooks[0];
|
|
11895
12552
|
return isPlainObject(hook) && typeof hook.command === "string" ? hook.command : void 0;
|
|
11896
12553
|
}
|
|
12554
|
+
async function mergeHooksToml(filePath, content, { dryRun, force, onWrite }) {
|
|
12555
|
+
const { mkdir: mkdir6, writeFile: writeFile5 } = await import("node:fs/promises");
|
|
12556
|
+
const pathMod = await import("node:path");
|
|
12557
|
+
if (dryRun) {
|
|
12558
|
+
onWrite(`Would merge ${filePath}`);
|
|
12559
|
+
return;
|
|
12560
|
+
}
|
|
12561
|
+
const existingText = await readTextIfExists(filePath);
|
|
12562
|
+
if (existingText !== null && existingText.trim() !== "" && !looksLikeToml(existingText) && !force) {
|
|
12563
|
+
throw new Error(
|
|
12564
|
+
`Refusing to update non-TOML file: ${filePath}. Re-run with --force if this is intentional.`
|
|
12565
|
+
);
|
|
12566
|
+
}
|
|
12567
|
+
const desired = Array.isArray(content.hooks) ? content.hooks : [];
|
|
12568
|
+
const nextText = mergeTomlHookRules(existingText ?? "", desired);
|
|
12569
|
+
if (existingText === nextText || existingText === null && nextText === "") {
|
|
12570
|
+
onWrite(`Already installed ${filePath}`);
|
|
12571
|
+
return;
|
|
12572
|
+
}
|
|
12573
|
+
if (existingText !== null) {
|
|
12574
|
+
const existingKeys = new Set(
|
|
12575
|
+
parseTomlHookRules(existingText).map((rule) => `${rule.event}\0${rule.command}`)
|
|
12576
|
+
);
|
|
12577
|
+
const allPresent = desired.every((rule) => existingKeys.has(`${rule.event}\0${rule.command}`));
|
|
12578
|
+
if (allPresent && desired.length > 0) {
|
|
12579
|
+
onWrite(`Already installed ${filePath}`);
|
|
12580
|
+
return;
|
|
12581
|
+
}
|
|
12582
|
+
}
|
|
12583
|
+
await mkdir6(pathMod.dirname(filePath), { recursive: true });
|
|
12584
|
+
await writeFile5(filePath, nextText, "utf8");
|
|
12585
|
+
onWrite(`Installed ${filePath}`);
|
|
12586
|
+
}
|
|
12587
|
+
async function uninstallHooksToml(filePath, content, { dryRun, onWrite }) {
|
|
12588
|
+
const existingText = await readTextIfExists(filePath);
|
|
12589
|
+
if (existingText === null) {
|
|
12590
|
+
onWrite(`Already uninstalled ${filePath}`);
|
|
12591
|
+
return;
|
|
12592
|
+
}
|
|
12593
|
+
const commands = Array.from(new Set(
|
|
12594
|
+
(Array.isArray(content.hooks) ? content.hooks : []).map((rule) => rule.command).filter((cmd) => typeof cmd === "string" && cmd.length > 0)
|
|
12595
|
+
));
|
|
12596
|
+
if (commands.length === 0) {
|
|
12597
|
+
onWrite(`Already uninstalled ${filePath}`);
|
|
12598
|
+
return;
|
|
12599
|
+
}
|
|
12600
|
+
const nextText = removeTomlHookRulesByCommand(existingText, commands);
|
|
12601
|
+
if (nextText === existingText) {
|
|
12602
|
+
onWrite(`Already uninstalled ${filePath}`);
|
|
12603
|
+
return;
|
|
12604
|
+
}
|
|
12605
|
+
if (dryRun) {
|
|
12606
|
+
onWrite(`Would uninstall ${filePath}`);
|
|
12607
|
+
return;
|
|
12608
|
+
}
|
|
12609
|
+
const { writeFile: writeFile5 } = await import("node:fs/promises");
|
|
12610
|
+
await writeFile5(filePath, nextText, "utf8");
|
|
12611
|
+
onWrite(`Uninstalled ${filePath}`);
|
|
12612
|
+
}
|
|
12613
|
+
function collectHookCommandsFromJsonContent(content) {
|
|
12614
|
+
const commands = /* @__PURE__ */ new Set();
|
|
12615
|
+
const walkGroups = (groups) => {
|
|
12616
|
+
if (!Array.isArray(groups)) {
|
|
12617
|
+
return;
|
|
12618
|
+
}
|
|
12619
|
+
for (const group of groups) {
|
|
12620
|
+
if (!isPlainObject(group) || !Array.isArray(group.hooks)) {
|
|
12621
|
+
continue;
|
|
12622
|
+
}
|
|
12623
|
+
for (const hook of group.hooks) {
|
|
12624
|
+
if (isPlainObject(hook) && typeof hook.command === "string" && hook.command) {
|
|
12625
|
+
commands.add(hook.command);
|
|
12626
|
+
}
|
|
12627
|
+
}
|
|
12628
|
+
}
|
|
12629
|
+
};
|
|
12630
|
+
if (isPlainObject(content.hooks)) {
|
|
12631
|
+
for (const groups of Object.values(content.hooks)) {
|
|
12632
|
+
walkGroups(groups);
|
|
12633
|
+
}
|
|
12634
|
+
}
|
|
12635
|
+
for (const [key, value] of Object.entries(content)) {
|
|
12636
|
+
if (key === "hooks" || key === "enable_json_hooks" || !isPlainObject(value)) {
|
|
12637
|
+
continue;
|
|
12638
|
+
}
|
|
12639
|
+
for (const groups of Object.values(value)) {
|
|
12640
|
+
walkGroups(groups);
|
|
12641
|
+
}
|
|
12642
|
+
}
|
|
12643
|
+
return [...commands];
|
|
12644
|
+
}
|
|
12645
|
+
function stripHookCommandsFromGroups(groups, commands) {
|
|
12646
|
+
if (!Array.isArray(groups)) {
|
|
12647
|
+
return groups;
|
|
12648
|
+
}
|
|
12649
|
+
return groups.map((group) => {
|
|
12650
|
+
if (!isPlainObject(group) || !Array.isArray(group.hooks)) {
|
|
12651
|
+
return group;
|
|
12652
|
+
}
|
|
12653
|
+
const nextHooks = group.hooks.filter(
|
|
12654
|
+
(hook) => !(isPlainObject(hook) && typeof hook.command === "string" && commands.has(hook.command))
|
|
12655
|
+
);
|
|
12656
|
+
if (nextHooks.length === 0) {
|
|
12657
|
+
return null;
|
|
12658
|
+
}
|
|
12659
|
+
return { ...group, hooks: nextHooks };
|
|
12660
|
+
}).filter(Boolean);
|
|
12661
|
+
}
|
|
12662
|
+
async function uninstallHooksJson(filePath, content, { dryRun, onWrite }) {
|
|
12663
|
+
const existingText = await readTextIfExists(filePath);
|
|
12664
|
+
if (existingText === null) {
|
|
12665
|
+
onWrite(`Already uninstalled ${filePath}`);
|
|
12666
|
+
return;
|
|
12667
|
+
}
|
|
12668
|
+
let existing;
|
|
12669
|
+
try {
|
|
12670
|
+
existing = JSON.parse(existingText);
|
|
12671
|
+
} catch {
|
|
12672
|
+
onWrite(`Skipped non-JSON file ${filePath}`);
|
|
12673
|
+
return;
|
|
12674
|
+
}
|
|
12675
|
+
if (!isPlainObject(existing)) {
|
|
12676
|
+
onWrite(`Skipped non-object JSON file ${filePath}`);
|
|
12677
|
+
return;
|
|
12678
|
+
}
|
|
12679
|
+
const commands = new Set(collectHookCommandsFromJsonContent(content));
|
|
12680
|
+
if (commands.size === 0) {
|
|
12681
|
+
onWrite(`Already uninstalled ${filePath}`);
|
|
12682
|
+
return;
|
|
12683
|
+
}
|
|
12684
|
+
const next = structuredClone(existing);
|
|
12685
|
+
let changed = false;
|
|
12686
|
+
if (isPlainObject(next.hooks)) {
|
|
12687
|
+
for (const [event, groups] of Object.entries(next.hooks)) {
|
|
12688
|
+
const stripped = stripHookCommandsFromGroups(groups, commands);
|
|
12689
|
+
if (JSON.stringify(stripped) !== JSON.stringify(groups)) {
|
|
12690
|
+
changed = true;
|
|
12691
|
+
if (Array.isArray(stripped) && stripped.length === 0) {
|
|
12692
|
+
delete next.hooks[event];
|
|
12693
|
+
} else {
|
|
12694
|
+
next.hooks[event] = stripped;
|
|
12695
|
+
}
|
|
12696
|
+
}
|
|
12697
|
+
}
|
|
12698
|
+
if (isPlainObject(next.hooks) && Object.keys(next.hooks).length === 0) {
|
|
12699
|
+
delete next.hooks;
|
|
12700
|
+
changed = true;
|
|
12701
|
+
}
|
|
12702
|
+
}
|
|
12703
|
+
for (const [key, value] of Object.entries(next)) {
|
|
12704
|
+
if (key === "hooks" || key === "enable_json_hooks" || !isPlainObject(value)) {
|
|
12705
|
+
continue;
|
|
12706
|
+
}
|
|
12707
|
+
if (!Object.prototype.hasOwnProperty.call(content, key)) {
|
|
12708
|
+
continue;
|
|
12709
|
+
}
|
|
12710
|
+
for (const [event, groups] of Object.entries(value)) {
|
|
12711
|
+
const stripped = stripHookCommandsFromGroups(groups, commands);
|
|
12712
|
+
if (JSON.stringify(stripped) !== JSON.stringify(groups)) {
|
|
12713
|
+
changed = true;
|
|
12714
|
+
if (Array.isArray(stripped) && stripped.length === 0) {
|
|
12715
|
+
delete value[event];
|
|
12716
|
+
} else {
|
|
12717
|
+
value[event] = stripped;
|
|
12718
|
+
}
|
|
12719
|
+
}
|
|
12720
|
+
}
|
|
12721
|
+
if (Object.keys(value).length === 0) {
|
|
12722
|
+
delete next[key];
|
|
12723
|
+
changed = true;
|
|
12724
|
+
}
|
|
12725
|
+
}
|
|
12726
|
+
if (Object.prototype.hasOwnProperty.call(content, "enable_json_hooks") && next.enable_json_hooks === true) {
|
|
12727
|
+
const stillHasNamedHooks = Object.entries(next).some(
|
|
12728
|
+
([key, value]) => key !== "hooks" && key !== "enable_json_hooks" && isPlainObject(value) && Object.values(value).some((groups) => Array.isArray(groups) && groups.length > 0)
|
|
12729
|
+
);
|
|
12730
|
+
if (!stillHasNamedHooks) {
|
|
12731
|
+
delete next.enable_json_hooks;
|
|
12732
|
+
changed = true;
|
|
12733
|
+
}
|
|
12734
|
+
}
|
|
12735
|
+
if (!changed) {
|
|
12736
|
+
onWrite(`Already uninstalled ${filePath}`);
|
|
12737
|
+
return;
|
|
12738
|
+
}
|
|
12739
|
+
if (dryRun) {
|
|
12740
|
+
onWrite(`Would uninstall ${filePath}`);
|
|
12741
|
+
return;
|
|
12742
|
+
}
|
|
12743
|
+
const { writeFile: writeFile5 } = await import("node:fs/promises");
|
|
12744
|
+
await writeFile5(filePath, `${JSON.stringify(next, null, 2)}
|
|
12745
|
+
`, "utf8");
|
|
12746
|
+
onWrite(`Uninstalled ${filePath}`);
|
|
12747
|
+
}
|
|
12748
|
+
async function uninstallGeneratedFile(filePath, { dryRun, onWrite }) {
|
|
12749
|
+
const existingText = await readTextIfExists(filePath);
|
|
12750
|
+
if (existingText === null) {
|
|
12751
|
+
onWrite(`Already uninstalled ${filePath}`);
|
|
12752
|
+
return;
|
|
12753
|
+
}
|
|
12754
|
+
if (!existingText.includes(GENERATED_MARKER) && !existingText.includes("Generated by codetime.")) {
|
|
12755
|
+
onWrite(`Skipped non-vibetime file ${filePath}`);
|
|
12756
|
+
return;
|
|
12757
|
+
}
|
|
12758
|
+
if (dryRun) {
|
|
12759
|
+
onWrite(`Would uninstall ${filePath}`);
|
|
12760
|
+
return;
|
|
12761
|
+
}
|
|
12762
|
+
const { unlink } = await import("node:fs/promises");
|
|
12763
|
+
await unlink(filePath);
|
|
12764
|
+
onWrite(`Uninstalled ${filePath}`);
|
|
12765
|
+
}
|
|
11897
12766
|
|
|
11898
12767
|
// src/lib/config.ts
|
|
11899
12768
|
import { randomUUID } from "node:crypto";
|
|
11900
12769
|
import { existsSync as existsSync2, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
11901
12770
|
import { homedir, hostname } from "node:os";
|
|
11902
|
-
import
|
|
12771
|
+
import path23 from "node:path";
|
|
11903
12772
|
function configDir(home = homedir()) {
|
|
11904
|
-
return
|
|
12773
|
+
return path23.join(home, ".vibetime");
|
|
11905
12774
|
}
|
|
11906
12775
|
function configPath(home = homedir()) {
|
|
11907
|
-
return
|
|
12776
|
+
return path23.join(configDir(home), "config.json");
|
|
11908
12777
|
}
|
|
11909
12778
|
function machineIdPath(home = homedir()) {
|
|
11910
|
-
return
|
|
12779
|
+
return path23.join(configDir(home), "machine-id");
|
|
11911
12780
|
}
|
|
11912
12781
|
function readConfig(home = homedir()) {
|
|
11913
12782
|
const file = configPath(home);
|
|
@@ -11956,13 +12825,13 @@ init_fs();
|
|
|
11956
12825
|
// src/lib/logger.ts
|
|
11957
12826
|
import { appendFile, mkdir as mkdir4, rename, stat as stat12 } from "node:fs/promises";
|
|
11958
12827
|
import { homedir as homedir2 } from "node:os";
|
|
11959
|
-
import
|
|
12828
|
+
import path24 from "node:path";
|
|
11960
12829
|
var MAX_BYTES = 1 * 1024 * 1024;
|
|
11961
12830
|
function logDir(home = homedir2()) {
|
|
11962
|
-
return
|
|
12831
|
+
return path24.join(home, ".vibetime", "logs");
|
|
11963
12832
|
}
|
|
11964
12833
|
function logPath(home = homedir2(), name = "cli.log") {
|
|
11965
|
-
return
|
|
12834
|
+
return path24.join(logDir(home), name);
|
|
11966
12835
|
}
|
|
11967
12836
|
function serializeError(error) {
|
|
11968
12837
|
if (error instanceof Error) {
|
|
@@ -12137,8 +13006,8 @@ function buildHeaders(token, machine) {
|
|
|
12137
13006
|
...machine?.platform ? { "x-machine-platform": machine.platform } : {}
|
|
12138
13007
|
};
|
|
12139
13008
|
}
|
|
12140
|
-
function joinUrl(base,
|
|
12141
|
-
return new URL(
|
|
13009
|
+
function joinUrl(base, path26) {
|
|
13010
|
+
return new URL(path26, base.endsWith("/") ? base : `${base}/`).toString();
|
|
12142
13011
|
}
|
|
12143
13012
|
async function postRollupBatch(remote, rollups, options = {}) {
|
|
12144
13013
|
const response = await remote.fetchImpl(joinUrl(remote.baseUrl, "/v3/agent/ingest"), {
|
|
@@ -12228,6 +13097,7 @@ function createRegistry() {
|
|
|
12228
13097
|
registry.register(createZCodeAdapter());
|
|
12229
13098
|
registry.register(createGrokBuildAdapter());
|
|
12230
13099
|
registry.register(createZedAdapter());
|
|
13100
|
+
registry.register(createKimiCodeAdapter());
|
|
12231
13101
|
return registry;
|
|
12232
13102
|
}
|
|
12233
13103
|
var defaultContext = {
|
|
@@ -12285,6 +13155,7 @@ function createCli(ctx, registry) {
|
|
|
12285
13155
|
});
|
|
12286
13156
|
cli.command("detect", "Show supported local targets and install status").action((options) => detectCommand(normalizeOptions(options), ctx, registry).then(() => 0));
|
|
12287
13157
|
cli.command("install", "Install integration files into detected or requested targets").option("--target <targets>", "Target integrations, comma-separated").option("--targets <targets>", "Target integrations, comma-separated").option("--all", "Install all supported integrations").option("--force", "Overwrite existing non-generated files when needed").action((options) => installCommand(normalizeOptions(options), ctx, registry));
|
|
13158
|
+
cli.command("uninstall", "Remove vibetime integration hooks/files from targets").option("--target <targets>", "Target integrations, comma-separated").option("--targets <targets>", "Target integrations, comma-separated").option("--all", "Uninstall all supported integrations").action((options) => uninstallCommand(normalizeOptions(options), ctx, registry));
|
|
12288
13159
|
cli.command("upgrade", "Check for updates and upgrade to the latest version").option("--check", "Only check for updates, do not install").action((options) => upgradeCommand(normalizeOptions(options), ctx, registry));
|
|
12289
13160
|
cli.command("hook", "Read agent hook JSON from stdin and report a throttled event").option("--agent <name>", "Agent name").option("--project <name>", "Project name").option("--min-interval <seconds>", "Minimum seconds between similar hook reports").action((options) => hookCommand(normalizeOptions(options), ctx));
|
|
12290
13161
|
cli.command("sync-local-trigger", "Trigger one background local sync with throttle and locking").option("--min-interval <seconds>", "Minimum seconds between sync triggers").action((options) => syncLocalTriggerCommand(normalizeOptions(options), ctx, registry));
|
|
@@ -12381,6 +13252,44 @@ async function installCommand(options, ctx, registry) {
|
|
|
12381
13252
|
}
|
|
12382
13253
|
return 0;
|
|
12383
13254
|
}
|
|
13255
|
+
async function uninstallCommand(options, ctx, registry) {
|
|
13256
|
+
const home = resolveHome3(options, ctx);
|
|
13257
|
+
const env = ctx.env;
|
|
13258
|
+
const dryRun = Boolean(options["dry-run"]);
|
|
13259
|
+
const allAdapters = registry.all();
|
|
13260
|
+
const requested = requestedTargets(options);
|
|
13261
|
+
const unknown = requested.filter((id) => !allAdapters.some((a) => a.id === id));
|
|
13262
|
+
if (unknown.length > 0) {
|
|
13263
|
+
throw new Error(`Unknown target(s): ${unknown.join(", ")}`);
|
|
13264
|
+
}
|
|
13265
|
+
const installed = [];
|
|
13266
|
+
for (const adapter of allAdapters) {
|
|
13267
|
+
if (await adapter.isInstalled(home, env)) {
|
|
13268
|
+
installed.push(adapter.id);
|
|
13269
|
+
}
|
|
13270
|
+
}
|
|
13271
|
+
const selectedIds = requested.length > 0 ? requested : options.all ? allAdapters.map((a) => a.id) : installed;
|
|
13272
|
+
if (selectedIds.length === 0) {
|
|
13273
|
+
write(ctx.stderr, "No installed vibetime integrations were found. Use --target <id> or --all.\n");
|
|
13274
|
+
return 1;
|
|
13275
|
+
}
|
|
13276
|
+
for (const adapter of allAdapters.filter((a) => selectedIds.includes(a.id))) {
|
|
13277
|
+
const entries = adapter.installEntries(home, env);
|
|
13278
|
+
if (entries.length === 0) {
|
|
13279
|
+
write(ctx.stdout, `Nothing to uninstall for ${adapter.id}
|
|
13280
|
+
`);
|
|
13281
|
+
continue;
|
|
13282
|
+
}
|
|
13283
|
+
for (const entry of entries) {
|
|
13284
|
+
await uninstallEntry(entry, {
|
|
13285
|
+
dryRun,
|
|
13286
|
+
onWrite: (msg) => write(ctx.stdout, `${msg}
|
|
13287
|
+
`)
|
|
13288
|
+
});
|
|
13289
|
+
}
|
|
13290
|
+
}
|
|
13291
|
+
return 0;
|
|
13292
|
+
}
|
|
12384
13293
|
var NPM_PACKAGE = "@yhong91/vibetime";
|
|
12385
13294
|
async function fetchLatestVersion() {
|
|
12386
13295
|
try {
|
|
@@ -13123,13 +14032,13 @@ function selectBackfillFilesForImport(files, watermarkTs) {
|
|
|
13123
14032
|
return picked;
|
|
13124
14033
|
}
|
|
13125
14034
|
function backfillIncrementalStatePath(home) {
|
|
13126
|
-
return
|
|
14035
|
+
return path25.join(home, ".vibetime", "backfill-state.json");
|
|
13127
14036
|
}
|
|
13128
14037
|
function syncLocalTriggerStatePath(home) {
|
|
13129
|
-
return
|
|
14038
|
+
return path25.join(home, ".vibetime", "sync-local-trigger.json");
|
|
13130
14039
|
}
|
|
13131
14040
|
function syncLocalTriggerLockPath(home) {
|
|
13132
|
-
return
|
|
14041
|
+
return path25.join(home, ".vibetime", "sync-local-trigger.lock");
|
|
13133
14042
|
}
|
|
13134
14043
|
function backfillRemoteKey(baseUrl) {
|
|
13135
14044
|
try {
|
|
@@ -13191,7 +14100,7 @@ async function readBackfillIncrementalStateFile(home, ctx) {
|
|
|
13191
14100
|
}
|
|
13192
14101
|
async function writeBackfillIncrementalStateFile(home, file) {
|
|
13193
14102
|
const statePath = backfillIncrementalStatePath(home);
|
|
13194
|
-
await mkdir5(
|
|
14103
|
+
await mkdir5(path25.dirname(statePath), { recursive: true });
|
|
13195
14104
|
await writeFile4(statePath, `${JSON.stringify(file, null, 2)}
|
|
13196
14105
|
`, "utf8");
|
|
13197
14106
|
}
|
|
@@ -13240,7 +14149,7 @@ async function readSyncLocalTriggerState(statePath) {
|
|
|
13240
14149
|
return nextState;
|
|
13241
14150
|
}
|
|
13242
14151
|
async function writeSyncLocalTriggerState(statePath, state) {
|
|
13243
|
-
await mkdir5(
|
|
14152
|
+
await mkdir5(path25.dirname(statePath), { recursive: true });
|
|
13244
14153
|
await writeFile4(statePath, `${JSON.stringify(state, null, 2)}
|
|
13245
14154
|
`, "utf8");
|
|
13246
14155
|
}
|
|
@@ -13255,12 +14164,12 @@ async function readSyncLocalLock(lockPath) {
|
|
|
13255
14164
|
return { pid: lock.pid, startedAt: lock.startedAt };
|
|
13256
14165
|
}
|
|
13257
14166
|
async function writeSyncLocalLock(lockPath, lock) {
|
|
13258
|
-
await mkdir5(
|
|
14167
|
+
await mkdir5(path25.dirname(lockPath), { recursive: true });
|
|
13259
14168
|
await writeFile4(lockPath, `${JSON.stringify(lock, null, 2)}
|
|
13260
14169
|
`, "utf8");
|
|
13261
14170
|
}
|
|
13262
14171
|
async function acquireSyncLocalLock(lockPath, lock) {
|
|
13263
|
-
await mkdir5(
|
|
14172
|
+
await mkdir5(path25.dirname(lockPath), { recursive: true });
|
|
13264
14173
|
try {
|
|
13265
14174
|
const handle = await open(lockPath, "wx");
|
|
13266
14175
|
try {
|
|
@@ -13340,10 +14249,10 @@ function syncLocalRunnerEntryArgs(cliPath) {
|
|
|
13340
14249
|
if (cliPath.endsWith(".ts")) {
|
|
13341
14250
|
return ["--import", "tsx", cliPath];
|
|
13342
14251
|
}
|
|
13343
|
-
return [
|
|
14252
|
+
return [path25.resolve(path25.dirname(cliPath), "../bin/vibetime.mjs")];
|
|
13344
14253
|
}
|
|
13345
14254
|
function resolveHome3(options, ctx) {
|
|
13346
|
-
return
|
|
14255
|
+
return path25.resolve(stringOption(options.home) || ctx.env.HOME || os11.homedir());
|
|
13347
14256
|
}
|
|
13348
14257
|
function requestedTargets(options) {
|
|
13349
14258
|
const value = options.target || options.targets;
|
|
@@ -13514,6 +14423,7 @@ function helpText() {
|
|
|
13514
14423
|
Usage:
|
|
13515
14424
|
vibetime detect [--json] [--home <path>]
|
|
13516
14425
|
vibetime install [--target codex,claude,opencode,pi] [--all] [--dry-run] [--force] [--home <path>]
|
|
14426
|
+
vibetime uninstall [--target codex,claude,opencode,pi] [--all] [--dry-run] [--home <path>]
|
|
13517
14427
|
vibetime upgrade [--check]
|
|
13518
14428
|
vibetime hook --agent <name>
|
|
13519
14429
|
vibetime backfill discover|plan|import|verify --source codex|claude-code|opencode|pi|all --dry-run [--json] [--batch-size <count>] [--force [--purge-all]]
|
|
@@ -13528,6 +14438,7 @@ Setup:
|
|
|
13528
14438
|
Commands:
|
|
13529
14439
|
detect Show supported local targets and install status.
|
|
13530
14440
|
install Install integration files into detected or requested targets.
|
|
14441
|
+
uninstall Remove vibetime hooks/files from detected or requested targets.
|
|
13531
14442
|
upgrade Check for updates and upgrade to the latest version.
|
|
13532
14443
|
hook Read agent hook JSON from stdin and report a throttled event.
|
|
13533
14444
|
backfill Discover local history and create metadata-only import plans.
|