@qzhuli/qzhuli-cli 0.4.3 → 0.5.0
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 -20
- package/dist/cmd.js +273 -159
- package/package.json +1 -1
- package/scripts/install-skill.mjs +36 -2
- package/skills/qzhuli-cli/SKILL.md +35 -31
package/README.md
CHANGED
|
@@ -28,38 +28,35 @@ npx qz --version
|
|
|
28
28
|
The CLI ships with a `SKILL.md` that teaches AI agents how to use it. During `npm install -g`, the skill is
|
|
29
29
|
automatically installed to your agent's skill directory.
|
|
30
30
|
|
|
31
|
-
###
|
|
31
|
+
### Install skill to your agent
|
|
32
32
|
|
|
33
|
-
|
|
34
|
-
|
|
33
|
+
```bash
|
|
34
|
+
npx -p @qzhuli/qzhuli-cli qz-install-skill --target <your-agent-skill-dir>
|
|
35
|
+
```
|
|
35
36
|
|
|
36
|
-
|
|
37
|
+
Replace `<your-agent-skill-dir>` with your agent's skill path (e.g. `~/.claude/skills` for Claude Code, `~/.agents/skills` as universal fallback).
|
|
37
38
|
|
|
38
|
-
|
|
39
|
+
### Check skill version
|
|
39
40
|
|
|
40
41
|
```bash
|
|
41
|
-
|
|
42
|
-
|
|
42
|
+
npx -p @qzhuli/qzhuli-cli qz-install-skill --version
|
|
43
|
+
```
|
|
43
44
|
|
|
44
|
-
|
|
45
|
-
npx -p @qzhuli/qzhuli-cli qz-install-skill --project
|
|
45
|
+
### Update installed skills
|
|
46
46
|
|
|
47
|
-
|
|
48
|
-
npx -p @qzhuli/qzhuli-cli qz-install-skill --agent claude-code
|
|
47
|
+
If a newer version is available, update all installed skills:
|
|
49
48
|
|
|
50
|
-
|
|
49
|
+
```bash
|
|
51
50
|
npx -p @qzhuli/qzhuli-cli qz-install-skill --update
|
|
52
51
|
```
|
|
53
52
|
|
|
54
|
-
###
|
|
53
|
+
### Verify installation
|
|
55
54
|
|
|
56
|
-
|
|
57
|
-
npm update -g @qzhuli/qzhuli-cli
|
|
58
|
-
npx -p @qzhuli/qzhuli-cli qz-install-skill --update
|
|
59
|
-
```
|
|
55
|
+
After install or update, confirm everything works:
|
|
60
56
|
|
|
61
|
-
|
|
62
|
-
|
|
57
|
+
1. `qz --version` — CLI version matches the installed package
|
|
58
|
+
2. `npx -p @qzhuli/qzhuli-cli qz-install-skill --version` — skill version is current
|
|
59
|
+
3. Check that `SKILL.md` exists in your agent's skill directory and contains valid `name` and `version` fields
|
|
63
60
|
|
|
64
61
|
## Commands
|
|
65
62
|
|
|
@@ -92,7 +89,7 @@ qz conversation list [--limit <n>] [--offset <n>] # List conversations, def
|
|
|
92
89
|
qz conversation profile <conversation-id> [--type <n>] # Get conversation details with user profiles
|
|
93
90
|
|
|
94
91
|
# Messages
|
|
95
|
-
qz message send <conversation-id> <
|
|
92
|
+
qz message send <conversation-id> <content> [--role <n>] # 0=Assistant(default), 1=User
|
|
96
93
|
qz message history <conversation-id> [--from <id>] [--direction newer|older] [--limit <n>]
|
|
97
94
|
```
|
|
98
95
|
|
package/dist/cmd.js
CHANGED
|
@@ -11529,12 +11529,14 @@ init_cjs_shims();
|
|
|
11529
11529
|
var TEST_CONFIG = {
|
|
11530
11530
|
name: "test",
|
|
11531
11531
|
baseURL: "https://test.client.qzhuli.com",
|
|
11532
|
-
|
|
11532
|
+
imBaseURL: "https://test.im.qzhuli.com",
|
|
11533
|
+
imWsURL: "wss://test.im.qzhuli.com/ws"
|
|
11533
11534
|
};
|
|
11534
11535
|
var PRODUCTION_CONFIG = {
|
|
11535
11536
|
name: "production",
|
|
11536
11537
|
baseURL: "https://client.qzhuli.com",
|
|
11537
|
-
|
|
11538
|
+
imBaseURL: "https://im.qzhuli.com",
|
|
11539
|
+
imWsURL: "wss://im.qzhuli.com/ws"
|
|
11538
11540
|
};
|
|
11539
11541
|
var ENV_MAP = {
|
|
11540
11542
|
test: TEST_CONFIG,
|
|
@@ -11616,7 +11618,8 @@ function loadAppConfig(env) {
|
|
|
11616
11618
|
locale: prefs.locale ?? "en",
|
|
11617
11619
|
debug: prefs.debug ?? false,
|
|
11618
11620
|
baseURL: envConfig.baseURL,
|
|
11619
|
-
|
|
11621
|
+
imBaseURL: envConfig.imBaseURL,
|
|
11622
|
+
imWsURL: envConfig.imWsURL
|
|
11620
11623
|
};
|
|
11621
11624
|
}
|
|
11622
11625
|
|
|
@@ -12829,12 +12832,8 @@ async function messageHistoryRun(factory, opts) {
|
|
|
12829
12832
|
// src/commands/message/send.ts
|
|
12830
12833
|
init_cjs_shims();
|
|
12831
12834
|
async function messageSendRun(factory, opts) {
|
|
12832
|
-
const
|
|
12833
|
-
|
|
12834
|
-
opts.targetCid,
|
|
12835
|
-
opts.content
|
|
12836
|
-
);
|
|
12837
|
-
if (result.status === "error" && result.code === "AUTH_FAILED" /* AUTH_FAILED */) {
|
|
12835
|
+
const cid = factory.config.cid;
|
|
12836
|
+
if (!cid) {
|
|
12838
12837
|
return {
|
|
12839
12838
|
status: "error",
|
|
12840
12839
|
code: "AUTH_FAILED" /* AUTH_FAILED */,
|
|
@@ -12842,14 +12841,39 @@ async function messageSendRun(factory, opts) {
|
|
|
12842
12841
|
data: null
|
|
12843
12842
|
};
|
|
12844
12843
|
}
|
|
12845
|
-
|
|
12844
|
+
const req = {
|
|
12845
|
+
sender_cid: cid,
|
|
12846
|
+
conv_id: opts.conversationId,
|
|
12847
|
+
content: opts.content,
|
|
12848
|
+
role: opts.role ?? 0,
|
|
12849
|
+
msg_type: 0 /* Text */
|
|
12850
|
+
};
|
|
12851
|
+
const result = await factory.gateway.pushMessage(req);
|
|
12852
|
+
if (!result.ok) {
|
|
12853
|
+
return {
|
|
12854
|
+
status: "error",
|
|
12855
|
+
code: result.code,
|
|
12856
|
+
message: result.message,
|
|
12857
|
+
data: null
|
|
12858
|
+
};
|
|
12859
|
+
}
|
|
12860
|
+
return {
|
|
12861
|
+
status: "success",
|
|
12862
|
+
code: "MESSAGE_SENT" /* MESSAGE_SENT */,
|
|
12863
|
+
message: result.data.message,
|
|
12864
|
+
data: null
|
|
12865
|
+
};
|
|
12846
12866
|
}
|
|
12847
12867
|
|
|
12848
12868
|
// src/commands/message/index.ts
|
|
12849
12869
|
function NewCmdMessage(factory) {
|
|
12850
12870
|
const cmd = new import_commander6.Command("message").description(t("commands.message.desc"));
|
|
12851
|
-
cmd.command("send <conversation-id> <
|
|
12852
|
-
const result = await messageSendRun(factory, {
|
|
12871
|
+
cmd.command("send <conversation-id> <content>").description(t("commands.message.sendDesc")).option("--role <n>", "Sender role: 0=Assistant, 1=User (default 0)", "0").action(async (conversationId, content, options3) => {
|
|
12872
|
+
const result = await messageSendRun(factory, {
|
|
12873
|
+
conversationId,
|
|
12874
|
+
content,
|
|
12875
|
+
role: parseInt(options3.role, 10)
|
|
12876
|
+
});
|
|
12853
12877
|
handleCommand(result);
|
|
12854
12878
|
});
|
|
12855
12879
|
cmd.command("history <conversation-id>").description(t("commands.message.historyDesc")).option("--from <id>", t("commands.message.fromOption")).option("--direction <newer|older>", t("commands.message.directionOption"), "older").option("--limit <n>", t("commands.message.limitOption"), "20").action(async (conversationId, options3) => {
|
|
@@ -13055,150 +13079,7 @@ function NewCmdUser(factory) {
|
|
|
13055
13079
|
// src/factory.ts
|
|
13056
13080
|
init_cjs_shims();
|
|
13057
13081
|
|
|
13058
|
-
// src/internal/api/api-
|
|
13059
|
-
init_cjs_shims();
|
|
13060
|
-
var ApiGateway = class {
|
|
13061
|
-
constructor(client) {
|
|
13062
|
-
this.client = client;
|
|
13063
|
-
}
|
|
13064
|
-
client;
|
|
13065
|
-
setDryRun(enabled) {
|
|
13066
|
-
this.client.setDryRun(enabled);
|
|
13067
|
-
}
|
|
13068
|
-
/** Check auth and return uid or a fail result. */
|
|
13069
|
-
requireAuth() {
|
|
13070
|
-
const uid = this.client.getAuthUid();
|
|
13071
|
-
if (!uid) {
|
|
13072
|
-
return {
|
|
13073
|
-
ok: false,
|
|
13074
|
-
code: "AUTH_FAILED" /* AUTH_FAILED */,
|
|
13075
|
-
message: "Authentication required."
|
|
13076
|
-
};
|
|
13077
|
-
}
|
|
13078
|
-
return { ok: true, data: uid };
|
|
13079
|
-
}
|
|
13080
|
-
// MARK: Friends / Contacts
|
|
13081
|
-
/** POST /user/get_links_contacts — auth required */
|
|
13082
|
-
getLinksContacts() {
|
|
13083
|
-
const auth3 = this.requireAuth();
|
|
13084
|
-
if (!auth3.ok) return Promise.resolve(auth3);
|
|
13085
|
-
return this.client.performRequest("/user/get_links_contacts", "POST", {
|
|
13086
|
-
uid: auth3.data
|
|
13087
|
-
});
|
|
13088
|
-
}
|
|
13089
|
-
// MARK: Relation
|
|
13090
|
-
/** POST /user/get_link_name_type — auth required, body: uid + friend_uid */
|
|
13091
|
-
getLinkNameType(friendUid) {
|
|
13092
|
-
const auth3 = this.requireAuth();
|
|
13093
|
-
if (!auth3.ok) return Promise.resolve(auth3);
|
|
13094
|
-
return this.client.performRequest("/user/get_link_name_type", "POST", {
|
|
13095
|
-
uid: auth3.data,
|
|
13096
|
-
friend_uid: friendUid
|
|
13097
|
-
});
|
|
13098
|
-
}
|
|
13099
|
-
/** POST /user/update_link_name — auth required */
|
|
13100
|
-
updateLinkName(friendUid, remark) {
|
|
13101
|
-
const auth3 = this.requireAuth();
|
|
13102
|
-
if (!auth3.ok) return Promise.resolve(auth3);
|
|
13103
|
-
return this.client.performSimpleRequest("/user/update_link_name", "POST", {
|
|
13104
|
-
uid: auth3.data,
|
|
13105
|
-
friend_uid: friendUid,
|
|
13106
|
-
link_name: remark
|
|
13107
|
-
});
|
|
13108
|
-
}
|
|
13109
|
-
/** POST /user/update_link_type — auth required, body: friend_uid + link_type (string) */
|
|
13110
|
-
updateLinkType(friendUid, groupType) {
|
|
13111
|
-
const auth3 = this.requireAuth();
|
|
13112
|
-
if (!auth3.ok) return Promise.resolve(auth3);
|
|
13113
|
-
return this.client.performSimpleRequest("/user/update_link_type", "POST", {
|
|
13114
|
-
friend_uid: friendUid,
|
|
13115
|
-
link_type: String(groupType)
|
|
13116
|
-
});
|
|
13117
|
-
}
|
|
13118
|
-
// MARK: User
|
|
13119
|
-
/** POST /user/user_agent_profile — auth required, body: friend_uid + uid (+ agent_id) */
|
|
13120
|
-
findUserById(id) {
|
|
13121
|
-
const auth3 = this.requireAuth();
|
|
13122
|
-
if (!auth3.ok) return Promise.resolve(auth3);
|
|
13123
|
-
return this.client.performRequest("/user/user_agent_profile", "POST", {
|
|
13124
|
-
friend_uid: id,
|
|
13125
|
-
uid: auth3.data
|
|
13126
|
-
});
|
|
13127
|
-
}
|
|
13128
|
-
/** POST /user/user_agent_profile — auth required, body: friend_uid + uid (+ agent_id) */
|
|
13129
|
-
getUserAgentProfile(friendUid, agentId) {
|
|
13130
|
-
const auth3 = this.requireAuth();
|
|
13131
|
-
if (!auth3.ok) return Promise.resolve(auth3);
|
|
13132
|
-
const params = {
|
|
13133
|
-
friend_uid: friendUid,
|
|
13134
|
-
uid: auth3.data
|
|
13135
|
-
};
|
|
13136
|
-
if (agentId) {
|
|
13137
|
-
params.agent_id = agentId;
|
|
13138
|
-
}
|
|
13139
|
-
return this.client.performRequest("/user/user_agent_profile", "POST", params);
|
|
13140
|
-
}
|
|
13141
|
-
// MARK: QR Login
|
|
13142
|
-
/** POST /user/qr_login_create/3 — no auth required */
|
|
13143
|
-
qrLoginCreate() {
|
|
13144
|
-
return this.client.performUnauthenticated(
|
|
13145
|
-
"/user/qr_login_create/3",
|
|
13146
|
-
"POST",
|
|
13147
|
-
{}
|
|
13148
|
-
);
|
|
13149
|
-
}
|
|
13150
|
-
/** POST /user/qr_login_status — no auth required */
|
|
13151
|
-
qrLoginStatus(sceneId) {
|
|
13152
|
-
return this.client.performUnauthenticated("/user/qr_login_status", "POST", {
|
|
13153
|
-
scene_id: sceneId
|
|
13154
|
-
});
|
|
13155
|
-
}
|
|
13156
|
-
// MARK: Connectivity
|
|
13157
|
-
/** Quick connectivity check — calls the simplest endpoint. */
|
|
13158
|
-
async ping() {
|
|
13159
|
-
const result = await this.getLinksContacts();
|
|
13160
|
-
if (result.ok) {
|
|
13161
|
-
return { ok: true, data: true };
|
|
13162
|
-
}
|
|
13163
|
-
return result;
|
|
13164
|
-
}
|
|
13165
|
-
// MARK: Friend Add
|
|
13166
|
-
/** POST /user/find_user_by_id — search any user by Q助号 */
|
|
13167
|
-
searchUserById(id) {
|
|
13168
|
-
return this.client.performRequest("/user/find_user_by_id", "POST", {
|
|
13169
|
-
id
|
|
13170
|
-
});
|
|
13171
|
-
}
|
|
13172
|
-
/** POST /user/create_conversation — auth required, body: uid + friend_uid + friend_agent_id */
|
|
13173
|
-
createConversation(friendUid, friendAgentId) {
|
|
13174
|
-
const auth3 = this.requireAuth();
|
|
13175
|
-
if (!auth3.ok) return Promise.resolve(auth3);
|
|
13176
|
-
return this.client.performRequest("/user/create_conversation", "POST", {
|
|
13177
|
-
uid: auth3.data,
|
|
13178
|
-
friend_uid: friendUid,
|
|
13179
|
-
friend_agent_id: friendAgentId
|
|
13180
|
-
});
|
|
13181
|
-
}
|
|
13182
|
-
/** POST /user/get_profile_by_conversation_id — auth required */
|
|
13183
|
-
getProfileByConversationId(conversationId, conversationType) {
|
|
13184
|
-
const auth3 = this.requireAuth();
|
|
13185
|
-
if (!auth3.ok) return Promise.resolve(auth3);
|
|
13186
|
-
const params = {
|
|
13187
|
-
uid: auth3.data,
|
|
13188
|
-
conversation_id: conversationId
|
|
13189
|
-
};
|
|
13190
|
-
if (conversationType !== void 0) {
|
|
13191
|
-
params.conversation_type = conversationType;
|
|
13192
|
-
}
|
|
13193
|
-
return this.client.performRequest(
|
|
13194
|
-
"/user/get_profile_by_conversation_id",
|
|
13195
|
-
"POST",
|
|
13196
|
-
params
|
|
13197
|
-
);
|
|
13198
|
-
}
|
|
13199
|
-
};
|
|
13200
|
-
|
|
13201
|
-
// src/internal/api-client.ts
|
|
13082
|
+
// src/internal/api/api-client.ts
|
|
13202
13083
|
init_cjs_shims();
|
|
13203
13084
|
var import_node_crypto = require("crypto");
|
|
13204
13085
|
var APIClient = class {
|
|
@@ -13378,6 +13259,238 @@ function printDryRun(url, method, body) {
|
|
|
13378
13259
|
console.log();
|
|
13379
13260
|
}
|
|
13380
13261
|
|
|
13262
|
+
// src/internal/api/api-gateway.ts
|
|
13263
|
+
init_cjs_shims();
|
|
13264
|
+
var ApiGateway = class {
|
|
13265
|
+
constructor(client) {
|
|
13266
|
+
this.client = client;
|
|
13267
|
+
}
|
|
13268
|
+
client;
|
|
13269
|
+
imApiClient = null;
|
|
13270
|
+
/** Inject the IM HTTP client after gateway creation. */
|
|
13271
|
+
setIMApiClient(client) {
|
|
13272
|
+
this.imApiClient = client;
|
|
13273
|
+
}
|
|
13274
|
+
setDryRun(enabled) {
|
|
13275
|
+
this.client.setDryRun(enabled);
|
|
13276
|
+
this.imApiClient?.setDryRun(enabled);
|
|
13277
|
+
}
|
|
13278
|
+
/** Check auth and return uid or a fail result. */
|
|
13279
|
+
requireAuth() {
|
|
13280
|
+
const uid = this.client.getAuthUid();
|
|
13281
|
+
if (!uid) {
|
|
13282
|
+
return {
|
|
13283
|
+
ok: false,
|
|
13284
|
+
code: "AUTH_FAILED" /* AUTH_FAILED */,
|
|
13285
|
+
message: "Authentication required."
|
|
13286
|
+
};
|
|
13287
|
+
}
|
|
13288
|
+
return { ok: true, data: uid };
|
|
13289
|
+
}
|
|
13290
|
+
// MARK: Friends / Contacts
|
|
13291
|
+
/** POST /user/get_links_contacts — auth required */
|
|
13292
|
+
getLinksContacts() {
|
|
13293
|
+
const auth3 = this.requireAuth();
|
|
13294
|
+
if (!auth3.ok) return Promise.resolve(auth3);
|
|
13295
|
+
return this.client.performRequest("/user/get_links_contacts", "POST", {
|
|
13296
|
+
uid: auth3.data
|
|
13297
|
+
});
|
|
13298
|
+
}
|
|
13299
|
+
// MARK: Relation
|
|
13300
|
+
/** POST /user/get_link_name_type — auth required, body: uid + friend_uid */
|
|
13301
|
+
getLinkNameType(friendUid) {
|
|
13302
|
+
const auth3 = this.requireAuth();
|
|
13303
|
+
if (!auth3.ok) return Promise.resolve(auth3);
|
|
13304
|
+
return this.client.performRequest("/user/get_link_name_type", "POST", {
|
|
13305
|
+
uid: auth3.data,
|
|
13306
|
+
friend_uid: friendUid
|
|
13307
|
+
});
|
|
13308
|
+
}
|
|
13309
|
+
/** POST /user/update_link_name — auth required */
|
|
13310
|
+
updateLinkName(friendUid, remark) {
|
|
13311
|
+
const auth3 = this.requireAuth();
|
|
13312
|
+
if (!auth3.ok) return Promise.resolve(auth3);
|
|
13313
|
+
return this.client.performSimpleRequest("/user/update_link_name", "POST", {
|
|
13314
|
+
uid: auth3.data,
|
|
13315
|
+
friend_uid: friendUid,
|
|
13316
|
+
link_name: remark
|
|
13317
|
+
});
|
|
13318
|
+
}
|
|
13319
|
+
/** POST /user/update_link_type — auth required, body: friend_uid + link_type (string) */
|
|
13320
|
+
updateLinkType(friendUid, groupType) {
|
|
13321
|
+
const auth3 = this.requireAuth();
|
|
13322
|
+
if (!auth3.ok) return Promise.resolve(auth3);
|
|
13323
|
+
return this.client.performSimpleRequest("/user/update_link_type", "POST", {
|
|
13324
|
+
friend_uid: friendUid,
|
|
13325
|
+
link_type: String(groupType)
|
|
13326
|
+
});
|
|
13327
|
+
}
|
|
13328
|
+
// MARK: User
|
|
13329
|
+
/** POST /user/user_agent_profile — auth required, body: friend_uid + uid (+ agent_id) */
|
|
13330
|
+
findUserById(id) {
|
|
13331
|
+
const auth3 = this.requireAuth();
|
|
13332
|
+
if (!auth3.ok) return Promise.resolve(auth3);
|
|
13333
|
+
return this.client.performRequest("/user/user_agent_profile", "POST", {
|
|
13334
|
+
friend_uid: id,
|
|
13335
|
+
uid: auth3.data
|
|
13336
|
+
});
|
|
13337
|
+
}
|
|
13338
|
+
/** POST /user/user_agent_profile — auth required, body: friend_uid + uid (+ agent_id) */
|
|
13339
|
+
getUserAgentProfile(friendUid, agentId) {
|
|
13340
|
+
const auth3 = this.requireAuth();
|
|
13341
|
+
if (!auth3.ok) return Promise.resolve(auth3);
|
|
13342
|
+
const params = {
|
|
13343
|
+
friend_uid: friendUid,
|
|
13344
|
+
uid: auth3.data
|
|
13345
|
+
};
|
|
13346
|
+
if (agentId) {
|
|
13347
|
+
params.agent_id = agentId;
|
|
13348
|
+
}
|
|
13349
|
+
return this.client.performRequest("/user/user_agent_profile", "POST", params);
|
|
13350
|
+
}
|
|
13351
|
+
// MARK: QR Login
|
|
13352
|
+
/** POST /user/qr_login_create/3 — no auth required */
|
|
13353
|
+
qrLoginCreate() {
|
|
13354
|
+
return this.client.performUnauthenticated(
|
|
13355
|
+
"/user/qr_login_create/3",
|
|
13356
|
+
"POST",
|
|
13357
|
+
{}
|
|
13358
|
+
);
|
|
13359
|
+
}
|
|
13360
|
+
/** POST /user/qr_login_status — no auth required */
|
|
13361
|
+
qrLoginStatus(sceneId) {
|
|
13362
|
+
return this.client.performUnauthenticated("/user/qr_login_status", "POST", {
|
|
13363
|
+
scene_id: sceneId
|
|
13364
|
+
});
|
|
13365
|
+
}
|
|
13366
|
+
// MARK: Connectivity
|
|
13367
|
+
/** Quick connectivity check — calls the simplest endpoint. */
|
|
13368
|
+
async ping() {
|
|
13369
|
+
const result = await this.getLinksContacts();
|
|
13370
|
+
if (result.ok) {
|
|
13371
|
+
return { ok: true, data: true };
|
|
13372
|
+
}
|
|
13373
|
+
return result;
|
|
13374
|
+
}
|
|
13375
|
+
// MARK: Friend Add
|
|
13376
|
+
/** POST /user/find_user_by_id — search any user by Q助号 */
|
|
13377
|
+
searchUserById(id) {
|
|
13378
|
+
return this.client.performRequest("/user/find_user_by_id", "POST", {
|
|
13379
|
+
id
|
|
13380
|
+
});
|
|
13381
|
+
}
|
|
13382
|
+
/** POST /user/create_conversation — auth required, body: uid + friend_uid + friend_agent_id */
|
|
13383
|
+
createConversation(friendUid, friendAgentId) {
|
|
13384
|
+
const auth3 = this.requireAuth();
|
|
13385
|
+
if (!auth3.ok) return Promise.resolve(auth3);
|
|
13386
|
+
return this.client.performRequest("/user/create_conversation", "POST", {
|
|
13387
|
+
uid: auth3.data,
|
|
13388
|
+
friend_uid: friendUid,
|
|
13389
|
+
friend_agent_id: friendAgentId
|
|
13390
|
+
});
|
|
13391
|
+
}
|
|
13392
|
+
/** POST /user/get_profile_by_conversation_id — auth required */
|
|
13393
|
+
getProfileByConversationId(conversationId, conversationType) {
|
|
13394
|
+
const auth3 = this.requireAuth();
|
|
13395
|
+
if (!auth3.ok) return Promise.resolve(auth3);
|
|
13396
|
+
const params = {
|
|
13397
|
+
uid: auth3.data,
|
|
13398
|
+
conversation_id: conversationId
|
|
13399
|
+
};
|
|
13400
|
+
if (conversationType !== void 0) {
|
|
13401
|
+
params.conversation_type = conversationType;
|
|
13402
|
+
}
|
|
13403
|
+
return this.client.performRequest(
|
|
13404
|
+
"/user/get_profile_by_conversation_id",
|
|
13405
|
+
"POST",
|
|
13406
|
+
params
|
|
13407
|
+
);
|
|
13408
|
+
}
|
|
13409
|
+
// MARK: IM
|
|
13410
|
+
/** POST /api/v1/conversations/push_message — JSON body, no HMAC signature */
|
|
13411
|
+
async pushMessage(req) {
|
|
13412
|
+
if (!this.imApiClient) {
|
|
13413
|
+
return {
|
|
13414
|
+
ok: false,
|
|
13415
|
+
code: "INTERNAL_ERROR" /* INTERNAL_ERROR */,
|
|
13416
|
+
message: "IM API client not configured."
|
|
13417
|
+
};
|
|
13418
|
+
}
|
|
13419
|
+
return this.imApiClient.pushMessage(req);
|
|
13420
|
+
}
|
|
13421
|
+
};
|
|
13422
|
+
|
|
13423
|
+
// src/internal/api/im-api-client.ts
|
|
13424
|
+
init_cjs_shims();
|
|
13425
|
+
var IMApiClient = class {
|
|
13426
|
+
baseURL;
|
|
13427
|
+
debugEnabled = false;
|
|
13428
|
+
dryRun = false;
|
|
13429
|
+
constructor(baseURL, debugEnabled = false) {
|
|
13430
|
+
this.baseURL = baseURL;
|
|
13431
|
+
this.debugEnabled = debugEnabled;
|
|
13432
|
+
}
|
|
13433
|
+
setDryRun(enabled) {
|
|
13434
|
+
this.dryRun = enabled;
|
|
13435
|
+
}
|
|
13436
|
+
setDebug(enabled) {
|
|
13437
|
+
this.debugEnabled = enabled;
|
|
13438
|
+
}
|
|
13439
|
+
async pushMessage(req) {
|
|
13440
|
+
const url = `${this.baseURL}/api/v1/conversations/push_message`;
|
|
13441
|
+
if (this.dryRun) {
|
|
13442
|
+
this.log("[dry-run]", url, req);
|
|
13443
|
+
return ok({
|
|
13444
|
+
status: "success",
|
|
13445
|
+
sender_cid: req.sender_cid ?? "",
|
|
13446
|
+
conv_id: req.conv_id,
|
|
13447
|
+
message: "Message sent successfully"
|
|
13448
|
+
});
|
|
13449
|
+
}
|
|
13450
|
+
this.log("POST", url, req);
|
|
13451
|
+
try {
|
|
13452
|
+
const response = await fetch(url, {
|
|
13453
|
+
method: "POST",
|
|
13454
|
+
headers: {
|
|
13455
|
+
"Content-Type": "application/json"
|
|
13456
|
+
},
|
|
13457
|
+
body: JSON.stringify(req)
|
|
13458
|
+
});
|
|
13459
|
+
const text = await response.clone().text();
|
|
13460
|
+
this.log(`${response.status} ${response.statusText}`, text);
|
|
13461
|
+
if (!response.ok) {
|
|
13462
|
+
return fail(
|
|
13463
|
+
response.status === 401 || response.status === 403 ? "AUTH_FAILED" /* AUTH_FAILED */ : "NETWORK_ERROR" /* NETWORK_ERROR */,
|
|
13464
|
+
`HTTP ${response.status}: ${response.statusText}`
|
|
13465
|
+
);
|
|
13466
|
+
}
|
|
13467
|
+
let json;
|
|
13468
|
+
try {
|
|
13469
|
+
json = JSON.parse(text);
|
|
13470
|
+
} catch {
|
|
13471
|
+
return fail("DECODE_ERROR" /* DECODE_ERROR */, "Failed to parse push_message response");
|
|
13472
|
+
}
|
|
13473
|
+
if (json.code !== 200) {
|
|
13474
|
+
return fail("API_ERROR" /* API_ERROR */, json.msg || `API error (code: ${json.code})`);
|
|
13475
|
+
}
|
|
13476
|
+
if (json.data === null) {
|
|
13477
|
+
return fail("NOT_FOUND" /* NOT_FOUND */, "push_message returned empty data");
|
|
13478
|
+
}
|
|
13479
|
+
return ok(json.data);
|
|
13480
|
+
} catch (error) {
|
|
13481
|
+
return fail(
|
|
13482
|
+
"NETWORK_ERROR" /* NETWORK_ERROR */,
|
|
13483
|
+
error instanceof Error ? error.message : "Failed to push message"
|
|
13484
|
+
);
|
|
13485
|
+
}
|
|
13486
|
+
}
|
|
13487
|
+
log(...args) {
|
|
13488
|
+
if (this.debugEnabled) {
|
|
13489
|
+
console.log("[PUSH]", ...args);
|
|
13490
|
+
}
|
|
13491
|
+
}
|
|
13492
|
+
};
|
|
13493
|
+
|
|
13381
13494
|
// src/internal/im/im-gateway.ts
|
|
13382
13495
|
init_cjs_shims();
|
|
13383
13496
|
|
|
@@ -13738,7 +13851,7 @@ var IMClient = class {
|
|
|
13738
13851
|
}
|
|
13739
13852
|
// MARK: - Private
|
|
13740
13853
|
buildWsUrl() {
|
|
13741
|
-
const httpUrl = this.config.
|
|
13854
|
+
const httpUrl = this.config.imWsURL;
|
|
13742
13855
|
let wsUrl = httpUrl.replace(/^http/, "ws");
|
|
13743
13856
|
wsUrl = wsUrl.replace(/\/+$/, "");
|
|
13744
13857
|
if (!wsUrl.endsWith("/ws")) {
|
|
@@ -14736,8 +14849,9 @@ function createFactory(config) {
|
|
|
14736
14849
|
client.setAuth({ uid: config.uid, tk: config.tk, token: config.token });
|
|
14737
14850
|
}
|
|
14738
14851
|
const gateway = new ApiGateway(client);
|
|
14852
|
+
gateway.setIMApiClient(new IMApiClient(config.imBaseURL, config.debug));
|
|
14739
14853
|
const imGateway = new IMGateway({
|
|
14740
|
-
|
|
14854
|
+
imWsURL: config.imWsURL,
|
|
14741
14855
|
cid: config.cid,
|
|
14742
14856
|
debugEnabled: config.debug
|
|
14743
14857
|
});
|
|
@@ -14807,7 +14921,7 @@ async function main() {
|
|
|
14807
14921
|
${t("cli.banner")}` : t("cli.banner");
|
|
14808
14922
|
program.addHelpText("beforeAll", `${banner}
|
|
14809
14923
|
`);
|
|
14810
|
-
program.name("qz").version(`v${"0.
|
|
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"));
|
|
14811
14925
|
program.usage("<command> [subcommand] [options]");
|
|
14812
14926
|
program.hook("preAction", () => {
|
|
14813
14927
|
const opts = program.opts();
|
package/package.json
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
* node scripts/install-skill.mjs # install to all detected agents (global)
|
|
7
7
|
* node scripts/install-skill.mjs --project # install to current project's skills dir
|
|
8
8
|
* node scripts/install-skill.mjs --agent claude-code # install to specific agent only
|
|
9
|
+
* node scripts/install-skill.mjs --target /path/to/skills # install to custom directory
|
|
9
10
|
* node scripts/install-skill.mjs --help # show help
|
|
10
11
|
*
|
|
11
12
|
* Works with:
|
|
@@ -51,8 +52,27 @@ function getPkgVersion() {
|
|
|
51
52
|
|
|
52
53
|
// ── CLI parsing ────────────────────────────────────────────────────
|
|
53
54
|
|
|
55
|
+
function printVersion(sourceDir) {
|
|
56
|
+
const pkgVersion = getPkgVersion();
|
|
57
|
+
const skillVersion = sourceDir ? readSkillVersion(sourceDir) : 'n/a';
|
|
58
|
+
console.log(`@qzhuli/qzhuli-cli v${pkgVersion}`);
|
|
59
|
+
console.log(`qzhuli skill v${skillVersion}`);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function readSkillVersion(sourceDir) {
|
|
63
|
+
const skillMd = join(sourceDir, 'SKILL.md');
|
|
64
|
+
if (!existsSync(skillMd)) return 'n/a';
|
|
65
|
+
try {
|
|
66
|
+
const content = readFileSync(skillMd, 'utf8');
|
|
67
|
+
const match = content.match(/^version:\s*(.+)$/m);
|
|
68
|
+
return match ? match[1].trim() : 'n/a';
|
|
69
|
+
} catch {
|
|
70
|
+
return 'n/a';
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
54
74
|
function parseArgs(argv) {
|
|
55
|
-
const args = { project: false, agent: null, help: false, update: false };
|
|
75
|
+
const args = { project: false, agent: null, help: false, update: false, target: null, version: false };
|
|
56
76
|
for (let i = 0; i < argv.length; i++) {
|
|
57
77
|
switch (argv[i]) {
|
|
58
78
|
case '--project':
|
|
@@ -64,6 +84,12 @@ function parseArgs(argv) {
|
|
|
64
84
|
case '--update':
|
|
65
85
|
args.update = true;
|
|
66
86
|
break;
|
|
87
|
+
case '--target':
|
|
88
|
+
args.target = argv[++i];
|
|
89
|
+
break;
|
|
90
|
+
case '--version':
|
|
91
|
+
args.version = true;
|
|
92
|
+
break;
|
|
67
93
|
case '--help':
|
|
68
94
|
case '-h':
|
|
69
95
|
args.help = true;
|
|
@@ -80,13 +106,16 @@ Usage: node install-skill.mjs [options]
|
|
|
80
106
|
Options:
|
|
81
107
|
--project Install to current project's .claude/skills (or equivalent)
|
|
82
108
|
--agent <name> Install to a specific agent only (e.g. claude-code, cursor)
|
|
109
|
+
--target <path> Install to a custom directory path (skips agent detection)
|
|
83
110
|
--update Check for updates and reinstall if version changed
|
|
111
|
+
--version Show package and skill version
|
|
84
112
|
--help, -h Show this help message
|
|
85
113
|
|
|
86
114
|
Examples:
|
|
87
115
|
node scripts/install-skill.mjs # global, all agents
|
|
88
116
|
node scripts/install-skill.mjs --project # project-level, all agents
|
|
89
117
|
node scripts/install-skill.mjs --agent claude-code # global, claude-code only
|
|
118
|
+
node scripts/install-skill.mjs --target /path/to/skills # custom directory
|
|
90
119
|
node scripts/install-skill.mjs --update # update all installed agents
|
|
91
120
|
npx @qzhuli/qzhuli-cli scripts/install-skill.mjs # from published npm
|
|
92
121
|
`);
|
|
@@ -97,6 +126,7 @@ Examples:
|
|
|
97
126
|
function main() {
|
|
98
127
|
const opts = parseArgs(process.argv.slice(2));
|
|
99
128
|
if (opts.help) { printHelp(); process.exit(0); }
|
|
129
|
+
if (opts.version) { printVersion(findSourceDir()); process.exit(0); }
|
|
100
130
|
|
|
101
131
|
const sourceDir = findSourceDir();
|
|
102
132
|
if (!sourceDir) {
|
|
@@ -109,7 +139,11 @@ function main() {
|
|
|
109
139
|
|
|
110
140
|
// Resolve target agents
|
|
111
141
|
let targets = [];
|
|
112
|
-
if (opts.
|
|
142
|
+
if (opts.target) {
|
|
143
|
+
// --target: install to an arbitrary directory path
|
|
144
|
+
const targetPath = resolve(opts.target);
|
|
145
|
+
targets.push({ cfg: { name: `custom:${targetPath}`, global: targetPath }, isProject: false, isCustom: true });
|
|
146
|
+
} else if (opts.update) {
|
|
113
147
|
// --update: check agents that already have the skill installed
|
|
114
148
|
const candidates = opts.agent
|
|
115
149
|
? [AGENT_PATHS.find(a => a.name === opts.agent)]
|
|
@@ -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:
|
|
4
|
+
version: 0.1.0
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
# QZhuli CLI
|
|
@@ -38,9 +38,8 @@ When ANY of the following applies, STOP and ask the user first:
|
|
|
38
38
|
- **Ambiguous search results**: `status: "needs_resolution"` — show options and ask.
|
|
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
|
-
- **Message sending**: Before `message send`, show the target conversation, recipient, and message content.
|
|
42
41
|
- **Cache clearing**: Before `cache clear`, confirm scope (all tables vs single table).
|
|
43
|
-
- **Any write operation** (`user add`, `relation set`, `
|
|
42
|
+
- **Any write operation** (`user add`, `relation set`, `conversation create`, `cache clear`): confirm
|
|
44
43
|
with user.
|
|
45
44
|
|
|
46
45
|
### Use --dry-run for Preview
|
|
@@ -48,34 +47,39 @@ When ANY of the following applies, STOP and ask the user first:
|
|
|
48
47
|
Before any write operation the user hasn't explicitly confirmed, run with `--dry-run` first:
|
|
49
48
|
|
|
50
49
|
```bash
|
|
51
|
-
qz --dry-run message send <id>
|
|
50
|
+
qz --dry-run message send <id> "hello"
|
|
52
51
|
qz --dry-run relation set <uid> --remark "New Name"
|
|
53
52
|
```
|
|
54
53
|
|
|
55
54
|
### Least-Surprise Principle
|
|
56
55
|
|
|
57
|
-
- Never auto-send messages without explicit content approval.
|
|
58
56
|
- Never change a friend's remark without showing both old and new.
|
|
59
57
|
- Never delete cache data without confirming scope.
|
|
60
58
|
- If the user says "send a message" but doesn't specify content, draft it and ask before sending.
|
|
61
59
|
|
|
60
|
+
### Message Role Detection
|
|
61
|
+
|
|
62
|
+
When sending messages, determine the role based on the user's wording:
|
|
63
|
+
|
|
64
|
+
- If the user says something like **"以我的名义"** (in my name), **"帮我发给"** (help me send to), **"替我发送"** (send
|
|
65
|
+
on my behalf), or similar phrasing indicating they want to send as themselves → use `--role 1` (User).
|
|
66
|
+
- Otherwise, send normally without `--role` (defaults to `0` = Assistant).
|
|
67
|
+
|
|
62
68
|
## ID Reference
|
|
63
69
|
|
|
64
|
-
The CLI uses
|
|
70
|
+
The CLI uses 4 distinct ID types. **Using the wrong type will fail silently or hit the wrong target.**
|
|
65
71
|
|
|
66
|
-
| ID Type | Field Name | Format | Example
|
|
67
|
-
|
|
68
|
-
| Q助号 | `id` | Number, short | `10003`
|
|
69
|
-
| UID | `uid` | 32-char hex string | `d5b6308e3abad6bc96573c58`
|
|
70
|
-
|
|
|
71
|
-
|
|
|
72
|
-
| Agent ID | `agent.id` | Number | `5` | `conversation create --agent-id` |
|
|
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` |
|
|
73
78
|
|
|
74
79
|
**Quick identification by format**:
|
|
75
80
|
|
|
76
81
|
- A short integer → Q助号
|
|
77
82
|
- A 32-char hex string → UID
|
|
78
|
-
- A UUID with dashes → CID
|
|
79
83
|
- A long Base64-like string → conversationId
|
|
80
84
|
|
|
81
85
|
**Common mistake**: Using UID for `message send` instead of conversationId. Always resolve via `conversation search` or
|
|
@@ -138,7 +142,7 @@ login/logout, and preference writes.
|
|
|
138
142
|
| Create conversation | `qz conversation create <uid> --agent-id <id>` |
|
|
139
143
|
| Search conversations (Q助号) | `qz conversation search <q-number>` |
|
|
140
144
|
| Search conversations (UID) | `qz conversation search <uid> --uid` |
|
|
141
|
-
| Send message | `qz message send <conversation-id> <
|
|
145
|
+
| Send message | `qz message send <conversation-id> <content> [--role <n>]` |
|
|
142
146
|
| Read message history | `qz message history <conversation-id> [--from <id>] [--direction newer\|older] [--limit <n>]` |
|
|
143
147
|
| Sync cache | `qz cache sync` |
|
|
144
148
|
| Cache status | `qz cache status` |
|
|
@@ -184,10 +188,10 @@ entry contains `conversationId`, `isGroup`, `users`, and `visitors`.
|
|
|
184
188
|
1. Confirm auth: `qz auth status`
|
|
185
189
|
2. Get conversations: `qz conversation list --limit 10`
|
|
186
190
|
3. Pick the conversation `id` (conversationId).
|
|
187
|
-
4.
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
+
4. Determine role: if the user says "以我的名义" / "帮我发给" / "替我发送" etc., add `--role 1`; otherwise omit (
|
|
192
|
+
defaults to Assistant).
|
|
193
|
+
5. Send: `qz message send <conversation-id> "message text"`
|
|
194
|
+
6. Verify: `qz message history <conversation-id> --limit 5`
|
|
191
195
|
|
|
192
196
|
### Page Through Message History
|
|
193
197
|
|
|
@@ -219,15 +223,15 @@ Tables: `conversations_index`, `conversation_profiles`, `contacts_cache`, `user_
|
|
|
219
223
|
|
|
220
224
|
## Troubleshooting
|
|
221
225
|
|
|
222
|
-
| Symptom | Action
|
|
223
|
-
|
|
224
|
-
| Command not found | Confirm `qz` is on PATH. Install: `npm install -g @qzhuli/qzhuli-cli`
|
|
225
|
-
| Auth failure | `qz auth status`; then `qz auth login` if needed
|
|
226
|
-
| Unexpected language | `qz config --locale en` or `--locale zh`
|
|
227
|
-
| Too much JSON | Use `--jq ".data"` or another simple dot path
|
|
228
|
-
| Need no-op preview | Use `--dry-run`
|
|
229
|
-
| Message send
|
|
230
|
-
| Slow queries | Run `qz cache sync` first (incremental, fast), then retry
|
|
231
|
-
| Cache corrupted | `qz cache clear` to reset, then retry (falls back to API)
|
|
232
|
-
| Ambiguous search | `status: "needs_resolution"` — refine query with `--uid` or `--remark` flag
|
|
233
|
-
| `relation set` INVALID_ARGUMENT | Must include at least one of `--remark` or `--type`
|
|
226
|
+
| Symptom | Action |
|
|
227
|
+
|---------------------------------|-----------------------------------------------------------------------------|
|
|
228
|
+
| Command not found | Confirm `qz` is on PATH. Install: `npm install -g @qzhuli/qzhuli-cli` |
|
|
229
|
+
| Auth failure | `qz auth status`; then `qz auth login` if needed |
|
|
230
|
+
| Unexpected language | `qz config --locale en` or `--locale zh` |
|
|
231
|
+
| Too much JSON | Use `--jq ".data"` or another simple dot path |
|
|
232
|
+
| Need no-op preview | Use `--dry-run` |
|
|
233
|
+
| Message send fails | Re-check `auth status`, verify conversationId via `conversation list` |
|
|
234
|
+
| Slow queries | Run `qz cache sync` first (incremental, fast), then retry |
|
|
235
|
+
| Cache corrupted | `qz cache clear` to reset, then retry (falls back to API) |
|
|
236
|
+
| Ambiguous search | `status: "needs_resolution"` — refine query with `--uid` or `--remark` flag |
|
|
237
|
+
| `relation set` INVALID_ARGUMENT | Must include at least one of `--remark` or `--type` |
|