@qzhuli/qzhuli-cli 0.5.3 → 0.5.5
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/dist/cmd.js +400 -204
- package/package.json +1 -1
- package/skills/qzhuli-cli/SKILL.md +3 -1
package/dist/cmd.js
CHANGED
|
@@ -12302,37 +12302,46 @@ var import_commander2 = require("commander");
|
|
|
12302
12302
|
// src/commands/cache/clear.ts
|
|
12303
12303
|
init_cjs_shims();
|
|
12304
12304
|
async function cacheClearRun(factory, opts) {
|
|
12305
|
-
|
|
12306
|
-
|
|
12307
|
-
|
|
12308
|
-
|
|
12309
|
-
|
|
12310
|
-
|
|
12311
|
-
|
|
12312
|
-
|
|
12313
|
-
|
|
12314
|
-
|
|
12315
|
-
|
|
12316
|
-
|
|
12317
|
-
|
|
12318
|
-
|
|
12319
|
-
|
|
12320
|
-
|
|
12321
|
-
|
|
12322
|
-
|
|
12323
|
-
|
|
12324
|
-
|
|
12325
|
-
|
|
12326
|
-
|
|
12327
|
-
|
|
12328
|
-
|
|
12305
|
+
try {
|
|
12306
|
+
if (opts.table) {
|
|
12307
|
+
switch (opts.table) {
|
|
12308
|
+
case "conversations":
|
|
12309
|
+
factory.repos.conversation.clear();
|
|
12310
|
+
break;
|
|
12311
|
+
case "contacts":
|
|
12312
|
+
factory.repos.contact.clear();
|
|
12313
|
+
break;
|
|
12314
|
+
case "users":
|
|
12315
|
+
factory.repos.user.clear();
|
|
12316
|
+
break;
|
|
12317
|
+
case "relations":
|
|
12318
|
+
factory.repos.relation.clear();
|
|
12319
|
+
break;
|
|
12320
|
+
case "messages":
|
|
12321
|
+
factory.repos.message.clear();
|
|
12322
|
+
break;
|
|
12323
|
+
default:
|
|
12324
|
+
return {
|
|
12325
|
+
status: "error",
|
|
12326
|
+
code: "INVALID_ARGUMENT" /* INVALID_ARGUMENT */,
|
|
12327
|
+
message: t("commands.cache.unknownTable").replace("{table}", opts.table),
|
|
12328
|
+
data: null
|
|
12329
|
+
};
|
|
12330
|
+
}
|
|
12331
|
+
} else {
|
|
12332
|
+
factory.repos.conversation.clear();
|
|
12333
|
+
factory.repos.contact.clear();
|
|
12334
|
+
factory.repos.user.clear();
|
|
12335
|
+
factory.repos.relation.clear();
|
|
12336
|
+
factory.repos.message.clear();
|
|
12329
12337
|
}
|
|
12330
|
-
}
|
|
12331
|
-
|
|
12332
|
-
|
|
12333
|
-
|
|
12334
|
-
|
|
12335
|
-
|
|
12338
|
+
} catch (error) {
|
|
12339
|
+
return {
|
|
12340
|
+
status: "error",
|
|
12341
|
+
code: "INTERNAL_ERROR" /* INTERNAL_ERROR */,
|
|
12342
|
+
message: error instanceof Error ? error.message : "Failed to clear cache",
|
|
12343
|
+
data: null
|
|
12344
|
+
};
|
|
12336
12345
|
}
|
|
12337
12346
|
return {
|
|
12338
12347
|
status: "success",
|
|
@@ -12345,25 +12354,34 @@ async function cacheClearRun(factory, opts) {
|
|
|
12345
12354
|
// src/commands/cache/status.ts
|
|
12346
12355
|
init_cjs_shims();
|
|
12347
12356
|
async function cacheStatusRun(factory) {
|
|
12348
|
-
|
|
12349
|
-
|
|
12350
|
-
|
|
12351
|
-
|
|
12352
|
-
|
|
12353
|
-
|
|
12354
|
-
|
|
12355
|
-
|
|
12356
|
-
|
|
12357
|
-
|
|
12358
|
-
|
|
12359
|
-
|
|
12360
|
-
|
|
12361
|
-
|
|
12362
|
-
|
|
12363
|
-
|
|
12357
|
+
try {
|
|
12358
|
+
const [convStatus, contactStatus] = await Promise.all([
|
|
12359
|
+
factory.repos.conversation.getStatus(),
|
|
12360
|
+
factory.repos.contact.getStatus()
|
|
12361
|
+
]);
|
|
12362
|
+
return {
|
|
12363
|
+
status: "success",
|
|
12364
|
+
code: "CONFIG_RETRIEVED" /* CONFIG_RETRIEVED */,
|
|
12365
|
+
message: "Cache status:",
|
|
12366
|
+
data: {
|
|
12367
|
+
conversations: {
|
|
12368
|
+
count: convStatus.count,
|
|
12369
|
+
lastSyncAt: convStatus.lastSyncAt ? new Date(convStatus.lastSyncAt).toISOString() : "never"
|
|
12370
|
+
},
|
|
12371
|
+
contacts: {
|
|
12372
|
+
count: contactStatus.count,
|
|
12373
|
+
lastSyncAt: contactStatus.lastSyncAt ? new Date(contactStatus.lastSyncAt).toISOString() : "never"
|
|
12374
|
+
}
|
|
12364
12375
|
}
|
|
12365
|
-
}
|
|
12366
|
-
}
|
|
12376
|
+
};
|
|
12377
|
+
} catch (error) {
|
|
12378
|
+
return {
|
|
12379
|
+
status: "error",
|
|
12380
|
+
code: "INTERNAL_ERROR" /* INTERNAL_ERROR */,
|
|
12381
|
+
message: error instanceof Error ? error.message : "Failed to read cache status",
|
|
12382
|
+
data: null
|
|
12383
|
+
};
|
|
12384
|
+
}
|
|
12367
12385
|
}
|
|
12368
12386
|
|
|
12369
12387
|
// src/commands/cache/sync.ts
|
|
@@ -12727,11 +12745,24 @@ async function searchByName(factory, name) {
|
|
|
12727
12745
|
const profileResults = await Promise.all(
|
|
12728
12746
|
conversations3.map((c) => factory.repos.conversation.getProfile(c.id))
|
|
12729
12747
|
);
|
|
12730
|
-
const
|
|
12748
|
+
const queryLower = name.toLowerCase();
|
|
12749
|
+
const scored = profileResults.filter((r) => r.ok).flatMap((r) => {
|
|
12750
|
+
const p = r.data;
|
|
12731
12751
|
const convName = p.conversation.name ?? "";
|
|
12732
|
-
|
|
12733
|
-
|
|
12734
|
-
|
|
12752
|
+
const teamName = p.conversation.team_name ?? "";
|
|
12753
|
+
const fields = [convName, teamName].filter(Boolean);
|
|
12754
|
+
if (fields.length === 0) return [];
|
|
12755
|
+
let best = 0;
|
|
12756
|
+
for (const f of fields) {
|
|
12757
|
+
const lower = f.toLowerCase();
|
|
12758
|
+
if (lower === queryLower) best = Math.max(best, 3);
|
|
12759
|
+
else if (lower.startsWith(queryLower)) best = Math.max(best, 2);
|
|
12760
|
+
else if (lower.includes(queryLower)) best = Math.max(best, 1);
|
|
12761
|
+
}
|
|
12762
|
+
if (best === 0) return [];
|
|
12763
|
+
return [{ profile: p, score: best }];
|
|
12764
|
+
}).sort((a, b) => b.score - a.score);
|
|
12765
|
+
if (scored.length === 0) {
|
|
12735
12766
|
return {
|
|
12736
12767
|
status: "success",
|
|
12737
12768
|
code: "NOT_FOUND" /* NOT_FOUND */,
|
|
@@ -12739,7 +12770,7 @@ async function searchByName(factory, name) {
|
|
|
12739
12770
|
data: null
|
|
12740
12771
|
};
|
|
12741
12772
|
}
|
|
12742
|
-
const items =
|
|
12773
|
+
const items = scored.map((s) => profileToItem(s.profile));
|
|
12743
12774
|
const groupedByUid = /* @__PURE__ */ new Map();
|
|
12744
12775
|
for (const item of items) {
|
|
12745
12776
|
const primaryUser = item.users[0];
|
|
@@ -12757,7 +12788,19 @@ async function searchByName(factory, name) {
|
|
|
12757
12788
|
});
|
|
12758
12789
|
}
|
|
12759
12790
|
}
|
|
12760
|
-
const results = [...groupedByUid.values()]
|
|
12791
|
+
const results = [...groupedByUid.values()].sort((a, b) => {
|
|
12792
|
+
const maxScoreA = Math.max(
|
|
12793
|
+
...a.conversations.map(
|
|
12794
|
+
(c) => scored.find((s) => s.profile.conversation.conversation_id === c.conversationId)?.score ?? 0
|
|
12795
|
+
)
|
|
12796
|
+
);
|
|
12797
|
+
const maxScoreB = Math.max(
|
|
12798
|
+
...b.conversations.map(
|
|
12799
|
+
(c) => scored.find((s) => s.profile.conversation.conversation_id === c.conversationId)?.score ?? 0
|
|
12800
|
+
)
|
|
12801
|
+
);
|
|
12802
|
+
return maxScoreB - maxScoreA;
|
|
12803
|
+
});
|
|
12761
12804
|
if (results.length === 0) {
|
|
12762
12805
|
return {
|
|
12763
12806
|
status: "success",
|
|
@@ -12846,6 +12889,14 @@ async function friendListRun(factory) {
|
|
|
12846
12889
|
|
|
12847
12890
|
// src/commands/friend/profile.ts
|
|
12848
12891
|
init_cjs_shims();
|
|
12892
|
+
function matchScore(text, queryLower) {
|
|
12893
|
+
if (!text) return 0;
|
|
12894
|
+
const lower = text.toLowerCase();
|
|
12895
|
+
if (lower === queryLower) return 3;
|
|
12896
|
+
if (lower.startsWith(queryLower)) return 2;
|
|
12897
|
+
if (lower.includes(queryLower)) return 1;
|
|
12898
|
+
return 0;
|
|
12899
|
+
}
|
|
12849
12900
|
async function friendProfileRun(factory, opts) {
|
|
12850
12901
|
const listResult = await factory.repos.contact.getLinksContacts();
|
|
12851
12902
|
if (!listResult.ok) {
|
|
@@ -12886,17 +12937,17 @@ async function friendProfileRun(factory, opts) {
|
|
|
12886
12937
|
friend_name: team.name
|
|
12887
12938
|
}))
|
|
12888
12939
|
];
|
|
12889
|
-
const
|
|
12890
|
-
|
|
12891
|
-
|
|
12892
|
-
|
|
12893
|
-
|
|
12894
|
-
|
|
12895
|
-
|
|
12896
|
-
|
|
12897
|
-
|
|
12898
|
-
}
|
|
12899
|
-
if (
|
|
12940
|
+
const queryLower = opts.query.toLowerCase();
|
|
12941
|
+
let scored;
|
|
12942
|
+
if (opts.byUid) {
|
|
12943
|
+
scored = allEntries.filter((e) => e.uid?.toLowerCase() === queryLower).map((e) => ({ ...e, score: 3 }));
|
|
12944
|
+
} else {
|
|
12945
|
+
scored = allEntries.map((e) => {
|
|
12946
|
+
const field = opts.byRemark ? e.friend_name : e.nickname;
|
|
12947
|
+
return { ...e, score: matchScore(field, queryLower) };
|
|
12948
|
+
}).filter((e) => e.score > 0).sort((a, b) => b.score - a.score);
|
|
12949
|
+
}
|
|
12950
|
+
if (scored.length === 0) {
|
|
12900
12951
|
return {
|
|
12901
12952
|
status: "error",
|
|
12902
12953
|
code: "NOT_FOUND" /* NOT_FOUND */,
|
|
@@ -12904,12 +12955,13 @@ async function friendProfileRun(factory, opts) {
|
|
|
12904
12955
|
data: null
|
|
12905
12956
|
};
|
|
12906
12957
|
}
|
|
12907
|
-
if (
|
|
12908
|
-
const suggestions =
|
|
12958
|
+
if (scored.length > 1) {
|
|
12959
|
+
const suggestions = scored.map((m) => ({
|
|
12909
12960
|
uid: m.uid,
|
|
12910
12961
|
nickname: m.nickname ?? "",
|
|
12911
12962
|
remark: m.friend_name ?? "",
|
|
12912
|
-
type: m._type
|
|
12963
|
+
type: m._type,
|
|
12964
|
+
score: m.score
|
|
12913
12965
|
}));
|
|
12914
12966
|
return {
|
|
12915
12967
|
status: "needs_resolution",
|
|
@@ -12918,12 +12970,12 @@ async function friendProfileRun(factory, opts) {
|
|
|
12918
12970
|
data: suggestions
|
|
12919
12971
|
};
|
|
12920
12972
|
}
|
|
12921
|
-
const match =
|
|
12973
|
+
const match = scored[0];
|
|
12922
12974
|
if (match._type !== "link") {
|
|
12923
12975
|
return {
|
|
12924
12976
|
status: "success",
|
|
12925
12977
|
code: "USER_FOUND" /* USER_FOUND */,
|
|
12926
|
-
message: t("messages.found").replace("{name}", match.nickname ?? match.friend_name),
|
|
12978
|
+
message: t("messages.found").replace("{name}", match.nickname ?? match.friend_name ?? ""),
|
|
12927
12979
|
data: {
|
|
12928
12980
|
uid: match.uid,
|
|
12929
12981
|
nickname: match.nickname ?? match.friend_name,
|
|
@@ -14335,33 +14387,53 @@ var SqliteContactRepository = class {
|
|
|
14335
14387
|
this.db = db;
|
|
14336
14388
|
}
|
|
14337
14389
|
async getLinksContacts() {
|
|
14338
|
-
|
|
14339
|
-
|
|
14340
|
-
|
|
14341
|
-
|
|
14342
|
-
|
|
14390
|
+
try {
|
|
14391
|
+
const rows = this.db.prepare(
|
|
14392
|
+
"SELECT owner_uid, data, cached_at FROM contacts_cache ORDER BY cached_at DESC LIMIT 1"
|
|
14393
|
+
).all();
|
|
14394
|
+
if (!rows || rows.length === 0) {
|
|
14395
|
+
return fail("NOT_FOUND" /* NOT_FOUND */, "Contacts not found in cache");
|
|
14396
|
+
}
|
|
14397
|
+
return ok(JSON.parse(rows[0].data));
|
|
14398
|
+
} catch (error) {
|
|
14399
|
+
return fail(
|
|
14400
|
+
"INTERNAL_ERROR" /* INTERNAL_ERROR */,
|
|
14401
|
+
error instanceof Error ? error.message : "Failed to read contacts from cache"
|
|
14402
|
+
);
|
|
14343
14403
|
}
|
|
14344
|
-
return ok(JSON.parse(rows[0].data));
|
|
14345
14404
|
}
|
|
14346
14405
|
upsert(ownerUid, data) {
|
|
14347
|
-
|
|
14406
|
+
try {
|
|
14407
|
+
this.db.prepare("INSERT INTO contacts_cache (owner_uid, data, cached_at) VALUES (?, ?, ?)").run(ownerUid, JSON.stringify(data), Date.now());
|
|
14408
|
+
} catch {
|
|
14409
|
+
}
|
|
14348
14410
|
}
|
|
14349
14411
|
invalidate() {
|
|
14350
|
-
|
|
14412
|
+
try {
|
|
14413
|
+
this.db.exec("DELETE FROM contacts_cache");
|
|
14414
|
+
} catch {
|
|
14415
|
+
}
|
|
14351
14416
|
}
|
|
14352
14417
|
async sync() {
|
|
14353
14418
|
}
|
|
14354
14419
|
clear() {
|
|
14355
|
-
|
|
14356
|
-
|
|
14420
|
+
try {
|
|
14421
|
+
this.db.exec("DELETE FROM contacts_cache");
|
|
14422
|
+
this.db.exec("DELETE FROM cache_metadata WHERE key = 'last_sync_contacts'");
|
|
14423
|
+
} catch {
|
|
14424
|
+
}
|
|
14357
14425
|
}
|
|
14358
14426
|
async getStatus() {
|
|
14359
|
-
|
|
14360
|
-
|
|
14361
|
-
|
|
14362
|
-
|
|
14363
|
-
|
|
14364
|
-
|
|
14427
|
+
try {
|
|
14428
|
+
const count = this.db.prepare("SELECT COUNT(*) as c FROM contacts_cache").get();
|
|
14429
|
+
const meta = this.db.prepare("SELECT value FROM cache_metadata WHERE key = 'last_sync_contacts'").get();
|
|
14430
|
+
return {
|
|
14431
|
+
count: count.c,
|
|
14432
|
+
lastSyncAt: meta ? parseInt(meta.value, 10) : null
|
|
14433
|
+
};
|
|
14434
|
+
} catch {
|
|
14435
|
+
return { count: 0, lastSyncAt: null };
|
|
14436
|
+
}
|
|
14365
14437
|
}
|
|
14366
14438
|
};
|
|
14367
14439
|
var CachedContactRepository = class {
|
|
@@ -14374,17 +14446,23 @@ var CachedContactRepository = class {
|
|
|
14374
14446
|
remote;
|
|
14375
14447
|
ttlMs;
|
|
14376
14448
|
async getLinksContacts() {
|
|
14377
|
-
|
|
14378
|
-
|
|
14379
|
-
|
|
14380
|
-
|
|
14381
|
-
|
|
14449
|
+
try {
|
|
14450
|
+
const cached = await this.local.getLinksContacts();
|
|
14451
|
+
if (cached.ok) {
|
|
14452
|
+
const status = await this.local.getStatus();
|
|
14453
|
+
if (status.lastSyncAt !== null && Date.now() - status.lastSyncAt < this.ttlMs) {
|
|
14454
|
+
return cached;
|
|
14455
|
+
}
|
|
14382
14456
|
}
|
|
14457
|
+
} catch {
|
|
14383
14458
|
}
|
|
14384
14459
|
const remote = await this.remote.getLinksContacts();
|
|
14385
14460
|
if (remote.ok) {
|
|
14386
|
-
|
|
14387
|
-
|
|
14461
|
+
try {
|
|
14462
|
+
this.local.upsert("", remote.data);
|
|
14463
|
+
this.local.db.prepare("INSERT OR REPLACE INTO cache_metadata (key, value) VALUES (?, ?)").run("last_sync_contacts", String(Date.now()));
|
|
14464
|
+
} catch {
|
|
14465
|
+
}
|
|
14388
14466
|
}
|
|
14389
14467
|
return remote;
|
|
14390
14468
|
}
|
|
@@ -14394,8 +14472,11 @@ var CachedContactRepository = class {
|
|
|
14394
14472
|
async sync() {
|
|
14395
14473
|
const result = await this.remote.getLinksContacts();
|
|
14396
14474
|
if (result.ok) {
|
|
14397
|
-
|
|
14398
|
-
|
|
14475
|
+
try {
|
|
14476
|
+
this.local.upsert("", result.data);
|
|
14477
|
+
this.local.db.prepare("INSERT OR REPLACE INTO cache_metadata (key, value) VALUES (?, ?)").run("last_sync_contacts", String(Date.now()));
|
|
14478
|
+
} catch {
|
|
14479
|
+
}
|
|
14399
14480
|
}
|
|
14400
14481
|
}
|
|
14401
14482
|
clear() {
|
|
@@ -14461,38 +14542,62 @@ var SqliteConversationRepository = class {
|
|
|
14461
14542
|
return Promise.resolve(ok([]));
|
|
14462
14543
|
}
|
|
14463
14544
|
getProfile(conversationId) {
|
|
14464
|
-
|
|
14465
|
-
|
|
14545
|
+
try {
|
|
14546
|
+
const row = this.db.prepare("SELECT data FROM conversation_profiles WHERE conversation_id = ?").get(conversationId);
|
|
14547
|
+
if (!row) {
|
|
14548
|
+
return Promise.resolve(
|
|
14549
|
+
fail("NOT_FOUND" /* NOT_FOUND */, "Conversation profile not found in cache")
|
|
14550
|
+
);
|
|
14551
|
+
}
|
|
14552
|
+
return Promise.resolve(ok(JSON.parse(row.data)));
|
|
14553
|
+
} catch (error) {
|
|
14466
14554
|
return Promise.resolve(
|
|
14467
|
-
fail(
|
|
14555
|
+
fail(
|
|
14556
|
+
"INTERNAL_ERROR" /* INTERNAL_ERROR */,
|
|
14557
|
+
error instanceof Error ? error.message : "Failed to read conversation from cache"
|
|
14558
|
+
)
|
|
14468
14559
|
);
|
|
14469
14560
|
}
|
|
14470
|
-
return Promise.resolve(ok(JSON.parse(row.data)));
|
|
14471
14561
|
}
|
|
14472
14562
|
searchByUid(uid) {
|
|
14473
|
-
|
|
14474
|
-
|
|
14475
|
-
|
|
14476
|
-
|
|
14477
|
-
|
|
14563
|
+
try {
|
|
14564
|
+
const rows = this.db.prepare(
|
|
14565
|
+
"SELECT data FROM conversation_profiles WHERE user_ids LIKE ? OR user_ids LIKE ? OR user_ids LIKE ? OR user_ids = ?"
|
|
14566
|
+
).all(`%${uid},%`, `%,${uid},%`, `%,${uid}`, uid);
|
|
14567
|
+
const results = rows.map((r) => JSON.parse(r.data));
|
|
14568
|
+
return Promise.resolve(ok(results));
|
|
14569
|
+
} catch (error) {
|
|
14570
|
+
return Promise.resolve(
|
|
14571
|
+
fail(
|
|
14572
|
+
"INTERNAL_ERROR" /* INTERNAL_ERROR */,
|
|
14573
|
+
error instanceof Error ? error.message : "Failed to search conversations in cache"
|
|
14574
|
+
)
|
|
14575
|
+
);
|
|
14576
|
+
}
|
|
14478
14577
|
}
|
|
14479
14578
|
upsertProfile(conversationId, profile) {
|
|
14480
|
-
|
|
14481
|
-
|
|
14482
|
-
|
|
14483
|
-
|
|
14484
|
-
|
|
14485
|
-
|
|
14579
|
+
try {
|
|
14580
|
+
const user_ids = profile.users.map((u) => u.uid).join(",");
|
|
14581
|
+
const cached_at = Date.now();
|
|
14582
|
+
const data = JSON.stringify(profile);
|
|
14583
|
+
this.db.prepare(
|
|
14584
|
+
"INSERT INTO conversation_profiles (conversation_id, data, user_ids, cached_at) VALUES (?, ?, ?, ?) ON CONFLICT(conversation_id) DO UPDATE SET data=?, user_ids=?, cached_at=?"
|
|
14585
|
+
).run(conversationId, data, user_ids, cached_at, data, user_ids, cached_at);
|
|
14586
|
+
} catch {
|
|
14587
|
+
}
|
|
14486
14588
|
}
|
|
14487
14589
|
// MARK: Conversation Index (for incremental sync)
|
|
14488
14590
|
upsertIndex(conv) {
|
|
14489
|
-
|
|
14490
|
-
|
|
14491
|
-
|
|
14492
|
-
|
|
14493
|
-
|
|
14494
|
-
|
|
14495
|
-
|
|
14591
|
+
try {
|
|
14592
|
+
const members = JSON.stringify(conv.cids);
|
|
14593
|
+
const nicks = conv.nicks ? JSON.stringify(conv.nicks) : null;
|
|
14594
|
+
const im_version = conv.version;
|
|
14595
|
+
const cached_at = Date.now();
|
|
14596
|
+
this.db.prepare(
|
|
14597
|
+
"INSERT INTO conversations_index (conversation_id, im_version, members, nicks, cached_at) VALUES (?, ?, ?, ?, ?) ON CONFLICT(conversation_id) DO UPDATE SET im_version=?, members=?, nicks=?, cached_at=?"
|
|
14598
|
+
).run(conv.id, im_version, members, nicks, cached_at, im_version, members, nicks, cached_at);
|
|
14599
|
+
} catch {
|
|
14600
|
+
}
|
|
14496
14601
|
}
|
|
14497
14602
|
getIndexById(conversationId) {
|
|
14498
14603
|
return this.db.prepare(
|
|
@@ -14500,34 +14605,51 @@ var SqliteConversationRepository = class {
|
|
|
14500
14605
|
).get(conversationId);
|
|
14501
14606
|
}
|
|
14502
14607
|
getAllIds() {
|
|
14503
|
-
|
|
14504
|
-
|
|
14608
|
+
try {
|
|
14609
|
+
const rows = this.db.prepare("SELECT conversation_id FROM conversations_index").all();
|
|
14610
|
+
return rows.map((r) => r.conversation_id);
|
|
14611
|
+
} catch {
|
|
14612
|
+
return [];
|
|
14613
|
+
}
|
|
14505
14614
|
}
|
|
14506
14615
|
deleteStale(conversationIds) {
|
|
14507
|
-
|
|
14508
|
-
|
|
14509
|
-
|
|
14510
|
-
|
|
14616
|
+
try {
|
|
14617
|
+
if (conversationIds.length === 0) return;
|
|
14618
|
+
const placeholders = conversationIds.map(() => "?").join(",");
|
|
14619
|
+
this.db.prepare(`DELETE FROM conversation_profiles WHERE conversation_id NOT IN (${placeholders})`).run(...conversationIds);
|
|
14620
|
+
this.db.prepare(`DELETE FROM conversations_index WHERE conversation_id NOT IN (${placeholders})`).run(...conversationIds);
|
|
14621
|
+
} catch {
|
|
14622
|
+
}
|
|
14511
14623
|
}
|
|
14512
14624
|
invalidate(conversationId) {
|
|
14513
|
-
|
|
14514
|
-
|
|
14625
|
+
try {
|
|
14626
|
+
this.db.prepare("DELETE FROM conversation_profiles WHERE conversation_id = ?").run(conversationId);
|
|
14627
|
+
this.db.prepare("DELETE FROM conversations_index WHERE conversation_id = ?").run(conversationId);
|
|
14628
|
+
} catch {
|
|
14629
|
+
}
|
|
14515
14630
|
}
|
|
14516
14631
|
sync() {
|
|
14517
14632
|
return Promise.resolve();
|
|
14518
14633
|
}
|
|
14519
14634
|
clear() {
|
|
14520
|
-
|
|
14521
|
-
|
|
14522
|
-
|
|
14635
|
+
try {
|
|
14636
|
+
this.db.exec("DELETE FROM conversation_profiles");
|
|
14637
|
+
this.db.exec("DELETE FROM conversations_index");
|
|
14638
|
+
this.db.exec("DELETE FROM cache_metadata WHERE key = 'last_sync_conversations'");
|
|
14639
|
+
} catch {
|
|
14640
|
+
}
|
|
14523
14641
|
}
|
|
14524
14642
|
getStatus() {
|
|
14525
|
-
|
|
14526
|
-
|
|
14527
|
-
|
|
14528
|
-
|
|
14529
|
-
|
|
14530
|
-
|
|
14643
|
+
try {
|
|
14644
|
+
const count = this.db.prepare("SELECT COUNT(*) as c FROM conversation_profiles").get();
|
|
14645
|
+
const meta = this.db.prepare("SELECT value FROM cache_metadata WHERE key = 'last_sync_conversations'").get();
|
|
14646
|
+
return Promise.resolve({
|
|
14647
|
+
count: count.c,
|
|
14648
|
+
lastSyncAt: meta ? parseInt(meta.value, 10) : null
|
|
14649
|
+
});
|
|
14650
|
+
} catch {
|
|
14651
|
+
return Promise.resolve({ count: 0, lastSyncAt: null });
|
|
14652
|
+
}
|
|
14531
14653
|
}
|
|
14532
14654
|
};
|
|
14533
14655
|
var CachedConversationRepository = class {
|
|
@@ -14634,50 +14756,66 @@ var SqliteMessageRepository = class {
|
|
|
14634
14756
|
this.db = db;
|
|
14635
14757
|
}
|
|
14636
14758
|
async pullMessages(conversationId, options3) {
|
|
14637
|
-
|
|
14638
|
-
|
|
14639
|
-
|
|
14640
|
-
|
|
14641
|
-
|
|
14642
|
-
if (
|
|
14643
|
-
|
|
14644
|
-
|
|
14645
|
-
|
|
14646
|
-
|
|
14647
|
-
|
|
14759
|
+
try {
|
|
14760
|
+
const limit = options3?.limit ?? 50;
|
|
14761
|
+
const fromId = options3?.fromMessageId;
|
|
14762
|
+
let sql = "SELECT message_id, data FROM messages_cache WHERE conversation_id = ?";
|
|
14763
|
+
const params = [conversationId];
|
|
14764
|
+
if (fromId) {
|
|
14765
|
+
if (options3?.direction === "newer") {
|
|
14766
|
+
sql += " AND message_id > ?";
|
|
14767
|
+
params.push(fromId);
|
|
14768
|
+
} else {
|
|
14769
|
+
sql += " AND message_id < ?";
|
|
14770
|
+
params.push(fromId);
|
|
14771
|
+
}
|
|
14648
14772
|
}
|
|
14773
|
+
sql += " ORDER BY message_id DESC LIMIT ?";
|
|
14774
|
+
params.push(limit);
|
|
14775
|
+
const rows = this.db.prepare(sql).all(...params);
|
|
14776
|
+
if (rows.length === 0) {
|
|
14777
|
+
return fail("NOT_FOUND" /* NOT_FOUND */, "Messages not found in cache");
|
|
14778
|
+
}
|
|
14779
|
+
return ok({
|
|
14780
|
+
messages: rows.map((r) => JSON.parse(r.data)),
|
|
14781
|
+
finished: rows.length < limit,
|
|
14782
|
+
lastId: rows[rows.length - 1]?.message_id ?? ""
|
|
14783
|
+
});
|
|
14784
|
+
} catch (error) {
|
|
14785
|
+
return fail(
|
|
14786
|
+
"INTERNAL_ERROR" /* INTERNAL_ERROR */,
|
|
14787
|
+
error instanceof Error ? error.message : "Failed to read messages from cache"
|
|
14788
|
+
);
|
|
14649
14789
|
}
|
|
14650
|
-
sql += " ORDER BY message_id DESC LIMIT ?";
|
|
14651
|
-
params.push(limit);
|
|
14652
|
-
const rows = this.db.prepare(sql).all(...params);
|
|
14653
|
-
if (rows.length === 0) {
|
|
14654
|
-
return fail("NOT_FOUND" /* NOT_FOUND */, "Messages not found in cache");
|
|
14655
|
-
}
|
|
14656
|
-
return ok({
|
|
14657
|
-
messages: rows.map((r) => JSON.parse(r.data)),
|
|
14658
|
-
finished: rows.length < limit,
|
|
14659
|
-
lastId: rows[rows.length - 1]?.message_id ?? ""
|
|
14660
|
-
});
|
|
14661
14790
|
}
|
|
14662
14791
|
upsert(conversationId, messageId, data) {
|
|
14663
|
-
|
|
14664
|
-
|
|
14665
|
-
|
|
14666
|
-
|
|
14667
|
-
|
|
14668
|
-
|
|
14669
|
-
|
|
14670
|
-
|
|
14671
|
-
|
|
14672
|
-
|
|
14792
|
+
try {
|
|
14793
|
+
this.db.prepare(
|
|
14794
|
+
"INSERT INTO messages_cache (conversation_id, message_id, data, cached_at) VALUES (?, ?, ?, ?) ON CONFLICT(conversation_id, message_id) DO UPDATE SET data=?, cached_at=?"
|
|
14795
|
+
).run(
|
|
14796
|
+
conversationId,
|
|
14797
|
+
messageId,
|
|
14798
|
+
JSON.stringify(data),
|
|
14799
|
+
Date.now(),
|
|
14800
|
+
JSON.stringify(data),
|
|
14801
|
+
Date.now()
|
|
14802
|
+
);
|
|
14803
|
+
} catch {
|
|
14804
|
+
}
|
|
14673
14805
|
}
|
|
14674
14806
|
invalidate(conversationId) {
|
|
14675
|
-
|
|
14807
|
+
try {
|
|
14808
|
+
this.db.prepare("DELETE FROM messages_cache WHERE conversation_id = ?").run(conversationId);
|
|
14809
|
+
} catch {
|
|
14810
|
+
}
|
|
14676
14811
|
}
|
|
14677
14812
|
async sync(_conversationId) {
|
|
14678
14813
|
}
|
|
14679
14814
|
clear() {
|
|
14680
|
-
|
|
14815
|
+
try {
|
|
14816
|
+
this.db.exec("DELETE FROM messages_cache");
|
|
14817
|
+
} catch {
|
|
14818
|
+
}
|
|
14681
14819
|
}
|
|
14682
14820
|
};
|
|
14683
14821
|
var CachedMessageRepository = class {
|
|
@@ -14743,22 +14881,38 @@ var SqliteRelationRepository = class {
|
|
|
14743
14881
|
this.db = db;
|
|
14744
14882
|
}
|
|
14745
14883
|
async getLinkNameType(friendUid) {
|
|
14746
|
-
|
|
14747
|
-
|
|
14748
|
-
|
|
14884
|
+
try {
|
|
14885
|
+
const row = this.db.prepare("SELECT name, type, cached_at FROM relations_cache WHERE friend_uid = ?").get(friendUid);
|
|
14886
|
+
if (!row) return fail("NOT_FOUND" /* NOT_FOUND */, "Relation not found in cache");
|
|
14887
|
+
return ok({ name: row.name, type: row.type });
|
|
14888
|
+
} catch (error) {
|
|
14889
|
+
return fail(
|
|
14890
|
+
"INTERNAL_ERROR" /* INTERNAL_ERROR */,
|
|
14891
|
+
error instanceof Error ? error.message : "Failed to read relation from cache"
|
|
14892
|
+
);
|
|
14893
|
+
}
|
|
14749
14894
|
}
|
|
14750
14895
|
upsert(friendUid, name, type) {
|
|
14751
|
-
|
|
14752
|
-
|
|
14753
|
-
|
|
14896
|
+
try {
|
|
14897
|
+
this.db.prepare(
|
|
14898
|
+
"INSERT OR REPLACE INTO relations_cache (friend_uid, name, type, cached_at) VALUES (?, ?, ?, ?)"
|
|
14899
|
+
).run(friendUid, name, type, Date.now());
|
|
14900
|
+
} catch {
|
|
14901
|
+
}
|
|
14754
14902
|
}
|
|
14755
14903
|
invalidate(friendUid) {
|
|
14756
|
-
|
|
14904
|
+
try {
|
|
14905
|
+
this.db.prepare("DELETE FROM relations_cache WHERE friend_uid = ?").run(friendUid);
|
|
14906
|
+
} catch {
|
|
14907
|
+
}
|
|
14757
14908
|
}
|
|
14758
14909
|
async sync() {
|
|
14759
14910
|
}
|
|
14760
14911
|
clear() {
|
|
14761
|
-
|
|
14912
|
+
try {
|
|
14913
|
+
this.db.exec("DELETE FROM relations_cache");
|
|
14914
|
+
} catch {
|
|
14915
|
+
}
|
|
14762
14916
|
}
|
|
14763
14917
|
};
|
|
14764
14918
|
var CachedRelationRepository = class {
|
|
@@ -14771,13 +14925,19 @@ var CachedRelationRepository = class {
|
|
|
14771
14925
|
remote;
|
|
14772
14926
|
ttlMs;
|
|
14773
14927
|
async getLinkNameType(friendUid) {
|
|
14774
|
-
|
|
14775
|
-
|
|
14776
|
-
|
|
14928
|
+
try {
|
|
14929
|
+
const row = this.local.db.prepare("SELECT name, type, cached_at FROM relations_cache WHERE friend_uid = ?").get(friendUid);
|
|
14930
|
+
if (row && Date.now() - row.cached_at < this.ttlMs) {
|
|
14931
|
+
return ok({ name: row.name, type: row.type });
|
|
14932
|
+
}
|
|
14933
|
+
} catch {
|
|
14777
14934
|
}
|
|
14778
14935
|
const remote = await this.remote.getLinkNameType(friendUid);
|
|
14779
14936
|
if (remote.ok) {
|
|
14780
|
-
|
|
14937
|
+
try {
|
|
14938
|
+
this.local.upsert(friendUid, remote.data.name, remote.data.type);
|
|
14939
|
+
} catch {
|
|
14940
|
+
}
|
|
14781
14941
|
}
|
|
14782
14942
|
return remote;
|
|
14783
14943
|
}
|
|
@@ -14904,37 +15064,67 @@ var SqliteUserRepository = class {
|
|
|
14904
15064
|
this.db = db;
|
|
14905
15065
|
}
|
|
14906
15066
|
async findUserById(id) {
|
|
14907
|
-
|
|
14908
|
-
|
|
14909
|
-
|
|
15067
|
+
try {
|
|
15068
|
+
const row = this.db.prepare("SELECT data FROM user_profiles WHERE uid = ?").get(id);
|
|
15069
|
+
if (!row) return fail("NOT_FOUND" /* NOT_FOUND */, "User not found in cache");
|
|
15070
|
+
return ok(JSON.parse(row.data));
|
|
15071
|
+
} catch (error) {
|
|
15072
|
+
return fail(
|
|
15073
|
+
"INTERNAL_ERROR" /* INTERNAL_ERROR */,
|
|
15074
|
+
error instanceof Error ? error.message : "Failed to read user from cache"
|
|
15075
|
+
);
|
|
15076
|
+
}
|
|
14910
15077
|
}
|
|
14911
15078
|
async searchUserById(id) {
|
|
14912
|
-
|
|
14913
|
-
|
|
14914
|
-
|
|
14915
|
-
|
|
14916
|
-
|
|
14917
|
-
|
|
14918
|
-
|
|
14919
|
-
|
|
14920
|
-
|
|
14921
|
-
|
|
15079
|
+
try {
|
|
15080
|
+
const row = this.db.prepare("SELECT data FROM user_profiles WHERE uid = ?").get(id);
|
|
15081
|
+
if (!row) return fail("NOT_FOUND" /* NOT_FOUND */, "User not found in cache");
|
|
15082
|
+
const data = JSON.parse(row.data);
|
|
15083
|
+
return ok({
|
|
15084
|
+
id: data.id,
|
|
15085
|
+
uid: data.uid,
|
|
15086
|
+
nickname: data.nickname,
|
|
15087
|
+
avatar: data.avatar,
|
|
15088
|
+
agent: data.agent
|
|
15089
|
+
});
|
|
15090
|
+
} catch (error) {
|
|
15091
|
+
return fail(
|
|
15092
|
+
"INTERNAL_ERROR" /* INTERNAL_ERROR */,
|
|
15093
|
+
error instanceof Error ? error.message : "Failed to search user in cache"
|
|
15094
|
+
);
|
|
15095
|
+
}
|
|
14922
15096
|
}
|
|
14923
15097
|
async getUserAgentProfile(friendUid, _agentId) {
|
|
14924
|
-
|
|
14925
|
-
|
|
14926
|
-
|
|
15098
|
+
try {
|
|
15099
|
+
const row = this.db.prepare("SELECT data FROM user_profiles WHERE uid = ?").get(friendUid);
|
|
15100
|
+
if (!row) return fail("NOT_FOUND" /* NOT_FOUND */, "User profile not found in cache");
|
|
15101
|
+
return ok(JSON.parse(row.data));
|
|
15102
|
+
} catch (error) {
|
|
15103
|
+
return fail(
|
|
15104
|
+
"INTERNAL_ERROR" /* INTERNAL_ERROR */,
|
|
15105
|
+
error instanceof Error ? error.message : "Failed to read user profile from cache"
|
|
15106
|
+
);
|
|
15107
|
+
}
|
|
14927
15108
|
}
|
|
14928
15109
|
upsert(uid, data) {
|
|
14929
|
-
|
|
15110
|
+
try {
|
|
15111
|
+
this.db.prepare("INSERT OR REPLACE INTO user_profiles (uid, data, cached_at) VALUES (?, ?, ?)").run(uid, JSON.stringify(data), Date.now());
|
|
15112
|
+
} catch {
|
|
15113
|
+
}
|
|
14930
15114
|
}
|
|
14931
15115
|
invalidate(uid) {
|
|
14932
|
-
|
|
15116
|
+
try {
|
|
15117
|
+
this.db.prepare("DELETE FROM user_profiles WHERE uid = ?").run(uid);
|
|
15118
|
+
} catch {
|
|
15119
|
+
}
|
|
14933
15120
|
}
|
|
14934
15121
|
async sync() {
|
|
14935
15122
|
}
|
|
14936
15123
|
clear() {
|
|
14937
|
-
|
|
15124
|
+
try {
|
|
15125
|
+
this.db.exec("DELETE FROM user_profiles");
|
|
15126
|
+
} catch {
|
|
15127
|
+
}
|
|
14938
15128
|
}
|
|
14939
15129
|
};
|
|
14940
15130
|
var CachedUserRepository = class {
|
|
@@ -14981,13 +15171,19 @@ var CachedUserRepository = class {
|
|
|
14981
15171
|
);
|
|
14982
15172
|
}
|
|
14983
15173
|
async fetchWithCache(uid, fetch2, store) {
|
|
14984
|
-
|
|
14985
|
-
|
|
14986
|
-
|
|
15174
|
+
try {
|
|
15175
|
+
const row = this.local.db.prepare("SELECT data, cached_at FROM user_profiles WHERE uid = ?").get(uid);
|
|
15176
|
+
if (row && Date.now() - row.cached_at < this.ttlMs) {
|
|
15177
|
+
return ok(JSON.parse(row.data));
|
|
15178
|
+
}
|
|
15179
|
+
} catch (error) {
|
|
14987
15180
|
}
|
|
14988
15181
|
const remote = await fetch2();
|
|
14989
15182
|
if (remote.ok) {
|
|
14990
|
-
|
|
15183
|
+
try {
|
|
15184
|
+
store(remote.data);
|
|
15185
|
+
} catch {
|
|
15186
|
+
}
|
|
14991
15187
|
}
|
|
14992
15188
|
return remote;
|
|
14993
15189
|
}
|
|
@@ -15109,7 +15305,7 @@ async function main() {
|
|
|
15109
15305
|
${t("cli.banner")}` : t("cli.banner");
|
|
15110
15306
|
program.addHelpText("beforeAll", `${banner}
|
|
15111
15307
|
`);
|
|
15112
|
-
program.name("qz").version(`v${"0.5.
|
|
15308
|
+
program.name("qz").version(`v${"0.5.5"}`, "-v, --version", t("options.version")).helpOption("-h, --help", t("options.help")).option("-q, --jq <expr>", t("options.jq")).option("--dry-run", t("options.dryRun"));
|
|
15113
15309
|
program.usage("<command> [subcommand] [options]");
|
|
15114
15310
|
program.hook("preAction", () => {
|
|
15115
15311
|
const opts = program.opts();
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: qzhuli-cli
|
|
3
3
|
description: Use when operating the QZhuli CLI (`qz`), including login, auth status, config, friends, relations, users, conversations, messages, cache management, JSON filtering, dry-run, command help, and interpreting test-environment banners or config files.
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.3
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
# QZhuli CLI
|
|
@@ -127,6 +127,7 @@ qz --jq ".data" conversation list --limit 5
|
|
|
127
127
|
| Get conversation profile | `qz conversation profile <conversation-id> [--type <n>]` |
|
|
128
128
|
| Create conversation | `qz conversation create <uid> --agent-id <id>` |
|
|
129
129
|
| Search conversations (ID) | `qz conversation search <conversation-id>` |
|
|
130
|
+
| Search conversations (CID) | `qz conversation search <cid> --cid` |
|
|
130
131
|
| Search conversations (name) | `qz conversation search <name> --name` |
|
|
131
132
|
| Search conversations (Q助号) | `qz conversation search <q-number> --qnumber` |
|
|
132
133
|
| Search conversations (UID) | `qz conversation search <uid> --uid` |
|
|
@@ -165,6 +166,7 @@ Relation type values: `0=stranger`, `1=friend`, `2=family`, `3=colleague`.
|
|
|
165
166
|
|
|
166
167
|
```bash
|
|
167
168
|
qz conversation search <conversation-id> # default: by conversation ID
|
|
169
|
+
qz conversation search <cid> --cid # by user CID
|
|
168
170
|
qz conversation search <name> --name # by conversation name
|
|
169
171
|
qz conversation search <q-number> --qnumber # by user Q助号
|
|
170
172
|
qz conversation search <uid> --uid # by user UID
|