@hasna/mementos 0.14.5 → 0.14.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/README.md +17 -219
- package/dist/cli/index.js +174 -48
- package/dist/index.js +151 -25
- package/dist/mcp/index.js +151 -25
- package/dist/server/index.js +158 -32
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -22,6 +22,8 @@ import { homedir as homedir2 } from "os";
|
|
|
22
22
|
import { join as join2 } from "path";
|
|
23
23
|
import { homedir } from "os";
|
|
24
24
|
import { join, relative } from "path";
|
|
25
|
+
import { homedir as homedir3 } from "os";
|
|
26
|
+
import { join as join3 } from "path";
|
|
25
27
|
function __accessProp(key) {
|
|
26
28
|
return this[key];
|
|
27
29
|
}
|
|
@@ -55,6 +57,9 @@ class SqliteAdapter {
|
|
|
55
57
|
exec(sql) {
|
|
56
58
|
this.db.exec(sql);
|
|
57
59
|
}
|
|
60
|
+
query(sql) {
|
|
61
|
+
return this.db.query(sql);
|
|
62
|
+
}
|
|
58
63
|
prepare(sql) {
|
|
59
64
|
const stmt = this.db.prepare(sql);
|
|
60
65
|
return {
|
|
@@ -655,6 +660,126 @@ function custom(check, _params = {}, fatal) {
|
|
|
655
660
|
});
|
|
656
661
|
return ZodAny.create();
|
|
657
662
|
}
|
|
663
|
+
|
|
664
|
+
class SyncProgressTracker {
|
|
665
|
+
db;
|
|
666
|
+
progress = new Map;
|
|
667
|
+
startTimes = new Map;
|
|
668
|
+
callback;
|
|
669
|
+
constructor(db, callback) {
|
|
670
|
+
this.db = db;
|
|
671
|
+
this.callback = callback;
|
|
672
|
+
this.ensureResumeTable();
|
|
673
|
+
}
|
|
674
|
+
ensureResumeTable() {
|
|
675
|
+
this.db.exec(`
|
|
676
|
+
CREATE TABLE IF NOT EXISTS _sync_resume (
|
|
677
|
+
table_name TEXT PRIMARY KEY,
|
|
678
|
+
last_row_id TEXT,
|
|
679
|
+
direction TEXT,
|
|
680
|
+
started_at TEXT,
|
|
681
|
+
status TEXT DEFAULT 'in_progress'
|
|
682
|
+
)
|
|
683
|
+
`);
|
|
684
|
+
}
|
|
685
|
+
start(table, total, direction) {
|
|
686
|
+
const resumed = this.canResume(table);
|
|
687
|
+
const now = Date.now();
|
|
688
|
+
this.startTimes.set(table, now);
|
|
689
|
+
const status = resumed ? "resumed" : "in_progress";
|
|
690
|
+
const info = {
|
|
691
|
+
table,
|
|
692
|
+
total,
|
|
693
|
+
done: 0,
|
|
694
|
+
percent: 0,
|
|
695
|
+
elapsed_ms: 0,
|
|
696
|
+
eta_ms: 0,
|
|
697
|
+
status
|
|
698
|
+
};
|
|
699
|
+
this.progress.set(table, info);
|
|
700
|
+
this.db.run(`INSERT INTO _sync_resume (table_name, last_row_id, direction, started_at, status)
|
|
701
|
+
VALUES (?, ?, ?, datetime('now'), ?)
|
|
702
|
+
ON CONFLICT (table_name) DO UPDATE SET
|
|
703
|
+
direction = excluded.direction,
|
|
704
|
+
started_at = datetime('now'),
|
|
705
|
+
status = excluded.status`, table, "", direction, status);
|
|
706
|
+
this.notify(table);
|
|
707
|
+
}
|
|
708
|
+
update(table, done, lastRowId) {
|
|
709
|
+
const info = this.progress.get(table);
|
|
710
|
+
if (!info)
|
|
711
|
+
return;
|
|
712
|
+
const startTime = this.startTimes.get(table) ?? Date.now();
|
|
713
|
+
const elapsed = Date.now() - startTime;
|
|
714
|
+
const rate = done > 0 ? elapsed / done : 0;
|
|
715
|
+
const remaining = info.total - done;
|
|
716
|
+
const eta = remaining > 0 ? Math.round(rate * remaining) : 0;
|
|
717
|
+
info.done = done;
|
|
718
|
+
info.percent = info.total > 0 ? Math.round(done / info.total * 100) : 0;
|
|
719
|
+
info.elapsed_ms = elapsed;
|
|
720
|
+
info.eta_ms = eta;
|
|
721
|
+
info.status = "in_progress";
|
|
722
|
+
this.db.run(`UPDATE _sync_resume SET last_row_id = ?, status = 'in_progress' WHERE table_name = ?`, lastRowId, table);
|
|
723
|
+
this.notify(table);
|
|
724
|
+
}
|
|
725
|
+
markComplete(table) {
|
|
726
|
+
const info = this.progress.get(table);
|
|
727
|
+
if (info) {
|
|
728
|
+
const startTime = this.startTimes.get(table) ?? Date.now();
|
|
729
|
+
info.elapsed_ms = Date.now() - startTime;
|
|
730
|
+
info.done = info.total;
|
|
731
|
+
info.percent = 100;
|
|
732
|
+
info.eta_ms = 0;
|
|
733
|
+
info.status = "completed";
|
|
734
|
+
this.notify(table);
|
|
735
|
+
}
|
|
736
|
+
this.db.run(`UPDATE _sync_resume SET status = 'completed' WHERE table_name = ?`, table);
|
|
737
|
+
}
|
|
738
|
+
markFailed(table, _error) {
|
|
739
|
+
const info = this.progress.get(table);
|
|
740
|
+
if (info) {
|
|
741
|
+
const startTime = this.startTimes.get(table) ?? Date.now();
|
|
742
|
+
info.elapsed_ms = Date.now() - startTime;
|
|
743
|
+
info.status = "failed";
|
|
744
|
+
this.notify(table);
|
|
745
|
+
}
|
|
746
|
+
this.db.run(`UPDATE _sync_resume SET status = 'failed' WHERE table_name = ?`, table);
|
|
747
|
+
}
|
|
748
|
+
canResume(table) {
|
|
749
|
+
const row = this.db.get(`SELECT status FROM _sync_resume WHERE table_name = ?`, table);
|
|
750
|
+
if (!row)
|
|
751
|
+
return false;
|
|
752
|
+
return row.status === "in_progress" || row.status === "resumed";
|
|
753
|
+
}
|
|
754
|
+
getResumePoint(table) {
|
|
755
|
+
const row = this.db.get(`SELECT table_name, last_row_id, direction, started_at, status FROM _sync_resume WHERE table_name = ?`, table);
|
|
756
|
+
if (!row)
|
|
757
|
+
return null;
|
|
758
|
+
if (row.status !== "in_progress" && row.status !== "resumed")
|
|
759
|
+
return null;
|
|
760
|
+
return row;
|
|
761
|
+
}
|
|
762
|
+
clearResume(table) {
|
|
763
|
+
this.db.run(`DELETE FROM _sync_resume WHERE table_name = ?`, table);
|
|
764
|
+
this.progress.delete(table);
|
|
765
|
+
this.startTimes.delete(table);
|
|
766
|
+
}
|
|
767
|
+
getProgress(table) {
|
|
768
|
+
return this.progress.get(table) ?? null;
|
|
769
|
+
}
|
|
770
|
+
getAllProgress() {
|
|
771
|
+
return Array.from(this.progress.values());
|
|
772
|
+
}
|
|
773
|
+
listResumeRecords() {
|
|
774
|
+
return this.db.all(`SELECT table_name, last_row_id, direction, started_at, status FROM _sync_resume ORDER BY started_at DESC`);
|
|
775
|
+
}
|
|
776
|
+
notify(table) {
|
|
777
|
+
const info = this.progress.get(table);
|
|
778
|
+
if (info && this.callback) {
|
|
779
|
+
this.callback({ ...info });
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
}
|
|
658
783
|
var __create, __getProtoOf, __defProp2, __getOwnPropNames, __hasOwnProp, __toESMCache_node, __toESMCache_esm, __toESM = (mod, isNodeMode, target) => {
|
|
659
784
|
var canCache = mod != null && typeof mod === "object";
|
|
660
785
|
if (canCache) {
|
|
@@ -898,7 +1023,7 @@ var __create, __getProtoOf, __defProp2, __getOwnPropNames, __hasOwnProp, __toESM
|
|
|
898
1023
|
}
|
|
899
1024
|
}, ZodDiscriminatedUnion, ZodIntersection, ZodTuple, ZodRecord, ZodMap, ZodSet, ZodFunction, ZodLazy, ZodLiteral, ZodEnum, ZodNativeEnum, ZodPromise, ZodEffects, ZodOptional, ZodNullable, ZodDefault, ZodCatch, ZodNaN, BRAND, ZodBranded, ZodPipeline, ZodReadonly, late, ZodFirstPartyTypeKind, instanceOfType = (cls, params = {
|
|
900
1025
|
message: `Input not instance of ${cls.name}`
|
|
901
|
-
}) => custom((data) => data instanceof cls, params), stringType, numberType, nanType, bigIntType, booleanType, dateType, symbolType, undefinedType, nullType, anyType, unknownType, neverType, voidType, arrayType, objectType, strictObjectType, unionType, discriminatedUnionType, intersectionType, tupleType, recordType, mapType, setType, functionType, lazyType, literalType, enumType, nativeEnumType, promiseType, effectsType, optionalType, nullableType, preprocessType, pipelineType, ostring = () => stringType().optional(), onumber = () => numberType().optional(), oboolean = () => booleanType().optional(), coerce, NEVER, HASNA_DIR, CloudConfigSchema, CONFIG_DIR, CONFIG_PATH;
|
|
1026
|
+
}) => custom((data) => data instanceof cls, params), stringType, numberType, nanType, bigIntType, booleanType, dateType, symbolType, undefinedType, nullType, anyType, unknownType, neverType, voidType, arrayType, objectType, strictObjectType, unionType, discriminatedUnionType, intersectionType, tupleType, recordType, mapType, setType, functionType, lazyType, literalType, enumType, nativeEnumType, promiseType, effectsType, optionalType, nullableType, preprocessType, pipelineType, ostring = () => stringType().optional(), onumber = () => numberType().optional(), oboolean = () => booleanType().optional(), coerce, NEVER, HASNA_DIR, CloudConfigSchema, CONFIG_DIR, CONFIG_PATH, AUTO_SYNC_CONFIG_PATH;
|
|
902
1027
|
var init_dist = __esm(() => {
|
|
903
1028
|
__create = Object.create;
|
|
904
1029
|
__getProtoOf = Object.getPrototypeOf;
|
|
@@ -8841,6 +8966,7 @@ See https://www.postgresql.org/docs/current/libpq-ssl.html for libpq SSL mode de
|
|
|
8841
8966
|
});
|
|
8842
8967
|
CONFIG_DIR = join2(homedir2(), ".hasna", "cloud");
|
|
8843
8968
|
CONFIG_PATH = join2(CONFIG_DIR, "config.json");
|
|
8969
|
+
AUTO_SYNC_CONFIG_PATH = join3(homedir3(), ".hasna", "cloud", "config.json");
|
|
8844
8970
|
});
|
|
8845
8971
|
|
|
8846
8972
|
// src/db/database.ts
|
|
@@ -8856,14 +8982,14 @@ __export(exports_database, {
|
|
|
8856
8982
|
closeDatabase: () => closeDatabase
|
|
8857
8983
|
});
|
|
8858
8984
|
import { existsSync, mkdirSync, cpSync } from "fs";
|
|
8859
|
-
import { dirname, join as
|
|
8985
|
+
import { dirname, join as join4, resolve } from "path";
|
|
8860
8986
|
function isInMemoryDb(path) {
|
|
8861
8987
|
return path === ":memory:" || path.startsWith("file::memory:");
|
|
8862
8988
|
}
|
|
8863
8989
|
function findNearestMementosDb(startDir) {
|
|
8864
8990
|
let dir = resolve(startDir);
|
|
8865
8991
|
while (true) {
|
|
8866
|
-
const candidate =
|
|
8992
|
+
const candidate = join4(dir, ".mementos", "mementos.db");
|
|
8867
8993
|
if (existsSync(candidate))
|
|
8868
8994
|
return candidate;
|
|
8869
8995
|
const parent = dirname(dir);
|
|
@@ -8876,7 +9002,7 @@ function findNearestMementosDb(startDir) {
|
|
|
8876
9002
|
function findGitRoot(startDir) {
|
|
8877
9003
|
let dir = resolve(startDir);
|
|
8878
9004
|
while (true) {
|
|
8879
|
-
if (existsSync(
|
|
9005
|
+
if (existsSync(join4(dir, ".git")))
|
|
8880
9006
|
return dir;
|
|
8881
9007
|
const parent = dirname(dir);
|
|
8882
9008
|
if (parent === dir)
|
|
@@ -8887,10 +9013,10 @@ function findGitRoot(startDir) {
|
|
|
8887
9013
|
}
|
|
8888
9014
|
function migrateGlobalDir() {
|
|
8889
9015
|
const home = process.env["HOME"] || process.env["USERPROFILE"] || "~";
|
|
8890
|
-
const newDir =
|
|
8891
|
-
const oldDir =
|
|
9016
|
+
const newDir = join4(home, ".hasna", "mementos");
|
|
9017
|
+
const oldDir = join4(home, ".mementos");
|
|
8892
9018
|
if (!existsSync(newDir) && existsSync(oldDir)) {
|
|
8893
|
-
mkdirSync(
|
|
9019
|
+
mkdirSync(join4(home, ".hasna"), { recursive: true });
|
|
8894
9020
|
cpSync(oldDir, newDir, { recursive: true });
|
|
8895
9021
|
}
|
|
8896
9022
|
}
|
|
@@ -8906,12 +9032,12 @@ function getDbPath() {
|
|
|
8906
9032
|
if (process.env["MEMENTOS_DB_SCOPE"] === "project") {
|
|
8907
9033
|
const gitRoot = findGitRoot(cwd);
|
|
8908
9034
|
if (gitRoot) {
|
|
8909
|
-
return
|
|
9035
|
+
return join4(gitRoot, ".mementos", "mementos.db");
|
|
8910
9036
|
}
|
|
8911
9037
|
}
|
|
8912
9038
|
migrateGlobalDir();
|
|
8913
9039
|
const home = process.env["HOME"] || process.env["USERPROFILE"] || "~";
|
|
8914
|
-
return
|
|
9040
|
+
return join4(home, ".hasna", "mementos", "mementos.db");
|
|
8915
9041
|
}
|
|
8916
9042
|
function ensureDir(filePath) {
|
|
8917
9043
|
if (isInMemoryDb(filePath))
|
|
@@ -11768,8 +11894,8 @@ function logSearchQuery(query, resultCount, agentId, projectId, db) {
|
|
|
11768
11894
|
}
|
|
11769
11895
|
// src/lib/config.ts
|
|
11770
11896
|
import { existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync, readdirSync, writeFileSync, unlinkSync, cpSync as cpSync2 } from "fs";
|
|
11771
|
-
import { homedir as
|
|
11772
|
-
import { basename, dirname as dirname2, join as
|
|
11897
|
+
import { homedir as homedir4 } from "os";
|
|
11898
|
+
import { basename, dirname as dirname2, join as join6, resolve as resolve2 } from "path";
|
|
11773
11899
|
var DEFAULT_CONFIG = {
|
|
11774
11900
|
default_scope: "private",
|
|
11775
11901
|
default_category: "knowledge",
|
|
@@ -11826,7 +11952,7 @@ function isValidCategory(value) {
|
|
|
11826
11952
|
return VALID_CATEGORIES.includes(value);
|
|
11827
11953
|
}
|
|
11828
11954
|
function loadConfig() {
|
|
11829
|
-
const configPath =
|
|
11955
|
+
const configPath = join6(homedir4(), ".hasna", "mementos", "config.json");
|
|
11830
11956
|
let fileConfig = {};
|
|
11831
11957
|
if (existsSync2(configPath)) {
|
|
11832
11958
|
try {
|
|
@@ -11853,10 +11979,10 @@ function loadConfig() {
|
|
|
11853
11979
|
return merged;
|
|
11854
11980
|
}
|
|
11855
11981
|
function profilesDir() {
|
|
11856
|
-
return
|
|
11982
|
+
return join6(homedir4(), ".hasna", "mementos", "profiles");
|
|
11857
11983
|
}
|
|
11858
11984
|
function globalConfigPath() {
|
|
11859
|
-
return
|
|
11985
|
+
return join6(homedir4(), ".hasna", "mementos", "config.json");
|
|
11860
11986
|
}
|
|
11861
11987
|
function readGlobalConfig() {
|
|
11862
11988
|
const p = globalConfigPath();
|
|
@@ -11896,7 +12022,7 @@ function listProfiles() {
|
|
|
11896
12022
|
return readdirSync(dir).filter((f) => f.endsWith(".db")).map((f) => basename(f, ".db")).sort();
|
|
11897
12023
|
}
|
|
11898
12024
|
function deleteProfile(name) {
|
|
11899
|
-
const dbPath =
|
|
12025
|
+
const dbPath = join6(profilesDir(), `${name}.db`);
|
|
11900
12026
|
if (!existsSync2(dbPath))
|
|
11901
12027
|
return false;
|
|
11902
12028
|
unlinkSync(dbPath);
|
|
@@ -12259,17 +12385,17 @@ function runCleanup(config, db) {
|
|
|
12259
12385
|
}
|
|
12260
12386
|
// src/lib/sync.ts
|
|
12261
12387
|
import { existsSync as existsSync3, mkdirSync as mkdirSync3, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
12262
|
-
import { homedir as
|
|
12263
|
-
import { join as
|
|
12388
|
+
import { homedir as homedir6 } from "os";
|
|
12389
|
+
import { join as join7 } from "path";
|
|
12264
12390
|
function getAgentSyncDir(agentName) {
|
|
12265
|
-
const dir =
|
|
12391
|
+
const dir = join7(homedir6(), ".hasna", "mementos", "agents", agentName);
|
|
12266
12392
|
if (!existsSync3(dir)) {
|
|
12267
12393
|
mkdirSync3(dir, { recursive: true });
|
|
12268
12394
|
}
|
|
12269
12395
|
return dir;
|
|
12270
12396
|
}
|
|
12271
12397
|
function setHighWaterMark(agentDir, timestamp) {
|
|
12272
|
-
const markFile =
|
|
12398
|
+
const markFile = join7(agentDir, ".highwatermark");
|
|
12273
12399
|
writeFileSync2(markFile, timestamp, "utf-8");
|
|
12274
12400
|
}
|
|
12275
12401
|
function resolveConflict(local, remote, resolution) {
|
|
@@ -12290,7 +12416,7 @@ function pushMemories(agentName, agentId, projectId, db) {
|
|
|
12290
12416
|
status: "active",
|
|
12291
12417
|
limit: 1e4
|
|
12292
12418
|
}, db);
|
|
12293
|
-
const outFile =
|
|
12419
|
+
const outFile = join7(agentDir, "memories.json");
|
|
12294
12420
|
writeFileSync2(outFile, JSON.stringify(memories, null, 2), "utf-8");
|
|
12295
12421
|
if (memories.length > 0) {
|
|
12296
12422
|
const latest = memories.reduce((a, b) => new Date(a.updated_at).getTime() > new Date(b.updated_at).getTime() ? a : b);
|
|
@@ -12300,7 +12426,7 @@ function pushMemories(agentName, agentId, projectId, db) {
|
|
|
12300
12426
|
}
|
|
12301
12427
|
function pullMemories(agentName, conflictResolution = "prefer-newer", db) {
|
|
12302
12428
|
const agentDir = getAgentSyncDir(agentName);
|
|
12303
|
-
const inFile =
|
|
12429
|
+
const inFile = join7(agentDir, "memories.json");
|
|
12304
12430
|
if (!existsSync3(inFile)) {
|
|
12305
12431
|
return { pulled: 0, conflicts: 0 };
|
|
12306
12432
|
}
|
|
@@ -13369,11 +13495,11 @@ var gatherTrainingData = async (options = {}) => {
|
|
|
13369
13495
|
};
|
|
13370
13496
|
// src/lib/model-config.ts
|
|
13371
13497
|
import { existsSync as existsSync4, mkdirSync as mkdirSync4, readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "fs";
|
|
13372
|
-
import { homedir as
|
|
13373
|
-
import { join as
|
|
13498
|
+
import { homedir as homedir7 } from "os";
|
|
13499
|
+
import { join as join8 } from "path";
|
|
13374
13500
|
var DEFAULT_MODEL = "gpt-4o-mini";
|
|
13375
|
-
var CONFIG_DIR2 =
|
|
13376
|
-
var CONFIG_PATH2 =
|
|
13501
|
+
var CONFIG_DIR2 = join8(homedir7(), ".hasna", "mementos");
|
|
13502
|
+
var CONFIG_PATH2 = join8(CONFIG_DIR2, "config.json");
|
|
13377
13503
|
function readConfig() {
|
|
13378
13504
|
if (!existsSync4(CONFIG_PATH2))
|
|
13379
13505
|
return {};
|
package/dist/mcp/index.js
CHANGED
|
@@ -116,6 +116,8 @@ import { homedir as homedir2 } from "os";
|
|
|
116
116
|
import { join as join2 } from "path";
|
|
117
117
|
import { homedir } from "os";
|
|
118
118
|
import { join, relative } from "path";
|
|
119
|
+
import { homedir as homedir3 } from "os";
|
|
120
|
+
import { join as join3 } from "path";
|
|
119
121
|
function __accessProp2(key) {
|
|
120
122
|
return this[key];
|
|
121
123
|
}
|
|
@@ -149,6 +151,9 @@ class SqliteAdapter {
|
|
|
149
151
|
exec(sql) {
|
|
150
152
|
this.db.exec(sql);
|
|
151
153
|
}
|
|
154
|
+
query(sql) {
|
|
155
|
+
return this.db.query(sql);
|
|
156
|
+
}
|
|
152
157
|
prepare(sql) {
|
|
153
158
|
const stmt = this.db.prepare(sql);
|
|
154
159
|
return {
|
|
@@ -749,6 +754,126 @@ function custom2(check, _params = {}, fatal) {
|
|
|
749
754
|
});
|
|
750
755
|
return ZodAny2.create();
|
|
751
756
|
}
|
|
757
|
+
|
|
758
|
+
class SyncProgressTracker {
|
|
759
|
+
db;
|
|
760
|
+
progress = new Map;
|
|
761
|
+
startTimes = new Map;
|
|
762
|
+
callback;
|
|
763
|
+
constructor(db, callback) {
|
|
764
|
+
this.db = db;
|
|
765
|
+
this.callback = callback;
|
|
766
|
+
this.ensureResumeTable();
|
|
767
|
+
}
|
|
768
|
+
ensureResumeTable() {
|
|
769
|
+
this.db.exec(`
|
|
770
|
+
CREATE TABLE IF NOT EXISTS _sync_resume (
|
|
771
|
+
table_name TEXT PRIMARY KEY,
|
|
772
|
+
last_row_id TEXT,
|
|
773
|
+
direction TEXT,
|
|
774
|
+
started_at TEXT,
|
|
775
|
+
status TEXT DEFAULT 'in_progress'
|
|
776
|
+
)
|
|
777
|
+
`);
|
|
778
|
+
}
|
|
779
|
+
start(table, total, direction) {
|
|
780
|
+
const resumed = this.canResume(table);
|
|
781
|
+
const now = Date.now();
|
|
782
|
+
this.startTimes.set(table, now);
|
|
783
|
+
const status = resumed ? "resumed" : "in_progress";
|
|
784
|
+
const info = {
|
|
785
|
+
table,
|
|
786
|
+
total,
|
|
787
|
+
done: 0,
|
|
788
|
+
percent: 0,
|
|
789
|
+
elapsed_ms: 0,
|
|
790
|
+
eta_ms: 0,
|
|
791
|
+
status
|
|
792
|
+
};
|
|
793
|
+
this.progress.set(table, info);
|
|
794
|
+
this.db.run(`INSERT INTO _sync_resume (table_name, last_row_id, direction, started_at, status)
|
|
795
|
+
VALUES (?, ?, ?, datetime('now'), ?)
|
|
796
|
+
ON CONFLICT (table_name) DO UPDATE SET
|
|
797
|
+
direction = excluded.direction,
|
|
798
|
+
started_at = datetime('now'),
|
|
799
|
+
status = excluded.status`, table, "", direction, status);
|
|
800
|
+
this.notify(table);
|
|
801
|
+
}
|
|
802
|
+
update(table, done, lastRowId) {
|
|
803
|
+
const info = this.progress.get(table);
|
|
804
|
+
if (!info)
|
|
805
|
+
return;
|
|
806
|
+
const startTime = this.startTimes.get(table) ?? Date.now();
|
|
807
|
+
const elapsed = Date.now() - startTime;
|
|
808
|
+
const rate = done > 0 ? elapsed / done : 0;
|
|
809
|
+
const remaining = info.total - done;
|
|
810
|
+
const eta = remaining > 0 ? Math.round(rate * remaining) : 0;
|
|
811
|
+
info.done = done;
|
|
812
|
+
info.percent = info.total > 0 ? Math.round(done / info.total * 100) : 0;
|
|
813
|
+
info.elapsed_ms = elapsed;
|
|
814
|
+
info.eta_ms = eta;
|
|
815
|
+
info.status = "in_progress";
|
|
816
|
+
this.db.run(`UPDATE _sync_resume SET last_row_id = ?, status = 'in_progress' WHERE table_name = ?`, lastRowId, table);
|
|
817
|
+
this.notify(table);
|
|
818
|
+
}
|
|
819
|
+
markComplete(table) {
|
|
820
|
+
const info = this.progress.get(table);
|
|
821
|
+
if (info) {
|
|
822
|
+
const startTime = this.startTimes.get(table) ?? Date.now();
|
|
823
|
+
info.elapsed_ms = Date.now() - startTime;
|
|
824
|
+
info.done = info.total;
|
|
825
|
+
info.percent = 100;
|
|
826
|
+
info.eta_ms = 0;
|
|
827
|
+
info.status = "completed";
|
|
828
|
+
this.notify(table);
|
|
829
|
+
}
|
|
830
|
+
this.db.run(`UPDATE _sync_resume SET status = 'completed' WHERE table_name = ?`, table);
|
|
831
|
+
}
|
|
832
|
+
markFailed(table, _error) {
|
|
833
|
+
const info = this.progress.get(table);
|
|
834
|
+
if (info) {
|
|
835
|
+
const startTime = this.startTimes.get(table) ?? Date.now();
|
|
836
|
+
info.elapsed_ms = Date.now() - startTime;
|
|
837
|
+
info.status = "failed";
|
|
838
|
+
this.notify(table);
|
|
839
|
+
}
|
|
840
|
+
this.db.run(`UPDATE _sync_resume SET status = 'failed' WHERE table_name = ?`, table);
|
|
841
|
+
}
|
|
842
|
+
canResume(table) {
|
|
843
|
+
const row = this.db.get(`SELECT status FROM _sync_resume WHERE table_name = ?`, table);
|
|
844
|
+
if (!row)
|
|
845
|
+
return false;
|
|
846
|
+
return row.status === "in_progress" || row.status === "resumed";
|
|
847
|
+
}
|
|
848
|
+
getResumePoint(table) {
|
|
849
|
+
const row = this.db.get(`SELECT table_name, last_row_id, direction, started_at, status FROM _sync_resume WHERE table_name = ?`, table);
|
|
850
|
+
if (!row)
|
|
851
|
+
return null;
|
|
852
|
+
if (row.status !== "in_progress" && row.status !== "resumed")
|
|
853
|
+
return null;
|
|
854
|
+
return row;
|
|
855
|
+
}
|
|
856
|
+
clearResume(table) {
|
|
857
|
+
this.db.run(`DELETE FROM _sync_resume WHERE table_name = ?`, table);
|
|
858
|
+
this.progress.delete(table);
|
|
859
|
+
this.startTimes.delete(table);
|
|
860
|
+
}
|
|
861
|
+
getProgress(table) {
|
|
862
|
+
return this.progress.get(table) ?? null;
|
|
863
|
+
}
|
|
864
|
+
getAllProgress() {
|
|
865
|
+
return Array.from(this.progress.values());
|
|
866
|
+
}
|
|
867
|
+
listResumeRecords() {
|
|
868
|
+
return this.db.all(`SELECT table_name, last_row_id, direction, started_at, status FROM _sync_resume ORDER BY started_at DESC`);
|
|
869
|
+
}
|
|
870
|
+
notify(table) {
|
|
871
|
+
const info = this.progress.get(table);
|
|
872
|
+
if (info && this.callback) {
|
|
873
|
+
this.callback({ ...info });
|
|
874
|
+
}
|
|
875
|
+
}
|
|
876
|
+
}
|
|
752
877
|
var __create, __getProtoOf, __defProp2, __getOwnPropNames2, __hasOwnProp2, __toESMCache_node, __toESMCache_esm, __toESM = (mod, isNodeMode, target) => {
|
|
753
878
|
var canCache = mod != null && typeof mod === "object";
|
|
754
879
|
if (canCache) {
|
|
@@ -992,7 +1117,7 @@ var __create, __getProtoOf, __defProp2, __getOwnPropNames2, __hasOwnProp2, __toE
|
|
|
992
1117
|
}
|
|
993
1118
|
}, ZodDiscriminatedUnion2, ZodIntersection2, ZodTuple2, ZodRecord2, ZodMap2, ZodSet2, ZodFunction2, ZodLazy2, ZodLiteral2, ZodEnum2, ZodNativeEnum2, ZodPromise2, ZodEffects2, ZodOptional2, ZodNullable2, ZodDefault2, ZodCatch2, ZodNaN2, BRAND2, ZodBranded2, ZodPipeline2, ZodReadonly2, late2, ZodFirstPartyTypeKind2, instanceOfType2 = (cls, params = {
|
|
994
1119
|
message: `Input not instance of ${cls.name}`
|
|
995
|
-
}) => custom2((data) => data instanceof cls, params), stringType2, numberType2, nanType2, bigIntType2, booleanType2, dateType2, symbolType2, undefinedType2, nullType2, anyType2, unknownType2, neverType2, voidType2, arrayType2, objectType2, strictObjectType2, unionType2, discriminatedUnionType2, intersectionType2, tupleType2, recordType2, mapType2, setType2, functionType2, lazyType2, literalType2, enumType2, nativeEnumType2, promiseType2, effectsType2, optionalType2, nullableType2, preprocessType2, pipelineType2, ostring2 = () => stringType2().optional(), onumber2 = () => numberType2().optional(), oboolean2 = () => booleanType2().optional(), coerce2, NEVER2, HASNA_DIR, CloudConfigSchema, CONFIG_DIR, CONFIG_PATH;
|
|
1120
|
+
}) => custom2((data) => data instanceof cls, params), stringType2, numberType2, nanType2, bigIntType2, booleanType2, dateType2, symbolType2, undefinedType2, nullType2, anyType2, unknownType2, neverType2, voidType2, arrayType2, objectType2, strictObjectType2, unionType2, discriminatedUnionType2, intersectionType2, tupleType2, recordType2, mapType2, setType2, functionType2, lazyType2, literalType2, enumType2, nativeEnumType2, promiseType2, effectsType2, optionalType2, nullableType2, preprocessType2, pipelineType2, ostring2 = () => stringType2().optional(), onumber2 = () => numberType2().optional(), oboolean2 = () => booleanType2().optional(), coerce2, NEVER2, HASNA_DIR, CloudConfigSchema, CONFIG_DIR, CONFIG_PATH, AUTO_SYNC_CONFIG_PATH;
|
|
996
1121
|
var init_dist = __esm(() => {
|
|
997
1122
|
__create = Object.create;
|
|
998
1123
|
__getProtoOf = Object.getPrototypeOf;
|
|
@@ -8935,6 +9060,7 @@ See https://www.postgresql.org/docs/current/libpq-ssl.html for libpq SSL mode de
|
|
|
8935
9060
|
});
|
|
8936
9061
|
CONFIG_DIR = join2(homedir2(), ".hasna", "cloud");
|
|
8937
9062
|
CONFIG_PATH = join2(CONFIG_DIR, "config.json");
|
|
9063
|
+
AUTO_SYNC_CONFIG_PATH = join3(homedir3(), ".hasna", "cloud", "config.json");
|
|
8938
9064
|
});
|
|
8939
9065
|
|
|
8940
9066
|
// src/db/database.ts
|
|
@@ -8950,14 +9076,14 @@ __export(exports_database, {
|
|
|
8950
9076
|
closeDatabase: () => closeDatabase
|
|
8951
9077
|
});
|
|
8952
9078
|
import { existsSync, mkdirSync, cpSync } from "fs";
|
|
8953
|
-
import { dirname, join as
|
|
9079
|
+
import { dirname, join as join4, resolve } from "path";
|
|
8954
9080
|
function isInMemoryDb(path) {
|
|
8955
9081
|
return path === ":memory:" || path.startsWith("file::memory:");
|
|
8956
9082
|
}
|
|
8957
9083
|
function findNearestMementosDb(startDir) {
|
|
8958
9084
|
let dir = resolve(startDir);
|
|
8959
9085
|
while (true) {
|
|
8960
|
-
const candidate =
|
|
9086
|
+
const candidate = join4(dir, ".mementos", "mementos.db");
|
|
8961
9087
|
if (existsSync(candidate))
|
|
8962
9088
|
return candidate;
|
|
8963
9089
|
const parent = dirname(dir);
|
|
@@ -8970,7 +9096,7 @@ function findNearestMementosDb(startDir) {
|
|
|
8970
9096
|
function findGitRoot(startDir) {
|
|
8971
9097
|
let dir = resolve(startDir);
|
|
8972
9098
|
while (true) {
|
|
8973
|
-
if (existsSync(
|
|
9099
|
+
if (existsSync(join4(dir, ".git")))
|
|
8974
9100
|
return dir;
|
|
8975
9101
|
const parent = dirname(dir);
|
|
8976
9102
|
if (parent === dir)
|
|
@@ -8981,10 +9107,10 @@ function findGitRoot(startDir) {
|
|
|
8981
9107
|
}
|
|
8982
9108
|
function migrateGlobalDir() {
|
|
8983
9109
|
const home = process.env["HOME"] || process.env["USERPROFILE"] || "~";
|
|
8984
|
-
const newDir =
|
|
8985
|
-
const oldDir =
|
|
9110
|
+
const newDir = join4(home, ".hasna", "mementos");
|
|
9111
|
+
const oldDir = join4(home, ".mementos");
|
|
8986
9112
|
if (!existsSync(newDir) && existsSync(oldDir)) {
|
|
8987
|
-
mkdirSync(
|
|
9113
|
+
mkdirSync(join4(home, ".hasna"), { recursive: true });
|
|
8988
9114
|
cpSync(oldDir, newDir, { recursive: true });
|
|
8989
9115
|
}
|
|
8990
9116
|
}
|
|
@@ -9000,12 +9126,12 @@ function getDbPath() {
|
|
|
9000
9126
|
if (process.env["MEMENTOS_DB_SCOPE"] === "project") {
|
|
9001
9127
|
const gitRoot = findGitRoot(cwd);
|
|
9002
9128
|
if (gitRoot) {
|
|
9003
|
-
return
|
|
9129
|
+
return join4(gitRoot, ".mementos", "mementos.db");
|
|
9004
9130
|
}
|
|
9005
9131
|
}
|
|
9006
9132
|
migrateGlobalDir();
|
|
9007
9133
|
const home = process.env["HOME"] || process.env["USERPROFILE"] || "~";
|
|
9008
|
-
return
|
|
9134
|
+
return join4(home, ".hasna", "mementos", "mementos.db");
|
|
9009
9135
|
}
|
|
9010
9136
|
function ensureDir(filePath) {
|
|
9011
9137
|
if (isInMemoryDb(filePath))
|
|
@@ -13902,8 +14028,8 @@ var init_export_v1 = __esm(() => {
|
|
|
13902
14028
|
|
|
13903
14029
|
// src/lib/config.ts
|
|
13904
14030
|
import { existsSync as existsSync6, mkdirSync as mkdirSync3, readFileSync as readFileSync3, readdirSync as readdirSync3, writeFileSync, unlinkSync, cpSync as cpSync2 } from "fs";
|
|
13905
|
-
import { homedir as
|
|
13906
|
-
import { basename as basename3, dirname as dirname5, join as
|
|
14031
|
+
import { homedir as homedir4 } from "os";
|
|
14032
|
+
import { basename as basename3, dirname as dirname5, join as join10, resolve as resolve4 } from "path";
|
|
13907
14033
|
function deepMerge(target, source) {
|
|
13908
14034
|
const result = { ...target };
|
|
13909
14035
|
for (const key of Object.keys(source)) {
|
|
@@ -13924,7 +14050,7 @@ function isValidCategory(value) {
|
|
|
13924
14050
|
return VALID_CATEGORIES.includes(value);
|
|
13925
14051
|
}
|
|
13926
14052
|
function loadConfig() {
|
|
13927
|
-
const configPath =
|
|
14053
|
+
const configPath = join10(homedir4(), ".hasna", "mementos", "config.json");
|
|
13928
14054
|
let fileConfig = {};
|
|
13929
14055
|
if (existsSync6(configPath)) {
|
|
13930
14056
|
try {
|
|
@@ -14976,7 +15102,7 @@ var require_package = __commonJS((exports, module) => {
|
|
|
14976
15102
|
author: "Andrei Hasna <andrei@hasna.com>",
|
|
14977
15103
|
license: "Apache-2.0",
|
|
14978
15104
|
dependencies: {
|
|
14979
|
-
"@hasna/cloud": "
|
|
15105
|
+
"@hasna/cloud": "0.1.5",
|
|
14980
15106
|
"@modelcontextprotocol/sdk": "^1.12.1",
|
|
14981
15107
|
chalk: "^5.4.1",
|
|
14982
15108
|
commander: "^13.1.0",
|
|
@@ -19362,11 +19488,11 @@ init_search();
|
|
|
19362
19488
|
// src/lib/project-detect.ts
|
|
19363
19489
|
init_database();
|
|
19364
19490
|
import { existsSync as existsSync2 } from "fs";
|
|
19365
|
-
import { basename, dirname as dirname2, join as
|
|
19491
|
+
import { basename, dirname as dirname2, join as join6, resolve as resolve2 } from "path";
|
|
19366
19492
|
function findGitRoot2(startDir) {
|
|
19367
19493
|
let dir = resolve2(startDir);
|
|
19368
19494
|
while (true) {
|
|
19369
|
-
if (existsSync2(
|
|
19495
|
+
if (existsSync2(join6(dir, ".git")))
|
|
19370
19496
|
return dir;
|
|
19371
19497
|
const parent = dirname2(dir);
|
|
19372
19498
|
if (parent === dir)
|
|
@@ -19452,7 +19578,7 @@ init_database();
|
|
|
19452
19578
|
init_entities();
|
|
19453
19579
|
init_relations();
|
|
19454
19580
|
import { readdirSync, readFileSync, statSync, existsSync as existsSync3 } from "fs";
|
|
19455
|
-
import { join as
|
|
19581
|
+
import { join as join7, resolve as resolve3, relative as relative2, dirname as dirname3, extname, basename as basename2 } from "path";
|
|
19456
19582
|
var DEFAULT_EXTENSIONS = [".ts", ".tsx", ".js", ".jsx", ".mjs", ".py", ".go", ".rs"];
|
|
19457
19583
|
var DEFAULT_EXCLUDES = ["node_modules", ".git", "dist", "build", ".next", "__pycache__", "target", "vendor"];
|
|
19458
19584
|
function parseImports(_filePath, content) {
|
|
@@ -19489,7 +19615,7 @@ function resolveImport(fromFile, importPath, allFiles) {
|
|
|
19489
19615
|
const withExt = base + ext;
|
|
19490
19616
|
if (allFiles.has(withExt))
|
|
19491
19617
|
return withExt;
|
|
19492
|
-
const index =
|
|
19618
|
+
const index = join7(base, `index${ext}`);
|
|
19493
19619
|
if (allFiles.has(index))
|
|
19494
19620
|
return index;
|
|
19495
19621
|
}
|
|
@@ -19507,7 +19633,7 @@ function collectFiles(dir, extensions, excludes) {
|
|
|
19507
19633
|
for (const entry of entries) {
|
|
19508
19634
|
if (excludes.some((e) => entry === e || current.includes(`/${e}/`)))
|
|
19509
19635
|
continue;
|
|
19510
|
-
const full =
|
|
19636
|
+
const full = join7(current, entry);
|
|
19511
19637
|
let stat;
|
|
19512
19638
|
try {
|
|
19513
19639
|
stat = statSync(full);
|
|
@@ -21073,22 +21199,22 @@ async function _processNext() {
|
|
|
21073
21199
|
|
|
21074
21200
|
// src/lib/session-watcher.ts
|
|
21075
21201
|
import { watch, existsSync as existsSync4, statSync as statSync2, readFileSync as readFileSync2 } from "fs";
|
|
21076
|
-
import { join as
|
|
21202
|
+
import { join as join8 } from "path";
|
|
21077
21203
|
import { readdirSync as readdirSync2 } from "fs";
|
|
21078
21204
|
function encodeCwd(cwd) {
|
|
21079
21205
|
return cwd.replace(/\//g, "-");
|
|
21080
21206
|
}
|
|
21081
21207
|
function getProjectsDir() {
|
|
21082
21208
|
const home = process.env["HOME"] || process.env["USERPROFILE"] || "~";
|
|
21083
|
-
return
|
|
21209
|
+
return join8(home, ".claude", "projects");
|
|
21084
21210
|
}
|
|
21085
21211
|
function findActiveSession(projectDir) {
|
|
21086
21212
|
if (!existsSync4(projectDir))
|
|
21087
21213
|
return null;
|
|
21088
21214
|
const files = readdirSync2(projectDir).filter((f) => f.endsWith(".jsonl")).map((f) => ({
|
|
21089
21215
|
name: f,
|
|
21090
|
-
path:
|
|
21091
|
-
mtime: statSync2(
|
|
21216
|
+
path: join8(projectDir, f),
|
|
21217
|
+
mtime: statSync2(join8(projectDir, f)).mtimeMs
|
|
21092
21218
|
})).sort((a, b) => b.mtime - a.mtime);
|
|
21093
21219
|
return files[0]?.path || null;
|
|
21094
21220
|
}
|
|
@@ -21131,7 +21257,7 @@ function processNewLines(filePath, callback) {
|
|
|
21131
21257
|
}
|
|
21132
21258
|
function startSessionWatcher(cwd, callback) {
|
|
21133
21259
|
stopSessionWatcher();
|
|
21134
|
-
const projectDir =
|
|
21260
|
+
const projectDir = join8(getProjectsDir(), encodeCwd(cwd));
|
|
21135
21261
|
const sessionFile = findActiveSession(projectDir);
|
|
21136
21262
|
if (!sessionFile) {
|
|
21137
21263
|
return { sessionFile: null };
|
|
@@ -21364,8 +21490,8 @@ function getRecentlyPushedCount() {
|
|
|
21364
21490
|
// src/lib/session-registry.ts
|
|
21365
21491
|
import { Database as Database2 } from "bun:sqlite";
|
|
21366
21492
|
import { existsSync as existsSync5, mkdirSync as mkdirSync2 } from "fs";
|
|
21367
|
-
import { dirname as dirname4, join as
|
|
21368
|
-
var DB_PATH =
|
|
21493
|
+
import { dirname as dirname4, join as join9 } from "path";
|
|
21494
|
+
var DB_PATH = join9(process.env["HOME"] || process.env["USERPROFILE"] || "~", ".open-sessions-registry.db");
|
|
21369
21495
|
var _db2 = null;
|
|
21370
21496
|
function getDb() {
|
|
21371
21497
|
if (_db2)
|