@yhong91/vibetime 0.1.40 → 0.1.42
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 +167 -19
- package/package.json +1 -1
package/bin/vibetime.mjs
CHANGED
|
@@ -1928,7 +1928,7 @@ function countTextLines(text) {
|
|
|
1928
1928
|
}
|
|
1929
1929
|
|
|
1930
1930
|
// src/lib/constants.ts
|
|
1931
|
-
var PACKAGE_VERSION = true ? "0.1.
|
|
1931
|
+
var PACKAGE_VERSION = true ? "0.1.42" : "0.1.1";
|
|
1932
1932
|
var DEFAULT_API_URL = "http://121.196.224.82:3001";
|
|
1933
1933
|
var DEFAULT_BACKFILL_BATCH_SIZE = 50;
|
|
1934
1934
|
var DEFAULT_BACKFILL_BATCH_BYTES = 800 * 1024;
|
|
@@ -3308,7 +3308,11 @@ async function isHooksJsonInstalled(filePath, command) {
|
|
|
3308
3308
|
}
|
|
3309
3309
|
|
|
3310
3310
|
// src/adapters/claude-code.ts
|
|
3311
|
+
init_fs();
|
|
3311
3312
|
async function parseClaudeCodeSessionFile(filePath, options) {
|
|
3313
|
+
if (path7.basename(filePath) === "usage.jsonl" && path7.basename(path7.dirname(filePath)) === ".viberouter") {
|
|
3314
|
+
return parseClaudeRouterUsageFile(filePath, options);
|
|
3315
|
+
}
|
|
3312
3316
|
const text = await readFile3(filePath, "utf8");
|
|
3313
3317
|
const lines = text.split("\n").filter(Boolean);
|
|
3314
3318
|
const projectContext = await claudeProjectContextFromLines(filePath, lines, options);
|
|
@@ -3575,6 +3579,86 @@ async function parseClaudeCodeSessionFile(filePath, options) {
|
|
|
3575
3579
|
}
|
|
3576
3580
|
return state.events.filter((event) => validateCanonicalEvent(event).valid);
|
|
3577
3581
|
}
|
|
3582
|
+
async function parseClaudeRouterUsageFile(filePath, options) {
|
|
3583
|
+
const rows = (await readFile3(filePath, "utf8")).split("\n").map(parseJsonLine).filter((row) => Boolean(
|
|
3584
|
+
row && row.surface === "claude" && (numberField(row, "timestamp") ?? 0) > 0 && Object.keys(objectField(row, "usage")).length > 0
|
|
3585
|
+
));
|
|
3586
|
+
if (rows.length === 0) {
|
|
3587
|
+
return [];
|
|
3588
|
+
}
|
|
3589
|
+
const timestamps = rows.map((row) => numberField(row, "timestamp") ?? 0);
|
|
3590
|
+
const firstTs = Math.min(...timestamps) - 6e4;
|
|
3591
|
+
const lastTs = Math.max(...timestamps) + 6e4;
|
|
3592
|
+
const home = path7.resolve(stringOption(options.home) || path7.dirname(path7.dirname(filePath)));
|
|
3593
|
+
const transcriptCounts = /* @__PURE__ */ new Map();
|
|
3594
|
+
for (const transcript of await listJsonlFiles(path7.join(claudeConfigDir(home, process.env), "projects"))) {
|
|
3595
|
+
const seen = /* @__PURE__ */ new Set();
|
|
3596
|
+
for (const line of (await readFile3(transcript, "utf8")).split("\n")) {
|
|
3597
|
+
const raw = parseJsonLine(line);
|
|
3598
|
+
const ts = raw ? Date.parse(timestampFrom(raw.timestamp) || "") : Number.NaN;
|
|
3599
|
+
if (!raw || stringField(raw, "type") !== "assistant" || ts < firstTs || ts > lastTs) {
|
|
3600
|
+
continue;
|
|
3601
|
+
}
|
|
3602
|
+
const message = objectField(raw, "message");
|
|
3603
|
+
const messageId = stringField(message, "id");
|
|
3604
|
+
const usageKey = messageId ? `${messageId}:${stringField(raw, "requestId")}` : null;
|
|
3605
|
+
if (usageKey && seen.has(usageKey)) {
|
|
3606
|
+
continue;
|
|
3607
|
+
}
|
|
3608
|
+
if (usageKey) {
|
|
3609
|
+
seen.add(usageKey);
|
|
3610
|
+
}
|
|
3611
|
+
const usage = claudeUsageFromMessage(message);
|
|
3612
|
+
if (!usage) {
|
|
3613
|
+
continue;
|
|
3614
|
+
}
|
|
3615
|
+
const key = `${usage.tokensInput || 0}:${usage.tokensOutput || 0}`;
|
|
3616
|
+
transcriptCounts.set(key, (transcriptCounts.get(key) || 0) + 1);
|
|
3617
|
+
}
|
|
3618
|
+
}
|
|
3619
|
+
const sourcePathHash = `sha256:${createStableHash(filePath)}`;
|
|
3620
|
+
const events = [];
|
|
3621
|
+
for (const row of rows) {
|
|
3622
|
+
const usage = objectField(row, "usage");
|
|
3623
|
+
const input = numberField(usage, "inputTokens") || 0;
|
|
3624
|
+
const output = numberField(usage, "outputTokens") || 0;
|
|
3625
|
+
const key = `${input}:${output}`;
|
|
3626
|
+
const matched = transcriptCounts.get(key) || 0;
|
|
3627
|
+
if (matched > 0) {
|
|
3628
|
+
transcriptCounts.set(key, matched - 1);
|
|
3629
|
+
continue;
|
|
3630
|
+
}
|
|
3631
|
+
const timestamp = numberField(row, "timestamp") ?? 0;
|
|
3632
|
+
const requestId = stringField(row, "requestId") || createStableHash(row).slice(0, 24);
|
|
3633
|
+
const cacheCreation = numberField(usage, "cacheCreationInputTokens") || 0;
|
|
3634
|
+
const cacheRead = numberField(usage, "cacheReadInputTokens") || 0;
|
|
3635
|
+
const cached = numberField(usage, "cachedInputTokens") || cacheCreation + cacheRead;
|
|
3636
|
+
const event = baseClaudeEvent({
|
|
3637
|
+
ts: new Date(timestamp).toISOString(),
|
|
3638
|
+
type: "model.usage",
|
|
3639
|
+
sessionId: `viberouter:${new Date(timestamp).toISOString().slice(0, 10)}`,
|
|
3640
|
+
project: "unknown",
|
|
3641
|
+
model: stringField(row, "resolvedModel") || stringField(row, "requestedModel") || stringField(row, "model"),
|
|
3642
|
+
provider: stringField(row, "provider"),
|
|
3643
|
+
confidence: "exact",
|
|
3644
|
+
metrics: {
|
|
3645
|
+
tokensInput: input || void 0,
|
|
3646
|
+
tokensCachedInput: cached || void 0,
|
|
3647
|
+
tokensCacheCreationInput: cacheCreation || void 0,
|
|
3648
|
+
tokensCacheReadInput: cacheRead || void 0,
|
|
3649
|
+
tokensOutput: output || void 0,
|
|
3650
|
+
tokensReasoningOutput: numberField(usage, "reasoningOutputTokens") || void 0,
|
|
3651
|
+
tokensTotal: numberField(usage, "totalTokens") || input + output || void 0,
|
|
3652
|
+
modelCalls: 1
|
|
3653
|
+
},
|
|
3654
|
+
refs: stringRefs({ sourceId: requestId, sourcePathHash, importKey: `claude-code:viberouter:${requestId}` })
|
|
3655
|
+
});
|
|
3656
|
+
if (validateCanonicalEvent(event).valid) {
|
|
3657
|
+
events.push(event);
|
|
3658
|
+
}
|
|
3659
|
+
}
|
|
3660
|
+
return events;
|
|
3661
|
+
}
|
|
3578
3662
|
function baseClaudeEvent(event) {
|
|
3579
3663
|
return {
|
|
3580
3664
|
schemaVersion: AGENT_TIME_SCHEMA_VERSION,
|
|
@@ -3911,6 +3995,7 @@ function createClaudeCodeAdapter() {
|
|
|
3911
3995
|
const base = claudeConfigDir(home, env);
|
|
3912
3996
|
return [
|
|
3913
3997
|
path7.join(base, "projects"),
|
|
3998
|
+
path7.join(home, ".viberouter", "usage.jsonl"),
|
|
3914
3999
|
path7.join(base, ".claude.json"),
|
|
3915
4000
|
path7.join(home, ".claude.json")
|
|
3916
4001
|
];
|
|
@@ -7491,6 +7576,11 @@ function createPiAdapter() {
|
|
|
7491
7576
|
};
|
|
7492
7577
|
}
|
|
7493
7578
|
|
|
7579
|
+
// src/adapters/qoder-cn.ts
|
|
7580
|
+
import { readdir as readdir7, readFile as readFile10, stat as stat8 } from "node:fs/promises";
|
|
7581
|
+
import os8 from "node:os";
|
|
7582
|
+
import path17 from "node:path";
|
|
7583
|
+
|
|
7494
7584
|
// src/adapters/qoder-local-db.ts
|
|
7495
7585
|
import { access } from "node:fs/promises";
|
|
7496
7586
|
import os7 from "node:os";
|
|
@@ -7537,6 +7627,7 @@ async function loadQoderDbModelCalls(appDirName, sessionId, modelMap) {
|
|
|
7537
7627
|
}
|
|
7538
7628
|
const db = new DatabaseSync(dbPath, { readOnly: true });
|
|
7539
7629
|
try {
|
|
7630
|
+
calls.rootSessionId = resolveRootSessionId(db, sessionId);
|
|
7540
7631
|
const preferredModel = resolveSessionPreferredModel(db, sessionId, modelMap);
|
|
7541
7632
|
const rows = db.prepare(
|
|
7542
7633
|
`select request_id, token_info, model_info from chat_message where session_id = ? and role = 'assistant' and token_info != '' order by gmt_create asc`
|
|
@@ -7570,6 +7661,18 @@ async function loadQoderDbModelCalls(appDirName, sessionId, modelMap) {
|
|
|
7570
7661
|
}
|
|
7571
7662
|
return calls;
|
|
7572
7663
|
}
|
|
7664
|
+
function resolveRootSessionId(db, sessionId) {
|
|
7665
|
+
let current = sessionId;
|
|
7666
|
+
for (let depth = 0; depth < 5; depth += 1) {
|
|
7667
|
+
const row = db.prepare("select parent_session_id from chat_session where session_id = ?").get(current);
|
|
7668
|
+
const parent = row ? stringField(row, "parent_session_id") : void 0;
|
|
7669
|
+
if (!parent) {
|
|
7670
|
+
return current === sessionId ? void 0 : current;
|
|
7671
|
+
}
|
|
7672
|
+
current = parent;
|
|
7673
|
+
}
|
|
7674
|
+
return current === sessionId ? void 0 : current;
|
|
7675
|
+
}
|
|
7573
7676
|
function resolveSessionPreferredModel(db, sessionId, modelMap) {
|
|
7574
7677
|
let current = sessionId;
|
|
7575
7678
|
for (let depth = 0; depth < 5 && current; depth++) {
|
|
@@ -7595,9 +7698,6 @@ function resolveSessionPreferredModel(db, sessionId, modelMap) {
|
|
|
7595
7698
|
}
|
|
7596
7699
|
|
|
7597
7700
|
// src/adapters/qoder-cn.ts
|
|
7598
|
-
import { readdir as readdir7, readFile as readFile10, stat as stat8 } from "node:fs/promises";
|
|
7599
|
-
import os8 from "node:os";
|
|
7600
|
-
import path17 from "node:path";
|
|
7601
7701
|
function parseQoderCnPaths(filePath) {
|
|
7602
7702
|
const parts = filePath.split(path17.sep);
|
|
7603
7703
|
const subagentsIdx = parts.lastIndexOf("subagents");
|
|
@@ -7715,7 +7815,8 @@ async function loadQoderCnSegmentModelCalls(filePath, isSubagentSession, modelMa
|
|
|
7715
7815
|
async function parseQoderCnSessionFile(filePath, options) {
|
|
7716
7816
|
const text = await readFile10(filePath, "utf8");
|
|
7717
7817
|
const lines = text.split("\n").filter(Boolean);
|
|
7718
|
-
const
|
|
7818
|
+
const parsedPaths = parseQoderCnPaths(filePath);
|
|
7819
|
+
const { configDir: configDir2 } = parsedPaths;
|
|
7719
7820
|
const projectContext = await qoderCnProjectContextFromLines(filePath, lines, options, configDir2);
|
|
7720
7821
|
const pendingTools = /* @__PURE__ */ new Map();
|
|
7721
7822
|
const seenUsageKeys = /* @__PURE__ */ new Set();
|
|
@@ -8043,6 +8144,16 @@ async function parseQoderCnSessionFile(filePath, options) {
|
|
|
8043
8144
|
});
|
|
8044
8145
|
}
|
|
8045
8146
|
}
|
|
8147
|
+
dbModelCalls ??= await loadQoderDbModelCalls("QoderCN", parsedPaths.sessionId, modelMap);
|
|
8148
|
+
if (dbModelCalls.rootSessionId) {
|
|
8149
|
+
const parentPath = path17.join(path17.dirname(filePath), `${dbModelCalls.rootSessionId}.jsonl`);
|
|
8150
|
+
const parentSourcePathHash = `sha256:${createStableHash(parentPath)}`;
|
|
8151
|
+
return validEvents.map((event) => rebuildEventIdentity2({
|
|
8152
|
+
...event,
|
|
8153
|
+
sessionId: dbModelCalls.rootSessionId,
|
|
8154
|
+
refs: { ...event.refs, sourcePathHash: parentSourcePathHash }
|
|
8155
|
+
}));
|
|
8156
|
+
}
|
|
8046
8157
|
return validEvents;
|
|
8047
8158
|
}
|
|
8048
8159
|
function baseQoderCnEvent(event) {
|
|
@@ -8530,7 +8641,8 @@ async function loadQoderSegmentModelCalls(filePath, isSubagentSession, modelMap)
|
|
|
8530
8641
|
async function parseQoderSessionFile(filePath, options) {
|
|
8531
8642
|
const text = await readFile11(filePath, "utf8");
|
|
8532
8643
|
const lines = text.split("\n").filter(Boolean);
|
|
8533
|
-
const
|
|
8644
|
+
const parsedPaths = parseQoderPaths(filePath);
|
|
8645
|
+
const { configDir: configDir2 } = parsedPaths;
|
|
8534
8646
|
const projectContext = await qoderProjectContextFromLines(filePath, lines, options, configDir2);
|
|
8535
8647
|
const pendingTools = /* @__PURE__ */ new Map();
|
|
8536
8648
|
const seenUsageKeys = /* @__PURE__ */ new Set();
|
|
@@ -8858,6 +8970,16 @@ async function parseQoderSessionFile(filePath, options) {
|
|
|
8858
8970
|
});
|
|
8859
8971
|
}
|
|
8860
8972
|
}
|
|
8973
|
+
dbModelCalls ??= await loadQoderDbModelCalls("Qoder", parsedPaths.sessionId, modelMap);
|
|
8974
|
+
if (dbModelCalls.rootSessionId) {
|
|
8975
|
+
const parentPath = path18.join(path18.dirname(filePath), `${dbModelCalls.rootSessionId}.jsonl`);
|
|
8976
|
+
const parentSourcePathHash = `sha256:${createStableHash(parentPath)}`;
|
|
8977
|
+
return validEvents.map((event) => rebuildEventIdentity3({
|
|
8978
|
+
...event,
|
|
8979
|
+
sessionId: dbModelCalls.rootSessionId,
|
|
8980
|
+
refs: { ...event.refs, sourcePathHash: parentSourcePathHash }
|
|
8981
|
+
}));
|
|
8982
|
+
}
|
|
8861
8983
|
return validEvents;
|
|
8862
8984
|
}
|
|
8863
8985
|
function baseQoderEvent(event) {
|
|
@@ -9893,7 +10015,7 @@ function modelMetrics(row) {
|
|
|
9893
10015
|
toolCalls: numberField(row, "tool_call_count")
|
|
9894
10016
|
};
|
|
9895
10017
|
}
|
|
9896
|
-
function
|
|
10018
|
+
function resolveRootSessionId2(sessionId, sessions) {
|
|
9897
10019
|
let current = sessionId;
|
|
9898
10020
|
const visited = /* @__PURE__ */ new Set();
|
|
9899
10021
|
while (true) {
|
|
@@ -9915,7 +10037,7 @@ function sessionContext(row, sessions) {
|
|
|
9915
10037
|
const session = sessions.get(sessionId);
|
|
9916
10038
|
const cwd = stringField(session, "directory");
|
|
9917
10039
|
const project = projectFromDirectory(cwd);
|
|
9918
|
-
const rootSessionId =
|
|
10040
|
+
const rootSessionId = resolveRootSessionId2(sessionId, sessions);
|
|
9919
10041
|
return {
|
|
9920
10042
|
rawSessionId: sessionId,
|
|
9921
10043
|
sessionId: `zcode:${rootSessionId}`,
|
|
@@ -11275,7 +11397,11 @@ async function postRollupBatch(remote, rollups, options = {}) {
|
|
|
11275
11397
|
const response = await remote.fetchImpl(joinUrl(remote.baseUrl, "/v3/agent/ingest"), {
|
|
11276
11398
|
method: "POST",
|
|
11277
11399
|
headers: buildHeaders(remote.token, options.machine),
|
|
11278
|
-
body: JSON.stringify({
|
|
11400
|
+
body: JSON.stringify({
|
|
11401
|
+
rollups,
|
|
11402
|
+
replace: options.replace !== false,
|
|
11403
|
+
allowHistoricalRewrite: options.allowHistoricalRewrite === true
|
|
11404
|
+
})
|
|
11279
11405
|
});
|
|
11280
11406
|
if (!response.ok) {
|
|
11281
11407
|
const body = await response.text();
|
|
@@ -11289,9 +11415,10 @@ async function postRollupBatch(remote, rollups, options = {}) {
|
|
|
11289
11415
|
failed: 0
|
|
11290
11416
|
};
|
|
11291
11417
|
}
|
|
11292
|
-
async function deleteRollupsBySource(remote, source, machine) {
|
|
11418
|
+
async function deleteRollupsBySource(remote, source, machine, options = {}) {
|
|
11419
|
+
const query = `source=${encodeURIComponent(source)}${options.preserveTokens ? "&preserveTokens=1" : ""}${options.allowHistoricalRewrite ? "&allowHistoricalRewrite=1" : ""}`;
|
|
11293
11420
|
const response = await remote.fetchImpl(
|
|
11294
|
-
joinUrl(remote.baseUrl, `/v3/agent/sessions
|
|
11421
|
+
joinUrl(remote.baseUrl, `/v3/agent/sessions?${query}`),
|
|
11295
11422
|
{ method: "DELETE", headers: buildHeaders(remote.token, machine) }
|
|
11296
11423
|
);
|
|
11297
11424
|
if (!response.ok) {
|
|
@@ -11337,6 +11464,7 @@ async function deleteMachine(remote, id) {
|
|
|
11337
11464
|
var BACKFILL_STATE_SCHEMA_VERSION = 6;
|
|
11338
11465
|
|
|
11339
11466
|
// src/cli.ts
|
|
11467
|
+
var SESSION_REWRITE_DAYS = 7;
|
|
11340
11468
|
function createRegistry() {
|
|
11341
11469
|
const registry = new AdapterRegistry();
|
|
11342
11470
|
registry.register(createCodexAdapter());
|
|
@@ -11414,7 +11542,7 @@ function createCli(ctx, registry) {
|
|
|
11414
11542
|
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));
|
|
11415
11543
|
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));
|
|
11416
11544
|
cli.command("sync-local-runner", "Internal background local sync runner").option("--lock-file <path>", "Lock file for the active sync").option("--state-file <path>", "State file for trigger metadata").action((options) => syncLocalRunnerCommand(normalizeOptions(options), ctx, registry));
|
|
11417
|
-
cli.command("backfill [action]", "Inspect local history import candidates").option("--source <source>", "Backfill source").option("--since <time>", "Only include history after this time").option("--until <time>", "Only include history before this time").option("--project <name>", "Project filter").option("--source-root <path>", "Override source history root").option("--include-source-path", "Include local source paths in output").option("--import-run <id>", "Import run id for verify/resume workflows").option("--limit <count>", "Maximum session files to parse").option("--batch-size <count>", "Max rollups per request (also bounded by --batch-bytes)").option("--batch-bytes <bytes>", "Soft byte cap for the JSON body of a single ingest POST").option("--replace", "Replace conflicting records during import (default)").option("--skip-conflicts", "Skip conflicting records instead of replacing them").option("--force", "Force
|
|
11545
|
+
cli.command("backfill [action]", "Inspect local history import candidates").option("--source <source>", "Backfill source").option("--since <time>", "Only include history after this time").option("--until <time>", "Only include history before this time").option("--project <name>", "Project filter").option("--source-root <path>", "Override source history root").option("--include-source-path", "Include local source paths in output").option("--import-run <id>", "Import run id for verify/resume workflows").option("--limit <count>", "Maximum session files to parse").option("--batch-size <count>", "Max rollups per request (also bounded by --batch-bytes)").option("--batch-bytes <bytes>", "Soft byte cap for the JSON body of a single ingest POST").option("--replace", "Replace conflicting records during import (default)").option("--skip-conflicts", "Skip conflicting records instead of replacing them").option("--force", "Force re-import of sessions active within the last 7 days").option("--purge-all", "With --force: unlock and replace history older than 7 days (DANGEROUS)").action((action, options) => backfillCommand({ ...normalizeOptions(options), action }, ctx, registry));
|
|
11418
11546
|
cli.command("token [action] [value]", "Set, show, or clear the persisted API token").option("--remote <url>", "Override API base URL when setting a token").action((action, value, options) => tokenCommand(action, value, normalizeOptions(options), ctx));
|
|
11419
11547
|
cli.command("machine [action]", "List or rename machines (requires login)").option("--name <name>", "New display name (used by `machine rename`)").option("--id <id>", "Machine id (defaults to current machine)").action((action, options) => machineCommand(action, normalizeOptions(options), ctx));
|
|
11420
11548
|
return cli;
|
|
@@ -11433,7 +11561,8 @@ function normalizeOptions(options) {
|
|
|
11433
11561
|
importRun: "import-run",
|
|
11434
11562
|
batchSize: "batch-size",
|
|
11435
11563
|
batchBytes: "batch-bytes",
|
|
11436
|
-
skipConflicts: "skip-conflicts"
|
|
11564
|
+
skipConflicts: "skip-conflicts",
|
|
11565
|
+
purgeAll: "purge-all"
|
|
11437
11566
|
};
|
|
11438
11567
|
for (const [camel, dashed] of Object.entries(aliases)) {
|
|
11439
11568
|
if (normalized[camel] !== void 0 && normalized[dashed] === void 0) {
|
|
@@ -11703,6 +11832,10 @@ async function backfillCommand(options, ctx, registry) {
|
|
|
11703
11832
|
if (action === "verify") {
|
|
11704
11833
|
return backfillVerifyCommand(options, ctx);
|
|
11705
11834
|
}
|
|
11835
|
+
if (options["purge-all"] && !options.force) {
|
|
11836
|
+
write(ctx.stderr, "--purge-all requires --force\n");
|
|
11837
|
+
return 1;
|
|
11838
|
+
}
|
|
11706
11839
|
if (action === "import" && !options["dry-run"]) {
|
|
11707
11840
|
const requested = normalizeBackfillSource(stringOption(options.source) || "all");
|
|
11708
11841
|
const supported = /* @__PURE__ */ new Set(["all", ...BACKFILL_SOURCE_IDS]);
|
|
@@ -11978,7 +12111,7 @@ async function importBackfillPlan(plan, options, ctx, registry) {
|
|
|
11978
12111
|
options,
|
|
11979
12112
|
ctx
|
|
11980
12113
|
);
|
|
11981
|
-
const rollups = buildSessionRollups(canonicalEvents);
|
|
12114
|
+
const rollups = selectRollupsForUpload(buildSessionRollups(canonicalEvents), options);
|
|
11982
12115
|
const counts = await uploadSessionRollups(rollups, canonicalEvents.length, options, ctx);
|
|
11983
12116
|
const result = {
|
|
11984
12117
|
importRunId: plan.importRun.importRunId,
|
|
@@ -12005,11 +12138,13 @@ async function purgeForcedSources(sourceDefs, home, remoteKey, options, ctx) {
|
|
|
12005
12138
|
debug(ctx, `Failed to clear backfill watermark: ${error.message}
|
|
12006
12139
|
`);
|
|
12007
12140
|
}
|
|
12141
|
+
const preserveTokens = !options["purge-all"];
|
|
12008
12142
|
for (const item of sourceDefs) {
|
|
12009
12143
|
try {
|
|
12010
|
-
const deleted = await deleteSessionRollupsBySourceAPI(item.id, options, ctx);
|
|
12144
|
+
const deleted = await deleteSessionRollupsBySourceAPI(item.id, options, ctx, preserveTokens);
|
|
12011
12145
|
if (!options.json) {
|
|
12012
|
-
|
|
12146
|
+
const suffix = preserveTokens ? " (token-bearing and sessions older than 7 days kept)" : "";
|
|
12147
|
+
write(ctx.stdout, `purged ${item.id}: ${deleted} old rollups${suffix}
|
|
12013
12148
|
`);
|
|
12014
12149
|
}
|
|
12015
12150
|
} catch (error) {
|
|
@@ -12135,11 +12270,12 @@ async function sendSessionRollupBatch(rollups, options, ctx) {
|
|
|
12135
12270
|
};
|
|
12136
12271
|
const result = await postRollupBatch(remote, rollups, {
|
|
12137
12272
|
replace: options["skip-conflicts"] !== true,
|
|
12138
|
-
machine
|
|
12273
|
+
machine,
|
|
12274
|
+
allowHistoricalRewrite: options["purge-all"] === true
|
|
12139
12275
|
});
|
|
12140
12276
|
return result;
|
|
12141
12277
|
}
|
|
12142
|
-
async function deleteSessionRollupsBySourceAPI(source, options, ctx) {
|
|
12278
|
+
async function deleteSessionRollupsBySourceAPI(source, options, ctx, preserveTokens) {
|
|
12143
12279
|
const remote = resolveRemoteFromOptions(options, ctx);
|
|
12144
12280
|
if (!remote) {
|
|
12145
12281
|
throw new Error("No fetch available for HTTP delete");
|
|
@@ -12149,8 +12285,18 @@ async function deleteSessionRollupsBySourceAPI(source, options, ctx) {
|
|
|
12149
12285
|
id: ensureLocalMachineId(home),
|
|
12150
12286
|
hostname: defaultMachineName(),
|
|
12151
12287
|
platform: process.platform
|
|
12288
|
+
}, {
|
|
12289
|
+
preserveTokens,
|
|
12290
|
+
allowHistoricalRewrite: options["purge-all"] === true
|
|
12152
12291
|
});
|
|
12153
12292
|
}
|
|
12293
|
+
function selectRollupsForUpload(rollups, options, now = /* @__PURE__ */ new Date()) {
|
|
12294
|
+
if (!options.force || options["purge-all"]) {
|
|
12295
|
+
return rollups;
|
|
12296
|
+
}
|
|
12297
|
+
const cutoff = now.getTime() - SESSION_REWRITE_DAYS * 24 * 60 * 60 * 1e3;
|
|
12298
|
+
return rollups.filter((rollup) => Date.parse(rollup.lastEventAt) >= cutoff);
|
|
12299
|
+
}
|
|
12154
12300
|
function shouldUseIncrementalBackfill(options) {
|
|
12155
12301
|
return !stringOption(options.since) && !stringOption(options.until) && !stringOption(options["source-root"]) && numberOption(options.limit) === void 0;
|
|
12156
12302
|
}
|
|
@@ -12608,7 +12754,7 @@ Usage:
|
|
|
12608
12754
|
vibetime install [--target codex,claude,opencode,pi] [--all] [--dry-run] [--force] [--home <path>]
|
|
12609
12755
|
vibetime upgrade [--check]
|
|
12610
12756
|
vibetime hook --agent <name>
|
|
12611
|
-
vibetime backfill discover|plan|import|verify --source codex|claude-code|opencode|pi|all --dry-run [--json] [--batch-size <count>]
|
|
12757
|
+
vibetime backfill discover|plan|import|verify --source codex|claude-code|opencode|pi|all --dry-run [--json] [--batch-size <count>] [--force [--purge-all]]
|
|
12612
12758
|
vibetime token set <token>
|
|
12613
12759
|
vibetime token show
|
|
12614
12760
|
vibetime token clear
|
|
@@ -12641,8 +12787,10 @@ Environment:
|
|
|
12641
12787
|
`;
|
|
12642
12788
|
}
|
|
12643
12789
|
export {
|
|
12790
|
+
SESSION_REWRITE_DAYS,
|
|
12644
12791
|
run,
|
|
12645
12792
|
selectBackfillFilesForImport,
|
|
12793
|
+
selectRollupsForUpload,
|
|
12646
12794
|
syncLocalRunnerEntryArgs
|
|
12647
12795
|
};
|
|
12648
12796
|
const code = await run(process.argv.slice(2));process.exitCode = code;
|
package/package.json
CHANGED