@kweaver-ai/kweaver-sdk 0.5.1 → 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.
Files changed (66) hide show
  1. package/README.md +6 -1
  2. package/README.zh.md +5 -0
  3. package/dist/api/agent-chat.d.ts +1 -1
  4. package/dist/api/agent-chat.js +4 -4
  5. package/dist/api/agent-list.d.ts +35 -0
  6. package/dist/api/agent-list.js +86 -12
  7. package/dist/api/bkn-backend.d.ts +60 -0
  8. package/dist/api/bkn-backend.js +103 -10
  9. package/dist/api/conversations.d.ts +6 -3
  10. package/dist/api/conversations.js +26 -27
  11. package/dist/api/dataflow.js +1 -10
  12. package/dist/api/datasources.js +1 -10
  13. package/dist/api/dataviews.js +1 -10
  14. package/dist/api/headers.d.ts +9 -0
  15. package/dist/api/headers.js +25 -0
  16. package/dist/api/knowledge-networks.d.ts +41 -0
  17. package/dist/api/knowledge-networks.js +69 -22
  18. package/dist/api/ontology-query.d.ts +14 -1
  19. package/dist/api/ontology-query.js +63 -49
  20. package/dist/api/semantic-search.js +2 -12
  21. package/dist/api/skills.d.ts +141 -0
  22. package/dist/api/skills.js +216 -0
  23. package/dist/api/vega.d.ts +63 -0
  24. package/dist/api/vega.js +130 -10
  25. package/dist/auth/oauth.d.ts +5 -1
  26. package/dist/auth/oauth.js +293 -94
  27. package/dist/cli.js +28 -4
  28. package/dist/client.d.ts +3 -0
  29. package/dist/client.js +4 -0
  30. package/dist/commands/agent.d.ts +33 -1
  31. package/dist/commands/agent.js +721 -49
  32. package/dist/commands/auth.js +156 -33
  33. package/dist/commands/bkn-ops.d.ts +77 -0
  34. package/dist/commands/bkn-ops.js +1056 -0
  35. package/dist/commands/bkn-query.d.ts +14 -0
  36. package/dist/commands/bkn-query.js +370 -0
  37. package/dist/commands/bkn-schema.d.ts +135 -0
  38. package/dist/commands/bkn-schema.js +1461 -0
  39. package/dist/commands/bkn-utils.d.ts +36 -0
  40. package/dist/commands/bkn-utils.js +102 -0
  41. package/dist/commands/bkn.d.ts +7 -113
  42. package/dist/commands/bkn.js +175 -2429
  43. package/dist/commands/dataview.d.ts +7 -0
  44. package/dist/commands/dataview.js +38 -2
  45. package/dist/commands/ds.d.ts +1 -0
  46. package/dist/commands/ds.js +8 -1
  47. package/dist/commands/import-csv.d.ts +2 -0
  48. package/dist/commands/import-csv.js +3 -2
  49. package/dist/commands/skill.d.ts +26 -0
  50. package/dist/commands/skill.js +524 -0
  51. package/dist/commands/vega.js +371 -14
  52. package/dist/config/jwt.d.ts +6 -0
  53. package/dist/config/jwt.js +21 -0
  54. package/dist/config/store.d.ts +37 -5
  55. package/dist/config/store.js +363 -30
  56. package/dist/index.d.ts +6 -1
  57. package/dist/index.js +5 -1
  58. package/dist/resources/bkn.d.ts +4 -0
  59. package/dist/resources/bkn.js +4 -0
  60. package/dist/resources/conversations.d.ts +5 -2
  61. package/dist/resources/conversations.js +17 -3
  62. package/dist/resources/skills.d.ts +47 -0
  63. package/dist/resources/skills.js +47 -0
  64. package/dist/resources/vega.d.ts +11 -0
  65. package/dist/resources/vega.js +37 -1
  66. package/package.json +1 -1
@@ -1,12 +1,12 @@
1
- import { chmodSync, existsSync, mkdirSync, readFileSync, readdirSync, rmSync, statSync, writeFileSync, } from "node:fs";
1
+ import { chmodSync, existsSync, mkdirSync, readFileSync, readdirSync, renameSync, rmSync, statSync, writeFileSync, } from "node:fs";
2
2
  import { homedir } from "node:os";
3
3
  import { join } from "node:path";
4
4
  import { listBusinessDomains } from "../api/business-domains.js";
5
+ import { decodeJwtPayload, extractUserIdFromJwt } from "./jwt.js";
5
6
  const MCP_PATH = "/api/agent-retrieval/v1/mcp";
