@qzhuli/qzhuli-cli 0.1.0 → 0.2.1

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 H
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/dist/cmd.js CHANGED
@@ -11435,14 +11435,191 @@ function tf(key, values) {
11435
11435
 
11436
11436
  // src/iostreams.ts
11437
11437
  init_cjs_shims();
11438
+
11439
+ // src/internal/config/index.ts
11440
+ init_cjs_shims();
11441
+
11442
+ // src/internal/config/credentials.ts
11443
+ init_cjs_shims();
11444
+ var import_node_fs = require("fs");
11445
+ var import_node_path = require("path");
11446
+ function loadCredentials(configDir) {
11447
+ const path = (0, import_node_path.join)(configDir, "credentials.json");
11448
+ if (!(0, import_node_fs.existsSync)(path)) {
11449
+ return null;
11450
+ }
11451
+ try {
11452
+ const raw = (0, import_node_fs.readFileSync)(path, "utf-8");
11453
+ return JSON.parse(raw);
11454
+ } catch {
11455
+ console.error("[WARN] credentials.json is corrupted, skipping \u2014 please re-authenticate");
11456
+ return null;
11457
+ }
11458
+ }
11459
+ function saveCredentials(configDir, creds) {
11460
+ const path = (0, import_node_path.join)(configDir, "credentials.json");
11461
+ ensureConfigDirectory(configDir);
11462
+ (0, import_node_fs.writeFileSync)(path, JSON.stringify(creds, null, 2), {
11463
+ encoding: "utf-8",
11464
+ mode: 384
11465
+ });
11466
+ }
11467
+ function ensureConfigDirectory(configDir) {
11468
+ if (!(0, import_node_fs.existsSync)(configDir)) {
11469
+ (0, import_node_fs.mkdirSync)(configDir, { recursive: true });
11470
+ }
11471
+ }
11472
+ function clearCredentials(configDir) {
11473
+ const path = (0, import_node_path.join)(configDir, "credentials.json");
11474
+ if ((0, import_node_fs.existsSync)(path)) {
11475
+ (0, import_node_fs.rmSync)(path);
11476
+ }
11477
+ }
11478
+ function hasCredentials(configDir) {
11479
+ const creds = loadCredentials(configDir);
11480
+ return !!(creds?.uid && creds?.tk && creds?.token);
11481
+ }
11482
+
11483
+ // src/internal/config/directory.ts
11484
+ init_cjs_shims();
11485
+ var import_node_os = require("os");
11486
+ var import_node_path2 = require("path");
11487
+ var PROJECT_CONFIG_DIR = (0, import_node_path2.resolve)(process.cwd(), ".qzhuli-cli");
11488
+ var USER_CONFIG_DIR = (0, import_node_path2.resolve)((0, import_node_os.homedir)(), ".qzhuli-cli");
11489
+ function getConfigDirectory(env) {
11490
+ return env === "production" ? USER_CONFIG_DIR : PROJECT_CONFIG_DIR;
11491
+ }
11492
+
11493
+ // src/internal/config/environment.ts
11494
+ init_cjs_shims();
11495
+ var TEST_CONFIG = {
11496
+ name: "test",
11497
+ baseURL: "https://test.client.qzhuli.com",
11498
+ wsURL: "wss://test.im.qzhuli.com/ws"
11499
+ };
11500
+ var PRODUCTION_CONFIG = {
11501
+ name: "production",
11502
+ baseURL: "https://client.qzhuli.com",
11503
+ wsURL: "wss://im.qzhuli.com/ws"
11504
+ };
11505
+ var ENV_MAP = {
11506
+ test: TEST_CONFIG,
11507
+ production: PRODUCTION_CONFIG
11508
+ };
11509
+ function getEnvironmentConfig(name) {
11510
+ return ENV_MAP[name];
11511
+ }
11512
+ function detectEnvironment() {
11513
+ return false ? "test" : "production";
11514
+ }
11515
+
11516
+ // src/internal/config/preferences.ts
11517
+ init_cjs_shims();
11518
+ var import_node_fs2 = require("fs");
11519
+ var import_node_path3 = require("path");
11520
+ var DEFAULTS = {
11521
+ locale: "zh"
11522
+ };
11523
+ function loadPreferences(configDir) {
11524
+ const path = (0, import_node_path3.join)(configDir, "preferences.json");
11525
+ if (!(0, import_node_fs2.existsSync)(path)) {
11526
+ return { ...DEFAULTS };
11527
+ }
11528
+ try {
11529
+ const raw = (0, import_node_fs2.readFileSync)(path, "utf-8");
11530
+ const parsed = JSON.parse(raw);
11531
+ return { ...DEFAULTS, ...parsed };
11532
+ } catch {
11533
+ return { ...DEFAULTS };
11534
+ }
11535
+ }
11536
+ function savePreferences(configDir, updates) {
11537
+ if (isDryRun()) {
11538
+ return;
11539
+ }
11540
+ const current = loadPreferences(configDir);
11541
+ const merged = { ...current, ...updates };
11542
+ const path = (0, import_node_path3.join)(configDir, "preferences.json");
11543
+ ensureConfigDirectory2(configDir);
11544
+ (0, import_node_fs2.writeFileSync)(path, JSON.stringify(merged, null, 2), {
11545
+ encoding: "utf-8",
11546
+ mode: 384
11547
+ });
11548
+ }
11549
+ function ensureConfigDirectory2(configDir) {
11550
+ if (!(0, import_node_fs2.existsSync)(configDir)) {
11551
+ (0, import_node_fs2.mkdirSync)(configDir, { recursive: true });
11552
+ }
11553
+ }
11554
+ function initDefaultPreferences(configDir) {
11555
+ const path = (0, import_node_path3.join)(configDir, "preferences.json");
11556
+ if (!(0, import_node_fs2.existsSync)(path)) {
11557
+ ensureConfigDirectory2(configDir);
11558
+ (0, import_node_fs2.writeFileSync)(path, JSON.stringify(DEFAULTS, null, 2), {
11559
+ encoding: "utf-8",
11560
+ mode: 384
11561
+ });
11562
+ }
11563
+ }
11564
+
11565
+ // src/internal/config/index.ts
11566
+ function loadAppConfig(env) {
11567
+ const envName = env ?? detectEnvironment();
11568
+ const configDir = getConfigDirectory(envName);
11569
+ initDefaultPreferences(configDir);
11570
+ const envConfig = getEnvironmentConfig(envName);
11571
+ const prefs = loadPreferences(configDir);
11572
+ const creds = loadCredentials(configDir);
11573
+ const uid = creds?.uid || null;
11574
+ const tk = creds?.tk || null;
11575
+ const token = creds?.token || null;
11576
+ const cid = creds?.cid || null;
11577
+ return {
11578
+ uid,
11579
+ tk,
11580
+ token,
11581
+ cid,
11582
+ locale: prefs.locale ?? "en",
11583
+ debug: prefs.debug ?? false,
11584
+ baseURL: envConfig.baseURL,
11585
+ wsURL: envConfig.wsURL
11586
+ };
11587
+ }
11588
+
11589
+ // src/internal/config/logs.ts
11590
+ init_cjs_shims();
11591
+ var import_node_fs3 = require("fs");
11592
+ var import_node_path4 = require("path");
11593
+ function appendCliRunLog(configDir, entry, now = /* @__PURE__ */ new Date()) {
11594
+ const logDir = (0, import_node_path4.join)(configDir, "logs");
11595
+ (0, import_node_fs3.mkdirSync)(logDir, { recursive: true });
11596
+ (0, import_node_fs3.appendFileSync)(
11597
+ (0, import_node_path4.join)(logDir, `${formatLocalDate(now)}.log`),
11598
+ `${JSON.stringify(entry)}
11599
+ `,
11600
+ "utf-8"
11601
+ );
11602
+ }
11603
+ function formatLocalDate(value) {
11604
+ const year = value.getFullYear();
11605
+ const month = String(value.getMonth() + 1).padStart(2, "0");
11606
+ const day = String(value.getDate()).padStart(2, "0");
11607
+ return `${year}-${month}-${day}`;
11608
+ }
11609
+
11610
+ // src/iostreams.ts
11438
11611
  var jqFilter = null;
11439
11612
  var dryRun = false;
11613
+ var logDebug = false;
11440
11614
  function setJqFilter(expr) {
11441
11615
  jqFilter = expr;
11442
11616
  }
11443
11617
  function setDryRun(enabled) {
11444
11618
  dryRun = enabled;
11445
11619
  }
11620
+ function setLogDebug(enabled) {
11621
+ logDebug = enabled;
11622
+ }
11446
11623
  function isDryRun() {
11447
11624
  return dryRun;
11448
11625
  }
@@ -11473,11 +11650,34 @@ function printStatus(status) {
11473
11650
  `);
11474
11651
  }
11475
11652
  function handleCommand(result) {
11653
+ appendStatusLog(result);
11476
11654
  printStatus(result);
11477
11655
  if (!isDryRun() && result.status === "error") {
11478
11656
  process.exit(1);
11479
11657
  }
11480
11658
  }
11659
+ function appendStatusLog(status) {
11660
+ const argv = process.argv.slice(2);
11661
+ try {
11662
+ appendCliRunLog(getConfigDirectory(detectEnvironment()), {
11663
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
11664
+ argv,
11665
+ command: argv.join(" "),
11666
+ status: status.status,
11667
+ code: status.code,
11668
+ message: status.message,
11669
+ data: status.data,
11670
+ dryRun
11671
+ });
11672
+ } catch (error) {
11673
+ if (logDebug) {
11674
+ process.stderr.write(
11675
+ `[DEBUG] Failed to write CLI log: ${error instanceof Error ? error.message : String(error)}
11676
+ `
11677
+ );
11678
+ }
11679
+ }
11680
+ }
11481
11681
 
11482
11682
  // src/commands/auth/login.ts
11483
11683
  init_cjs_shims();
@@ -11914,156 +12114,6 @@ function promptRefresh(controller, onReset) {
11914
12114
  });
11915
12115
  }
11916
12116
 
11917
- // src/internal/config/index.ts
11918
- init_cjs_shims();
11919
-
11920
- // src/internal/config/credentials.ts
11921
- init_cjs_shims();
11922
- var import_node_fs = require("fs");
11923
- var import_node_path = require("path");
11924
- function loadCredentials(configDir) {
11925
- const path = (0, import_node_path.join)(configDir, "credentials.json");
11926
- if (!(0, import_node_fs.existsSync)(path)) {
11927
- return null;
11928
- }
11929
- try {
11930
- const raw = (0, import_node_fs.readFileSync)(path, "utf-8");
11931
- return JSON.parse(raw);
11932
- } catch {
11933
- console.error("[WARN] credentials.json is corrupted, skipping \u2014 please re-authenticate");
11934
- return null;
11935
- }
11936
- }
11937
- function saveCredentials(configDir, creds) {
11938
- const path = (0, import_node_path.join)(configDir, "credentials.json");
11939
- ensureConfigDirectory(configDir);
11940
- (0, import_node_fs.writeFileSync)(path, JSON.stringify(creds, null, 2), {
11941
- encoding: "utf-8",
11942
- mode: 384
11943
- });
11944
- }
11945
- function ensureConfigDirectory(configDir) {
11946
- if (!(0, import_node_fs.existsSync)(configDir)) {
11947
- (0, import_node_fs.mkdirSync)(configDir, { recursive: true });
11948
- }
11949
- }
11950
- function clearCredentials(configDir) {
11951
- const path = (0, import_node_path.join)(configDir, "credentials.json");
11952
- if ((0, import_node_fs.existsSync)(path)) {
11953
- (0, import_node_fs.rmSync)(path);
11954
- }
11955
- }
11956
- function hasCredentials(configDir) {
11957
- const creds = loadCredentials(configDir);
11958
- return !!(creds?.uid && creds?.tk && creds?.token);
11959
- }
11960
-
11961
- // src/internal/config/directory.ts
11962
- init_cjs_shims();
11963
- var import_node_os = require("os");
11964
- var import_node_path2 = require("path");
11965
- var PROJECT_CONFIG_DIR = (0, import_node_path2.resolve)(process.cwd(), ".qzhuli-cli");
11966
- var USER_CONFIG_DIR = (0, import_node_path2.resolve)((0, import_node_os.homedir)(), ".qzhuli-cli");
11967
- function getConfigDirectory(env) {
11968
- return env === "production" ? USER_CONFIG_DIR : PROJECT_CONFIG_DIR;
11969
- }
11970
-
11971
- // src/internal/config/environment.ts
11972
- init_cjs_shims();
11973
- var TEST_CONFIG = {
11974
- name: "test",
11975
- baseURL: "https://test.client.qzhuli.com",
11976
- wsURL: "wss://test.im.qzhuli.com/ws"
11977
- };
11978
- var PRODUCTION_CONFIG = {
11979
- name: "production",
11980
- baseURL: "https://client.qzhuli.com",
11981
- wsURL: "wss://im.qzhuli.com/ws"
11982
- };
11983
- var ENV_MAP = {
11984
- test: TEST_CONFIG,
11985
- production: PRODUCTION_CONFIG
11986
- };
11987
- function getEnvironmentConfig(name) {
11988
- return ENV_MAP[name];
11989
- }
11990
- function detectEnvironment() {
11991
- return false ? "test" : "production";
11992
- }
11993
-
11994
- // src/internal/config/preferences.ts
11995
- init_cjs_shims();
11996
- var import_node_fs2 = require("fs");
11997
- var import_node_path3 = require("path");
11998
- var DEFAULTS = {
11999
- locale: "zh"
12000
- };
12001
- function loadPreferences(configDir) {
12002
- const path = (0, import_node_path3.join)(configDir, "preferences.json");
12003
- if (!(0, import_node_fs2.existsSync)(path)) {
12004
- return { ...DEFAULTS };
12005
- }
12006
- try {
12007
- const raw = (0, import_node_fs2.readFileSync)(path, "utf-8");
12008
- const parsed = JSON.parse(raw);
12009
- return { ...DEFAULTS, ...parsed };
12010
- } catch {
12011
- return { ...DEFAULTS };
12012
- }
12013
- }
12014
- function savePreferences(configDir, updates) {
12015
- if (isDryRun()) {
12016
- return;
12017
- }
12018
- const current = loadPreferences(configDir);
12019
- const merged = { ...current, ...updates };
12020
- const path = (0, import_node_path3.join)(configDir, "preferences.json");
12021
- ensureConfigDirectory2(configDir);
12022
- (0, import_node_fs2.writeFileSync)(path, JSON.stringify(merged, null, 2), {
12023
- encoding: "utf-8",
12024
- mode: 384
12025
- });
12026
- }
12027
- function ensureConfigDirectory2(configDir) {
12028
- if (!(0, import_node_fs2.existsSync)(configDir)) {
12029
- (0, import_node_fs2.mkdirSync)(configDir, { recursive: true });
12030
- }
12031
- }
12032
- function initDefaultPreferences(configDir) {
12033
- const path = (0, import_node_path3.join)(configDir, "preferences.json");
12034
- if (!(0, import_node_fs2.existsSync)(path)) {
12035
- ensureConfigDirectory2(configDir);
12036
- (0, import_node_fs2.writeFileSync)(path, JSON.stringify(DEFAULTS, null, 2), {
12037
- encoding: "utf-8",
12038
- mode: 384
12039
- });
12040
- }
12041
- }
12042
-
12043
- // src/internal/config/index.ts
12044
- function loadAppConfig(env) {
12045
- const envName = env ?? detectEnvironment();
12046
- const configDir = getConfigDirectory(envName);
12047
- initDefaultPreferences(configDir);
12048
- const envConfig = getEnvironmentConfig(envName);
12049
- const prefs = loadPreferences(configDir);
12050
- const creds = loadCredentials(configDir);
12051
- const uid = creds?.uid || null;
12052
- const tk = creds?.tk || null;
12053
- const token = creds?.token || null;
12054
- const cid = creds?.cid || null;
12055
- return {
12056
- uid,
12057
- tk,
12058
- token,
12059
- cid,
12060
- locale: prefs.locale ?? "en",
12061
- debug: prefs.debug ?? false,
12062
- baseURL: envConfig.baseURL,
12063
- wsURL: envConfig.wsURL
12064
- };
12065
- }
12066
-
12067
12117
  // src/commands/auth/login.ts
12068
12118
  async function authLoginRun(factory, opts) {
12069
12119
  try {
@@ -13529,7 +13579,7 @@ var IMGateway = class {
13529
13579
  return {
13530
13580
  status: "success",
13531
13581
  code: "CONVERSATION_LIST" /* CONVERSATION_LIST */,
13532
- message: conversations3.length > 0 ? "Conversations ({count}):" : "No conversations found.",
13582
+ message: conversations3.length > 0 ? `Conversations (${conversations3.length}):` : "No conversations found.",
13533
13583
  data: { conversations: conversations3 }
13534
13584
  };
13535
13585
  } catch (error) {
@@ -13589,6 +13639,7 @@ function createFactory(config) {
13589
13639
  }
13590
13640
 
13591
13641
  // src/cmd.ts
13642
+ var logDebugEnabled = false;
13592
13643
  var LocalizedHelp = class extends import_commander8.Help {
13593
13644
  /** Format an argument name with <required> or [optional] brackets. */
13594
13645
  argDisplayName(arg) {
@@ -13629,6 +13680,8 @@ async function main() {
13629
13680
  const envName = detectEnvironment();
13630
13681
  const config = loadAppConfig(envName);
13631
13682
  setLocale(config.locale);
13683
+ logDebugEnabled = config.debug;
13684
+ setLogDebug(config.debug);
13632
13685
  const factory = createFactory(config);
13633
13686
  const program = new import_commander8.Command();
13634
13687
  const testModeBanner = `
@@ -13641,7 +13694,7 @@ async function main() {
13641
13694
  ${t("cli.banner")}` : t("cli.banner");
13642
13695
  program.addHelpText("beforeAll", `${banner}
13643
13696
  `);
13644
- program.name("qz").version(`v${"0.1.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"));
13697
+ program.name("qz").version(`v${"0.2.1"}`, "-v, --version", t("options.version")).helpOption("-h, --help", t("options.help")).option("-q, --jq <expr>", t("options.jq")).option("--dry-run", t("options.dryRun"));
13645
13698
  program.usage("<command> [subcommand] [options]");
13646
13699
  program.hook("preAction", () => {
13647
13700
  const opts = program.opts();
@@ -13667,6 +13720,27 @@ ${t("messages.helpFooter")}
13667
13720
  await program.parseAsync(process.argv);
13668
13721
  }
13669
13722
  main().catch((err) => {
13670
- console.error(`Error: ${err.message}`);
13723
+ const envName = detectEnvironment();
13724
+ const message = err instanceof Error ? err.message : String(err);
13725
+ try {
13726
+ appendCliRunLog(getConfigDirectory(envName), {
13727
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
13728
+ argv: process.argv.slice(2),
13729
+ command: process.argv.slice(2).join(" "),
13730
+ status: "error",
13731
+ code: "INTERNAL_ERROR" /* INTERNAL_ERROR */,
13732
+ message,
13733
+ data: null,
13734
+ dryRun: process.argv.includes("--dry-run")
13735
+ });
13736
+ } catch (logError) {
13737
+ if (logDebugEnabled) {
13738
+ process.stderr.write(
13739
+ `[DEBUG] Failed to write CLI log: ${logError instanceof Error ? logError.message : String(logError)}
13740
+ `
13741
+ );
13742
+ }
13743
+ }
13744
+ console.error(`Error: ${message}`);
13671
13745
  process.exit(1);
13672
13746
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@qzhuli/qzhuli-cli",
3
- "version": "0.1.0",
3
+ "version": "0.2.1",
4
4
  "description": "CLI tool for Q助理 (QZhuli)",
5
5
  "main": "dist/cmd.js",
6
6
  "bin": {
@@ -8,6 +8,7 @@
8
8
  },
9
9
  "files": [
10
10
  "dist",
11
+ "skills",
11
12
  "README.md",
12
13
  "CONTRIBUTING.md"
13
14
  ],
@@ -35,6 +36,7 @@
35
36
  "qzhuli"
36
37
  ],
37
38
  "author": "shootdev <npmjs@aisheshou.com>",
39
+ "license": "MIT",
38
40
  "packageManager": "pnpm@10.33.0",
39
41
  "engines": {
40
42
  "node": ">=18.0.0"
@@ -0,0 +1,166 @@
1
+ ---
2
+ name: qzhuli-cli
3
+ description: Use when operating the QZhuli CLI with qz, including login, auth status, config, friends, relations, users, conversations, messages, JSON filtering, dry-run, command help, and interpreting test-environment banners or config files.
4
+ version: 1
5
+ ---
6
+
7
+ # QZhuli CLI
8
+
9
+ Use this skill as a concise operating manual for the `qz` command.
10
+
11
+ ## First Checks
12
+
13
+ 1. Check whether the user is logged in:
14
+ ```bash
15
+ qz auth status
16
+ ```
17
+ 2. If auth is missing or expired, run QR login and ask the user to scan:
18
+ ```bash
19
+ qz auth login
20
+ ```
21
+ 3. For exact syntax, use command help:
22
+ ```bash
23
+ qz --help
24
+ qz <command> --help
25
+ qz <command> <subcommand> --help
26
+ ```
27
+
28
+ ## Environment and Config Files
29
+
30
+ If the CLI prints a `DEVELOPMENT BUILD` / `Running in TEST environment` banner, it is using the test environment and
31
+ stores config under `./.qzhuli-cli/` relative to the process working directory.
32
+
33
+ | Visible behavior | Environment | Config directory |
34
+ |-------------------|-------------|------------------|
35
+ | Shows test banner | test | `./.qzhuli-cli/` |
36
+ | No test banner | production | `~/.qzhuli-cli/` |
37
+
38
+ Treat `credentials.json` as secret. Preferences live in `preferences.json`.
39
+
40
+ ## Output and Global Options
41
+
42
+ Commands return JSON with `status`, `code`, `message`, and `data`. Check `status`, not only the exit code; ambiguous
43
+ friend lookups can return `status: "needs_resolution"`.
44
+
45
+ Use `--jq` for simple dot-path filtering:
46
+
47
+ ```bash
48
+ qz --jq ".data.uid" auth status
49
+ qz --jq ".data.links" friend list
50
+ qz --jq ".data" conversation list --limit 5
51
+ ```
52
+
53
+ `--jq` is not full jq. Prefer simple paths such as `.data`, `.data.uid`, `.data.links`.
54
+
55
+ Use `--dry-run` when you need to avoid side effects. It is wired through output handling, HTTP API calls, IM WebSocket
56
+ actions, auth login/logout, and preference writes.
57
+
58
+ ## Command Map
59
+
60
+ | Goal | Command |
61
+ |----------------------------|-----------------------------------------------------------------------------------------------|
62
+ | Check login | `qz auth status` |
63
+ | QR login | `qz auth login [--method qr-code]` |
64
+ | Clear credentials | `qz auth logout` |
65
+ | Show preferences | `qz config` |
66
+ | Update preferences | `qz config [--locale en\|zh] [--debug\|--no-debug]` |
67
+ | Search user (Q助号) | `qz user search <query>` |
68
+ | Search user (UID) | `qz user search <query> --uid` |
69
+ | Add friend (Q助号) | `qz user add <q-number>` |
70
+ | List friends | `qz friend list` |
71
+ | Resolve profile (nickname) | `qz friend profile <query>` |
72
+ | Resolve profile (UID) | `qz friend profile <query> --uid` |
73
+ | Resolve profile (remark) | `qz friend profile <query> --remark` |
74
+ | Read relation | `qz relation get <uid>` |
75
+ | Update relation | `qz relation set <uid> [-r, --remark <name>] [-t, --type <type>]` |
76
+ | List conversations | `qz conversation list [--limit <n>]` |
77
+ | Create conversation | `qz conversation create <uid> --agent-id <id>` |
78
+ | Send message | `qz message send <conversation-id> <target-cid> <content>` |
79
+ | Read message history | `qz message history <conversation-id> [--from <id>] [--direction newer\|older] [--limit <n>]` |
80
+
81
+ Relation type values are `0=stranger`, `1=friend`, `2=family`, `3=colleague`.
82
+
83
+ ## Common Workflows
84
+
85
+ ### Find a Friend and Inspect Relation
86
+
87
+ 1. List candidates:
88
+ ```bash
89
+ qz friend list
90
+ ```
91
+ 2. Resolve ambiguous names:
92
+ ```bash
93
+ qz friend profile "<nickname>"
94
+ ```
95
+ 3. Use the resolved `uid`:
96
+ ```bash
97
+ qz relation get <uid>
98
+ ```
99
+
100
+ ### Update a Friend Relation
101
+
102
+ 1. Resolve the exact `uid` first with `friend list` or `friend profile`.
103
+ 2. Confirm with the user before changing data.
104
+ 3. Execute one or both updates:
105
+ ```bash
106
+ qz relation set <uid> --remark "Product Manager" --type 1
107
+ ```
108
+ 4. Verify:
109
+ ```bash
110
+ qz relation get <uid>
111
+ ```
112
+
113
+ Do not run `relation set` without `--remark` or `--type`; the CLI returns `INVALID_ARGUMENT`.
114
+
115
+ ### Search and Add a Friend
116
+
117
+ 1. Search by Q助号:
118
+ ```bash
119
+ qz user search 10000
120
+ ```
121
+ 2. Add directly by Q助号:
122
+ ```bash
123
+ qz user add 10000
124
+ ```
125
+
126
+ ### Send a Message
127
+
128
+ 1. Confirm auth:
129
+ ```bash
130
+ qz auth status
131
+ ```
132
+ 2. Get conversations:
133
+ ```bash
134
+ qz conversation list --limit 10
135
+ ```
136
+ 3. Pick the conversation `id`.
137
+ 4. Pick `target-cid` from that conversation's `cids`; for one-to-one chats this is usually the other participant's cid,
138
+ not the current user's own `cid`.
139
+ 5. Confirm recipient and content with the user if there is any ambiguity.
140
+ 6. Send:
141
+ ```bash
142
+ qz message send <conversation-id> <target-cid> "message text"
143
+ ```
144
+ 7. Verify:
145
+ ```bash
146
+ qz message history <conversation-id> --limit 5
147
+ ```
148
+
149
+ ### Page Through Message History
150
+
151
+ ```bash
152
+ qz message history <conversation-id> --limit 20
153
+ qz message history <conversation-id> --from <message-id> --direction older --limit 20
154
+ qz message history <conversation-id> --from <message-id> --direction newer --limit 20
155
+ ```
156
+
157
+ ## Troubleshooting
158
+
159
+ | Symptom | Action |
160
+ |------------------------|--------------------------------------------------------------------------|
161
+ | Command not found | Confirm `qz` is installed or available on `PATH`. |
162
+ | Auth failure | Run `qz auth status`; then `qz auth login` if needed. |
163
+ | Unexpected language | Run `qz config --locale en` or `qz config --locale zh`. |
164
+ | Too much JSON | Use `--jq ".data"` or another simple dot path. |
165
+ | Need a no-op preview | Use `--dry-run`. |
166
+ | Message send cid error | Re-check `auth status` and choose `target-cid` from `conversation list`. |