@kweaver-ai/kweaver-sdk 0.6.6 → 0.6.8
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 +7 -2
- package/README.zh.md +6 -2
- package/dist/api/toolboxes.js +4 -4
- package/dist/auth/eacp-modify-password.d.ts +25 -0
- package/dist/auth/eacp-modify-password.js +84 -0
- package/dist/auth/oauth.d.ts +71 -3
- package/dist/auth/oauth.js +140 -18
- package/dist/cli.js +2 -1
- package/dist/commands/agent-members.d.ts +68 -0
- package/dist/commands/agent-members.js +383 -0
- package/dist/commands/agent.js +4 -0
- package/dist/commands/auth.js +298 -47
- package/dist/commands/config.js +80 -28
- package/dist/index.d.ts +2 -1
- package/dist/index.js +2 -1
- package/package.json +1 -1
|
@@ -0,0 +1,383 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure helpers and orchestrators for managing agent member associations
|
|
3
|
+
* (skills, tools, mcps) via get → mutate(config) → update.
|
|
4
|
+
*/
|
|
5
|
+
import { getAgent, updateAgent } from "../api/agent-list.js";
|
|
6
|
+
import { getSkill } from "../api/skills.js";
|
|
7
|
+
import { ensureValidToken, formatHttpError } from "../auth/oauth.js";
|
|
8
|
+
import { resolveBusinessDomain } from "../config/store.js";
|
|
9
|
+
/** Deep-clone a JSON-serializable object so mutations don't leak to callers. */
|
|
10
|
+
function clone(value) {
|
|
11
|
+
return JSON.parse(JSON.stringify(value));
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Descend into `config` along `path`, creating empty objects and a terminal
|
|
15
|
+
* empty array along the way if any node is missing. Returns the terminal array.
|
|
16
|
+
*/
|
|
17
|
+
function ensureArrayAtPath(root, path) {
|
|
18
|
+
let cursor = root;
|
|
19
|
+
for (let i = 0; i < path.length - 1; i += 1) {
|
|
20
|
+
const key = path[i];
|
|
21
|
+
const next = cursor[key];
|
|
22
|
+
if (next === undefined || next === null) {
|
|
23
|
+
cursor[key] = {};
|
|
24
|
+
}
|
|
25
|
+
else if (typeof next !== "object" || Array.isArray(next)) {
|
|
26
|
+
throw new Error(`Config path conflict at ${path.slice(0, i + 1).join(".")}: expected object, got ${Array.isArray(next) ? "array" : typeof next}`);
|
|
27
|
+
}
|
|
28
|
+
cursor = cursor[key];
|
|
29
|
+
}
|
|
30
|
+
const terminalKey = path[path.length - 1];
|
|
31
|
+
const terminal = cursor[terminalKey];
|
|
32
|
+
if (terminal === undefined || terminal === null) {
|
|
33
|
+
cursor[terminalKey] = [];
|
|
34
|
+
}
|
|
35
|
+
else if (!Array.isArray(terminal)) {
|
|
36
|
+
throw new Error(`Config path conflict at ${path.join(".")}: expected array, got ${typeof terminal}`);
|
|
37
|
+
}
|
|
38
|
+
return cursor[terminalKey];
|
|
39
|
+
}
|
|
40
|
+
export function mutateConfigMembers(input) {
|
|
41
|
+
if (input.path.length === 0) {
|
|
42
|
+
throw new Error("mutateConfigMembers: path must have at least one segment");
|
|
43
|
+
}
|
|
44
|
+
const newConfig = clone(input.config);
|
|
45
|
+
const arr = ensureArrayAtPath(newConfig, input.path);
|
|
46
|
+
const existingIds = arr.map((el) => String(el[input.idField] ?? ""));
|
|
47
|
+
const currentSet = new Set(existingIds);
|
|
48
|
+
const added = [];
|
|
49
|
+
const alreadyAttached = [];
|
|
50
|
+
for (const id of input.addIds) {
|
|
51
|
+
if (currentSet.has(id)) {
|
|
52
|
+
alreadyAttached.push(id);
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
arr.push({ [input.idField]: id });
|
|
56
|
+
currentSet.add(id);
|
|
57
|
+
added.push(id);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
const removeSet = new Set(input.removeIds);
|
|
61
|
+
const removed = [];
|
|
62
|
+
const notAttached = [];
|
|
63
|
+
if (removeSet.size > 0) {
|
|
64
|
+
const survivors = [];
|
|
65
|
+
const survivingIdSet = new Set();
|
|
66
|
+
for (const el of arr) {
|
|
67
|
+
const id = String(el[input.idField] ?? "");
|
|
68
|
+
if (removeSet.has(id)) {
|
|
69
|
+
if (!removed.includes(id))
|
|
70
|
+
removed.push(id);
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
survivors.push(el);
|
|
74
|
+
survivingIdSet.add(id);
|
|
75
|
+
}
|
|
76
|
+
for (const id of input.removeIds) {
|
|
77
|
+
if (!removed.includes(id) && !survivingIdSet.has(id)) {
|
|
78
|
+
notAttached.push(id);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
arr.length = 0;
|
|
82
|
+
arr.push(...survivors);
|
|
83
|
+
}
|
|
84
|
+
const finalIds = arr.map((el) => String(el[input.idField] ?? ""));
|
|
85
|
+
return {
|
|
86
|
+
newConfig,
|
|
87
|
+
finalIds,
|
|
88
|
+
report: {
|
|
89
|
+
finalIds,
|
|
90
|
+
added,
|
|
91
|
+
alreadyAttached,
|
|
92
|
+
removed,
|
|
93
|
+
notAttached,
|
|
94
|
+
},
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
function mergeAgentBody(current, newConfig) {
|
|
98
|
+
return {
|
|
99
|
+
name: current.name,
|
|
100
|
+
profile: current.profile,
|
|
101
|
+
avatar_type: current.avatar_type,
|
|
102
|
+
avatar: current.avatar,
|
|
103
|
+
product_key: current.product_key,
|
|
104
|
+
config: newConfig,
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
export async function patchAgentMembers(input) {
|
|
108
|
+
const { agentId, spec, addIds, removeIds, strict, deps } = input;
|
|
109
|
+
const warnings = [];
|
|
110
|
+
// 1. validate (add only)
|
|
111
|
+
if (addIds.length > 0) {
|
|
112
|
+
const results = await Promise.all(addIds.map(async (id) => ({ id, info: await deps.fetchById(id) })));
|
|
113
|
+
const missing = results.filter((r) => !r.info.exists).map((r) => r.id);
|
|
114
|
+
if (missing.length > 0) {
|
|
115
|
+
throw new Error(`${spec.memberKind}(s) ${missing.join(", ")} not found (aborting, agent not modified)`);
|
|
116
|
+
}
|
|
117
|
+
const drafts = results.filter((r) => r.info.exists && !r.info.published).map((r) => r.id);
|
|
118
|
+
if (drafts.length > 0) {
|
|
119
|
+
if (strict) {
|
|
120
|
+
throw new Error(`${spec.memberKind}(s) ${drafts.join(", ")} are in draft status (aborted by --strict)`);
|
|
121
|
+
}
|
|
122
|
+
for (const id of drafts) {
|
|
123
|
+
warnings.push(`${spec.memberKind} ${id} is in draft status (use --strict to reject, or publish it first)`);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
// 2. fetch current agent
|
|
128
|
+
const currentRaw = await deps.getAgent(agentId);
|
|
129
|
+
const current = JSON.parse(currentRaw);
|
|
130
|
+
const config = (current.config ?? {});
|
|
131
|
+
// 3. mutate
|
|
132
|
+
const { newConfig, report } = mutateConfigMembers({
|
|
133
|
+
config,
|
|
134
|
+
path: spec.configPath,
|
|
135
|
+
idField: spec.idField,
|
|
136
|
+
addIds,
|
|
137
|
+
removeIds,
|
|
138
|
+
});
|
|
139
|
+
// Short-circuit: no-op (skip the write if neither add nor remove changed anything)
|
|
140
|
+
const nothingChanged = report.added.length === 0 && report.removed.length === 0;
|
|
141
|
+
if (nothingChanged) {
|
|
142
|
+
return { ...report, warnings };
|
|
143
|
+
}
|
|
144
|
+
// 4. write
|
|
145
|
+
await deps.updateAgent(agentId, mergeAgentBody(current, newConfig));
|
|
146
|
+
// 5. report
|
|
147
|
+
return { ...report, warnings };
|
|
148
|
+
}
|
|
149
|
+
export async function listAgentMembers(input) {
|
|
150
|
+
const { agentId, spec, deps } = input;
|
|
151
|
+
const currentRaw = await deps.getAgent(agentId);
|
|
152
|
+
const current = JSON.parse(currentRaw);
|
|
153
|
+
const config = (current.config ?? {});
|
|
154
|
+
// Read (don't create) the path. If any segment is missing, result is empty.
|
|
155
|
+
let cursor = config;
|
|
156
|
+
for (const key of spec.configPath) {
|
|
157
|
+
if (cursor && typeof cursor === "object" && !Array.isArray(cursor) && key in cursor) {
|
|
158
|
+
cursor = cursor[key];
|
|
159
|
+
}
|
|
160
|
+
else {
|
|
161
|
+
return [];
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
if (!Array.isArray(cursor))
|
|
165
|
+
return [];
|
|
166
|
+
const ids = cursor.map((el) => String(el[spec.idField] ?? ""));
|
|
167
|
+
const results = await Promise.all(ids.map(async (id) => {
|
|
168
|
+
try {
|
|
169
|
+
const info = await deps.fetchById(id);
|
|
170
|
+
return {
|
|
171
|
+
id,
|
|
172
|
+
name: info.name ?? null,
|
|
173
|
+
status: info.status ?? (info.exists ? (info.published ? "published" : "unpublish") : "unknown"),
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
catch {
|
|
177
|
+
return { id, name: null, status: "unknown" };
|
|
178
|
+
}
|
|
179
|
+
}));
|
|
180
|
+
return results;
|
|
181
|
+
}
|
|
182
|
+
// ── Skill command handler ────────────────────────────────────────────────────
|
|
183
|
+
const SKILL_SPEC = {
|
|
184
|
+
memberKind: "skill",
|
|
185
|
+
configPath: ["skills", "skills"],
|
|
186
|
+
idField: "skill_id",
|
|
187
|
+
};
|
|
188
|
+
function parseWriteArgs(args, verb) {
|
|
189
|
+
const agentId = args[0];
|
|
190
|
+
if (!agentId || agentId.startsWith("-")) {
|
|
191
|
+
throw new Error(`Missing <agent-id> for ${verb}`);
|
|
192
|
+
}
|
|
193
|
+
const ids = [];
|
|
194
|
+
let strict = false;
|
|
195
|
+
let businessDomain = "";
|
|
196
|
+
for (let i = 1; i < args.length; i += 1) {
|
|
197
|
+
const arg = args[i];
|
|
198
|
+
if (arg === "--strict") {
|
|
199
|
+
strict = true;
|
|
200
|
+
continue;
|
|
201
|
+
}
|
|
202
|
+
if (arg === "-bd" || arg === "--biz-domain") {
|
|
203
|
+
businessDomain = args[i + 1] ?? "";
|
|
204
|
+
if (!businessDomain || businessDomain.startsWith("-")) {
|
|
205
|
+
throw new Error("Missing value for biz-domain flag");
|
|
206
|
+
}
|
|
207
|
+
i += 1;
|
|
208
|
+
continue;
|
|
209
|
+
}
|
|
210
|
+
if (arg.startsWith("-")) {
|
|
211
|
+
throw new Error(`Unsupported flag: ${arg}`);
|
|
212
|
+
}
|
|
213
|
+
ids.push(arg);
|
|
214
|
+
}
|
|
215
|
+
if (ids.length === 0) {
|
|
216
|
+
throw new Error(`Missing <member-id> for ${verb}`);
|
|
217
|
+
}
|
|
218
|
+
return { agentId, ids, strict, businessDomain };
|
|
219
|
+
}
|
|
220
|
+
function printReport(kind, agentId, report) {
|
|
221
|
+
for (const w of report.warnings)
|
|
222
|
+
process.stderr.write(`! ${w}\n`);
|
|
223
|
+
for (const id of report.added)
|
|
224
|
+
console.log(`✓ ${id} added`);
|
|
225
|
+
for (const id of report.alreadyAttached)
|
|
226
|
+
console.log(`• ${id} already attached (skipped)`);
|
|
227
|
+
for (const id of report.removed)
|
|
228
|
+
console.log(`✓ ${id} removed`);
|
|
229
|
+
for (const id of report.notAttached)
|
|
230
|
+
console.log(`• ${id} not attached (skipped)`);
|
|
231
|
+
console.log(`Agent ${agentId} now has ${report.finalIds.length} ${kind}(s) attached.`);
|
|
232
|
+
}
|
|
233
|
+
async function runSkillAdd(args) {
|
|
234
|
+
try {
|
|
235
|
+
const parsed = parseWriteArgs(args, "add");
|
|
236
|
+
const token = await ensureValidToken();
|
|
237
|
+
const businessDomain = parsed.businessDomain || resolveBusinessDomain();
|
|
238
|
+
const deps = {
|
|
239
|
+
getAgent: (id) => getAgent({ baseUrl: token.baseUrl, accessToken: token.accessToken, agentId: id, businessDomain }),
|
|
240
|
+
updateAgent: (id, body) => updateAgent({ baseUrl: token.baseUrl, accessToken: token.accessToken, agentId: id, body: JSON.stringify(body), businessDomain }),
|
|
241
|
+
fetchById: async (id) => {
|
|
242
|
+
try {
|
|
243
|
+
const info = await getSkill({ baseUrl: token.baseUrl, accessToken: token.accessToken, skillId: id, businessDomain });
|
|
244
|
+
return {
|
|
245
|
+
exists: true,
|
|
246
|
+
published: info.status === "published",
|
|
247
|
+
name: info.name,
|
|
248
|
+
status: info.status,
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
catch {
|
|
252
|
+
return { exists: false, published: false };
|
|
253
|
+
}
|
|
254
|
+
},
|
|
255
|
+
};
|
|
256
|
+
const report = await patchAgentMembers({
|
|
257
|
+
agentId: parsed.agentId,
|
|
258
|
+
spec: SKILL_SPEC,
|
|
259
|
+
addIds: parsed.ids,
|
|
260
|
+
removeIds: [],
|
|
261
|
+
strict: parsed.strict,
|
|
262
|
+
deps,
|
|
263
|
+
});
|
|
264
|
+
printReport("skill", parsed.agentId, report);
|
|
265
|
+
return 0;
|
|
266
|
+
}
|
|
267
|
+
catch (error) {
|
|
268
|
+
process.stderr.write(`✗ ${error instanceof Error ? error.message : String(error)}\n`);
|
|
269
|
+
return 1;
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
async function runSkillRemove(args) {
|
|
273
|
+
try {
|
|
274
|
+
const parsed = parseWriteArgs(args, "remove");
|
|
275
|
+
const token = await ensureValidToken();
|
|
276
|
+
const businessDomain = parsed.businessDomain || resolveBusinessDomain();
|
|
277
|
+
const deps = {
|
|
278
|
+
getAgent: (id) => getAgent({ baseUrl: token.baseUrl, accessToken: token.accessToken, agentId: id, businessDomain }),
|
|
279
|
+
updateAgent: (id, body) => updateAgent({ baseUrl: token.baseUrl, accessToken: token.accessToken, agentId: id, body: JSON.stringify(body), businessDomain }),
|
|
280
|
+
fetchById: async () => ({ exists: true, published: true }),
|
|
281
|
+
};
|
|
282
|
+
const report = await patchAgentMembers({
|
|
283
|
+
agentId: parsed.agentId,
|
|
284
|
+
spec: SKILL_SPEC,
|
|
285
|
+
addIds: [],
|
|
286
|
+
removeIds: parsed.ids,
|
|
287
|
+
strict: false,
|
|
288
|
+
deps,
|
|
289
|
+
});
|
|
290
|
+
printReport("skill", parsed.agentId, report);
|
|
291
|
+
return 0;
|
|
292
|
+
}
|
|
293
|
+
catch (error) {
|
|
294
|
+
process.stderr.write(`✗ ${error instanceof Error ? error.message : String(error)}\n`);
|
|
295
|
+
return 1;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
async function runSkillList(args) {
|
|
299
|
+
const agentId = args[0];
|
|
300
|
+
if (!agentId || agentId.startsWith("-")) {
|
|
301
|
+
process.stderr.write("Missing <agent-id> for list\n");
|
|
302
|
+
return 1;
|
|
303
|
+
}
|
|
304
|
+
let pretty = true;
|
|
305
|
+
let businessDomain = "";
|
|
306
|
+
for (let i = 1; i < args.length; i += 1) {
|
|
307
|
+
const arg = args[i];
|
|
308
|
+
if (arg === "--pretty") {
|
|
309
|
+
pretty = true;
|
|
310
|
+
continue;
|
|
311
|
+
}
|
|
312
|
+
if (arg === "--compact") {
|
|
313
|
+
pretty = false;
|
|
314
|
+
continue;
|
|
315
|
+
}
|
|
316
|
+
if (arg === "-bd" || arg === "--biz-domain") {
|
|
317
|
+
businessDomain = args[i + 1] ?? "";
|
|
318
|
+
if (!businessDomain || businessDomain.startsWith("-")) {
|
|
319
|
+
process.stderr.write("Missing value for biz-domain flag\n");
|
|
320
|
+
return 1;
|
|
321
|
+
}
|
|
322
|
+
i += 1;
|
|
323
|
+
continue;
|
|
324
|
+
}
|
|
325
|
+
process.stderr.write(`Unsupported flag: ${arg}\n`);
|
|
326
|
+
return 1;
|
|
327
|
+
}
|
|
328
|
+
const token = await ensureValidToken();
|
|
329
|
+
businessDomain = businessDomain || resolveBusinessDomain();
|
|
330
|
+
const deps = {
|
|
331
|
+
getAgent: (id) => getAgent({ baseUrl: token.baseUrl, accessToken: token.accessToken, agentId: id, businessDomain }),
|
|
332
|
+
fetchById: async (id) => {
|
|
333
|
+
try {
|
|
334
|
+
const info = await getSkill({ baseUrl: token.baseUrl, accessToken: token.accessToken, skillId: id, businessDomain });
|
|
335
|
+
return { exists: true, published: info.status === "published", name: info.name, status: info.status };
|
|
336
|
+
}
|
|
337
|
+
catch {
|
|
338
|
+
return { exists: false, published: false };
|
|
339
|
+
}
|
|
340
|
+
},
|
|
341
|
+
};
|
|
342
|
+
try {
|
|
343
|
+
const rows = await listAgentMembers({ agentId, spec: SKILL_SPEC, deps });
|
|
344
|
+
const output = rows.map((r) => ({ skill_id: r.id, name: r.name, status: r.status }));
|
|
345
|
+
console.log(JSON.stringify(output, null, pretty ? 2 : 0));
|
|
346
|
+
return 0;
|
|
347
|
+
}
|
|
348
|
+
catch (error) {
|
|
349
|
+
process.stderr.write(`✗ ${error instanceof Error ? error.message : String(error)}\n`);
|
|
350
|
+
return 1;
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
export async function runAgentSkillCommand(args) {
|
|
354
|
+
const [verb, ...rest] = args;
|
|
355
|
+
if (!verb || verb === "--help" || verb === "-h") {
|
|
356
|
+
console.log(`kweaver agent skill
|
|
357
|
+
|
|
358
|
+
Subcommands:
|
|
359
|
+
add <agent-id> <skill-id>... [--strict] [-bd <bd>] Attach skills to an agent
|
|
360
|
+
remove <agent-id> <skill-id>... [-bd <bd>] Detach skills from an agent
|
|
361
|
+
list <agent-id> [--pretty|--compact] [-bd <bd>] List skills attached to an agent
|
|
362
|
+
|
|
363
|
+
Notes:
|
|
364
|
+
--strict On add, reject skills that exist but are not in 'published' status.
|
|
365
|
+
Default behaviour: warn and continue.
|
|
366
|
+
Dedupe is automatic for add; remove silently skips not-attached ids.`);
|
|
367
|
+
return 0;
|
|
368
|
+
}
|
|
369
|
+
try {
|
|
370
|
+
if (verb === "add")
|
|
371
|
+
return await runSkillAdd(rest);
|
|
372
|
+
if (verb === "remove")
|
|
373
|
+
return await runSkillRemove(rest);
|
|
374
|
+
if (verb === "list")
|
|
375
|
+
return await runSkillList(rest);
|
|
376
|
+
process.stderr.write(`Unknown agent skill subcommand: ${verb}\n`);
|
|
377
|
+
return 1;
|
|
378
|
+
}
|
|
379
|
+
catch (error) {
|
|
380
|
+
process.stderr.write(`${formatHttpError(error)}\n`);
|
|
381
|
+
return 1;
|
|
382
|
+
}
|
|
383
|
+
}
|
package/dist/commands/agent.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { ensureValidToken, formatHttpError, with401RefreshRetry } from "../auth/oauth.js";
|
|
2
2
|
import { runAgentChatCommand } from "./agent-chat.js";
|
|
3
|
+
import { runAgentSkillCommand } from "./agent-members.js";
|
|
3
4
|
import { listAgents, getAgent, getAgentByKey, createAgent, updateAgent, deleteAgent, publishAgent, unpublishAgent, listPersonalAgents, listPublishedAgentTemplates, getPublishedAgentTemplate, listAgentCategories, } from "../api/agent-list.js";
|
|
4
5
|
import { listConversations, listMessages, getTracesByConversation } from "../api/conversations.js";
|
|
5
6
|
import { fetchAgentInfo } from "../api/agent-chat.js";
|
|
@@ -516,6 +517,7 @@ Subcommands:
|
|
|
516
517
|
unpublish <agent_id> Unpublish an agent
|
|
517
518
|
chat <agent_id> Start interactive chat with an agent
|
|
518
519
|
chat <agent_id> -m "message" Send a single message (non-interactive)
|
|
520
|
+
skill <verb> ... Manage skills attached to an agent (add/remove/list)
|
|
519
521
|
sessions <agent_id> List all conversations for an agent
|
|
520
522
|
history <agent_id> <conversation_id> Show message history for a conversation
|
|
521
523
|
trace <agent_id> <conversation_id> Get trace data for a conversation`);
|
|
@@ -554,6 +556,8 @@ Subcommands:
|
|
|
554
556
|
return runAgentPublishCommand(rest);
|
|
555
557
|
if (subcommand === "unpublish")
|
|
556
558
|
return runAgentUnpublishCommand(rest);
|
|
559
|
+
if (subcommand === "skill")
|
|
560
|
+
return runAgentSkillCommand(rest);
|
|
557
561
|
return -1;
|
|
558
562
|
};
|
|
559
563
|
// Show subcommand-specific help inline (no retry needed)
|