6
7
  function buildMcpUrl(baseUrl) {
7
8
  return baseUrl.replace(/\/+$/, "") + MCP_PATH;
8
9
  }
9
- const CONFIG_DIR = process.env.KWEAVERC_CONFIG_DIR || join(homedir(), ".kweaver");
10
10
  function getConfigDirPath() {
11
11
  return process.env.KWEAVERC_CONFIG_DIR || join(homedir(), ".kweaver");
12
12
  }
@@ -43,6 +43,8 @@ function readJsonFile(filePath) {
43
43
  }
44
44
  function writeJsonFile(filePath, value) {
45
45
  ensureConfigDir();
46
+ const dir = join(filePath, "..");
47
+ ensureDir(dir);
46
48
  writeFileSync(filePath, `${JSON.stringify(value, null, 2)}\n`, IS_WIN32 ? {} : { mode: 0o600 });
47
49
  if (!IS_WIN32)
48
50
  chmodSync(filePath, 0o600);
@@ -63,6 +65,56 @@ function getPlatformFile(baseUrl, filename) {
63
65
  function ensurePlatformDir(baseUrl) {
64
66
  ensureDir(getPlatformDir(baseUrl));
65
67
  }
68
+ // ---------------------------------------------------------------------------
69
+ // User-scoped file routing
70
+ // ---------------------------------------------------------------------------
71
+ /** Files that live under users/<userId>/ instead of at platform root. */
72
+ const USER_SCOPED_FILES = new Set(["token.json", "config.json", "context-loader.json"]);
73
+ function getUserDir(baseUrl, userId) {
74
+ return join(getPlatformDir(baseUrl), "users", userId);
75
+ }
76
+ function getUsersDirPath(baseUrl) {
77
+ return join(getPlatformDir(baseUrl), "users");
78
+ }
79
+ /**
80
+ * Resolve a per-platform file path, routing user-scoped files through the
81
+ * active user's subdirectory with auto-migration fallback.
82
+ */
83
+ function resolveFile(baseUrl, filename) {
84
+ if (!USER_SCOPED_FILES.has(filename)) {
85
+ return getPlatformFile(baseUrl, filename);
86
+ }
87
+ const userId = getActiveUserRaw(baseUrl);
88
+ if (userId) {
89
+ const userPath = join(getUserDir(baseUrl, userId), filename);
90
+ if (existsSync(userPath))
91
+ return userPath;
92
+ }
93
+ // Fallback: check platform root for legacy / partially-migrated files
94
+ const legacyPath = getPlatformFile(baseUrl, filename);
95
+ if (existsSync(legacyPath)) {
96
+ // If legacy token.json exists, trigger full on-demand migration
97
+ const legacyToken = getPlatformFile(baseUrl, "token.json");
98
+ if (existsSync(legacyToken)) {
99
+ migratePlatformToUserScoped(baseUrl);
100
+ const migratedUser = getActiveUserRaw(baseUrl);
101
+ if (migratedUser) {
102
+ const migratedPath = join(getUserDir(baseUrl, migratedUser), filename);
103
+ if (existsSync(migratedPath))
104
+ return migratedPath;
105
+ }
106
+ }
107
+ // File exists at legacy root but no migration possible (e.g. token already migrated).
108
+ // Return legacy path so the caller can still read it.
109
+ return legacyPath;
110
+ }
111
+ if (userId)
112
+ return join(getUserDir(baseUrl, userId), filename);
113
+ return legacyPath;
114
+ }
115
+ // ---------------------------------------------------------------------------
116
+ // State helpers
117
+ // ---------------------------------------------------------------------------
66
118
  function readState() {
67
119
  return readJsonFile(getStateFilePath()) ?? {};
68
120
  }
@@ -72,6 +124,9 @@ function writeState(state) {
72
124
  function normalizeAlias(value) {
73
125
  return value.trim().toLowerCase();
74
126
  }
127
+ // ---------------------------------------------------------------------------
128
+ // Migration: legacy flat files → platforms/<encoded>/
129
+ // ---------------------------------------------------------------------------
75
130
  function migrateLegacyFilesIfNeeded() {
76
131
  const legacyClientFile = getLegacyClientFilePath();
77
132
  const legacyTokenFile = getLegacyTokenFilePath();
@@ -105,10 +160,182 @@ function migrateLegacyFilesIfNeeded() {
105
160
  writeState({ ...state, currentPlatform: baseUrl });
106
161
  }
107
162
  }
163
+ // ---------------------------------------------------------------------------
164
+ // Migration: flat platform dir → users/<userId>/ scoped layout
165
+ // ---------------------------------------------------------------------------
166
+ /** Extract userId from a TokenConfig (try idToken, then accessToken, fallback "default"). */
167
+ export function extractUserId(token) {
168
+ if (token.idToken) {
169
+ const sub = extractUserIdFromJwt(token.idToken);
170
+ if (sub)
171
+ return sub;
172
+ }
173
+ if (token.accessToken) {
174
+ const sub = extractUserIdFromJwt(token.accessToken);
175
+ if (sub)
176
+ return sub;
177
+ }
178
+ return "default";
179
+ }
180
+ function migratePlatformToUserScoped(baseUrl) {
181
+ const platformDir = getPlatformDir(baseUrl);
182
+ const usersDir = getUsersDirPath(baseUrl);
183
+ const rootTokenFile = join(platformDir, "token.json");
184
+ if (!existsSync(rootTokenFile) || existsSync(usersDir)) {
185
+ return;
186
+ }
187
+ const token = readJsonFile(rootTokenFile);
188
+ if (!token)
189
+ return;
190
+ const userId = extractUserId(token);
191
+ const userDir = getUserDir(baseUrl, userId);
192
+ ensureDir(userDir);
193
+ renameSync(rootTokenFile, join(userDir, "token.json"));
194
+ const rootConfigFile = join(platformDir, "config.json");
195
+ if (existsSync(rootConfigFile)) {
196
+ renameSync(rootConfigFile, join(userDir, "config.json"));
197
+ }
198
+ const rootContextLoaderFile = join(platformDir, "context-loader.json");
199
+ if (existsSync(rootContextLoaderFile)) {
200
+ renameSync(rootContextLoaderFile, join(userDir, "context-loader.json"));
201
+ }
202
+ const resolvedUrl = token.baseUrl || baseUrl;
203
+ const state = readState();
204
+ const activeUsers = { ...(state.activeUsers ?? {}) };
205
+ activeUsers[resolvedUrl] = userId;
206
+ writeState({ ...state, activeUsers });
207
+ }
208
+ function migrateAllPlatformsToUserScoped() {
209
+ const platformsDir = getPlatformsDirPath();
210
+ if (!existsSync(platformsDir))
211
+ return;
212
+ for (const entry of readdirSync(platformsDir)) {
213
+ const dirPath = join(platformsDir, entry);
214
+ if (!statSync(dirPath).isDirectory())
215
+ continue;
216
+ const rootToken = join(dirPath, "token.json");
217
+ const usersDir = join(dirPath, "users");
218
+ if (!existsSync(rootToken) || existsSync(usersDir))
219
+ continue;
220
+ const token = readJsonFile(rootToken);
221
+ if (!token?.baseUrl)
222
+ continue;
223
+ migratePlatformToUserScoped(token.baseUrl);
224
+ }
225
+ }
226
+ // ---------------------------------------------------------------------------
227
+ // Store initialization
228
+ // ---------------------------------------------------------------------------
108
229
  function ensureStoreReady() {
109
230
  ensureConfigDir();
110
231
  migrateLegacyFilesIfNeeded();
232
+ migrateAllPlatformsToUserScoped();
233
+ }
234
+ // ---------------------------------------------------------------------------
235
+ // Active user management
236
+ // ---------------------------------------------------------------------------
237
+ /** Read active user from state without triggering ensureStoreReady (avoids recursion). */
238
+ function getActiveUserRaw(baseUrl) {
239
+ const state = readJsonFile(getStateFilePath()) ?? {};
240
+ const userId = state.activeUsers?.[baseUrl];
241
+ if (userId)
242
+ return userId;
243
+ // Fallback: scan users/ dir and pick the first one
244
+ const usersDir = getUsersDirPath(baseUrl);
245
+ if (!existsSync(usersDir))
246
+ return null;
247
+ for (const entry of readdirSync(usersDir)) {
248
+ const entryPath = join(usersDir, entry);
249
+ if (statSync(entryPath).isDirectory() && existsSync(join(entryPath, "token.json"))) {
250
+ return entry;
251
+ }
252
+ }
253
+ return null;
254
+ }
255
+ /** Get the active userId for a platform. */
256
+ export function getActiveUser(baseUrl) {
257
+ ensureStoreReady();
258
+ return getActiveUserRaw(baseUrl);
259
+ }
260
+ /** Set the active userId for a platform. */
261
+ export function setActiveUser(baseUrl, userId) {
262
+ ensureStoreReady();
263
+ const state = readState();
264
+ const activeUsers = { ...(state.activeUsers ?? {}) };
265
+ activeUsers[baseUrl] = userId;
266
+ writeState({ ...state, activeUsers });
111
267
  }
268
+ /** List all user IDs stored under a platform. */
269
+ export function listUsers(baseUrl) {
270
+ ensureStoreReady();
271
+ const usersDir = getUsersDirPath(baseUrl);
272
+ if (!existsSync(usersDir))
273
+ return [];
274
+ const users = [];
275
+ for (const entry of readdirSync(usersDir)) {
276
+ const entryPath = join(usersDir, entry);
277
+ if (statSync(entryPath).isDirectory()) {
278
+ users.push(entry);
279
+ }
280
+ }
281
+ return users.sort();
282
+ }
283
+ /** Load a specific user's token (not necessarily the active user). */
284
+ export function loadUserTokenConfig(baseUrl, userId) {
285
+ ensureStoreReady();
286
+ return readJsonFile(join(getUserDir(baseUrl, userId), "token.json"));
287
+ }
288
+ /**
289
+ * List all user profiles for a platform, enriched with display names.
290
+ *
291
+ * Resolution order for username:
292
+ * 1. ``displayName`` field persisted in token.json (set at login via /oauth2/userinfo)
293
+ * 2. ``preferred_username`` or ``name`` decoded from id_token JWT
294
+ */
295
+ export function listUserProfiles(baseUrl) {
296
+ const userIds = listUsers(baseUrl);
297
+ return userIds.map((userId) => {
298
+ const token = loadUserTokenConfig(baseUrl, userId);
299
+ let username;
300
+ let email;
301
+ if (token?.displayName) {
302
+ username = token.displayName;
303
+ }
304
+ if (token?.idToken) {
305
+ const payload = decodeJwtPayload(token.idToken);
306
+ if (payload) {
307
+ if (!username) {
308
+ if (typeof payload.preferred_username === "string")
309
+ username = payload.preferred_username;
310
+ else if (typeof payload.name === "string")
311
+ username = payload.name;
312
+ }
313
+ if (typeof payload.email === "string")
314
+ email = payload.email;
315
+ }
316
+ }
317
+ return { userId, username, email };
318
+ });
319
+ }
320
+ /** Resolve a user identifier (userId, username, or email) to a userId for the given platform.
321
+ * userId and username are matched case-sensitively; email is case-insensitive. */
322
+ export function resolveUserId(baseUrl, identifier) {
323
+ const users = listUsers(baseUrl);
324
+ if (users.includes(identifier))
325
+ return identifier;
326
+ const profiles = listUserProfiles(baseUrl);
327
+ // Exact match on username (case-sensitive)
328
+ const exact = profiles.find((p) => p.username === identifier);
329
+ if (exact)
330
+ return exact.userId;
331
+ // Email match (case-insensitive per RFC 5321)
332
+ const lower = identifier.toLowerCase();
333
+ const byEmail = profiles.find((p) => p.email?.toLowerCase() === lower);
334
+ return byEmail?.userId ?? null;
335
+ }
336
+ // ---------------------------------------------------------------------------
337
+ // Public API — platform & alias management
338
+ // ---------------------------------------------------------------------------
112
339
  export function getConfigDir() {
113
340
  return getConfigDirPath();
114
341
  }
@@ -178,19 +405,32 @@ export function resolvePlatformIdentifier(value) {
178
405
  }
179
406
  return normalized;
180
407
  }
408
+ // ---------------------------------------------------------------------------
409
+ // Token config (user-scoped)
410
+ // ---------------------------------------------------------------------------
181
411
  export function loadTokenConfig(baseUrl) {
182
412
  ensureStoreReady();
183
413
  const targetBaseUrl = baseUrl ?? getCurrentPlatform();
184
414
  if (!targetBaseUrl) {
185
415
  return null;
186
416
  }
187
- return readJsonFile(getPlatformFile(targetBaseUrl, "token.json"));
417
+ return readJsonFile(resolveFile(targetBaseUrl, "token.json"));
188
418
  }
189
- export function saveTokenConfig(config) {
419
+ export function saveTokenConfig(config, userId) {
190
420
  ensureStoreReady();
191
- ensurePlatformDir(config.baseUrl);
192
- writeJsonFile(getPlatformFile(config.baseUrl, "token.json"), config);
193
- }
421
+ const resolvedUser = userId ?? extractUserId(config);
422
+ const dir = getUserDir(config.baseUrl, resolvedUser);
423
+ ensureDir(dir);
424
+ writeJsonFile(join(dir, "token.json"), config);
425
+ // When KWEAVER_USER is set the caller is doing a one-off operation;
426
+ // don't change the persisted active user.
427
+ if (!process.env.KWEAVER_USER) {
428
+ setActiveUser(config.baseUrl, resolvedUser);
429
+ }
430
+ }
431
+ // ---------------------------------------------------------------------------
432
+ // Client config (platform-level — shared across users)
433
+ // ---------------------------------------------------------------------------
194
434
  export function loadClientConfig(baseUrl) {
195
435
  ensureStoreReady();
196
436
  const targetBaseUrl = baseUrl ?? getCurrentPlatform();
@@ -204,6 +444,11 @@ export function saveClientConfig(baseUrl, config) {
204
444
  ensurePlatformDir(baseUrl);
205
445
  writeJsonFile(getPlatformFile(baseUrl, "client.json"), { ...config, baseUrl });
206
446
  }
447
+ export function deleteClientConfig(baseUrl) {
448
+ const filePath = getPlatformFile(baseUrl, "client.json");
449
+ if (existsSync(filePath))
450
+ rmSync(filePath, { force: true });
451
+ }
207
452
  function migrateLegacyContextLoader(raw) {
208
453
  const leg = raw;
209
454
  if (leg?.knId && !Array.isArray(raw.configs)) {
@@ -220,7 +465,7 @@ export function loadContextLoaderConfig(baseUrl) {
220
465
  if (!targetBaseUrl) {
221
466
  return null;
222
467
  }
223
- const raw = readJsonFile(getPlatformFile(targetBaseUrl, "context-loader.json"));
468
+ const raw = readJsonFile(resolveFile(targetBaseUrl, "context-loader.json"));
224
469
  if (!raw)
225
470
  return null;
226
471
  const migrated = migrateLegacyContextLoader(raw);
@@ -240,8 +485,16 @@ export function loadContextLoaderConfig(baseUrl) {
240
485
  }
241
486
  export function saveContextLoaderConfig(baseUrl, config) {
242
487
  ensureStoreReady();
243
- ensurePlatformDir(baseUrl);
244
- writeJsonFile(getPlatformFile(baseUrl, "context-loader.json"), config);
488
+ const userId = getActiveUser(baseUrl);
489
+ if (userId) {
490
+ const dir = getUserDir(baseUrl, userId);
491
+ ensureDir(dir);
492
+ writeJsonFile(join(dir, "context-loader.json"), config);
493
+ }
494
+ else {
495
+ ensurePlatformDir(baseUrl);
496
+ writeJsonFile(getPlatformFile(baseUrl, "context-loader.json"), config);
497
+ }
245
498
  }
246
499
  export function getCurrentContextLoaderKn(baseUrl) {
247
500
  ensureStoreReady();
@@ -297,7 +550,7 @@ export function removeContextLoaderEntry(baseUrl, name) {
297
550
  return;
298
551
  const newConfigs = config.configs.filter((c) => c.name !== name);
299
552
  if (newConfigs.length === 0) {
300
- const file = getPlatformFile(baseUrl, "context-loader.json");
553
+ const file = resolveFile(baseUrl, "context-loader.json");
301
554
  if (existsSync(file))
302
555
  rmSync(file, { force: true });
303
556
  return;
@@ -308,18 +561,47 @@ export function removeContextLoaderEntry(baseUrl, name) {
308
561
  }
309
562
  saveContextLoaderConfig(baseUrl, { configs: newConfigs, current: newCurrent });
310
563
  }
564
+ // ---------------------------------------------------------------------------
565
+ // Platform existence / session management
566
+ // ---------------------------------------------------------------------------
311
567
  export function hasPlatform(baseUrl) {
312
568
  ensureStoreReady();
313
- return existsSync(getPlatformFile(baseUrl, "token.json"));
569
+ const file = resolveFile(baseUrl, "token.json");
570
+ if (existsSync(file))
571
+ return true;
572
+ return listUsers(baseUrl).length > 0;
314
573
  }
315
- /**
316
- * Remove token for a platform so the next auth will do a full login.
317
- */
318
- export function clearPlatformSession(baseUrl) {
574
+ export function clearPlatformSession(baseUrl, userId) {
575
+ ensureStoreReady();
576
+ const target = userId ?? getActiveUser(baseUrl);
577
+ if (target) {
578
+ const tokenFile = join(getUserDir(baseUrl, target), "token.json");
579
+ if (existsSync(tokenFile))
580
+ rmSync(tokenFile, { force: true });
581
+ return;
582
+ }
583
+ // Fallback: legacy flat layout
584
+ const legacyToken = getPlatformFile(baseUrl, "token.json");
585
+ if (existsSync(legacyToken))
586
+ rmSync(legacyToken, { force: true });
587
+ }
588
+ /** Delete a single user's profile directory under a platform. */
589
+ export function deleteUser(baseUrl, userId) {
319
590
  ensureStoreReady();
320
- const tokenFile = getPlatformFile(baseUrl, "token.json");
321
- if (existsSync(tokenFile)) {
322
- rmSync(tokenFile, { force: true });
591
+ const dir = getUserDir(baseUrl, userId);
592
+ if (existsSync(dir))
593
+ rmSync(dir, { recursive: true, force: true });
594
+ const state = readState();
595
+ if (state.activeUsers?.[baseUrl] === userId) {
596
+ const remaining = listUsers(baseUrl);
597
+ const au = { ...(state.activeUsers ?? {}) };
598
+ if (remaining.length > 0) {
599
+ au[baseUrl] = remaining[0];
600
+ }
601
+ else {
602
+ delete au[baseUrl];
603
+ }
604
+ writeState({ ...state, activeUsers: Object.keys(au).length > 0 ? au : undefined });
323
605
  }
324
606
  }
325
607
  export function deletePlatform(baseUrl) {
@@ -331,33 +613,76 @@ export function deletePlatform(baseUrl) {
331
613
  deletePlatformAlias(baseUrl);
332
614
  rmSync(platformDir, { recursive: true, force: true });
333
615
  const state = readState();
616
+ const au = { ...(state.activeUsers ?? {}) };
617
+ delete au[baseUrl];
334
618
  if (state.currentPlatform !== baseUrl) {
619
+ writeState({ ...state, activeUsers: Object.keys(au).length > 0 ? au : undefined });
335
620
  return;
336
621
  }
337
622
  const remainingPlatforms = listPlatforms();
338
623
  writeState({
339
624
  ...readState(),
340
625
  currentPlatform: remainingPlatforms[0]?.baseUrl,
626
+ activeUsers: Object.keys(au).length > 0 ? au : undefined,
341
627
  });
342
628
  }
343
629
  export function listPlatforms() {
344
630
  ensureStoreReady();
345
631
  const currentPlatform = getCurrentPlatform();
346
632
  const items = [];
347
- for (const entry of readdirSync(getPlatformsDirPath())) {
348
- const dirPath = join(getPlatformsDirPath(), entry);
633
+ const platformsDir = getPlatformsDirPath();
634
+ if (!existsSync(platformsDir))
635
+ return items;
636
+ for (const entry of readdirSync(platformsDir)) {
637
+ const dirPath = join(platformsDir, entry);
349
638
  if (!statSync(dirPath).isDirectory()) {
350
639
  continue;
351
640
  }
352
- const token = readJsonFile(join(dirPath, "token.json"));
353
- if (!token?.baseUrl) {
641
+ // Try to find the baseUrl from any available token
642
+ let baseUrl = null;
643
+ // Check users/ subdirectory first (new layout)
644
+ const usersDir = join(dirPath, "users");
645
+ if (existsSync(usersDir)) {
646
+ for (const userEntry of readdirSync(usersDir)) {
647
+ const userTokenPath = join(usersDir, userEntry, "token.json");
648
+ const userToken = readJsonFile(userTokenPath);
649
+ if (userToken?.baseUrl) {
650
+ baseUrl = userToken.baseUrl;
651
+ break;
652
+ }
653
+ }
654
+ }
655
+ // Fallback: legacy flat layout
656
+ if (!baseUrl) {
657
+ const token = readJsonFile(join(dirPath, "token.json"));
658
+ if (token?.baseUrl) {
659
+ baseUrl = token.baseUrl;
660
+ }
661
+ }
662
+ // Fallback: client.json
663
+ if (!baseUrl) {
664
+ const client = readJsonFile(join(dirPath, "client.json"));
665
+ if (client?.baseUrl) {
666
+ baseUrl = client.baseUrl;
667
+ }
668
+ }
669
+ if (!baseUrl)
354
670
  continue;
671
+ const hasToken = existsSync(resolveFile(baseUrl, "token.json"));
672
+ const activeUser = getActiveUserRaw(baseUrl);
673
+ let displayName;
674
+ if (activeUser) {
675
+ const tok = loadUserTokenConfig(baseUrl, activeUser);
676
+ if (tok?.displayName)
677
+ displayName = tok.displayName;
355
678
  }
356
679
  items.push({
357
- baseUrl: token.baseUrl,
358
- hasToken: true,
359
- isCurrent: token.baseUrl === currentPlatform,
360
- alias: getPlatformAlias(token.baseUrl) ?? undefined,
680
+ baseUrl,
681
+ hasToken,
682
+ isCurrent: baseUrl === currentPlatform,
683
+ alias: getPlatformAlias(baseUrl) ?? undefined,
684
+ userId: activeUser ?? undefined,
685
+ displayName,
361
686
  });
362
687
  }
363
688
  items.sort((a, b) => a.baseUrl.localeCompare(b.baseUrl));
@@ -365,12 +690,20 @@ export function listPlatforms() {
365
690
  }
366
691
  function loadPlatformConfig(baseUrl) {
367
692
  ensureStoreReady();
368
- return readJsonFile(getPlatformFile(baseUrl, "config.json"));
693
+ return readJsonFile(resolveFile(baseUrl, "config.json"));
369
694
  }
370
695
  function savePlatformConfig(baseUrl, config) {
371
696
  ensureStoreReady();
372
- ensurePlatformDir(baseUrl);
373
- writeJsonFile(getPlatformFile(baseUrl, "config.json"), config);
697
+ const userId = getActiveUser(baseUrl);
698
+ if (userId) {
699
+ const dir = getUserDir(baseUrl, userId);
700
+ ensureDir(dir);
701
+ writeJsonFile(join(dir, "config.json"), config);
702
+ }
703
+ else {
704
+ ensurePlatformDir(baseUrl);
705
+ writeJsonFile(getPlatformFile(baseUrl, "config.json"), config);
706
+ }
374
707
  }
375
708
  export function loadPlatformBusinessDomain(baseUrl) {
376
709
  return loadPlatformConfig(baseUrl)?.businessDomain ?? null;
package/dist/index.d.ts CHANGED
@@ -49,6 +49,9 @@ export type { AgentConfig, AgentInput, AgentInputField, AgentOutput, AgentLlmCon
49
49
  export { BknResource } from "./resources/bkn.js";
50
50
  export { ConversationsResource } from "./resources/conversations.js";
51
51
  export { ContextLoaderResource } from "./resources/context-loader.js";
52
+ export { SkillsResource } from "./resources/skills.js";
53
+ export type { SkillStatus, SkillSummary, SkillInfo, SkillFileSummary, SkillContentIndex, SkillFileReadResult, RegisterSkillResult, DeleteSkillResult, UpdateSkillStatusResult, SkillListResult, ListSkillsOptions, ListSkillMarketOptions, GetSkillOptions, RegisterSkillContentOptions, RegisterSkillZipOptions, UpdateSkillStatusOptions, ReadSkillFileOptions, DownloadSkillOptions, DownloadedSkillArchive, } from "./api/skills.js";
54
+ export { listSkills, listSkillMarket, getSkill, deleteSkill, updateSkillStatus, registerSkillContent, registerSkillZip, getSkillContentIndex, fetchSkillContent, readSkillFile, fetchSkillFile, downloadSkill, installSkillArchive, } from "./api/skills.js";
52
55
  export type { ViewField, DataView, CreateDataViewOptions, GetDataViewOptions, ListDataViewsOptions, DeleteDataViewOptions, FindDataViewOptions, QueryDataViewOptions, DataViewQueryResult, } from "./api/dataviews.js";
53
56
  export { parseDataView, createDataView, getDataView, listDataViews, deleteDataView, findDataView, queryDataView, } from "./api/dataviews.js";
54
57
  export { DataViewsResource } from "./resources/dataviews.js";
@@ -56,4 +59,6 @@ export type { BusinessDomain, ListBusinessDomainsOptions } from "./api/business-
56
59
  export { listBusinessDomains } from "./api/business-domains.js";
57
60
  export { HttpError, NetworkRequestError, fetchTextOrThrow } from "./utils/http.js";
58
61
  export type { TokenConfig, ContextLoaderEntry, ContextLoaderConfig, } from "./config/store.js";
59
- export { autoSelectBusinessDomain, getConfigDir, getCurrentPlatform } from "./config/store.js";
62
+ export type { UserProfile } from "./config/store.js";
63
+ export { autoSelectBusinessDomain, getConfigDir, getCurrentPlatform, getActiveUser, setActiveUser, listUsers, listUserProfiles, resolveUserId, extractUserId, } from "./config/store.js";
64
+ export { decodeJwtPayload, extractUserIdFromJwt } from "./config/jwt.js";
package/dist/index.js CHANGED
@@ -39,9 +39,13 @@ export { AgentsResource } from "./resources/agents.js";
39
39
  export { BknResource } from "./resources/bkn.js";
40
40
  export { ConversationsResource } from "./resources/conversations.js";
41
41
  export { ContextLoaderResource } from "./resources/context-loader.js";
42
+ export { SkillsResource } from "./resources/skills.js";
43
+ export { listSkills, listSkillMarket, getSkill, deleteSkill, updateSkillStatus, registerSkillContent, registerSkillZip, getSkillContentIndex, fetchSkillContent, readSkillFile, fetchSkillFile, downloadSkill, installSkillArchive, } from "./api/skills.js";
42
44
  export { parseDataView, createDataView, getDataView, listDataViews, deleteDataView, findDataView, queryDataView, } from "./api/dataviews.js";
43
45
  export { DataViewsResource } from "./resources/dataviews.js";
44
46
  export { listBusinessDomains } from "./api/business-domains.js";
45
47
  // ── HTTP utilities ────────────────────────────────────────────────────────────
46
48
  export { HttpError, NetworkRequestError, fetchTextOrThrow } from "./utils/http.js";
47
- export { autoSelectBusinessDomain, getConfigDir, getCurrentPlatform } from "./config/store.js";
49
+ export { autoSelectBusinessDomain, getConfigDir, getCurrentPlatform, getActiveUser, setActiveUser, listUsers, listUserProfiles, resolveUserId, extractUserId, } from "./config/store.js";
50
+ // ── JWT utilities ─────────────────────────────────────────────────────────────
51
+ export { decodeJwtPayload, extractUserIdFromJwt } from "./config/jwt.js";
@@ -32,6 +32,10 @@ export declare class BknResource {
32
32
  queryProperties(knId: string, otId: string, body: Record<string, unknown>): Promise<unknown>;
33
33
  querySubgraph(knId: string, body: Record<string, unknown>): Promise<unknown>;
34
34
  queryAction(knId: string, atId: string, body: Record<string, unknown>): Promise<unknown>;
35
+ /**
36
+ * Execute an action type (has side effects).
37
+ * @param body - Must include `_instance_identities`: `[{"<primary_key>": "<value>"}]`
38
+ */
35
39
  executeAction(knId: string, atId: string, body: Record<string, unknown>): Promise<unknown>;
36
40
  getExecution(knId: string, executionId: string): Promise<unknown>;
37
41
  listActionLogs(knId: string, opts?: {
@@ -57,6 +57,10 @@ export class BknResource {
57
57
  const raw = await actionTypeQuery({ ...this.ctx.base(), knId, atId, body: JSON.stringify(body) });
58
58
  return JSON.parse(raw);
59
59
  }
60
+ /**
61
+ * Execute an action type (has side effects).
62
+ * @param body - Must include `_instance_identities`: `[{"<primary_key>": "<value>"}]`
63
+ */
60
64
  async executeAction(knId, atId, body) {
61
65
  const raw = await actionTypeExecute({ ...this.ctx.base(), knId, atId, body: JSON.stringify(body) });
62
66
  return JSON.parse(raw);
@@ -4,8 +4,11 @@ export declare class ConversationsResource {
4
4
  constructor(ctx: ClientContext);
5
5
  list(agentId: string, opts?: {
6
6
  limit?: number;
7
+ page?: number;
8
+ size?: number;
9
+ version?: string;
7
10
  }): Promise<unknown[]>;
8
- listMessages(conversationId: string, opts?: {
9
- limit?: number;
11
+ listMessages(agentId: string, conversationId: string, opts?: {
12
+ version?: string;
10
13
  }): Promise<unknown[]>;
11
14
  }
@@ -1,16 +1,30 @@
1
1
  import { listConversations, listMessages } from "../api/conversations.js";
2
+ import { fetchAgentInfo } from "../api/agent-chat.js";
2
3
  export class ConversationsResource {
3
4
  ctx;
4
5
  constructor(ctx) {
5
6
  this.ctx = ctx;
6
7
  }
7
8
  async list(agentId, opts = {}) {
8
- const raw = await listConversations({ ...this.ctx.base(), agentId, ...opts });
9
+ const { version = "v0", limit, page, size } = opts;
10
+ const info = await fetchAgentInfo({ ...this.ctx.base(), agentId, version });
11
+ const raw = await listConversations({
12
+ ...this.ctx.base(),
13
+ agentKey: info.key,
14
+ page: page ?? 1,
15
+ size: size ?? (limit ?? 10),
16
+ });
9
17
  const parsed = JSON.parse(raw);
10
18
  return Array.isArray(parsed) ? parsed : [];
11
19
  }
12
- async listMessages(conversationId, opts = {}) {
13
- const raw = await listMessages({ ...this.ctx.base(), conversationId, ...opts });
20
+ async listMessages(agentId, conversationId, opts = {}) {
21
+ const { version = "v0" } = opts;
22
+ const info = await fetchAgentInfo({ ...this.ctx.base(), agentId, version });
23
+ const raw = await listMessages({
24
+ ...this.ctx.base(),
25
+ agentKey: info.key,
26
+ conversationId,
27
+ });
14
28
  const parsed = JSON.parse(raw);
15
29
  return Array.isArray(parsed) ? parsed : [];
16
30
  }