@dosu/cli 0.53.0-beta.4 → 0.53.0-beta.6
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/dosu.js +433 -306
- package/package.json +1 -1
package/bin/dosu.js
CHANGED
|
@@ -5006,7 +5006,7 @@ function isNpxInvocation(channel = INSTALL_CHANNEL, env = process.env) {
|
|
|
5006
5006
|
function getVersionString() {
|
|
5007
5007
|
return `v${VERSION}`;
|
|
5008
5008
|
}
|
|
5009
|
-
var VERSION = "0.53.0-beta.
|
|
5009
|
+
var VERSION = "0.53.0-beta.6", INSTALL_CHANNEL = "npm";
|
|
5010
5010
|
|
|
5011
5011
|
// src/debug/logger.ts
|
|
5012
5012
|
import {
|
|
@@ -8346,258 +8346,25 @@ var init_agents = __esm(() => {
|
|
|
8346
8346
|
init_formats();
|
|
8347
8347
|
});
|
|
8348
8348
|
|
|
8349
|
-
// src/sync/lock.ts
|
|
8350
|
-
import { existsSync as existsSync9, readFileSync as readFileSync9, rmSync as rmSync2, statSync as statSync3, writeFileSync as writeFileSync6 } from "node:fs";
|
|
8351
|
-
import { join as join8 } from "node:path";
|
|
8352
|
-
function processAlive(pid) {
|
|
8353
|
-
try {
|
|
8354
|
-
process.kill(pid, 0);
|
|
8355
|
-
return true;
|
|
8356
|
-
} catch (err) {
|
|
8357
|
-
return err.code === "EPERM";
|
|
8358
|
-
}
|
|
8359
|
-
}
|
|
8360
|
-
function lockPath(configDir = getConfigDir()) {
|
|
8361
|
-
return join8(configDir, LOCK_FILENAME);
|
|
8362
|
-
}
|
|
8363
|
-
function fileLock(configDir = getConfigDir(), now = () => new Date) {
|
|
8364
|
-
const path3 = lockPath(configDir);
|
|
8365
|
-
let held = false;
|
|
8366
|
-
const tryCreate = () => {
|
|
8367
|
-
try {
|
|
8368
|
-
writeFileSync6(path3, String(process.pid), { flag: "wx" });
|
|
8369
|
-
return true;
|
|
8370
|
-
} catch {
|
|
8371
|
-
return false;
|
|
8372
|
-
}
|
|
8373
|
-
};
|
|
8374
|
-
return {
|
|
8375
|
-
acquire() {
|
|
8376
|
-
if (tryCreate()) {
|
|
8377
|
-
held = true;
|
|
8378
|
-
return true;
|
|
8379
|
-
}
|
|
8380
|
-
try {
|
|
8381
|
-
const age = now().getTime() - statSync3(path3).mtimeMs;
|
|
8382
|
-
const holder = Number.parseInt(readFileSync9(path3, "utf8"), 10);
|
|
8383
|
-
const holderDead = Number.isInteger(holder) && holder > 0 && !processAlive(holder);
|
|
8384
|
-
if (age > STALE_LOCK_MS || holderDead) {
|
|
8385
|
-
rmSync2(path3, { force: true });
|
|
8386
|
-
held = tryCreate();
|
|
8387
|
-
return held;
|
|
8388
|
-
}
|
|
8389
|
-
} catch {}
|
|
8390
|
-
return false;
|
|
8391
|
-
},
|
|
8392
|
-
release() {
|
|
8393
|
-
if (!held)
|
|
8394
|
-
return;
|
|
8395
|
-
held = false;
|
|
8396
|
-
try {
|
|
8397
|
-
if (existsSync9(path3) && readFileSync9(path3, "utf8") === String(process.pid)) {
|
|
8398
|
-
rmSync2(path3, { force: true });
|
|
8399
|
-
}
|
|
8400
|
-
} catch {}
|
|
8401
|
-
}
|
|
8402
|
-
};
|
|
8403
|
-
}
|
|
8404
|
-
var LOCK_FILENAME = "knowledge-sync.lock", STALE_LOCK_MS;
|
|
8405
|
-
var init_lock = __esm(() => {
|
|
8406
|
-
init_config();
|
|
8407
|
-
STALE_LOCK_MS = 15 * 60 * 1000;
|
|
8408
|
-
});
|
|
8409
|
-
|
|
8410
|
-
// src/sync/watermark.ts
|
|
8411
|
-
import { existsSync as existsSync10, mkdirSync as mkdirSync6, readFileSync as readFileSync10, renameSync as renameSync2, writeFileSync as writeFileSync7 } from "node:fs";
|
|
8412
|
-
import { join as join9 } from "node:path";
|
|
8413
|
-
function syncStatePath(configDir = getConfigDir()) {
|
|
8414
|
-
return join9(configDir, STATE_FILENAME);
|
|
8415
|
-
}
|
|
8416
|
-
function loadSyncState(configDir = getConfigDir()) {
|
|
8417
|
-
const empty = {
|
|
8418
|
-
schema_version: STATE_SCHEMA_VERSION,
|
|
8419
|
-
watermark: null,
|
|
8420
|
-
consecutive_failures: 0
|
|
8421
|
-
};
|
|
8422
|
-
const path3 = syncStatePath(configDir);
|
|
8423
|
-
if (!existsSync10(path3))
|
|
8424
|
-
return empty;
|
|
8425
|
-
try {
|
|
8426
|
-
const raw = JSON.parse(readFileSync10(path3, "utf-8"));
|
|
8427
|
-
if (raw.schema_version !== STATE_SCHEMA_VERSION)
|
|
8428
|
-
return empty;
|
|
8429
|
-
const minedSessions = Array.isArray(raw.mined_sessions) ? raw.mined_sessions.filter((record) => typeof record === "object" && record !== null && typeof record.at === "string" && typeof record.session === "string").map((record) => ({
|
|
8430
|
-
at: record.at,
|
|
8431
|
-
session: record.session,
|
|
8432
|
-
...typeof record.project === "string" ? { project: record.project } : {}
|
|
8433
|
-
})) : [];
|
|
8434
|
-
const rawRefusal = raw.last_refusal;
|
|
8435
|
-
const lastRefusal = rawRefusal && typeof rawRefusal.at === "string" && typeof rawRefusal.outcome === "string" && typeof rawRefusal.message === "string" ? { at: rawRefusal.at, outcome: rawRefusal.outcome, message: rawRefusal.message } : undefined;
|
|
8436
|
-
const rawRun = raw.run;
|
|
8437
|
-
const run2 = rawRun && typeof rawRun.pid === "number" && typeof rawRun.started_at === "string" && typeof rawRun.baseline_mined === "number" && rawRun.baseline_mined >= 0 ? { pid: rawRun.pid, started_at: rawRun.started_at, baseline_mined: rawRun.baseline_mined } : undefined;
|
|
8438
|
-
return {
|
|
8439
|
-
schema_version: STATE_SCHEMA_VERSION,
|
|
8440
|
-
watermark: typeof raw.watermark === "string" ? raw.watermark : null,
|
|
8441
|
-
last_attempt_at: typeof raw.last_attempt_at === "string" ? raw.last_attempt_at : undefined,
|
|
8442
|
-
consecutive_failures: typeof raw.consecutive_failures === "number" && raw.consecutive_failures >= 0 ? raw.consecutive_failures : 0,
|
|
8443
|
-
mined_sessions: minedSessions,
|
|
8444
|
-
total_mined: typeof raw.total_mined === "number" && raw.total_mined >= 0 ? raw.total_mined : minedSessions.length,
|
|
8445
|
-
total_notes: typeof raw.total_notes === "number" && raw.total_notes >= 0 ? raw.total_notes : 0,
|
|
8446
|
-
total_learning_tokens: typeof raw.total_learning_tokens === "number" && raw.total_learning_tokens >= 0 ? raw.total_learning_tokens : 0,
|
|
8447
|
-
...lastRefusal ? { last_refusal: lastRefusal } : {},
|
|
8448
|
-
...run2 ? { run: run2 } : {},
|
|
8449
|
-
...Array.isArray(raw.project_filter) ? {
|
|
8450
|
-
project_filter: raw.project_filter.filter((p2) => typeof p2 === "string")
|
|
8451
|
-
} : {}
|
|
8452
|
-
};
|
|
8453
|
-
} catch {
|
|
8454
|
-
return empty;
|
|
8455
|
-
}
|
|
8456
|
-
}
|
|
8457
|
-
function saveSyncState(state, configDir = getConfigDir()) {
|
|
8458
|
-
if (!existsSync10(configDir))
|
|
8459
|
-
mkdirSync6(configDir, { recursive: true, mode: 448 });
|
|
8460
|
-
const path3 = syncStatePath(configDir);
|
|
8461
|
-
const tmp = `${path3}.${process.pid}.tmp`;
|
|
8462
|
-
writeFileSync7(tmp, JSON.stringify(state, null, 2), { mode: 384 });
|
|
8463
|
-
renameSync2(tmp, path3);
|
|
8464
|
-
}
|
|
8465
|
-
function backoffUntil(state) {
|
|
8466
|
-
if (state.consecutive_failures === 0 || !state.last_attempt_at)
|
|
8467
|
-
return null;
|
|
8468
|
-
const last = Date.parse(state.last_attempt_at);
|
|
8469
|
-
if (Number.isNaN(last))
|
|
8470
|
-
return null;
|
|
8471
|
-
const delay = Math.min(BACKOFF_BASE_MS * 2 ** (state.consecutive_failures - 1), BACKOFF_MAX_MS);
|
|
8472
|
-
return new Date(last + delay);
|
|
8473
|
-
}
|
|
8474
|
-
function isUnderDir(dir, base) {
|
|
8475
|
-
const root = base.endsWith("/") ? base.slice(0, -1) : base;
|
|
8476
|
-
return dir === root || dir.startsWith(`${root}/`);
|
|
8477
|
-
}
|
|
8478
|
-
function filterSessionsByProject(sessions, filter, resolveDir) {
|
|
8479
|
-
if (!filter || filter.length === 0)
|
|
8480
|
-
return [...sessions];
|
|
8481
|
-
const includeUnknown = filter.includes(UNKNOWN_PROJECT);
|
|
8482
|
-
const dirs = filter.filter((entry) => entry !== UNKNOWN_PROJECT);
|
|
8483
|
-
return sessions.filter((session) => {
|
|
8484
|
-
const dir = resolveDir(session);
|
|
8485
|
-
if (!dir)
|
|
8486
|
-
return includeUnknown;
|
|
8487
|
-
return dirs.some((base) => isUnderDir(dir, base));
|
|
8488
|
-
});
|
|
8489
|
-
}
|
|
8490
|
-
function gateSessions(sessions, watermark, now = new Date, quietPeriodMs = DEFAULT_QUIET_PERIOD_MS) {
|
|
8491
|
-
const watermarkMs = watermark ? Date.parse(watermark) : Number.NEGATIVE_INFINITY;
|
|
8492
|
-
const completedBefore = now.getTime() - quietPeriodMs;
|
|
8493
|
-
const ready = [];
|
|
8494
|
-
const open2 = [];
|
|
8495
|
-
for (const session of sessions) {
|
|
8496
|
-
const updated = Date.parse(session.updated);
|
|
8497
|
-
if (Number.isNaN(updated) || updated <= watermarkMs)
|
|
8498
|
-
continue;
|
|
8499
|
-
if (updated > completedBefore) {
|
|
8500
|
-
open2.push(session);
|
|
8501
|
-
} else {
|
|
8502
|
-
ready.push(session);
|
|
8503
|
-
}
|
|
8504
|
-
}
|
|
8505
|
-
return { ready, open: open2 };
|
|
8506
|
-
}
|
|
8507
|
-
var STATE_FILENAME = "knowledge-sync.json", STATE_SCHEMA_VERSION = 1, DEFAULT_QUIET_PERIOD_MS, BACKOFF_BASE_MS, BACKOFF_MAX_MS, MINED_HISTORY_LIMIT = 500, UNKNOWN_PROJECT = "(unknown)";
|
|
8508
|
-
var init_watermark = __esm(() => {
|
|
8509
|
-
init_config();
|
|
8510
|
-
DEFAULT_QUIET_PERIOD_MS = 5 * 60 * 1000;
|
|
8511
|
-
BACKOFF_BASE_MS = 15 * 60 * 1000;
|
|
8512
|
-
BACKOFF_MAX_MS = 24 * 60 * 60 * 1000;
|
|
8513
|
-
});
|
|
8514
|
-
|
|
8515
|
-
// src/sync/status.ts
|
|
8516
|
-
import { readFileSync as readFileSync11, statSync as statSync4 } from "node:fs";
|
|
8517
|
-
function formatTokenCount(tokens) {
|
|
8518
|
-
if (tokens < 1000)
|
|
8519
|
-
return String(tokens);
|
|
8520
|
-
if (tokens < 1e6) {
|
|
8521
|
-
const k = tokens / 1000;
|
|
8522
|
-
return `~${k >= 100 ? Math.round(k) : k.toFixed(1).replace(/\.0$/, "")}k`;
|
|
8523
|
-
}
|
|
8524
|
-
const m3 = tokens / 1e6;
|
|
8525
|
-
return `~${m3 >= 100 ? Math.round(m3) : m3.toFixed(1).replace(/\.0$/, "")}M`;
|
|
8526
|
-
}
|
|
8527
|
-
function defaultIsProcessAlive2(pid) {
|
|
8528
|
-
if (!Number.isInteger(pid) || pid <= 0)
|
|
8529
|
-
return false;
|
|
8530
|
-
try {
|
|
8531
|
-
process.kill(pid, 0);
|
|
8532
|
-
return true;
|
|
8533
|
-
} catch (err) {
|
|
8534
|
-
return err.code === "EPERM";
|
|
8535
|
-
}
|
|
8536
|
-
}
|
|
8537
|
-
function defaultReadLog() {
|
|
8538
|
-
try {
|
|
8539
|
-
return readFileSync11(logger.getLogPath(), "utf-8");
|
|
8540
|
-
} catch {
|
|
8541
|
-
return "";
|
|
8542
|
-
}
|
|
8543
|
-
}
|
|
8544
|
-
function readLockFile(configDir) {
|
|
8545
|
-
const path3 = lockPath(configDir);
|
|
8546
|
-
try {
|
|
8547
|
-
const pid = Number.parseInt(readFileSync11(path3, "utf-8"), 10);
|
|
8548
|
-
return { pid: Number.isNaN(pid) ? -1 : pid, mtime: statSync4(path3).mtime };
|
|
8549
|
-
} catch {
|
|
8550
|
-
return null;
|
|
8551
|
-
}
|
|
8552
|
-
}
|
|
8553
|
-
function recentSyncActivity(log3) {
|
|
8554
|
-
return log3.split(`
|
|
8555
|
-
`).filter((line) => line.includes("[sync]") || line.includes("[miner]")).slice(-ACTIVITY_LINES).map((line) => line.replace(/ \[(DEBUG|INFO|WARN|ERROR)\]/, ""));
|
|
8556
|
-
}
|
|
8557
|
-
function getSyncStatus(deps = {}) {
|
|
8558
|
-
const configDir = deps.configDir ?? getConfigDir();
|
|
8559
|
-
const isProcessAlive = deps.isProcessAlive ?? defaultIsProcessAlive2;
|
|
8560
|
-
const readLog = deps.readLog ?? defaultReadLog;
|
|
8561
|
-
const lock = readLockFile(configDir);
|
|
8562
|
-
const running = lock !== null && isProcessAlive(lock.pid);
|
|
8563
|
-
const state = loadSyncState(configDir);
|
|
8564
|
-
const retryAt = backoffUntil(state);
|
|
8565
|
-
return {
|
|
8566
|
-
running,
|
|
8567
|
-
...lock ? { pid: lock.pid, startedAt: lock.mtime.toISOString() } : {},
|
|
8568
|
-
...lock && !running ? { staleLock: true } : {},
|
|
8569
|
-
state,
|
|
8570
|
-
...retryAt ? { backoffUntil: retryAt.toISOString() } : {},
|
|
8571
|
-
recentActivity: recentSyncActivity(readLog())
|
|
8572
|
-
};
|
|
8573
|
-
}
|
|
8574
|
-
var ACTIVITY_LINES = 10;
|
|
8575
|
-
var init_status = __esm(() => {
|
|
8576
|
-
init_config();
|
|
8577
|
-
init_logger();
|
|
8578
|
-
init_lock();
|
|
8579
|
-
init_watermark();
|
|
8580
|
-
});
|
|
8581
|
-
|
|
8582
8349
|
// src/sessions/project-dir.ts
|
|
8583
8350
|
import {
|
|
8584
8351
|
closeSync as closeSync2,
|
|
8585
|
-
existsSync as
|
|
8586
|
-
mkdirSync as
|
|
8352
|
+
existsSync as existsSync9,
|
|
8353
|
+
mkdirSync as mkdirSync6,
|
|
8587
8354
|
openSync as openSync2,
|
|
8588
|
-
readFileSync as
|
|
8355
|
+
readFileSync as readFileSync9,
|
|
8589
8356
|
readSync,
|
|
8590
|
-
renameSync as
|
|
8591
|
-
statSync as
|
|
8592
|
-
writeFileSync as
|
|
8357
|
+
renameSync as renameSync2,
|
|
8358
|
+
statSync as statSync3,
|
|
8359
|
+
writeFileSync as writeFileSync6
|
|
8593
8360
|
} from "node:fs";
|
|
8594
|
-
import { join as
|
|
8361
|
+
import { join as join8 } from "node:path";
|
|
8595
8362
|
function cachePath(configDir) {
|
|
8596
|
-
return
|
|
8363
|
+
return join8(configDir, CACHE_FILENAME2);
|
|
8597
8364
|
}
|
|
8598
8365
|
function loadCacheFile(configDir) {
|
|
8599
8366
|
try {
|
|
8600
|
-
const raw = JSON.parse(
|
|
8367
|
+
const raw = JSON.parse(readFileSync9(cachePath(configDir), "utf-8"));
|
|
8601
8368
|
if (raw.schema_version !== CACHE_SCHEMA_VERSION || typeof raw.entries !== "object")
|
|
8602
8369
|
return {};
|
|
8603
8370
|
return raw.entries;
|
|
@@ -8607,13 +8374,13 @@ function loadCacheFile(configDir) {
|
|
|
8607
8374
|
}
|
|
8608
8375
|
function saveCacheFile(configDir, entries) {
|
|
8609
8376
|
try {
|
|
8610
|
-
if (!
|
|
8611
|
-
|
|
8377
|
+
if (!existsSync9(configDir))
|
|
8378
|
+
mkdirSync6(configDir, { recursive: true, mode: 448 });
|
|
8612
8379
|
const path3 = cachePath(configDir);
|
|
8613
8380
|
const tmp = `${path3}.${process.pid}.tmp`;
|
|
8614
8381
|
const file = { schema_version: CACHE_SCHEMA_VERSION, entries };
|
|
8615
|
-
|
|
8616
|
-
|
|
8382
|
+
writeFileSync6(tmp, JSON.stringify(file), { mode: 384 });
|
|
8383
|
+
renameSync2(tmp, path3);
|
|
8617
8384
|
} catch {}
|
|
8618
8385
|
}
|
|
8619
8386
|
function readHead(path3, bytes = HEAD_BYTES) {
|
|
@@ -8647,7 +8414,7 @@ function cwdFromJsonlHead(text) {
|
|
|
8647
8414
|
}
|
|
8648
8415
|
return null;
|
|
8649
8416
|
}
|
|
8650
|
-
function unmungeSlug(slug, exists =
|
|
8417
|
+
function unmungeSlug(slug, exists = existsSync9) {
|
|
8651
8418
|
const tokens = slug.replace(/^-/, "").split("-");
|
|
8652
8419
|
if (tokens.length === 0 || tokens[0] === "")
|
|
8653
8420
|
return null;
|
|
@@ -8666,13 +8433,13 @@ function unmungeSlug(slug, exists = existsSync11) {
|
|
|
8666
8433
|
}
|
|
8667
8434
|
function fileMtime(path3) {
|
|
8668
8435
|
try {
|
|
8669
|
-
return
|
|
8436
|
+
return statSync3(path3).mtime.toISOString();
|
|
8670
8437
|
} catch {
|
|
8671
8438
|
return "";
|
|
8672
8439
|
}
|
|
8673
8440
|
}
|
|
8674
8441
|
function createProjectDirResolver(configDir = getConfigDir(), deps = {}) {
|
|
8675
|
-
const exists = deps.exists ??
|
|
8442
|
+
const exists = deps.exists ?? existsSync9;
|
|
8676
8443
|
const head = deps.readHead ?? readHead;
|
|
8677
8444
|
const mtime = deps.mtime ?? fileMtime;
|
|
8678
8445
|
const entries = loadCacheFile(configDir);
|
|
@@ -8725,15 +8492,15 @@ var init_project_dir = __esm(() => {
|
|
|
8725
8492
|
});
|
|
8726
8493
|
|
|
8727
8494
|
// src/sessions/scan.ts
|
|
8728
|
-
import { existsSync as
|
|
8495
|
+
import { existsSync as existsSync10, readdirSync, statSync as statSync4 } from "node:fs";
|
|
8729
8496
|
import { createRequire as createRequire2 } from "node:module";
|
|
8730
8497
|
import { homedir as homedir3 } from "node:os";
|
|
8731
|
-
import { basename, join as
|
|
8498
|
+
import { basename, join as join9 } from "node:path";
|
|
8732
8499
|
function listDir(dir) {
|
|
8733
8500
|
try {
|
|
8734
8501
|
return readdirSync(dir, { withFileTypes: true }).map((entry) => ({
|
|
8735
8502
|
name: entry.name,
|
|
8736
|
-
path:
|
|
8503
|
+
path: join9(dir, entry.name),
|
|
8737
8504
|
isDir: entry.isDirectory()
|
|
8738
8505
|
}));
|
|
8739
8506
|
} catch {
|
|
@@ -8746,7 +8513,7 @@ function sessionFromFile(filePath, harness, project) {
|
|
|
8746
8513
|
return null;
|
|
8747
8514
|
let mtime;
|
|
8748
8515
|
try {
|
|
8749
|
-
mtime =
|
|
8516
|
+
mtime = statSync4(filePath).mtime;
|
|
8750
8517
|
} catch {
|
|
8751
8518
|
return null;
|
|
8752
8519
|
}
|
|
@@ -8760,7 +8527,7 @@ function sessionFromFile(filePath, harness, project) {
|
|
|
8760
8527
|
}
|
|
8761
8528
|
function scanClaude(home) {
|
|
8762
8529
|
const sessions = [];
|
|
8763
|
-
for (const project of listDir(
|
|
8530
|
+
for (const project of listDir(join9(home, ".claude", "projects"))) {
|
|
8764
8531
|
if (!project.isDir)
|
|
8765
8532
|
continue;
|
|
8766
8533
|
for (const entry of listDir(project.path)) {
|
|
@@ -8775,10 +8542,10 @@ function scanClaude(home) {
|
|
|
8775
8542
|
}
|
|
8776
8543
|
function scanCursor(home) {
|
|
8777
8544
|
const sessions = [];
|
|
8778
|
-
for (const project of listDir(
|
|
8545
|
+
for (const project of listDir(join9(home, ".cursor", "projects"))) {
|
|
8779
8546
|
if (!project.isDir)
|
|
8780
8547
|
continue;
|
|
8781
|
-
for (const entry of listDir(
|
|
8548
|
+
for (const entry of listDir(join9(project.path, "agent-transcripts"))) {
|
|
8782
8549
|
const files = entry.isDir ? listDir(entry.path).filter((f2) => !f2.isDir) : [entry];
|
|
8783
8550
|
for (const file of files) {
|
|
8784
8551
|
const session = sessionFromFile(file.path, "cursor", project.name);
|
|
@@ -8790,9 +8557,9 @@ function scanCursor(home) {
|
|
|
8790
8557
|
return sessions;
|
|
8791
8558
|
}
|
|
8792
8559
|
function scanCodex(home, env) {
|
|
8793
|
-
const codexHome2 = env.CODEX_HOME ??
|
|
8560
|
+
const codexHome2 = env.CODEX_HOME ?? join9(home, ".codex");
|
|
8794
8561
|
const sessions = [];
|
|
8795
|
-
for (const year of listDir(
|
|
8562
|
+
for (const year of listDir(join9(codexHome2, "sessions"))) {
|
|
8796
8563
|
if (!year.isDir)
|
|
8797
8564
|
continue;
|
|
8798
8565
|
for (const month of listDir(year.path)) {
|
|
@@ -8848,9 +8615,9 @@ function querySqlite(dbPath, sql) {
|
|
|
8848
8615
|
}
|
|
8849
8616
|
}
|
|
8850
8617
|
function scanOpencode(home, env) {
|
|
8851
|
-
const dataDir = env.XDG_DATA_HOME ??
|
|
8852
|
-
const dbPath =
|
|
8853
|
-
if (!
|
|
8618
|
+
const dataDir = env.XDG_DATA_HOME ?? join9(home, ".local", "share");
|
|
8619
|
+
const dbPath = join9(dataDir, "opencode", "opencode.db");
|
|
8620
|
+
if (!existsSync10(dbPath))
|
|
8854
8621
|
return [];
|
|
8855
8622
|
const rows = querySqlite(dbPath, "SELECT id, directory, time_updated FROM session WHERE parent_id IS NULL");
|
|
8856
8623
|
if (!rows)
|
|
@@ -8887,6 +8654,291 @@ function scanAgentSessions(options = {}) {
|
|
|
8887
8654
|
}
|
|
8888
8655
|
var init_scan = () => {};
|
|
8889
8656
|
|
|
8657
|
+
// src/sync/watermark.ts
|
|
8658
|
+
import { existsSync as existsSync11, mkdirSync as mkdirSync7, readFileSync as readFileSync10, renameSync as renameSync3, writeFileSync as writeFileSync7 } from "node:fs";
|
|
8659
|
+
import { join as join10 } from "node:path";
|
|
8660
|
+
function syncStatePath(configDir = getConfigDir()) {
|
|
8661
|
+
return join10(configDir, STATE_FILENAME);
|
|
8662
|
+
}
|
|
8663
|
+
function loadSyncState(configDir = getConfigDir()) {
|
|
8664
|
+
const empty = {
|
|
8665
|
+
schema_version: STATE_SCHEMA_VERSION,
|
|
8666
|
+
watermark: null,
|
|
8667
|
+
consecutive_failures: 0
|
|
8668
|
+
};
|
|
8669
|
+
const path3 = syncStatePath(configDir);
|
|
8670
|
+
if (!existsSync11(path3))
|
|
8671
|
+
return empty;
|
|
8672
|
+
try {
|
|
8673
|
+
const raw = JSON.parse(readFileSync10(path3, "utf-8"));
|
|
8674
|
+
if (raw.schema_version !== STATE_SCHEMA_VERSION)
|
|
8675
|
+
return empty;
|
|
8676
|
+
const minedSessions = Array.isArray(raw.mined_sessions) ? raw.mined_sessions.filter((record) => typeof record === "object" && record !== null && typeof record.at === "string" && typeof record.session === "string").map((record) => ({
|
|
8677
|
+
at: record.at,
|
|
8678
|
+
session: record.session,
|
|
8679
|
+
...typeof record.project === "string" ? { project: record.project } : {}
|
|
8680
|
+
})) : [];
|
|
8681
|
+
const rawRefusal = raw.last_refusal;
|
|
8682
|
+
const lastRefusal = rawRefusal && typeof rawRefusal.at === "string" && typeof rawRefusal.outcome === "string" && typeof rawRefusal.message === "string" ? { at: rawRefusal.at, outcome: rawRefusal.outcome, message: rawRefusal.message } : undefined;
|
|
8683
|
+
const rawRun = raw.run;
|
|
8684
|
+
const run2 = rawRun && typeof rawRun.pid === "number" && typeof rawRun.started_at === "string" && typeof rawRun.baseline_mined === "number" && rawRun.baseline_mined >= 0 ? { pid: rawRun.pid, started_at: rawRun.started_at, baseline_mined: rawRun.baseline_mined } : undefined;
|
|
8685
|
+
return {
|
|
8686
|
+
schema_version: STATE_SCHEMA_VERSION,
|
|
8687
|
+
watermark: typeof raw.watermark === "string" ? raw.watermark : null,
|
|
8688
|
+
last_attempt_at: typeof raw.last_attempt_at === "string" ? raw.last_attempt_at : undefined,
|
|
8689
|
+
consecutive_failures: typeof raw.consecutive_failures === "number" && raw.consecutive_failures >= 0 ? raw.consecutive_failures : 0,
|
|
8690
|
+
mined_sessions: minedSessions,
|
|
8691
|
+
total_mined: typeof raw.total_mined === "number" && raw.total_mined >= 0 ? raw.total_mined : minedSessions.length,
|
|
8692
|
+
total_notes: typeof raw.total_notes === "number" && raw.total_notes >= 0 ? raw.total_notes : 0,
|
|
8693
|
+
total_learning_tokens: typeof raw.total_learning_tokens === "number" && raw.total_learning_tokens >= 0 ? raw.total_learning_tokens : 0,
|
|
8694
|
+
...lastRefusal ? { last_refusal: lastRefusal } : {},
|
|
8695
|
+
...run2 ? { run: run2 } : {},
|
|
8696
|
+
...raw.paused === true ? { paused: true } : {},
|
|
8697
|
+
...Array.isArray(raw.project_filter) ? {
|
|
8698
|
+
project_filter: raw.project_filter.filter((p2) => typeof p2 === "string")
|
|
8699
|
+
} : {}
|
|
8700
|
+
};
|
|
8701
|
+
} catch {
|
|
8702
|
+
return empty;
|
|
8703
|
+
}
|
|
8704
|
+
}
|
|
8705
|
+
function setSyncPaused(paused, configDir = getConfigDir()) {
|
|
8706
|
+
const state = loadSyncState(configDir);
|
|
8707
|
+
if (paused)
|
|
8708
|
+
state.paused = true;
|
|
8709
|
+
else
|
|
8710
|
+
delete state.paused;
|
|
8711
|
+
saveSyncState(state, configDir);
|
|
8712
|
+
}
|
|
8713
|
+
function saveSyncState(state, configDir = getConfigDir()) {
|
|
8714
|
+
if (!existsSync11(configDir))
|
|
8715
|
+
mkdirSync7(configDir, { recursive: true, mode: 448 });
|
|
8716
|
+
const path3 = syncStatePath(configDir);
|
|
8717
|
+
const tmp = `${path3}.${process.pid}.tmp`;
|
|
8718
|
+
writeFileSync7(tmp, JSON.stringify(state, null, 2), { mode: 384 });
|
|
8719
|
+
renameSync3(tmp, path3);
|
|
8720
|
+
}
|
|
8721
|
+
function backoffUntil(state) {
|
|
8722
|
+
if (state.consecutive_failures === 0 || !state.last_attempt_at)
|
|
8723
|
+
return null;
|
|
8724
|
+
const last = Date.parse(state.last_attempt_at);
|
|
8725
|
+
if (Number.isNaN(last))
|
|
8726
|
+
return null;
|
|
8727
|
+
const delay = Math.min(BACKOFF_BASE_MS * 2 ** (state.consecutive_failures - 1), BACKOFF_MAX_MS);
|
|
8728
|
+
return new Date(last + delay);
|
|
8729
|
+
}
|
|
8730
|
+
function isUnderDir(dir, base) {
|
|
8731
|
+
const root = base.endsWith("/") ? base.slice(0, -1) : base;
|
|
8732
|
+
return dir === root || dir.startsWith(`${root}/`);
|
|
8733
|
+
}
|
|
8734
|
+
function filterSessionsByProject(sessions, filter, resolveDir) {
|
|
8735
|
+
if (!filter || filter.length === 0)
|
|
8736
|
+
return [...sessions];
|
|
8737
|
+
const includeUnknown = filter.includes(UNKNOWN_PROJECT);
|
|
8738
|
+
const dirs = filter.filter((entry) => entry !== UNKNOWN_PROJECT);
|
|
8739
|
+
return sessions.filter((session) => {
|
|
8740
|
+
const dir = resolveDir(session);
|
|
8741
|
+
if (!dir)
|
|
8742
|
+
return includeUnknown;
|
|
8743
|
+
return dirs.some((base) => isUnderDir(dir, base));
|
|
8744
|
+
});
|
|
8745
|
+
}
|
|
8746
|
+
function gateSessions(sessions, watermark, now = new Date, quietPeriodMs = DEFAULT_QUIET_PERIOD_MS) {
|
|
8747
|
+
const watermarkMs = watermark ? Date.parse(watermark) : Number.NEGATIVE_INFINITY;
|
|
8748
|
+
const completedBefore = now.getTime() - quietPeriodMs;
|
|
8749
|
+
const ready = [];
|
|
8750
|
+
const open2 = [];
|
|
8751
|
+
for (const session of sessions) {
|
|
8752
|
+
const updated = Date.parse(session.updated);
|
|
8753
|
+
if (Number.isNaN(updated) || updated <= watermarkMs)
|
|
8754
|
+
continue;
|
|
8755
|
+
if (updated > completedBefore) {
|
|
8756
|
+
open2.push(session);
|
|
8757
|
+
} else {
|
|
8758
|
+
ready.push(session);
|
|
8759
|
+
}
|
|
8760
|
+
}
|
|
8761
|
+
return { ready, open: open2 };
|
|
8762
|
+
}
|
|
8763
|
+
var STATE_FILENAME = "knowledge-sync.json", STATE_SCHEMA_VERSION = 1, DEFAULT_QUIET_PERIOD_MS, BACKOFF_BASE_MS, BACKOFF_MAX_MS, MINED_HISTORY_LIMIT = 500, UNKNOWN_PROJECT = "(unknown)";
|
|
8764
|
+
var init_watermark = __esm(() => {
|
|
8765
|
+
init_config();
|
|
8766
|
+
DEFAULT_QUIET_PERIOD_MS = 5 * 60 * 1000;
|
|
8767
|
+
BACKOFF_BASE_MS = 15 * 60 * 1000;
|
|
8768
|
+
BACKOFF_MAX_MS = 24 * 60 * 60 * 1000;
|
|
8769
|
+
});
|
|
8770
|
+
|
|
8771
|
+
// src/sync/backlog.ts
|
|
8772
|
+
function listSessionBacklog() {
|
|
8773
|
+
try {
|
|
8774
|
+
const state = loadSyncState();
|
|
8775
|
+
let sessions = scanAgentSessions({});
|
|
8776
|
+
if (state.project_filter?.length) {
|
|
8777
|
+
const resolver = createProjectDirResolver();
|
|
8778
|
+
sessions = filterSessionsByProject(sessions, state.project_filter, resolver.resolve);
|
|
8779
|
+
resolver.flush();
|
|
8780
|
+
}
|
|
8781
|
+
const gate = gateSessions(sessions, state.watermark);
|
|
8782
|
+
return { queued: gate.ready.reverse(), open: gate.open.reverse() };
|
|
8783
|
+
} catch {
|
|
8784
|
+
return { queued: [], open: [] };
|
|
8785
|
+
}
|
|
8786
|
+
}
|
|
8787
|
+
var init_backlog = __esm(() => {
|
|
8788
|
+
init_project_dir();
|
|
8789
|
+
init_scan();
|
|
8790
|
+
init_watermark();
|
|
8791
|
+
});
|
|
8792
|
+
|
|
8793
|
+
// src/sync/lock.ts
|
|
8794
|
+
import { existsSync as existsSync12, readFileSync as readFileSync11, rmSync as rmSync2, statSync as statSync5, writeFileSync as writeFileSync8 } from "node:fs";
|
|
8795
|
+
import { join as join11 } from "node:path";
|
|
8796
|
+
function processAlive(pid) {
|
|
8797
|
+
try {
|
|
8798
|
+
process.kill(pid, 0);
|
|
8799
|
+
return true;
|
|
8800
|
+
} catch (err) {
|
|
8801
|
+
return err.code === "EPERM";
|
|
8802
|
+
}
|
|
8803
|
+
}
|
|
8804
|
+
function lockPath(configDir = getConfigDir()) {
|
|
8805
|
+
return join11(configDir, LOCK_FILENAME);
|
|
8806
|
+
}
|
|
8807
|
+
function stopSyncRun(pid, configDir = getConfigDir(), kill = process.kill.bind(process)) {
|
|
8808
|
+
if (!Number.isInteger(pid) || pid <= 0)
|
|
8809
|
+
return false;
|
|
8810
|
+
let killed = false;
|
|
8811
|
+
try {
|
|
8812
|
+
kill(-pid, "SIGTERM");
|
|
8813
|
+
killed = true;
|
|
8814
|
+
} catch {
|
|
8815
|
+
try {
|
|
8816
|
+
kill(pid, "SIGTERM");
|
|
8817
|
+
killed = true;
|
|
8818
|
+
} catch {}
|
|
8819
|
+
}
|
|
8820
|
+
try {
|
|
8821
|
+
const path3 = lockPath(configDir);
|
|
8822
|
+
if (existsSync12(path3) && readFileSync11(path3, "utf8") === String(pid)) {
|
|
8823
|
+
rmSync2(path3, { force: true });
|
|
8824
|
+
}
|
|
8825
|
+
} catch {}
|
|
8826
|
+
return killed;
|
|
8827
|
+
}
|
|
8828
|
+
function fileLock(configDir = getConfigDir(), now = () => new Date) {
|
|
8829
|
+
const path3 = lockPath(configDir);
|
|
8830
|
+
let held = false;
|
|
8831
|
+
const tryCreate = () => {
|
|
8832
|
+
try {
|
|
8833
|
+
writeFileSync8(path3, String(process.pid), { flag: "wx" });
|
|
8834
|
+
return true;
|
|
8835
|
+
} catch {
|
|
8836
|
+
return false;
|
|
8837
|
+
}
|
|
8838
|
+
};
|
|
8839
|
+
return {
|
|
8840
|
+
acquire() {
|
|
8841
|
+
if (tryCreate()) {
|
|
8842
|
+
held = true;
|
|
8843
|
+
return true;
|
|
8844
|
+
}
|
|
8845
|
+
try {
|
|
8846
|
+
const age = now().getTime() - statSync5(path3).mtimeMs;
|
|
8847
|
+
const holder = Number.parseInt(readFileSync11(path3, "utf8"), 10);
|
|
8848
|
+
const holderDead = Number.isInteger(holder) && holder > 0 && !processAlive(holder);
|
|
8849
|
+
if (age > STALE_LOCK_MS || holderDead) {
|
|
8850
|
+
rmSync2(path3, { force: true });
|
|
8851
|
+
held = tryCreate();
|
|
8852
|
+
return held;
|
|
8853
|
+
}
|
|
8854
|
+
} catch {}
|
|
8855
|
+
return false;
|
|
8856
|
+
},
|
|
8857
|
+
release() {
|
|
8858
|
+
if (!held)
|
|
8859
|
+
return;
|
|
8860
|
+
held = false;
|
|
8861
|
+
try {
|
|
8862
|
+
if (existsSync12(path3) && readFileSync11(path3, "utf8") === String(process.pid)) {
|
|
8863
|
+
rmSync2(path3, { force: true });
|
|
8864
|
+
}
|
|
8865
|
+
} catch {}
|
|
8866
|
+
}
|
|
8867
|
+
};
|
|
8868
|
+
}
|
|
8869
|
+
var LOCK_FILENAME = "knowledge-sync.lock", STALE_LOCK_MS;
|
|
8870
|
+
var init_lock = __esm(() => {
|
|
8871
|
+
init_config();
|
|
8872
|
+
STALE_LOCK_MS = 15 * 60 * 1000;
|
|
8873
|
+
});
|
|
8874
|
+
|
|
8875
|
+
// src/sync/status.ts
|
|
8876
|
+
import { readFileSync as readFileSync12, statSync as statSync6 } from "node:fs";
|
|
8877
|
+
function formatTokenCount(tokens) {
|
|
8878
|
+
if (tokens < 1000)
|
|
8879
|
+
return String(tokens);
|
|
8880
|
+
if (tokens < 1e6) {
|
|
8881
|
+
const k = tokens / 1000;
|
|
8882
|
+
return `~${k >= 100 ? Math.round(k) : k.toFixed(1).replace(/\.0$/, "")}k`;
|
|
8883
|
+
}
|
|
8884
|
+
const m3 = tokens / 1e6;
|
|
8885
|
+
return `~${m3 >= 100 ? Math.round(m3) : m3.toFixed(1).replace(/\.0$/, "")}M`;
|
|
8886
|
+
}
|
|
8887
|
+
function defaultIsProcessAlive2(pid) {
|
|
8888
|
+
if (!Number.isInteger(pid) || pid <= 0)
|
|
8889
|
+
return false;
|
|
8890
|
+
try {
|
|
8891
|
+
process.kill(pid, 0);
|
|
8892
|
+
return true;
|
|
8893
|
+
} catch (err) {
|
|
8894
|
+
return err.code === "EPERM";
|
|
8895
|
+
}
|
|
8896
|
+
}
|
|
8897
|
+
function defaultReadLog() {
|
|
8898
|
+
try {
|
|
8899
|
+
return readFileSync12(logger.getLogPath(), "utf-8");
|
|
8900
|
+
} catch {
|
|
8901
|
+
return "";
|
|
8902
|
+
}
|
|
8903
|
+
}
|
|
8904
|
+
function readLockFile(configDir) {
|
|
8905
|
+
const path3 = lockPath(configDir);
|
|
8906
|
+
try {
|
|
8907
|
+
const pid = Number.parseInt(readFileSync12(path3, "utf-8"), 10);
|
|
8908
|
+
return { pid: Number.isNaN(pid) ? -1 : pid, mtime: statSync6(path3).mtime };
|
|
8909
|
+
} catch {
|
|
8910
|
+
return null;
|
|
8911
|
+
}
|
|
8912
|
+
}
|
|
8913
|
+
function recentSyncActivity(log3) {
|
|
8914
|
+
return log3.split(`
|
|
8915
|
+
`).filter((line) => line.includes("[sync]") || line.includes("[miner]")).slice(-ACTIVITY_LINES).map((line) => line.replace(/ \[(DEBUG|INFO|WARN|ERROR)\]/, ""));
|
|
8916
|
+
}
|
|
8917
|
+
function getSyncStatus(deps = {}) {
|
|
8918
|
+
const configDir = deps.configDir ?? getConfigDir();
|
|
8919
|
+
const isProcessAlive = deps.isProcessAlive ?? defaultIsProcessAlive2;
|
|
8920
|
+
const readLog = deps.readLog ?? defaultReadLog;
|
|
8921
|
+
const lock = readLockFile(configDir);
|
|
8922
|
+
const running = lock !== null && isProcessAlive(lock.pid);
|
|
8923
|
+
const state = loadSyncState(configDir);
|
|
8924
|
+
const retryAt = backoffUntil(state);
|
|
8925
|
+
return {
|
|
8926
|
+
running,
|
|
8927
|
+
...lock ? { pid: lock.pid, startedAt: lock.mtime.toISOString() } : {},
|
|
8928
|
+
...lock && !running ? { staleLock: true } : {},
|
|
8929
|
+
state,
|
|
8930
|
+
...retryAt ? { backoffUntil: retryAt.toISOString() } : {},
|
|
8931
|
+
recentActivity: recentSyncActivity(readLog())
|
|
8932
|
+
};
|
|
8933
|
+
}
|
|
8934
|
+
var ACTIVITY_LINES = 10;
|
|
8935
|
+
var init_status = __esm(() => {
|
|
8936
|
+
init_config();
|
|
8937
|
+
init_logger();
|
|
8938
|
+
init_lock();
|
|
8939
|
+
init_watermark();
|
|
8940
|
+
});
|
|
8941
|
+
|
|
8890
8942
|
// src/sessions/read.ts
|
|
8891
8943
|
import { readFileSync as readFileSync13 } from "node:fs";
|
|
8892
8944
|
function asRecord(value) {
|
|
@@ -9062,11 +9114,18 @@ async function runKnowledgeSync(options = {}) {
|
|
|
9062
9114
|
const now = deps.now ?? (() => new Date);
|
|
9063
9115
|
const state = loadState();
|
|
9064
9116
|
if (options.quiet) {
|
|
9117
|
+
if (state.paused) {
|
|
9118
|
+
logger.debug("sync", "skipping quiet sync: mining is paused");
|
|
9119
|
+
return { status: "skipped-paused", readySessions: 0, inFlightSessions: 0, sessions: [] };
|
|
9120
|
+
}
|
|
9065
9121
|
const retryAt = backoffUntil(state);
|
|
9066
9122
|
if (retryAt && now() < retryAt) {
|
|
9067
9123
|
logger.debug("sync", `skipping quiet sync: backoff until ${retryAt.toISOString()}`);
|
|
9068
9124
|
return { status: "skipped-backoff", readySessions: 0, inFlightSessions: 0, sessions: [] };
|
|
9069
9125
|
}
|
|
9126
|
+
} else if (state.paused) {
|
|
9127
|
+
delete state.paused;
|
|
9128
|
+
logger.debug("sync", "manual sync resumes paused mining");
|
|
9070
9129
|
}
|
|
9071
9130
|
let ready;
|
|
9072
9131
|
let open2;
|
|
@@ -71924,7 +71983,7 @@ var init_settings = __esm(() => {
|
|
|
71924
71983
|
});
|
|
71925
71984
|
|
|
71926
71985
|
// src/telemetry/debug-id.ts
|
|
71927
|
-
var BUNDLE_DEBUG_ID_PLACEHOLDER = "
|
|
71986
|
+
var BUNDLE_DEBUG_ID_PLACEHOLDER = "1952c680-18dc-c6aa-6475-6e2164756e21";
|
|
71928
71987
|
|
|
71929
71988
|
// src/telemetry/telemetry.ts
|
|
71930
71989
|
import { randomUUID as randomUUID4 } from "node:crypto";
|
|
@@ -73852,6 +73911,13 @@ function reduceSyncConfirmKey(key) {
|
|
|
73852
73911
|
return "cancel";
|
|
73853
73912
|
return "none";
|
|
73854
73913
|
}
|
|
73914
|
+
function syncConfirmMode(status) {
|
|
73915
|
+
if (status.running)
|
|
73916
|
+
return "stop";
|
|
73917
|
+
if (status.state.paused)
|
|
73918
|
+
return "resume";
|
|
73919
|
+
return "start";
|
|
73920
|
+
}
|
|
73855
73921
|
function formatActivityLine(line, width) {
|
|
73856
73922
|
const compact = stripAnsiCodes(line).replace(/^\[(\d{4}-\d{2}-\d{2})T(\d{2}:\d{2}:\d{2})[^\]]*\]/, (_match, _date2, time3) => time3);
|
|
73857
73923
|
if (compact.length <= width)
|
|
@@ -73880,21 +73946,6 @@ function formatQueuedRow(session) {
|
|
|
73880
73946
|
const project = clip(session.project ?? "-", 28);
|
|
73881
73947
|
return `${session.harness.padEnd(8)} ${stamp} ${project} ${clip(session.id, 24)}`;
|
|
73882
73948
|
}
|
|
73883
|
-
function defaultListBacklog() {
|
|
73884
|
-
try {
|
|
73885
|
-
const state = loadSyncState();
|
|
73886
|
-
let sessions = scanAgentSessions({});
|
|
73887
|
-
if (state.project_filter?.length) {
|
|
73888
|
-
const resolver = createProjectDirResolver();
|
|
73889
|
-
sessions = filterSessionsByProject(sessions, state.project_filter, resolver.resolve);
|
|
73890
|
-
resolver.flush();
|
|
73891
|
-
}
|
|
73892
|
-
const gate = gateSessions(sessions, state.watermark);
|
|
73893
|
-
return { queued: gate.ready.reverse(), open: gate.open.reverse() };
|
|
73894
|
-
} catch {
|
|
73895
|
-
return { queued: [], open: [] };
|
|
73896
|
-
}
|
|
73897
|
-
}
|
|
73898
73949
|
function windowList(lines, scroll, height = ACTIVITY_VIEW_LIST_LINES) {
|
|
73899
73950
|
const clamped = Math.max(0, Math.min(scroll, Math.max(0, lines.length - height)));
|
|
73900
73951
|
const end = lines.length - clamped;
|
|
@@ -73953,6 +74004,9 @@ function statusLine(status) {
|
|
|
73953
74004
|
const pid = status.pid !== undefined ? ` (pid ${status.pid})` : "";
|
|
73954
74005
|
return `⛏️ ${import_picocolors30.default.bold(brand("Mining sessions..."))}${import_picocolors30.default.dim(`${pid}${since}`)}`;
|
|
73955
74006
|
}
|
|
74007
|
+
if (status.state.paused) {
|
|
74008
|
+
return `${import_picocolors30.default.yellow("○")} ${import_picocolors30.default.bold("Paused")} ${import_picocolors30.default.dim("· mining stays off until you resume")}`;
|
|
74009
|
+
}
|
|
73956
74010
|
if (status.staleLock) {
|
|
73957
74011
|
return `${import_picocolors30.default.yellow("○")} ${import_picocolors30.default.bold("Not running")} ${import_picocolors30.default.dim("· a previous run exited without cleaning up")}`;
|
|
73958
74012
|
}
|
|
@@ -73996,15 +74050,17 @@ function wrapLine(text, width, indent = " ") {
|
|
|
73996
74050
|
out.push(line);
|
|
73997
74051
|
return out;
|
|
73998
74052
|
}
|
|
73999
|
-
function confirmBox(queuedCount, backlog, width) {
|
|
74053
|
+
function confirmBox(queuedCount, backlog, width, mode = "start") {
|
|
74000
74054
|
const inFlight = backlog && backlog.inFlight > 0 ? ` (+${backlog.inFlight} open, mined once ${backlog.inFlight === 1 ? "it goes" : "they go"} quiet)` : "";
|
|
74001
|
-
const scope = queuedCount > 0 ? `${queuedCount} session${queuedCount === 1 ? "" : "s"} queued${inFlight} · runs in the background` : `queue empty${inFlight} · a run would only pick up sessions that finish from here`;
|
|
74055
|
+
const scope = mode === "stop" ? "the run is killed mid-batch · mining stays paused until you resume" : queuedCount > 0 ? `${queuedCount} session${queuedCount === 1 ? "" : "s"} queued${inFlight} · runs in the background` : `queue empty${inFlight} · a run would only pick up sessions that finish from here`;
|
|
74056
|
+
const title = mode === "stop" ? "Stop mining?" : mode === "resume" ? "Resume mining?" : "Start mining now?";
|
|
74057
|
+
const verb = mode === "stop" ? "stop" : mode === "resume" ? "resume" : "start";
|
|
74002
74058
|
const maxInner = Math.max(20, Math.min(width, contentWidth()) - 4);
|
|
74003
74059
|
const rows = [
|
|
74004
|
-
|
|
74060
|
+
import_picocolors30.default.bold(brand(title)),
|
|
74005
74061
|
...wrapLine(scope, maxInner, "").map((line) => import_picocolors30.default.dim(line)),
|
|
74006
74062
|
"",
|
|
74007
|
-
`${import_picocolors30.default.bold("enter")}
|
|
74063
|
+
`${import_picocolors30.default.bold("enter")} ${verb} · ${import_picocolors30.default.bold("esc")} cancel`
|
|
74008
74064
|
];
|
|
74009
74065
|
const inner = Math.min(maxInner, Math.max(...rows.map(visibleWidth)));
|
|
74010
74066
|
const pad = (row) => row + " ".repeat(Math.max(0, inner - visibleWidth(row)));
|
|
@@ -74018,7 +74074,7 @@ function renderActivityFrame(status, activity, width, backlog = null, pane = DEF
|
|
|
74018
74074
|
const mined = status.state.watermark ? `Mined sessions up to ${localTime(status.state.watermark)}` : "Nothing mined yet";
|
|
74019
74075
|
const queueDetail = [];
|
|
74020
74076
|
if (status.backoffUntil) {
|
|
74021
|
-
queueDetail.push(`retrying after ${localTime(status.backoffUntil)}`);
|
|
74077
|
+
queueDetail.push(`retrying after ${localTime(status.backoffUntil)} · s syncs now`);
|
|
74022
74078
|
}
|
|
74023
74079
|
let progress = null;
|
|
74024
74080
|
if (status.running && backlog) {
|
|
@@ -74041,6 +74097,9 @@ function renderActivityFrame(status, activity, width, backlog = null, pane = DEF
|
|
|
74041
74097
|
scrollParts.push(`↑ ${above} earlier`);
|
|
74042
74098
|
if (below > 0)
|
|
74043
74099
|
scrollParts.push(`↓ ${below} newer`);
|
|
74100
|
+
if (pane.tab !== "activity" && visible.length > 0) {
|
|
74101
|
+
scrollParts.push(`full rows: dosu knowledge sessions --${pane.tab}`);
|
|
74102
|
+
}
|
|
74044
74103
|
const lines = [
|
|
74045
74104
|
breadcrumb(["Home", "Activity"], width),
|
|
74046
74105
|
"",
|
|
@@ -74054,11 +74113,11 @@ function renderActivityFrame(status, activity, width, backlog = null, pane = DEF
|
|
|
74054
74113
|
...listRows,
|
|
74055
74114
|
...scrollParts.length > 0 ? [import_picocolors30.default.dim(scrollParts.join(" · "))] : [],
|
|
74056
74115
|
"",
|
|
74057
|
-
...pane.confirm ? centerBlock(confirmBox(queued.length, backlog, width), width) : [
|
|
74116
|
+
...pane.confirm ? centerBlock(confirmBox(queued.length, backlog, width, pane.confirm), width) : [
|
|
74058
74117
|
import_picocolors30.default.dim([
|
|
74059
74118
|
"tab switch",
|
|
74060
74119
|
"↑↓ scroll",
|
|
74061
|
-
|
|
74120
|
+
status.running ? "s stop" : status.state.paused ? "s resume" : "s sync now",
|
|
74062
74121
|
"esc back"
|
|
74063
74122
|
].join(" · "))
|
|
74064
74123
|
]
|
|
@@ -74081,8 +74140,10 @@ function runActivityView(io2 = {}) {
|
|
|
74081
74140
|
const getStatus = io2.getStatus ?? (() => getSyncStatus({ readLog: () => "" }));
|
|
74082
74141
|
const readLog = io2.readLog ?? defaultReadLog2;
|
|
74083
74142
|
const createFollower = io2.createFollower ?? ((emit) => createLogFollower(logger.getLogPath(), emit));
|
|
74084
|
-
const startSync = io2.startSync ?? (() => spawnDetachedSelf(["knowledge", "sync", "--
|
|
74085
|
-
const
|
|
74143
|
+
const startSync = io2.startSync ?? (() => spawnDetachedSelf(["knowledge", "sync", "--bootstrap"]));
|
|
74144
|
+
const stopSync = io2.stopSync ?? stopSyncRun;
|
|
74145
|
+
const setPaused = io2.setPaused ?? setSyncPaused;
|
|
74146
|
+
const listBacklog = io2.listBacklog ?? listSessionBacklog;
|
|
74086
74147
|
const pollMs = io2.pollMs ?? ACTIVITY_VIEW_POLL_MS;
|
|
74087
74148
|
const seed = readLog();
|
|
74088
74149
|
let activity = appendSyncActivity([], seed);
|
|
@@ -74090,7 +74151,7 @@ function runActivityView(io2 = {}) {
|
|
|
74090
74151
|
let runProgress = foldRunProgress(null, seed);
|
|
74091
74152
|
let tab = "activity";
|
|
74092
74153
|
let scroll = 0;
|
|
74093
|
-
let confirmSync =
|
|
74154
|
+
let confirmSync = null;
|
|
74094
74155
|
let status = getStatus();
|
|
74095
74156
|
let sessions = listBacklog();
|
|
74096
74157
|
let queuedWatermark = status.state.watermark;
|
|
@@ -74126,10 +74187,12 @@ function runActivityView(io2 = {}) {
|
|
|
74126
74187
|
queuedWatermark = status.state.watermark;
|
|
74127
74188
|
sessions = listBacklog();
|
|
74128
74189
|
}
|
|
74129
|
-
if (confirmSync && status.running)
|
|
74130
|
-
confirmSync =
|
|
74190
|
+
if ((confirmSync === "start" || confirmSync === "resume") && status.running)
|
|
74191
|
+
confirmSync = null;
|
|
74192
|
+
if (confirmSync === "stop" && !status.running)
|
|
74193
|
+
confirmSync = null;
|
|
74131
74194
|
const width = activityWidth(output2.columns ?? 80);
|
|
74132
|
-
const frame = renderActivityFrame(status, activity, width, backlog, { tab, scroll, confirm: confirmSync }, sessions.queued, minedBeforeRun ?? 0, sessions.open, runProgress);
|
|
74195
|
+
const frame = renderActivityFrame(status, activity, width, backlog, { tab, scroll, confirm: confirmSync ?? undefined }, sessions.queued, minedBeforeRun ?? 0, sessions.open, runProgress);
|
|
74133
74196
|
if (frame === lastFrame)
|
|
74134
74197
|
return;
|
|
74135
74198
|
lastFrame = frame;
|
|
@@ -74173,14 +74236,25 @@ ${CLEAR_BELOW}`);
|
|
|
74173
74236
|
if (confirmSync) {
|
|
74174
74237
|
const decision = reduceSyncConfirmKey(key);
|
|
74175
74238
|
if (decision === "start") {
|
|
74176
|
-
|
|
74177
|
-
|
|
74178
|
-
|
|
74239
|
+
const mode = confirmSync;
|
|
74240
|
+
confirmSync = null;
|
|
74241
|
+
let note;
|
|
74242
|
+
if (mode === "stop") {
|
|
74243
|
+
const ok2 = status.pid !== undefined && stopSync(status.pid);
|
|
74244
|
+
if (ok2)
|
|
74245
|
+
setPaused(true);
|
|
74246
|
+
note = ok2 ? "[sync] mining stopped · paused until you resume" : "[sync] could not stop the run · it may have just finished";
|
|
74247
|
+
} else {
|
|
74248
|
+
if (mode === "resume")
|
|
74249
|
+
setPaused(false);
|
|
74250
|
+
const ok2 = startSync();
|
|
74251
|
+
note = ok2 ? "[sync] sync requested · starting a background run" : "[sync] could not start a background run · try `dosu knowledge sync`";
|
|
74252
|
+
}
|
|
74179
74253
|
activity = appendSyncActivity(activity, `[${new Date().toISOString()}] [INFO] ${note}
|
|
74180
74254
|
`);
|
|
74181
74255
|
draw();
|
|
74182
74256
|
} else if (decision === "cancel") {
|
|
74183
|
-
confirmSync =
|
|
74257
|
+
confirmSync = null;
|
|
74184
74258
|
draw();
|
|
74185
74259
|
}
|
|
74186
74260
|
continue;
|
|
@@ -74191,10 +74265,8 @@ ${CLEAR_BELOW}`);
|
|
|
74191
74265
|
return;
|
|
74192
74266
|
}
|
|
74193
74267
|
if (action === "sync") {
|
|
74194
|
-
|
|
74195
|
-
|
|
74196
|
-
draw();
|
|
74197
|
-
}
|
|
74268
|
+
confirmSync = syncConfirmMode(status);
|
|
74269
|
+
draw();
|
|
74198
74270
|
} else if (action === "tab" || action === "tab-back") {
|
|
74199
74271
|
tab = cycleTab(tab, action === "tab" ? 1 : -1);
|
|
74200
74272
|
scroll = 0;
|
|
@@ -74222,10 +74294,10 @@ var import_picocolors30, ESC7, HIDE_CURSOR3, SHOW_CURSOR3, CTRL_C4, KEY_UP4, KEY
|
|
|
74222
74294
|
var init_activity_view = __esm(() => {
|
|
74223
74295
|
init_follow();
|
|
74224
74296
|
init_logger();
|
|
74225
|
-
init_project_dir();
|
|
74226
|
-
init_scan();
|
|
74227
74297
|
init_styles();
|
|
74298
|
+
init_backlog();
|
|
74228
74299
|
init_detach();
|
|
74300
|
+
init_lock();
|
|
74229
74301
|
init_status();
|
|
74230
74302
|
init_watermark();
|
|
74231
74303
|
init_alt_screen();
|
|
@@ -75934,18 +76006,19 @@ function projectRows(state) {
|
|
|
75934
76006
|
const key = record2.project ?? "(unknown)";
|
|
75935
76007
|
byProject.set(key, (byProject.get(key) ?? 0) + 1);
|
|
75936
76008
|
}
|
|
75937
|
-
|
|
76009
|
+
const data = [...byProject.entries()].sort((a9, b2) => b2[1] - a9[1]).map(([project, count]) => `${label(clip2(project, 24))}${count}`);
|
|
76010
|
+
return data.length > 0 ? [`${label("Project")}Sessions`, ...data] : [];
|
|
75938
76011
|
}
|
|
75939
76012
|
function pageRows(pageStats) {
|
|
75940
76013
|
const rows = [];
|
|
75941
76014
|
if (pageStats && pageStats.topCited.length > 0) {
|
|
75942
|
-
rows.push(`Top cited
|
|
76015
|
+
rows.push(`${label(`Top cited (${TOP_CITED_DAYS}d)`)}Citations`);
|
|
75943
76016
|
for (const row of pageStats.topCited) {
|
|
75944
76017
|
rows.push(`${label(clip2(row.title, 24))}${row.citation_count}`);
|
|
75945
76018
|
}
|
|
75946
76019
|
}
|
|
75947
76020
|
if (pageStats && pageStats.topUpdated.length > 0) {
|
|
75948
|
-
rows.push(...rows.length > 0 ? [""] : [], "Recently updated
|
|
76021
|
+
rows.push(...rows.length > 0 ? [""] : [], `${label("Recently updated")}Updated`);
|
|
75949
76022
|
for (const row of pageStats.topUpdated) {
|
|
75950
76023
|
rows.push(`${label(clip2(row.title, 24))}${row.updated_at.slice(0, 10)}`);
|
|
75951
76024
|
}
|
|
@@ -75985,6 +76058,7 @@ function renderAnalyticsFrame(state, tab, scroll, pageStats = null, pagesPending
|
|
|
75985
76058
|
["projects", "Projects"],
|
|
75986
76059
|
["pages", "Pages"]
|
|
75987
76060
|
], tab, width, { spread: false }),
|
|
76061
|
+
"",
|
|
75988
76062
|
...listRows,
|
|
75989
76063
|
...scrollParts.length > 0 ? [import_picocolors32.default.dim(scrollParts.join(" · "))] : [],
|
|
75990
76064
|
"",
|
|
@@ -76083,7 +76157,7 @@ ${CLEAR_BELOW2}`);
|
|
|
76083
76157
|
input2.on("data", onData);
|
|
76084
76158
|
});
|
|
76085
76159
|
}
|
|
76086
|
-
var import_picocolors32, ESC8, HIDE_CURSOR4, SHOW_CURSOR4, CTRL_C5, KEY_UP5, KEY_DOWN5, KEY_RIGHT4, KEY_LEFT4, CURSOR_HOME2, CLEAR_BELOW2, CLEAR_EOL2, ANALYTICS_VIEW_POLL_MS = 1000, ANALYTICS_VIEW_LINES =
|
|
76160
|
+
var import_picocolors32, ESC8, HIDE_CURSOR4, SHOW_CURSOR4, CTRL_C5, KEY_UP5, KEY_DOWN5, KEY_RIGHT4, KEY_LEFT4, CURSOR_HOME2, CLEAR_BELOW2, CLEAR_EOL2, ANALYTICS_VIEW_POLL_MS = 1000, ANALYTICS_VIEW_LINES = 14, ANALYTICS_VIEW_TABS, TOP_PAGES_LIMIT = 5, TOP_CITED_DAYS = 30, label = (text) => text.padEnd(26);
|
|
76087
76161
|
var init_analytics_view = __esm(() => {
|
|
76088
76162
|
init_trpc();
|
|
76089
76163
|
init_config();
|
|
@@ -80634,9 +80708,11 @@ init_trpc();
|
|
|
80634
80708
|
init_config();
|
|
80635
80709
|
init_agents();
|
|
80636
80710
|
init_formats();
|
|
80711
|
+
init_backlog();
|
|
80637
80712
|
init_detach();
|
|
80638
80713
|
init_status();
|
|
80639
80714
|
init_sync();
|
|
80715
|
+
init_watermark();
|
|
80640
80716
|
var import_picocolors18 = __toESM(require_picocolors(), 1);
|
|
80641
80717
|
import { existsSync as existsSync16 } from "node:fs";
|
|
80642
80718
|
import { homedir as homedir5 } from "node:os";
|
|
@@ -80703,6 +80779,50 @@ ${results.length - opts.limit} more results not shown.`));
|
|
|
80703
80779
|
console.log(` ID: ${store.id}`);
|
|
80704
80780
|
console.log(` Space ID: ${store.space_id}`);
|
|
80705
80781
|
});
|
|
80782
|
+
cmd.command("sessions").description("List local agent sessions with full project and session ids (the untruncated view of the Activity screen's tabs)").option("--queued", "Only sessions queued for mining").option("--open", "Only live sessions still inside the quiet period").option("--mined", "Only recent mined-session history").option("--json", "Output as JSON").action((opts) => {
|
|
80783
|
+
const all = !opts.queued && !opts.open && !opts.mined;
|
|
80784
|
+
const wantQueued = all || Boolean(opts.queued);
|
|
80785
|
+
const wantOpen = all || Boolean(opts.open);
|
|
80786
|
+
const wantMined = all || Boolean(opts.mined);
|
|
80787
|
+
const backlog = wantQueued || wantOpen ? listSessionBacklog() : { queued: [], open: [] };
|
|
80788
|
+
const mined = wantMined ? loadSyncState().mined_sessions ?? [] : [];
|
|
80789
|
+
if (opts.json) {
|
|
80790
|
+
printResult({
|
|
80791
|
+
...wantQueued ? { queued: backlog.queued } : {},
|
|
80792
|
+
...wantOpen ? { open: backlog.open } : {},
|
|
80793
|
+
...wantMined ? { mined } : {}
|
|
80794
|
+
}, opts);
|
|
80795
|
+
return;
|
|
80796
|
+
}
|
|
80797
|
+
const sessionRows = (sessions) => sessions.map((s) => [s.harness, s.updated, s.project ?? "-", s.id]);
|
|
80798
|
+
const minedRows = mined.map((record2) => {
|
|
80799
|
+
const slash = record2.session.indexOf("/");
|
|
80800
|
+
const harness = slash > 0 ? record2.session.slice(0, slash) : "-";
|
|
80801
|
+
const id2 = slash > 0 ? record2.session.slice(slash + 1) : record2.session;
|
|
80802
|
+
return [harness, record2.at, record2.project ?? "-", id2];
|
|
80803
|
+
});
|
|
80804
|
+
let first = true;
|
|
80805
|
+
const section = (title, rows, stamp, emptyMsg) => {
|
|
80806
|
+
if (!first)
|
|
80807
|
+
console.log();
|
|
80808
|
+
first = false;
|
|
80809
|
+
console.log(import_picocolors18.default.bold(`${title} (${rows.length})`));
|
|
80810
|
+
if (rows.length === 0) {
|
|
80811
|
+
console.log(import_picocolors18.default.dim(` ${emptyMsg}`));
|
|
80812
|
+
return;
|
|
80813
|
+
}
|
|
80814
|
+
printTable(["Agent", stamp, "Project", "Session"], rows);
|
|
80815
|
+
};
|
|
80816
|
+
if (wantQueued) {
|
|
80817
|
+
section("Queued", sessionRows(backlog.queued), "Updated", "Queue empty. Finished agent sessions appear here.");
|
|
80818
|
+
}
|
|
80819
|
+
if (wantOpen) {
|
|
80820
|
+
section("Open", sessionRows(backlog.open), "Updated", "No open sessions. Live agent sessions sit here until they go quiet.");
|
|
80821
|
+
}
|
|
80822
|
+
if (wantMined) {
|
|
80823
|
+
section("Mined", minedRows, "Mined at", "No mined sessions recorded yet.");
|
|
80824
|
+
}
|
|
80825
|
+
});
|
|
80706
80826
|
cmd.command("sync").description("Scan local agent session history and report the mining backlog").option("--quiet", "Background mode for hooks: honor backoff, exit 0, print nothing").option("--detach", "Re-spawn detached and return immediately (used by agent hooks)").option("--bootstrap", "Backfill mode: mine the full local session history regardless of age and drain the backlog (used by setup)").option("--status", "Show whether a sync is running now, plus watermark and recent activity").option("--json", "Output as JSON").action(async (opts) => {
|
|
80707
80827
|
if (opts.status) {
|
|
80708
80828
|
const status = getSyncStatus();
|
|
@@ -80784,6 +80904,9 @@ function printSyncStatus(status, now = new Date) {
|
|
|
80784
80904
|
} else {
|
|
80785
80905
|
console.log("○ No sync running.");
|
|
80786
80906
|
}
|
|
80907
|
+
if (status.state.paused) {
|
|
80908
|
+
console.log(import_picocolors18.default.yellow(" Mining paused: stopped by you. Resume from the Activity screen or run 'dosu knowledge sync'."));
|
|
80909
|
+
}
|
|
80787
80910
|
const wm2 = status.state.watermark;
|
|
80788
80911
|
console.log(` Mined through: ${wm2 ? `${wm2} (${formatAge(wm2, now)})` : "nothing mined yet"}`);
|
|
80789
80912
|
if (status.state.project_filter?.length) {
|
|
@@ -80860,6 +80983,10 @@ function printSyncOutcome(outcome) {
|
|
|
80860
80983
|
console.log(import_picocolors18.default.dim("Skipped: a recent sync failed; waiting out the retry backoff."));
|
|
80861
80984
|
break;
|
|
80862
80985
|
}
|
|
80986
|
+
case "skipped-paused": {
|
|
80987
|
+
console.log(import_picocolors18.default.dim("Skipped: mining is paused. Run 'dosu knowledge sync' to resume."));
|
|
80988
|
+
break;
|
|
80989
|
+
}
|
|
80863
80990
|
}
|
|
80864
80991
|
}
|
|
80865
80992
|
function resolveHookAgents(ids) {
|
|
@@ -82702,4 +82829,4 @@ execute().catch((err) => {
|
|
|
82702
82829
|
process.exit(1);
|
|
82703
82830
|
});
|
|
82704
82831
|
|
|
82705
|
-
//# debugId=
|
|
82832
|
+
//# debugId=1952c680-18dc-c6aa-6475-6e2164756e21
|