@qzhuli/qzhuli-cli 0.5.0 → 0.5.2

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 CHANGED
@@ -11203,6 +11203,7 @@ var commands = {
11203
11203
  searchDesc: "Search user",
11204
11204
  searchArgDesc: "Query string (default: Q\u52A9\u53F7)",
11205
11205
  searchByUid: "Search by UID",
11206
+ searchByQNumber: "Search by Q\u52A9\u53F7",
11206
11207
  addDesc: "Add friend by Q\u52A9\u53F7",
11207
11208
  addArgDesc: "Q\u52A9\u53F7",
11208
11209
  searchNotFound: "No user found with that Q\u52A9\u53F7.",
@@ -11216,6 +11217,7 @@ var commands = {
11216
11217
  profileArgDesc: "Query string (default: nickname)",
11217
11218
  profileByUid: "Search by UID",
11218
11219
  profileByRemark: "Search by remark",
11220
+ profileByNickname: "Search by nickname",
11219
11221
  pageOption: "Page number",
11220
11222
  pageSizeOption: "Page size",
11221
11223
  noFriends: "No friends or contacts found.",
@@ -11234,7 +11236,7 @@ var commands = {
11234
11236
  },
11235
11237
  message: {
11236
11238
  desc: "Message operations",
11237
- sendDesc: "Send a message to a conversation via WebSocket IM",
11239
+ sendDesc: "Send a message to a conversation",
11238
11240
  historyDesc: "View message history from a conversation",
11239
11241
  fromOption: "Start from message ID",
11240
11242
  directionOption: "Direction to pull",
@@ -11253,11 +11255,17 @@ var commands = {
11253
11255
  profileArgDesc: "Conversation ID",
11254
11256
  typeOption: "Conversation type (1=private, 2=group, auto-detect if omitted)",
11255
11257
  profileSuccess: "Conversation details retrieved.",
11256
- searchDesc: "Find all conversations with a user by Q\u52A9\u53F7 or UID",
11257
- searchArgDesc: "Query string (default: Q\u52A9\u53F7)",
11258
- searchByUid: "Search by UID",
11258
+ searchDesc: "Find conversations by conversation ID, name, or user",
11259
+ searchArgDesc: "Query string (default: conversation ID)",
11260
+ searchByQNumber: "Search by user Q\u52A9\u53F7",
11261
+ searchByUid: "Search by user UID",
11262
+ searchByName: "Search by conversation name",
11263
+ searchByCid: "Search by conversation ID",
11259
11264
  searchNotFound: "No conversations found with {name}.",
11260
- searchSuccess: "Found {count} conversation(s) with {name}."
11265
+ searchSuccess: "Found {count} conversation(s) with {name}.",
11266
+ searchByIdSuccess: "Found conversation {id}.",
11267
+ searchByNameNotFound: 'No conversations found with name containing "{name}".',
11268
+ searchByNameSuccess: 'Found {count} conversation(s) with name containing "{name}".'
11261
11269
  },
11262
11270
  cache: {
11263
11271
  desc: "Manage local cache",
@@ -11301,7 +11309,7 @@ __export(zh_exports, {
11301
11309
  });
11302
11310
  init_cjs_shims();
11303
11311
  var cli2 = {
11304
- banner: "qz \u2014 CLI for Q\u52A9\u7406."
11312
+ banner: "qz \u2014 CLI for Q \u52A9\u7406\u3002"
11305
11313
  };
11306
11314
  var options2 = {
11307
11315
  version: "\u8F93\u51FA\u7248\u672C\u53F7",
@@ -11320,7 +11328,7 @@ var auth2 = {
11320
11328
  // 扫码登录
11321
11329
  qrLogin: "\u626B\u7801\u767B\u5F55",
11322
11330
  creatingQr: "\u6B63\u5728\u751F\u6210\u4E8C\u7EF4\u7801...",
11323
- qrInstruction: "\u8BF7\u4F7F\u7528Q\u52A9\u7406\u624B\u673AApp\u626B\u63CF\u4E0B\u65B9\u4E8C\u7EF4\u7801",
11331
+ qrInstruction: "\u8BF7\u4F7F\u7528 Q \u52A9\u7406\u624B\u673A App \u626B\u63CF\u4E0B\u65B9\u4E8C\u7EF4\u7801\u3002",
11324
11332
  qrWaiting: "\u7B49\u5F85\u626B\u7801\u4E2D...",
11325
11333
  qrLoginSuccess: "\u767B\u5F55\u6210\u529F\uFF01",
11326
11334
  qrLoginSuccessWithName: "\u767B\u5F55\u6210\u529F\uFF01\u6B22\u8FCE\uFF0C{name}",
@@ -11348,12 +11356,13 @@ var commands2 = {
11348
11356
  user: {
11349
11357
  desc: "\u7528\u6237\u64CD\u4F5C",
11350
11358
  searchDesc: "\u641C\u7D22\u7528\u6237",
11351
- searchArgDesc: "\u67E5\u8BE2\u5185\u5BB9\uFF08\u9ED8\u8BA4\u6309 Q\u52A9\u53F7\uFF09",
11359
+ searchArgDesc: "\u67E5\u8BE2\u5185\u5BB9\uFF08\u9ED8\u8BA4\u6309 Q \u52A9\u53F7\uFF09",
11352
11360
  searchByUid: "\u6309 UID \u641C\u7D22",
11353
- addDesc: "\u901A\u8FC7 Q\u52A9\u53F7\u6DFB\u52A0\u597D\u53CB",
11354
- addArgDesc: "Q\u52A9\u53F7",
11355
- searchNotFound: "\u672A\u627E\u5230\u8BE5 Q\u52A9\u53F7\u7684\u7528\u6237\u3002",
11356
- addNoAgentId: "\u7528\u6237\u6CA1\u6709\u52A9\u7406ID\uFF0C\u65E0\u6CD5\u6DFB\u52A0\u3002",
11361
+ searchByQNumber: "\u6309 Q \u52A9\u53F7\u641C\u7D22",
11362
+ addDesc: "\u901A\u8FC7 Q \u52A9\u53F7\u6DFB\u52A0\u597D\u53CB",
11363
+ addArgDesc: "Q \u52A9\u53F7",
11364
+ searchNotFound: "\u672A\u627E\u5230\u8BE5 Q \u52A9\u53F7\u7684\u7528\u6237\u3002",
11365
+ addNoAgentId: "\u7528\u6237\u6CA1\u6709\u52A9\u7406 ID\uFF0C\u65E0\u6CD5\u6DFB\u52A0\u3002",
11357
11366
  addSuccess: "\u5DF2\u6DFB\u52A0\u597D\u53CB {name}\u3002"
11358
11367
  },
11359
11368
  friend: {
@@ -11363,6 +11372,7 @@ var commands2 = {
11363
11372
  profileArgDesc: "\u67E5\u8BE2\u5185\u5BB9\uFF08\u9ED8\u8BA4\u6309\u6635\u79F0\uFF09",
11364
11373
  profileByUid: "\u6309 UID \u641C\u7D22",
11365
11374
  profileByRemark: "\u6309\u5907\u6CE8\u641C\u7D22",
11375
+ profileByNickname: "\u6309\u6635\u79F0\u641C\u7D22",
11366
11376
  pageOption: "\u9875\u7801",
11367
11377
  pageSizeOption: "\u6BCF\u9875\u6570\u91CF",
11368
11378
  noFriends: "\u672A\u627E\u5230\u597D\u53CB\u6216\u8054\u7CFB\u4EBA\u3002",
@@ -11381,7 +11391,7 @@ var commands2 = {
11381
11391
  },
11382
11392
  message: {
11383
11393
  desc: "\u6D88\u606F\u64CD\u4F5C",
11384
- sendDesc: "\u901A\u8FC7 WebSocket IM \u5411\u4F1A\u8BDD\u53D1\u9001\u6D88\u606F",
11394
+ sendDesc: "\u5411\u4F1A\u8BDD\u53D1\u9001\u6D88\u606F",
11385
11395
  historyDesc: "\u67E5\u770B\u4F1A\u8BDD\u6D88\u606F\u5386\u53F2",
11386
11396
  fromOption: "\u8D77\u59CB\u6D88\u606F ID",
11387
11397
  directionOption: "\u62C9\u53D6\u65B9\u5411",
@@ -11393,18 +11403,24 @@ var commands2 = {
11393
11403
  limitOption: "\u6700\u5927\u4F1A\u8BDD\u6570",
11394
11404
  offsetOption: "\u8DF3\u8FC7\u524D N \u6761\u4F1A\u8BDD",
11395
11405
  createDesc: "\u521B\u5EFA\u4F1A\u8BDD",
11396
- createArgDesc: "\u7528\u6237UID",
11397
- createAgentIdOption: "\u52A9\u7406ID",
11406
+ createArgDesc: "\u7528\u6237 UID",
11407
+ createAgentIdOption: "\u52A9\u7406 ID",
11398
11408
  createSuccess: "\u4F1A\u8BDD\u521B\u5EFA\u6210\u529F\u3002",
11399
11409
  profileDesc: "\u67E5\u8BE2\u4F1A\u8BDD\u8BE6\u60C5",
11400
- profileArgDesc: "\u4F1A\u8BDDID",
11401
- typeOption: "\u4F1A\u8BDD\u7C7B\u578B\uFF081=\u79C1\u804A, 2=\u7FA4\u804A\uFF0C\u4E0D\u4F20\u81EA\u52A8\u5224\u65AD\uFF09",
11410
+ profileArgDesc: "\u4F1A\u8BDD ID",
11411
+ typeOption: "\u4F1A\u8BDD\u7C7B\u578B\uFF081 = \u79C1\u804A, 2 = \u7FA4\u804A\uFF0C\u4E0D\u4F20\u81EA\u52A8\u5224\u65AD\uFF09",
11402
11412
  profileSuccess: "\u5DF2\u83B7\u53D6\u4F1A\u8BDD\u8BE6\u60C5\u3002",
11403
- searchDesc: "\u901A\u8FC7Q\u52A9\u53F7\u6216UID\u67E5\u627E\u4E0E\u67D0\u7528\u6237\u7684\u6240\u6709\u4F1A\u8BDD",
11404
- searchArgDesc: "\u67E5\u8BE2\u5185\u5BB9\uFF08\u9ED8\u8BA4\u6309 Q\u52A9\u53F7\uFF09",
11405
- searchByUid: "\u6309 UID \u641C\u7D22",
11413
+ searchDesc: "\u901A\u8FC7\u4F1A\u8BDD ID\u3001\u4F1A\u8BDD\u540D\u79F0\u6216\u7528\u6237\u67E5\u627E\u4F1A\u8BDD",
11414
+ searchArgDesc: "\u67E5\u8BE2\u5185\u5BB9\uFF08\u9ED8\u8BA4\u6309\u4F1A\u8BDD ID\uFF09",
11415
+ searchByQNumber: "\u6309\u7528\u6237 Q \u52A9\u53F7\u641C\u7D22",
11416
+ searchByUid: "\u6309\u7528\u6237 UID \u641C\u7D22",
11417
+ searchByName: "\u6309\u4F1A\u8BDD\u540D\u79F0\u641C\u7D22",
11418
+ searchByCid: "\u6309\u4F1A\u8BDD ID \u641C\u7D22",
11406
11419
  searchNotFound: "\u672A\u627E\u5230\u4E0E {name} \u7684\u4F1A\u8BDD\u3002",
11407
- searchSuccess: "\u627E\u5230\u4E0E {name} \u7684 {count} \u4E2A\u4F1A\u8BDD\u3002"
11420
+ searchSuccess: "\u627E\u5230\u4E0E {name} \u7684 {count} \u4E2A\u4F1A\u8BDD\u3002",
11421
+ searchByIdSuccess: "\u627E\u5230\u4F1A\u8BDD {id}\u3002",
11422
+ searchByNameNotFound: '\u672A\u627E\u5230\u540D\u79F0\u5305\u542B "{name}" \u7684\u4F1A\u8BDD\u3002',
11423
+ searchByNameSuccess: '\u627E\u5230 {count} \u4E2A\u540D\u79F0\u5305\u542B "{name}" \u7684\u4F1A\u8BDD\u3002'
11408
11424
  },
11409
11425
  cache: {
11410
11426
  desc: "\u7BA1\u7406\u672C\u5730\u7F13\u5B58",
@@ -12557,7 +12573,96 @@ function profileToItem(profile) {
12557
12573
  };
12558
12574
  }
12559
12575
  async function conversationSearchRun(factory, opts) {
12560
- const userResult = opts.byUid ? await factory.repos.user.findUserById(opts.query) : await factory.repos.user.searchUserById(opts.query);
12576
+ if (opts.byCid || !opts.byQNumber && !opts.byUid && !opts.byName) {
12577
+ return searchByConversationId(factory, opts.query);
12578
+ }
12579
+ if (opts.byQNumber) {
12580
+ return searchByQNumber(factory, opts.query);
12581
+ }
12582
+ if (opts.byUid) {
12583
+ return searchByUid(factory, opts.query);
12584
+ }
12585
+ return searchByName(factory, opts.query);
12586
+ }
12587
+ async function searchByConversationId(factory, conversationId) {
12588
+ const profileResult = await factory.repos.conversation.getProfile(conversationId);
12589
+ if (!profileResult.ok) {
12590
+ return {
12591
+ status: "error",
12592
+ code: profileResult.code,
12593
+ message: profileResult.code === "AUTH_FAILED" /* AUTH_FAILED */ ? t("auth.notAuthenticated") : profileResult.message,
12594
+ data: null
12595
+ };
12596
+ }
12597
+ const profile = profileToItem(profileResult.data);
12598
+ const firstUser = profile.users[0];
12599
+ if (!firstUser) {
12600
+ return {
12601
+ status: "success",
12602
+ code: "NOT_FOUND" /* NOT_FOUND */,
12603
+ message: t("commands.conversation.searchNotFound"),
12604
+ data: null
12605
+ };
12606
+ }
12607
+ const result = {
12608
+ id: firstUser.id,
12609
+ uid: firstUser.uid,
12610
+ nickname: firstUser.nickname,
12611
+ conversations: [profile]
12612
+ };
12613
+ return {
12614
+ status: "success",
12615
+ code: "CONVERSATION_LIST" /* CONVERSATION_LIST */,
12616
+ message: t("commands.conversation.searchByIdSuccess").replace("{id}", conversationId),
12617
+ data: result
12618
+ };
12619
+ }
12620
+ async function searchByQNumber(factory, qNumber) {
12621
+ const userResult = await factory.repos.user.searchUserById(qNumber);
12622
+ if (!userResult.ok) {
12623
+ return {
12624
+ status: "error",
12625
+ code: userResult.code,
12626
+ message: userResult.code === "AUTH_FAILED" /* AUTH_FAILED */ ? t("auth.notAuthenticated") : userResult.message,
12627
+ data: null
12628
+ };
12629
+ }
12630
+ if (!userResult.data) {
12631
+ return {
12632
+ status: "error",
12633
+ code: "USER_NOT_FOUND_NEEDS_RESOLUTION" /* USER_NOT_FOUND_NEEDS_RESOLUTION */,
12634
+ message: t("messages.notFound"),
12635
+ data: null
12636
+ };
12637
+ }
12638
+ const targetId = userResult.data.id;
12639
+ const targetUid = userResult.data.uid;
12640
+ const nickname = userResult.data.nickname;
12641
+ const profilesResult = await factory.repos.conversation.searchByUid(targetUid);
12642
+ if (!profilesResult.ok || profilesResult.data.length === 0) {
12643
+ return {
12644
+ status: "success",
12645
+ code: "NOT_FOUND" /* NOT_FOUND */,
12646
+ message: t("commands.conversation.searchNotFound").replace("{name}", nickname),
12647
+ data: null
12648
+ };
12649
+ }
12650
+ const results = profilesResult.data.map(profileToItem);
12651
+ const result = {
12652
+ id: targetId,
12653
+ uid: targetUid,
12654
+ nickname,
12655
+ conversations: results
12656
+ };
12657
+ return {
12658
+ status: "success",
12659
+ code: "CONVERSATION_LIST" /* CONVERSATION_LIST */,
12660
+ message: t("commands.conversation.searchSuccess").replace("{name}", nickname).replace("{count}", String(results.length)),
12661
+ data: result
12662
+ };
12663
+ }
12664
+ async function searchByUid(factory, uid) {
12665
+ const userResult = await factory.repos.user.findUserById(uid);
12561
12666
  if (!userResult.ok) {
12562
12667
  return {
12563
12668
  status: "error",
@@ -12600,6 +12705,72 @@ async function conversationSearchRun(factory, opts) {
12600
12705
  data: result
12601
12706
  };
12602
12707
  }
12708
+ async function searchByName(factory, name) {
12709
+ const listResult = await factory.repos.conversation.queryAll({ limit: 0, offset: 0 });
12710
+ if (!listResult.ok) {
12711
+ return {
12712
+ status: "error",
12713
+ code: listResult.code,
12714
+ message: listResult.code === "AUTH_FAILED" /* AUTH_FAILED */ ? t("auth.notAuthenticated") : listResult.message,
12715
+ data: null
12716
+ };
12717
+ }
12718
+ const conversations3 = listResult.data ?? [];
12719
+ const matchedIds = conversations3.filter((c) => c.name?.includes(name)).map((c) => c.id);
12720
+ if (matchedIds.length === 0) {
12721
+ return {
12722
+ status: "success",
12723
+ code: "NOT_FOUND" /* NOT_FOUND */,
12724
+ message: t("commands.conversation.searchByNameNotFound").replace("{name}", name),
12725
+ data: null
12726
+ };
12727
+ }
12728
+ const profileResults = await Promise.all(
12729
+ matchedIds.map((convId) => factory.repos.conversation.getProfile(convId))
12730
+ );
12731
+ const profiles = profileResults.filter((r) => r.ok).map((r) => r.data);
12732
+ if (profiles.length === 0) {
12733
+ return {
12734
+ status: "success",
12735
+ code: "NOT_FOUND" /* NOT_FOUND */,
12736
+ message: t("commands.conversation.searchByNameNotFound").replace("{name}", name),
12737
+ data: null
12738
+ };
12739
+ }
12740
+ const items = profiles.map(profileToItem);
12741
+ const groupedByUid = /* @__PURE__ */ new Map();
12742
+ for (const item of items) {
12743
+ const primaryUser = item.users[0];
12744
+ if (!primaryUser) continue;
12745
+ const uid = primaryUser.uid;
12746
+ const existing = groupedByUid.get(uid);
12747
+ if (existing) {
12748
+ existing.conversations.push(item);
12749
+ } else {
12750
+ groupedByUid.set(uid, {
12751
+ id: primaryUser.id,
12752
+ uid,
12753
+ nickname: primaryUser.nickname,
12754
+ conversations: [item]
12755
+ });
12756
+ }
12757
+ }
12758
+ const results = [...groupedByUid.values()];
12759
+ if (results.length === 0) {
12760
+ return {
12761
+ status: "success",
12762
+ code: "NOT_FOUND" /* NOT_FOUND */,
12763
+ message: t("commands.conversation.searchByNameNotFound").replace("{name}", name),
12764
+ data: null
12765
+ };
12766
+ }
12767
+ return {
12768
+ status: "success",
12769
+ code: "CONVERSATION_LIST" /* CONVERSATION_LIST */,
12770
+ message: t("commands.conversation.searchByNameSuccess").replace("{name}", name).replace("{count}", String(results.length)),
12771
+ data: results
12772
+ };
12773
+ }
12603
12774
 
12604
12775
  // src/commands/conversation/index.ts
12605
12776
  function NewCmdConversation(factory) {
@@ -12622,10 +12793,18 @@ function NewCmdConversation(factory) {
12622
12793
  const result = await conversationCreateRun(factory, { uid, agentId: opts.agentId });
12623
12794
  handleCommand(result);
12624
12795
  });
12625
- cmd.command("search").description(t("commands.conversation.searchDesc")).argument("<query>", t("commands.conversation.searchArgDesc")).option("--uid", t("commands.conversation.searchByUid")).action(async (query, opts) => {
12626
- const result = await conversationSearchRun(factory, { query, byUid: opts.uid });
12627
- handleCommand(result);
12628
- });
12796
+ cmd.command("search").description(t("commands.conversation.searchDesc")).argument("<query>", t("commands.conversation.searchArgDesc")).option("--qnumber", t("commands.conversation.searchByQNumber")).option("--uid", t("commands.conversation.searchByUid")).option("--name", t("commands.conversation.searchByName")).option("--cid", t("commands.conversation.searchByCid")).action(
12797
+ async (query, opts) => {
12798
+ const result = await conversationSearchRun(factory, {
12799
+ query,
12800
+ byQNumber: opts.qnumber,
12801
+ byUid: opts.uid,
12802
+ byName: opts.name,
12803
+ byCid: opts.cid
12804
+ });
12805
+ handleCommand(result);
12806
+ }
12807
+ );
12629
12808
  return cmd;
12630
12809
  }
12631
12810
 
@@ -12789,14 +12968,17 @@ function NewCmdFriend(factory) {
12789
12968
  const result = await friendListRun(factory);
12790
12969
  handleCommand(result);
12791
12970
  });
12792
- cmd.command("profile").description(t("commands.friend.profileDesc")).argument("<query>", t("commands.friend.profileArgDesc")).option("--uid", t("commands.friend.profileByUid")).option("--remark", t("commands.friend.profileByRemark")).action(async (query, opts) => {
12793
- const result = await friendProfileRun(factory, {
12794
- query,
12795
- byUid: opts.uid,
12796
- byRemark: opts.remark
12797
- });
12798
- handleCommand(result);
12799
- });
12971
+ cmd.command("profile").description(t("commands.friend.profileDesc")).argument("<query>", t("commands.friend.profileArgDesc")).option("--uid", t("commands.friend.profileByUid")).option("--remark", t("commands.friend.profileByRemark")).option("--nickname", t("commands.friend.profileByNickname")).action(
12972
+ async (query, opts) => {
12973
+ const result = await friendProfileRun(factory, {
12974
+ query,
12975
+ byUid: opts.uid,
12976
+ byRemark: opts.remark,
12977
+ byNickname: opts.nickname
12978
+ });
12979
+ handleCommand(result);
12980
+ }
12981
+ );
12800
12982
  return cmd;
12801
12983
  }
12802
12984
 
@@ -13065,8 +13247,12 @@ async function userSearchRun(factory, opts) {
13065
13247
  // src/commands/user/index.ts
13066
13248
  function NewCmdUser(factory) {
13067
13249
  const cmd = new import_commander8.Command("user").description(t("commands.user.desc"));
13068
- cmd.command("search").description(t("commands.user.searchDesc")).argument("<query>", t("commands.user.searchArgDesc")).option("--uid", t("commands.user.searchByUid")).action(async (query, opts) => {
13069
- const result = await userSearchRun(factory, { query, byUid: opts.uid });
13250
+ cmd.command("search").description(t("commands.user.searchDesc")).argument("<query>", t("commands.user.searchArgDesc")).option("--uid", t("commands.user.searchByUid")).option("--qnumber", t("commands.user.searchByQNumber")).action(async (query, opts) => {
13251
+ const result = await userSearchRun(factory, {
13252
+ query,
13253
+ byUid: opts.uid,
13254
+ byQNumber: opts.qnumber
13255
+ });
13070
13256
  handleCommand(result);
13071
13257
  });
13072
13258
  cmd.command("add").description(t("commands.user.addDesc")).argument("<q-number>", t("commands.user.addArgDesc")).action(async (qNumber) => {
@@ -14921,7 +15107,7 @@ async function main() {
14921
15107
  ${t("cli.banner")}` : t("cli.banner");
14922
15108
  program.addHelpText("beforeAll", `${banner}
14923
15109
  `);
14924
- program.name("qz").version(`v${"0.5.0"}`, "-v, --version", t("options.version")).helpOption("-h, --help", t("options.help")).option("-q, --jq <expr>", t("options.jq")).option("--dry-run", t("options.dryRun"));
15110
+ program.name("qz").version(`v${"0.5.2"}`, "-v, --version", t("options.version")).helpOption("-h, --help", t("options.help")).option("-q, --jq <expr>", t("options.jq")).option("--dry-run", t("options.dryRun"));
14925
15111
  program.usage("<command> [subcommand] [options]");
14926
15112
  program.hook("preAction", () => {
14927
15113
  const opts = program.opts();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@qzhuli/qzhuli-cli",
3
- "version": "0.5.0",
3
+ "version": "0.5.2",
4
4
  "description": "CLI tool for Q助理 (QZhuli)",
5
5
  "main": "dist/cmd.js",
6
6
  "bin": {
@@ -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.0
4
+ version: 0.1.2
5
5
  ---
6
6
 
7
7
  # QZhuli CLI
@@ -39,18 +39,11 @@ When ANY of the following applies, STOP and ask the user first:
39
39
  - **Friend operations**: Before `user add`, show the target profile and confirm.
40
40
  - **Relation changes**: Before `relation set`, show the current value and the new value, then confirm.
41
41
  - **Cache clearing**: Before `cache clear`, confirm scope (all tables vs single table).
42
+ - **Message without conversation**: Before `message send`, check if a conversation with the target user exists. If not,
43
+ ask the user whether to create one first.
42
44
  - **Any write operation** (`user add`, `relation set`, `conversation create`, `cache clear`): confirm
43
45
  with user.
44
46
 
45
- ### Use --dry-run for Preview
46
-
47
- Before any write operation the user hasn't explicitly confirmed, run with `--dry-run` first:
48
-
49
- ```bash
50
- qz --dry-run message send <id> "hello"
51
- qz --dry-run relation set <uid> --remark "New Name"
52
- ```
53
-
54
47
  ### Least-Surprise Principle
55
48
 
56
49
  - Never change a friend's remark without showing both old and new.
@@ -69,18 +62,12 @@ When sending messages, determine the role based on the user's wording:
69
62
 
70
63
  The CLI uses 4 distinct ID types. **Using the wrong type will fail silently or hit the wrong target.**
71
64
 
72
- | ID Type | Field Name | Format | Example | Used By |
73
- |-----------------|------------------|-------------------------|------------------------------|---------------------------------------------------------------------------------------------------------------------|
74
- | Q助号 | `id` | Number, short | `10003` | `user add <q-number>`, `user search`, `conversation search` (default) |
75
- | UID | `uid` | 32-char hex string | `d5b6308e3abad6bc96573c58` | `relation get/set`, `friend profile --uid`, `user search --uid`, `conversation search --uid`, `conversation create` |
76
- | Conversation ID | `conversationId` | Base64-like long string | `9boGaR7iii2Jdjhmb5LSo37...` | `message send`, `message history`, `conversation profile` |
77
- | Agent ID | `agent.id` | Number | `5` | `conversation create --agent-id` |
78
-
79
- **Quick identification by format**:
80
-
81
- - A short integer → Q助号
82
- - A 32-char hex string → UID
83
- - A long Base64-like string → conversationId
65
+ | ID Type | Field Name | Format | Example | Used By |
66
+ |-----------------|------------------|-------------------------|------------------------------|--------------------------------------------------------------------------------------------|
67
+ | Q助号 | `id` | Number, short | `10003` | `user add <q-number>`, `user search`, `conversation search` (with `--qnumber`) |
68
+ | UID | `uid` | 32-char hex string | `d5b6308e3abad6bc96573c58` | `relation get/set`, `friend profile --uid`, `user search --uid`, `conversation create` |
69
+ | Conversation ID | `conversationId` | Base64-like long string | `9boGaR7iii2Jdjhmb5LSo37...` | `message send`, `message history`, `conversation profile`, `conversation search` (default) |
70
+ | Agent ID | `agent.id` | Number | `5` | `conversation create --agent-id` |
84
71
 
85
72
  **Common mistake**: Using UID for `message send` instead of conversationId. Always resolve via `conversation search` or
86
73
  `conversation list` first.
@@ -116,37 +103,38 @@ qz --jq ".data" conversation list --limit 5
116
103
 
117
104
  `--jq` is not full jq. Prefer simple paths: `.data`, `.data.uid`, `.data.links`.
118
105
 
119
- Use `--dry-run` to preview without side effects. Wired through: output, HTTP API calls, IM WebSocket actions, auth
120
- login/logout, and preference writes.
121
-
122
106
  ## Command Map
123
107
 
124
- | Goal | Command |
125
- |----------------------------|-----------------------------------------------------------------------------------------------|
126
- | Check login | `qz auth status` |
127
- | QR login | `qz auth login [--method qr-code]` |
128
- | Clear credentials | `qz auth logout` |
129
- | Show preferences | `qz config` |
130
- | Update preferences | `qz config [--locale en\|zh] [--debug\|--no-debug]` |
131
- | Search user (Q助号) | `qz user search <q-number>` |
132
- | Search user (UID) | `qz user search <uid> --uid` |
133
- | Add friend (Q助号) | `qz user add <q-number>` |
134
- | List friends | `qz friend list` |
135
- | Resolve profile (nickname) | `qz friend profile <nickname>` |
136
- | Resolve profile (UID) | `qz friend profile <uid> --uid` |
137
- | Resolve profile (remark) | `qz friend profile <remark> --remark` |
138
- | Read relation | `qz relation get <uid>` |
139
- | Update relation | `qz relation set <uid> [-r, --remark <name>] [-t, --type <type>]` |
140
- | List conversations | `qz conversation list [--limit <n>] [--offset <n>]` |
141
- | Get conversation profile | `qz conversation profile <conversation-id> [--type <n>]` |
142
- | Create conversation | `qz conversation create <uid> --agent-id <id>` |
143
- | Search conversations (Q助号) | `qz conversation search <q-number>` |
144
- | Search conversations (UID) | `qz conversation search <uid> --uid` |
145
- | Send message | `qz message send <conversation-id> <content> [--role <n>]` |
146
- | Read message history | `qz message history <conversation-id> [--from <id>] [--direction newer\|older] [--limit <n>]` |
147
- | Sync cache | `qz cache sync` |
148
- | Cache status | `qz cache status` |
149
- | Clear cache | `qz cache clear [--table <name>]` |
108
+ | Goal | Command |
109
+ |--------------------------------------|-----------------------------------------------------------------------------------------------|
110
+ | Check login | `qz auth status` |
111
+ | QR login | `qz auth login [--method qr-code]` |
112
+ | Clear credentials | `qz auth logout` |
113
+ | Show preferences | `qz config` |
114
+ | Update preferences | `qz config [--locale en\|zh] [--debug\|--no-debug]` |
115
+ | Search user (Q助号) | `qz user search <q-number>` |
116
+ | Search user (Q助号, explicit) | `qz user search <q-number> --qnumber` |
117
+ | Search user (UID) | `qz user search <uid> --uid` |
118
+ | Add friend (Q助号) | `qz user add <q-number>` |
119
+ | List friends | `qz friend list` |
120
+ | Resolve profile (nickname) | `qz friend profile <nickname>` |
121
+ | Resolve profile (nickname, explicit) | `qz friend profile <nickname> --nickname` |
122
+ | Resolve profile (UID) | `qz friend profile <uid> --uid` |
123
+ | Resolve profile (remark) | `qz friend profile <remark> --remark` |
124
+ | Read relation | `qz relation get <uid>` |
125
+ | Update relation | `qz relation set <uid> [-r, --remark <name>] [-t, --type <type>]` |
126
+ | List conversations | `qz conversation list [--limit <n>] [--offset <n>]` |
127
+ | Get conversation profile | `qz conversation profile <conversation-id> [--type <n>]` |
128
+ | Create conversation | `qz conversation create <uid> --agent-id <id>` |
129
+ | Search conversations (ID) | `qz conversation search <conversation-id>` |
130
+ | Search conversations (name) | `qz conversation search <name> --name` |
131
+ | Search conversations (Q助号) | `qz conversation search <q-number> --qnumber` |
132
+ | Search conversations (UID) | `qz conversation search <uid> --uid` |
133
+ | Send message | `qz message send <conversation-id> <content> [--role <n>]` |
134
+ | Read message history | `qz message history <conversation-id> [--from <id>] [--direction newer\|older] [--limit <n>]` |
135
+ | Sync cache | `qz cache sync` |
136
+ | Cache status | `qz cache status` |
137
+ | Clear cache | `qz cache clear [--table <name>]` |
150
138
 
151
139
  Relation type values: `0=stranger`, `1=friend`, `2=family`, `3=colleague`.
152
140
 
@@ -160,8 +148,8 @@ Relation type values: `0=stranger`, `1=friend`, `2=family`, `3=colleague`.
160
148
 
161
149
  ### Update a Friend Relation
162
150
 
163
- 1. Resolve the exact `uid` with `friend list` or `friend profile`.
164
- 2. Show current value to user, confirm the change.
151
+ 1. Resolve the exact `uid` via `friend list` or `friend profile`.
152
+ 2. Show current value, confirm the change.
165
153
  3. Execute: `qz relation set <uid> --remark "New Name" --type 1`
166
154
  4. Verify: `qz relation get <uid>`
167
155
 
@@ -176,18 +164,25 @@ Relation type values: `0=stranger`, `1=friend`, `2=family`, `3=colleague`.
176
164
  ### Find All Conversations with a User
177
165
 
178
166
  ```bash
179
- qz conversation search 10000 # by Q助号 (default)
180
- qz conversation search d5b6308e3abad6bc96573c58 --uid # by UID
167
+ qz conversation search <conversation-id> # default: by conversation ID
168
+ qz conversation search <name> --name # by conversation name
169
+ qz conversation search <q-number> --qnumber # by user Q助号
170
+ qz conversation search <uid> --uid # by user UID
181
171
  ```
182
172
 
183
- Response includes `id` (Q助号), `uid` (internal user ID), and `conversations` with full profile data. Each conversation
184
- entry contains `conversationId`, `isGroup`, `users`, and `visitors`.
173
+ Response includes `id`, `uid`, and `conversations` with full profile data. Each entry contains `conversationId`,
174
+ `isGroup`, `users`, and `visitors`.
185
175
 
186
176
  ### Send a Message
187
177
 
188
178
  1. Confirm auth: `qz auth status`
189
- 2. Get conversations: `qz conversation list --limit 10`
190
- 3. Pick the conversation `id` (conversationId).
179
+ 2. **Check existing conversation**: If you have a `conversationId`, use `conversation search <conversation-id>`
180
+ directly. Otherwise, use `conversation search <q-number> --qnumber`, `conversation search <uid> --uid`,
181
+ or `conversation search <name> --name` to find conversations with the target user.
182
+ 3. **Decision**:
183
+ - If conversations exist → pick the relevant `conversationId`.
184
+ - If no conversation exists → **ask the user first** whether to create one, then
185
+ `qz conversation create <uid> --agent-id <id>`.
191
186
  4. Determine role: if the user says "以我的名义" / "帮我发给" / "替我发送" etc., add `--role 1`; otherwise omit (
192
187
  defaults to Assistant).
193
188
  5. Send: `qz message send <conversation-id> "message text"`
@@ -203,23 +198,7 @@ qz message history <conversation-id> --from <message-id> --direction newer --lim
203
198
 
204
199
  ### Pre-Sync Cache for Offline Speed
205
200
 
206
- ```bash
207
- qz cache sync # fetch all data into local SQLite
208
- qz cache status # verify record counts and sync time
209
- qz conversation search 10000 # now instant from cache
210
- ```
211
-
212
- ## Cache Architecture (Reference)
213
-
214
- Read operations use a **Repository Pattern** with SQLite-backed caching (`~/.qzhuli-cli/cache.db`):
215
-
216
- - **TTL**: conversations 30 min, contacts/relations 5 min, user profiles 1 hour
217
- - **Incremental sync**: only fetches profiles for *new* conversations
218
- - **Cache miss** → auto incremental sync (not full refetch)
219
- - **Write operations** bypass cache, invalidate relevant entries
220
-
221
- Tables: `conversations_index`, `conversation_profiles`, `contacts_cache`, `user_profiles`, `relations_cache`,
222
- `messages_cache`.
201
+ Run `qz cache sync` before heavy read operations. Verify with `qz cache status`.
223
202
 
224
203
  ## Troubleshooting
225
204
 
@@ -231,6 +210,7 @@ Tables: `conversations_index`, `conversation_profiles`, `contacts_cache`, `user_
231
210
  | Too much JSON | Use `--jq ".data"` or another simple dot path |
232
211
  | Need no-op preview | Use `--dry-run` |
233
212
  | Message send fails | Re-check `auth status`, verify conversationId via `conversation list` |
213
+ | Target has no conversation | Ask user to create one, then `conversation create <uid> --agent-id <id>` |
234
214
  | Slow queries | Run `qz cache sync` first (incremental, fast), then retry |
235
215
  | Cache corrupted | `qz cache clear` to reset, then retry (falls back to API) |
236
216
  | Ambiguous search | `status: "needs_resolution"` — refine query with `--uid` or `--remark` flag |