@kweaver-ai/kweaver-sdk 0.5.1 → 0.6.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 +25 -2
- package/README.zh.md +24 -1
- package/dist/api/agent-chat.d.ts +8 -2
- package/dist/api/agent-chat.js +150 -44
- package/dist/api/agent-list.d.ts +35 -0
- package/dist/api/agent-list.js +95 -21
- package/dist/api/bkn-backend.d.ts +60 -0
- package/dist/api/bkn-backend.js +103 -10
- package/dist/api/business-domains.js +9 -5
- package/dist/api/context-loader.js +4 -1
- package/dist/api/conversations.d.ts +6 -3
- package/dist/api/conversations.js +29 -35
- package/dist/api/dataflow.js +1 -10
- package/dist/api/dataflow2.d.ts +95 -0
- package/dist/api/dataflow2.js +80 -0
- package/dist/api/datasources.js +1 -10
- package/dist/api/dataviews.js +1 -10
- package/dist/api/headers.d.ts +11 -0
- package/dist/api/headers.js +30 -0
- package/dist/api/knowledge-networks.d.ts +41 -0
- package/dist/api/knowledge-networks.js +69 -22
- package/dist/api/ontology-query.d.ts +14 -1
- package/dist/api/ontology-query.js +63 -49
- package/dist/api/semantic-search.js +2 -12
- package/dist/api/skills.d.ts +141 -0
- package/dist/api/skills.js +208 -0
- package/dist/api/vega.d.ts +54 -7
- package/dist/api/vega.js +112 -25
- package/dist/auth/oauth.d.ts +5 -1
- package/dist/auth/oauth.js +351 -95
- package/dist/cli.js +49 -5
- package/dist/client.d.ts +12 -0
- package/dist/client.js +52 -8
- package/dist/commands/agent.d.ts +33 -1
- package/dist/commands/agent.js +721 -49
- package/dist/commands/auth.js +226 -55
- package/dist/commands/bkn-ops.d.ts +77 -0
- package/dist/commands/bkn-ops.js +1056 -0
- package/dist/commands/bkn-query.d.ts +14 -0
- package/dist/commands/bkn-query.js +370 -0
- package/dist/commands/bkn-schema.d.ts +135 -0
- package/dist/commands/bkn-schema.js +1483 -0
- package/dist/commands/bkn-utils.d.ts +36 -0
- package/dist/commands/bkn-utils.js +102 -0
- package/dist/commands/bkn.d.ts +7 -113
- package/dist/commands/bkn.js +175 -2429
- package/dist/commands/call.js +8 -5
- package/dist/commands/dataflow.d.ts +1 -0
- package/dist/commands/dataflow.js +251 -0
- package/dist/commands/dataview.d.ts +7 -0
- package/dist/commands/dataview.js +38 -2
- package/dist/commands/ds.d.ts +1 -0
- package/dist/commands/ds.js +8 -1
- package/dist/commands/explore-bkn.d.ts +79 -0
- package/dist/commands/explore-bkn.js +273 -0
- package/dist/commands/explore-chat.d.ts +3 -0
- package/dist/commands/explore-chat.js +193 -0
- package/dist/commands/explore-vega.d.ts +3 -0
- package/dist/commands/explore-vega.js +71 -0
- package/dist/commands/explore.d.ts +9 -0
- package/dist/commands/explore.js +258 -0
- package/dist/commands/import-csv.d.ts +2 -0
- package/dist/commands/import-csv.js +3 -2
- package/dist/commands/skill.d.ts +26 -0
- package/dist/commands/skill.js +524 -0
- package/dist/commands/vega.js +372 -117
- package/dist/config/jwt.d.ts +6 -0
- package/dist/config/jwt.js +21 -0
- package/dist/config/no-auth.d.ts +3 -0
- package/dist/config/no-auth.js +5 -0
- package/dist/config/store.d.ts +45 -5
- package/dist/config/store.js +385 -30
- package/dist/index.d.ts +6 -1
- package/dist/index.js +5 -1
- package/dist/kweaver.d.ts +5 -0
- package/dist/kweaver.js +32 -2
- package/dist/resources/bkn.d.ts +4 -0
- package/dist/resources/bkn.js +6 -3
- package/dist/resources/conversations.d.ts +5 -2
- package/dist/resources/conversations.js +17 -3
- package/dist/resources/knowledge-networks.js +3 -8
- package/dist/resources/skills.d.ts +47 -0
- package/dist/resources/skills.js +47 -0
- package/dist/resources/vega.d.ts +11 -6
- package/dist/resources/vega.js +37 -10
- package/dist/templates/explorer/app.js +136 -0
- package/dist/templates/explorer/bkn.js +747 -0
- package/dist/templates/explorer/chat.js +980 -0
- package/dist/templates/explorer/dashboard.js +82 -0
- package/dist/templates/explorer/index.html +35 -0
- package/dist/templates/explorer/style.css +2440 -0
- package/dist/templates/explorer/vega.js +291 -0
- package/dist/utils/http.d.ts +3 -0
- package/dist/utils/http.js +37 -1
- package/package.json +9 -5
package/dist/config/store.d.ts
CHANGED
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
export { NO_AUTH_TOKEN, isNoAuth } from "./no-auth.js";
|
|
2
|
+
/**
|
|
3
|
+
* Persist a no-auth session for a platform (users/default/token.json) and set it as current.
|
|
4
|
+
* Used by `kweaver auth <url> --no-auth` and when OAuth registration returns 404.
|
|
5
|
+
*/
|
|
6
|
+
export declare function saveNoAuthPlatform(baseUrl: string, opts?: {
|
|
7
|
+
tlsInsecure?: boolean;
|
|
8
|
+
}): TokenConfig;
|
|
1
9
|
export interface TokenConfig {
|
|
2
10
|
baseUrl: string;
|
|
3
11
|
accessToken: string;
|
|
@@ -10,6 +18,8 @@ export interface TokenConfig {
|
|
|
10
18
|
obtainedAt: string;
|
|
11
19
|
/** When true, skip TLS certificate verification for this platform (saved by `kweaver auth --insecure`). */
|
|
12
20
|
tlsInsecure?: boolean;
|
|
21
|
+
/** Human-readable display name fetched from userinfo at login time. */
|
|
22
|
+
displayName?: string;
|
|
13
23
|
}
|
|
14
24
|
/** OAuth2 client registration (per platform), used for refresh_token grant. */
|
|
15
25
|
export interface ClientConfig {
|
|
@@ -38,7 +48,37 @@ export interface PlatformSummary {
|
|
|
38
48
|
hasToken: boolean;
|
|
39
49
|
isCurrent: boolean;
|
|
40
50
|
alias?: string;
|
|
51
|
+
/** Active user ID for this platform (from id_token sub claim). */
|
|
52
|
+
userId?: string;
|
|
53
|
+
/** Human-readable name persisted from /oauth2/userinfo at login time. */
|
|
54
|
+
displayName?: string;
|
|
55
|
+
}
|
|
56
|
+
/** Extract userId from a TokenConfig (try idToken, then accessToken, fallback "default"). */
|
|
57
|
+
export declare function extractUserId(token: TokenConfig): string;
|
|
58
|
+
/** Get the active userId for a platform. */
|
|
59
|
+
export declare function getActiveUser(baseUrl: string): string | null;
|
|
60
|
+
/** Set the active userId for a platform. */
|
|
61
|
+
export declare function setActiveUser(baseUrl: string, userId: string): void;
|
|
62
|
+
/** List all user IDs stored under a platform. */
|
|
63
|
+
export declare function listUsers(baseUrl: string): string[];
|
|
64
|
+
/** Load a specific user's token (not necessarily the active user). */
|
|
65
|
+
export declare function loadUserTokenConfig(baseUrl: string, userId: string): TokenConfig | null;
|
|
66
|
+
export interface UserProfile {
|
|
67
|
+
userId: string;
|
|
68
|
+
username?: string;
|
|
69
|
+
email?: string;
|
|
41
70
|
}
|
|
71
|
+
/**
|
|
72
|
+
* List all user profiles for a platform, enriched with display names.
|
|
73
|
+
*
|
|
74
|
+
* Resolution order for username:
|
|
75
|
+
* 1. ``displayName`` field persisted in token.json (set at login via /oauth2/userinfo)
|
|
76
|
+
* 2. ``preferred_username`` or ``name`` decoded from id_token JWT
|
|
77
|
+
*/
|
|
78
|
+
export declare function listUserProfiles(baseUrl: string): UserProfile[];
|
|
79
|
+
/** Resolve a user identifier (userId, username, or email) to a userId for the given platform.
|
|
80
|
+
* userId and username are matched case-sensitively; email is case-insensitive. */
|
|
81
|
+
export declare function resolveUserId(baseUrl: string, identifier: string): string | null;
|
|
42
82
|
export declare function getConfigDir(): string;
|
|
43
83
|
export declare function getCurrentPlatform(): string | null;
|
|
44
84
|
export declare function setCurrentPlatform(baseUrl: string): void;
|
|
@@ -47,9 +87,10 @@ export declare function deletePlatformAlias(baseUrl: string): void;
|
|
|
47
87
|
export declare function getPlatformAlias(baseUrl: string): string | null;
|
|
48
88
|
export declare function resolvePlatformIdentifier(value: string): string | null;
|
|
49
89
|
export declare function loadTokenConfig(baseUrl?: string): TokenConfig | null;
|
|
50
|
-
export declare function saveTokenConfig(config: TokenConfig): void;
|
|
90
|
+
export declare function saveTokenConfig(config: TokenConfig, userId?: string): void;
|
|
51
91
|
export declare function loadClientConfig(baseUrl?: string): ClientConfig | null;
|
|
52
92
|
export declare function saveClientConfig(baseUrl: string, config: ClientConfig): void;
|
|
93
|
+
export declare function deleteClientConfig(baseUrl: string): void;
|
|
53
94
|
export declare function loadContextLoaderConfig(baseUrl?: string): ContextLoaderConfig | null;
|
|
54
95
|
export declare function saveContextLoaderConfig(baseUrl: string, config: ContextLoaderConfig): void;
|
|
55
96
|
export interface CurrentContextLoaderKn {
|
|
@@ -61,10 +102,9 @@ export declare function addContextLoaderEntry(baseUrl: string, name: string, knI
|
|
|
61
102
|
export declare function setCurrentContextLoader(baseUrl: string, name: string): void;
|
|
62
103
|
export declare function removeContextLoaderEntry(baseUrl: string, name: string): void;
|
|
63
104
|
export declare function hasPlatform(baseUrl: string): boolean;
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
export declare function clearPlatformSession(baseUrl: string): void;
|
|
105
|
+
export declare function clearPlatformSession(baseUrl: string, userId?: string): void;
|
|
106
|
+
/** Delete a single user's profile directory under a platform. */
|
|
107
|
+
export declare function deleteUser(baseUrl: string, userId: string): void;
|
|
68
108
|
export declare function deletePlatform(baseUrl: string): void;
|
|
69
109
|
export declare function listPlatforms(): PlatformSummary[];
|
|
70
110
|
/** Per-platform config (not auth — general settings). */
|
package/dist/config/store.js
CHANGED
|
@@ -1,12 +1,34 @@
|
|
|
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 { NO_AUTH_TOKEN } from "./no-auth.js";
|
|
6
|
+
import { decodeJwtPayload, extractUserIdFromJwt } from "./jwt.js";
|
|
7
|
+
export { NO_AUTH_TOKEN, isNoAuth } from "./no-auth.js";
|
|
8
|
+
/**
|
|
9
|
+
* Persist a no-auth session for a platform (users/default/token.json) and set it as current.
|
|
10
|
+
* Used by `kweaver auth <url> --no-auth` and when OAuth registration returns 404.
|
|
11
|
+
*/
|
|
12
|
+
export function saveNoAuthPlatform(baseUrl, opts) {
|
|
13
|
+
const base = baseUrl.replace(/\/+$/, "");
|
|
14
|
+
const token = {
|
|
15
|
+
baseUrl: base,
|
|
16
|
+
accessToken: NO_AUTH_TOKEN,
|
|
17
|
+
tokenType: "none",
|
|
18
|
+
scope: "",
|
|
19
|
+
obtainedAt: new Date().toISOString(),
|
|
20
|
+
};
|
|
21
|
+
if (opts?.tlsInsecure) {
|
|
22
|
+
token.tlsInsecure = true;
|
|
23
|
+
}
|
|
24
|
+
saveTokenConfig(token);
|
|
25
|
+
setCurrentPlatform(base);
|
|
26
|
+
return token;
|
|
27
|
+
}
|
|
5
28
|
const MCP_PATH = "/api/agent-retrieval/v1/mcp";
|
|
6
29
|
function buildMcpUrl(baseUrl) {
|
|
7
30
|
return baseUrl.replace(/\/+$/, "") + MCP_PATH;
|
|
8
31
|
}
|
|
9
|
-
const CONFIG_DIR = process.env.KWEAVERC_CONFIG_DIR || join(homedir(), ".kweaver");
|
|
10
32
|
function getConfigDirPath() {
|
|
11
33
|
return process.env.KWEAVERC_CONFIG_DIR || join(homedir(), ".kweaver");
|
|
12
34
|
}
|
|
@@ -43,6 +65,8 @@ function readJsonFile(filePath) {
|
|
|
43
65
|
}
|
|
44
66
|
function writeJsonFile(filePath, value) {
|
|
45
67
|
ensureConfigDir();
|
|
68
|
+
const dir = join(filePath, "..");
|
|
69
|
+
ensureDir(dir);
|
|
46
70
|
writeFileSync(filePath, `${JSON.stringify(value, null, 2)}\n`, IS_WIN32 ? {} : { mode: 0o600 });
|
|
47
71
|
if (!IS_WIN32)
|
|
48
72
|
chmodSync(filePath, 0o600);
|
|
@@ -63,6 +87,56 @@ function getPlatformFile(baseUrl, filename) {
|
|
|
63
87
|
function ensurePlatformDir(baseUrl) {
|
|
64
88
|
ensureDir(getPlatformDir(baseUrl));
|
|
65
89
|
}
|
|
90
|
+
// ---------------------------------------------------------------------------
|
|
91
|
+
// User-scoped file routing
|
|
92
|
+
// ---------------------------------------------------------------------------
|
|
93
|
+
/** Files that live under users/<userId>/ instead of at platform root. */
|
|
94
|
+
const USER_SCOPED_FILES = new Set(["token.json", "config.json", "context-loader.json"]);
|
|
95
|
+
function getUserDir(baseUrl, userId) {
|
|
96
|
+
return join(getPlatformDir(baseUrl), "users", userId);
|
|
97
|
+
}
|
|
98
|
+
function getUsersDirPath(baseUrl) {
|
|
99
|
+
return join(getPlatformDir(baseUrl), "users");
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Resolve a per-platform file path, routing user-scoped files through the
|
|
103
|
+
* active user's subdirectory with auto-migration fallback.
|
|
104
|
+
*/
|
|
105
|
+
function resolveFile(baseUrl, filename) {
|
|
106
|
+
if (!USER_SCOPED_FILES.has(filename)) {
|
|
107
|
+
return getPlatformFile(baseUrl, filename);
|
|
108
|
+
}
|
|
109
|
+
const userId = getActiveUserRaw(baseUrl);
|
|
110
|
+
if (userId) {
|
|
111
|
+
const userPath = join(getUserDir(baseUrl, userId), filename);
|
|
112
|
+
if (existsSync(userPath))
|
|
113
|
+
return userPath;
|
|
114
|
+
}
|
|
115
|
+
// Fallback: check platform root for legacy / partially-migrated files
|
|
116
|
+
const legacyPath = getPlatformFile(baseUrl, filename);
|
|
117
|
+
if (existsSync(legacyPath)) {
|
|
118
|
+
// If legacy token.json exists, trigger full on-demand migration
|
|
119
|
+
const legacyToken = getPlatformFile(baseUrl, "token.json");
|
|
120
|
+
if (existsSync(legacyToken)) {
|
|
121
|
+
migratePlatformToUserScoped(baseUrl);
|
|
122
|
+
const migratedUser = getActiveUserRaw(baseUrl);
|
|
123
|
+
if (migratedUser) {
|
|
124
|
+
const migratedPath = join(getUserDir(baseUrl, migratedUser), filename);
|
|
125
|
+
if (existsSync(migratedPath))
|
|
126
|
+
return migratedPath;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
// File exists at legacy root but no migration possible (e.g. token already migrated).
|
|
130
|
+
// Return legacy path so the caller can still read it.
|
|
131
|
+
return legacyPath;
|
|
132
|
+
}
|
|
133
|
+
if (userId)
|
|
134
|
+
return join(getUserDir(baseUrl, userId), filename);
|
|
135
|
+
return legacyPath;
|
|
136
|
+
}
|
|
137
|
+
// ---------------------------------------------------------------------------
|
|
138
|
+
// State helpers
|
|
139
|
+
// ---------------------------------------------------------------------------
|
|
66
140
|
function readState() {
|
|
67
141
|
return readJsonFile(getStateFilePath()) ?? {};
|
|
68
142
|
}
|
|
@@ -72,6 +146,9 @@ function writeState(state) {
|
|
|
72
146
|
function normalizeAlias(value) {
|
|
73
147
|
return value.trim().toLowerCase();
|
|
74
148
|
}
|
|
149
|
+
// ---------------------------------------------------------------------------
|
|
150
|
+
// Migration: legacy flat files → platforms/<encoded>/
|
|
151
|
+
// ---------------------------------------------------------------------------
|
|
75
152
|
function migrateLegacyFilesIfNeeded() {
|
|
76
153
|
const legacyClientFile = getLegacyClientFilePath();
|
|
77
154
|
const legacyTokenFile = getLegacyTokenFilePath();
|
|
@@ -105,10 +182,182 @@ function migrateLegacyFilesIfNeeded() {
|
|
|
105
182
|
writeState({ ...state, currentPlatform: baseUrl });
|
|
106
183
|
}
|
|
107
184
|
}
|
|
185
|
+
// ---------------------------------------------------------------------------
|
|
186
|
+
// Migration: flat platform dir → users/<userId>/ scoped layout
|
|
187
|
+
// ---------------------------------------------------------------------------
|
|
188
|
+
/** Extract userId from a TokenConfig (try idToken, then accessToken, fallback "default"). */
|
|
189
|
+
export function extractUserId(token) {
|
|
190
|
+
if (token.idToken) {
|
|
191
|
+
const sub = extractUserIdFromJwt(token.idToken);
|
|
192
|
+
if (sub)
|
|
193
|
+
return sub;
|
|
194
|
+
}
|
|
195
|
+
if (token.accessToken) {
|
|
196
|
+
const sub = extractUserIdFromJwt(token.accessToken);
|
|
197
|
+
if (sub)
|
|
198
|
+
return sub;
|
|
199
|
+
}
|
|
200
|
+
return "default";
|
|
201
|
+
}
|
|
202
|
+
function migratePlatformToUserScoped(baseUrl) {
|
|
203
|
+
const platformDir = getPlatformDir(baseUrl);
|
|
204
|
+
const usersDir = getUsersDirPath(baseUrl);
|
|
205
|
+
const rootTokenFile = join(platformDir, "token.json");
|
|
206
|
+
if (!existsSync(rootTokenFile) || existsSync(usersDir)) {
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
const token = readJsonFile(rootTokenFile);
|
|
210
|
+
if (!token)
|
|
211
|
+
return;
|
|
212
|
+
const userId = extractUserId(token);
|
|
213
|
+
const userDir = getUserDir(baseUrl, userId);
|
|
214
|
+
ensureDir(userDir);
|
|
215
|
+
renameSync(rootTokenFile, join(userDir, "token.json"));
|
|
216
|
+
const rootConfigFile = join(platformDir, "config.json");
|
|
217
|
+
if (existsSync(rootConfigFile)) {
|
|
218
|
+
renameSync(rootConfigFile, join(userDir, "config.json"));
|
|
219
|
+
}
|
|
220
|
+
const rootContextLoaderFile = join(platformDir, "context-loader.json");
|
|
221
|
+
if (existsSync(rootContextLoaderFile)) {
|
|
222
|
+
renameSync(rootContextLoaderFile, join(userDir, "context-loader.json"));
|
|
223
|
+
}
|
|
224
|
+
const resolvedUrl = token.baseUrl || baseUrl;
|
|
225
|
+
const state = readState();
|
|
226
|
+
const activeUsers = { ...(state.activeUsers ?? {}) };
|
|
227
|
+
activeUsers[resolvedUrl] = userId;
|
|
228
|
+
writeState({ ...state, activeUsers });
|
|
229
|
+
}
|
|
230
|
+
function migrateAllPlatformsToUserScoped() {
|
|
231
|
+
const platformsDir = getPlatformsDirPath();
|
|
232
|
+
if (!existsSync(platformsDir))
|
|
233
|
+
return;
|
|
234
|
+
for (const entry of readdirSync(platformsDir)) {
|
|
235
|
+
const dirPath = join(platformsDir, entry);
|
|
236
|
+
if (!statSync(dirPath).isDirectory())
|
|
237
|
+
continue;
|
|
238
|
+
const rootToken = join(dirPath, "token.json");
|
|
239
|
+
const usersDir = join(dirPath, "users");
|
|
240
|
+
if (!existsSync(rootToken) || existsSync(usersDir))
|
|
241
|
+
continue;
|
|
242
|
+
const token = readJsonFile(rootToken);
|
|
243
|
+
if (!token?.baseUrl)
|
|
244
|
+
continue;
|
|
245
|
+
migratePlatformToUserScoped(token.baseUrl);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
// ---------------------------------------------------------------------------
|
|
249
|
+
// Store initialization
|
|
250
|
+
// ---------------------------------------------------------------------------
|
|
108
251
|
function ensureStoreReady() {
|
|
109
252
|
ensureConfigDir();
|
|
110
253
|
migrateLegacyFilesIfNeeded();
|
|
254
|
+
migrateAllPlatformsToUserScoped();
|
|
255
|
+
}
|
|
256
|
+
// ---------------------------------------------------------------------------
|
|
257
|
+
// Active user management
|
|
258
|
+
// ---------------------------------------------------------------------------
|
|
259
|
+
/** Read active user from state without triggering ensureStoreReady (avoids recursion). */
|
|
260
|
+
function getActiveUserRaw(baseUrl) {
|
|
261
|
+
const state = readJsonFile(getStateFilePath()) ?? {};
|
|
262
|
+
const userId = state.activeUsers?.[baseUrl];
|
|
263
|
+
if (userId)
|
|
264
|
+
return userId;
|
|
265
|
+
// Fallback: scan users/ dir and pick the first one
|
|
266
|
+
const usersDir = getUsersDirPath(baseUrl);
|
|
267
|
+
if (!existsSync(usersDir))
|
|
268
|
+
return null;
|
|
269
|
+
for (const entry of readdirSync(usersDir)) {
|
|
270
|
+
const entryPath = join(usersDir, entry);
|
|
271
|
+
if (statSync(entryPath).isDirectory() && existsSync(join(entryPath, "token.json"))) {
|
|
272
|
+
return entry;
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
return null;
|
|
276
|
+
}
|
|
277
|
+
/** Get the active userId for a platform. */
|
|
278
|
+
export function getActiveUser(baseUrl) {
|
|
279
|
+
ensureStoreReady();
|
|
280
|
+
return getActiveUserRaw(baseUrl);
|
|
281
|
+
}
|
|
282
|
+
/** Set the active userId for a platform. */
|
|
283
|
+
export function setActiveUser(baseUrl, userId) {
|
|
284
|
+
ensureStoreReady();
|
|
285
|
+
const state = readState();
|
|
286
|
+
const activeUsers = { ...(state.activeUsers ?? {}) };
|
|
287
|
+
activeUsers[baseUrl] = userId;
|
|
288
|
+
writeState({ ...state, activeUsers });
|
|
289
|
+
}
|
|
290
|
+
/** List all user IDs stored under a platform. */
|
|
291
|
+
export function listUsers(baseUrl) {
|
|
292
|
+
ensureStoreReady();
|
|
293
|
+
const usersDir = getUsersDirPath(baseUrl);
|
|
294
|
+
if (!existsSync(usersDir))
|
|
295
|
+
return [];
|
|
296
|
+
const users = [];
|
|
297
|
+
for (const entry of readdirSync(usersDir)) {
|
|
298
|
+
const entryPath = join(usersDir, entry);
|
|
299
|
+
if (statSync(entryPath).isDirectory()) {
|
|
300
|
+
users.push(entry);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
return users.sort();
|
|
304
|
+
}
|
|
305
|
+
/** Load a specific user's token (not necessarily the active user). */
|
|
306
|
+
export function loadUserTokenConfig(baseUrl, userId) {
|
|
307
|
+
ensureStoreReady();
|
|
308
|
+
return readJsonFile(join(getUserDir(baseUrl, userId), "token.json"));
|
|
309
|
+
}
|
|
310
|
+
/**
|
|
311
|
+
* List all user profiles for a platform, enriched with display names.
|
|
312
|
+
*
|
|
313
|
+
* Resolution order for username:
|
|
314
|
+
* 1. ``displayName`` field persisted in token.json (set at login via /oauth2/userinfo)
|
|
315
|
+
* 2. ``preferred_username`` or ``name`` decoded from id_token JWT
|
|
316
|
+
*/
|
|
317
|
+
export function listUserProfiles(baseUrl) {
|
|
318
|
+
const userIds = listUsers(baseUrl);
|
|
319
|
+
return userIds.map((userId) => {
|
|
320
|
+
const token = loadUserTokenConfig(baseUrl, userId);
|
|
321
|
+
let username;
|
|
322
|
+
let email;
|
|
323
|
+
if (token?.displayName) {
|
|
324
|
+
username = token.displayName;
|
|
325
|
+
}
|
|
326
|
+
if (token?.idToken) {
|
|
327
|
+
const payload = decodeJwtPayload(token.idToken);
|
|
328
|
+
if (payload) {
|
|
329
|
+
if (!username) {
|
|
330
|
+
if (typeof payload.preferred_username === "string")
|
|
331
|
+
username = payload.preferred_username;
|
|
332
|
+
else if (typeof payload.name === "string")
|
|
333
|
+
username = payload.name;
|
|
334
|
+
}
|
|
335
|
+
if (typeof payload.email === "string")
|
|
336
|
+
email = payload.email;
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
return { userId, username, email };
|
|
340
|
+
});
|
|
111
341
|
}
|
|
342
|
+
/** Resolve a user identifier (userId, username, or email) to a userId for the given platform.
|
|
343
|
+
* userId and username are matched case-sensitively; email is case-insensitive. */
|
|
344
|
+
export function resolveUserId(baseUrl, identifier) {
|
|
345
|
+
const users = listUsers(baseUrl);
|
|
346
|
+
if (users.includes(identifier))
|
|
347
|
+
return identifier;
|
|
348
|
+
const profiles = listUserProfiles(baseUrl);
|
|
349
|
+
// Exact match on username (case-sensitive)
|
|
350
|
+
const exact = profiles.find((p) => p.username === identifier);
|
|
351
|
+
if (exact)
|
|
352
|
+
return exact.userId;
|
|
353
|
+
// Email match (case-insensitive per RFC 5321)
|
|
354
|
+
const lower = identifier.toLowerCase();
|
|
355
|
+
const byEmail = profiles.find((p) => p.email?.toLowerCase() === lower);
|
|
356
|
+
return byEmail?.userId ?? null;
|
|
357
|
+
}
|
|
358
|
+
// ---------------------------------------------------------------------------
|
|
359
|
+
// Public API — platform & alias management
|
|
360
|
+
// ---------------------------------------------------------------------------
|
|
112
361
|
export function getConfigDir() {
|
|
113
362
|
return getConfigDirPath();
|
|
114
363
|
}
|
|
@@ -178,19 +427,32 @@ export function resolvePlatformIdentifier(value) {
|
|
|
178
427
|
}
|
|
179
428
|
return normalized;
|
|
180
429
|
}
|
|
430
|
+
// ---------------------------------------------------------------------------
|
|
431
|
+
// Token config (user-scoped)
|
|
432
|
+
// ---------------------------------------------------------------------------
|
|
181
433
|
export function loadTokenConfig(baseUrl) {
|
|
182
434
|
ensureStoreReady();
|
|
183
435
|
const targetBaseUrl = baseUrl ?? getCurrentPlatform();
|
|
184
436
|
if (!targetBaseUrl) {
|
|
185
437
|
return null;
|
|
186
438
|
}
|
|
187
|
-
return readJsonFile(
|
|
439
|
+
return readJsonFile(resolveFile(targetBaseUrl, "token.json"));
|
|
188
440
|
}
|
|
189
|
-
export function saveTokenConfig(config) {
|
|
441
|
+
export function saveTokenConfig(config, userId) {
|
|
190
442
|
ensureStoreReady();
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
443
|
+
const resolvedUser = userId ?? extractUserId(config);
|
|
444
|
+
const dir = getUserDir(config.baseUrl, resolvedUser);
|
|
445
|
+
ensureDir(dir);
|
|
446
|
+
writeJsonFile(join(dir, "token.json"), config);
|
|
447
|
+
// When KWEAVER_USER is set the caller is doing a one-off operation;
|
|
448
|
+
// don't change the persisted active user.
|
|
449
|
+
if (!process.env.KWEAVER_USER) {
|
|
450
|
+
setActiveUser(config.baseUrl, resolvedUser);
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
// ---------------------------------------------------------------------------
|
|
454
|
+
// Client config (platform-level — shared across users)
|
|
455
|
+
// ---------------------------------------------------------------------------
|
|
194
456
|
export function loadClientConfig(baseUrl) {
|
|
195
457
|
ensureStoreReady();
|
|
196
458
|
const targetBaseUrl = baseUrl ?? getCurrentPlatform();
|
|
@@ -204,6 +466,11 @@ export function saveClientConfig(baseUrl, config) {
|
|
|
204
466
|
ensurePlatformDir(baseUrl);
|
|
205
467
|
writeJsonFile(getPlatformFile(baseUrl, "client.json"), { ...config, baseUrl });
|
|
206
468
|
}
|
|
469
|
+
export function deleteClientConfig(baseUrl) {
|
|
470
|
+
const filePath = getPlatformFile(baseUrl, "client.json");
|
|
471
|
+
if (existsSync(filePath))
|
|
472
|
+
rmSync(filePath, { force: true });
|
|
473
|
+
}
|
|
207
474
|
function migrateLegacyContextLoader(raw) {
|
|
208
475
|
const leg = raw;
|
|
209
476
|
if (leg?.knId && !Array.isArray(raw.configs)) {
|
|
@@ -220,7 +487,7 @@ export function loadContextLoaderConfig(baseUrl) {
|
|
|
220
487
|
if (!targetBaseUrl) {
|
|
221
488
|
return null;
|
|
222
489
|
}
|
|
223
|
-
const raw = readJsonFile(
|
|
490
|
+
const raw = readJsonFile(resolveFile(targetBaseUrl, "context-loader.json"));
|
|
224
491
|
if (!raw)
|
|
225
492
|
return null;
|
|
226
493
|
const migrated = migrateLegacyContextLoader(raw);
|
|
@@ -240,8 +507,16 @@ export function loadContextLoaderConfig(baseUrl) {
|
|
|
240
507
|
}
|
|
241
508
|
export function saveContextLoaderConfig(baseUrl, config) {
|
|
242
509
|
ensureStoreReady();
|
|
243
|
-
|
|
244
|
-
|
|
510
|
+
const userId = getActiveUser(baseUrl);
|
|
511
|
+
if (userId) {
|
|
512
|
+
const dir = getUserDir(baseUrl, userId);
|
|
513
|
+
ensureDir(dir);
|
|
514
|
+
writeJsonFile(join(dir, "context-loader.json"), config);
|
|
515
|
+
}
|
|
516
|
+
else {
|
|
517
|
+
ensurePlatformDir(baseUrl);
|
|
518
|
+
writeJsonFile(getPlatformFile(baseUrl, "context-loader.json"), config);
|
|
519
|
+
}
|
|
245
520
|
}
|
|
246
521
|
export function getCurrentContextLoaderKn(baseUrl) {
|
|
247
522
|
ensureStoreReady();
|
|
@@ -297,7 +572,7 @@ export function removeContextLoaderEntry(baseUrl, name) {
|
|
|
297
572
|
return;
|
|
298
573
|
const newConfigs = config.configs.filter((c) => c.name !== name);
|
|
299
574
|
if (newConfigs.length === 0) {
|
|
300
|
-
const file =
|
|
575
|
+
const file = resolveFile(baseUrl, "context-loader.json");
|
|
301
576
|
if (existsSync(file))
|
|
302
577
|
rmSync(file, { force: true });
|
|
303
578
|
return;
|
|
@@ -308,18 +583,47 @@ export function removeContextLoaderEntry(baseUrl, name) {
|
|
|
308
583
|
}
|
|
309
584
|
saveContextLoaderConfig(baseUrl, { configs: newConfigs, current: newCurrent });
|
|
310
585
|
}
|
|
586
|
+
// ---------------------------------------------------------------------------
|
|
587
|
+
// Platform existence / session management
|
|
588
|
+
// ---------------------------------------------------------------------------
|
|
311
589
|
export function hasPlatform(baseUrl) {
|
|
312
590
|
ensureStoreReady();
|
|
313
|
-
|
|
591
|
+
const file = resolveFile(baseUrl, "token.json");
|
|
592
|
+
if (existsSync(file))
|
|
593
|
+
return true;
|
|
594
|
+
return listUsers(baseUrl).length > 0;
|
|
314
595
|
}
|
|
315
|
-
|
|
316
|
-
* Remove token for a platform so the next auth will do a full login.
|
|
317
|
-
*/
|
|
318
|
-
export function clearPlatformSession(baseUrl) {
|
|
596
|
+
export function clearPlatformSession(baseUrl, userId) {
|
|
319
597
|
ensureStoreReady();
|
|
320
|
-
const
|
|
321
|
-
if (
|
|
322
|
-
|
|
598
|
+
const target = userId ?? getActiveUser(baseUrl);
|
|
599
|
+
if (target) {
|
|
600
|
+
const tokenFile = join(getUserDir(baseUrl, target), "token.json");
|
|
601
|
+
if (existsSync(tokenFile))
|
|
602
|
+
rmSync(tokenFile, { force: true });
|
|
603
|
+
return;
|
|
604
|
+
}
|
|
605
|
+
// Fallback: legacy flat layout
|
|
606
|
+
const legacyToken = getPlatformFile(baseUrl, "token.json");
|
|
607
|
+
if (existsSync(legacyToken))
|
|
608
|
+
rmSync(legacyToken, { force: true });
|
|
609
|
+
}
|
|
610
|
+
/** Delete a single user's profile directory under a platform. */
|
|
611
|
+
export function deleteUser(baseUrl, userId) {
|
|
612
|
+
ensureStoreReady();
|
|
613
|
+
const dir = getUserDir(baseUrl, userId);
|
|
614
|
+
if (existsSync(dir))
|
|
615
|
+
rmSync(dir, { recursive: true, force: true });
|
|
616
|
+
const state = readState();
|
|
617
|
+
if (state.activeUsers?.[baseUrl] === userId) {
|
|
618
|
+
const remaining = listUsers(baseUrl);
|
|
619
|
+
const au = { ...(state.activeUsers ?? {}) };
|
|
620
|
+
if (remaining.length > 0) {
|
|
621
|
+
au[baseUrl] = remaining[0];
|
|
622
|
+
}
|
|
623
|
+
else {
|
|
624
|
+
delete au[baseUrl];
|
|
625
|
+
}
|
|
626
|
+
writeState({ ...state, activeUsers: Object.keys(au).length > 0 ? au : undefined });
|
|
323
627
|
}
|
|
324
628
|
}
|
|
325
629
|
export function deletePlatform(baseUrl) {
|
|
@@ -331,33 +635,76 @@ export function deletePlatform(baseUrl) {
|
|
|
331
635
|
deletePlatformAlias(baseUrl);
|
|
332
636
|
rmSync(platformDir, { recursive: true, force: true });
|
|
333
637
|
const state = readState();
|
|
638
|
+
const au = { ...(state.activeUsers ?? {}) };
|
|
639
|
+
delete au[baseUrl];
|
|
334
640
|
if (state.currentPlatform !== baseUrl) {
|
|
641
|
+
writeState({ ...state, activeUsers: Object.keys(au).length > 0 ? au : undefined });
|
|
335
642
|
return;
|
|
336
643
|
}
|
|
337
644
|
const remainingPlatforms = listPlatforms();
|
|
338
645
|
writeState({
|
|
339
646
|
...readState(),
|
|
340
647
|
currentPlatform: remainingPlatforms[0]?.baseUrl,
|
|
648
|
+
activeUsers: Object.keys(au).length > 0 ? au : undefined,
|
|
341
649
|
});
|
|
342
650
|
}
|
|
343
651
|
export function listPlatforms() {
|
|
344
652
|
ensureStoreReady();
|
|
345
653
|
const currentPlatform = getCurrentPlatform();
|
|
346
654
|
const items = [];
|
|
347
|
-
|
|
348
|
-
|
|
655
|
+
const platformsDir = getPlatformsDirPath();
|
|
656
|
+
if (!existsSync(platformsDir))
|
|
657
|
+
return items;
|
|
658
|
+
for (const entry of readdirSync(platformsDir)) {
|
|
659
|
+
const dirPath = join(platformsDir, entry);
|
|
349
660
|
if (!statSync(dirPath).isDirectory()) {
|
|
350
661
|
continue;
|
|
351
662
|
}
|
|
352
|
-
|
|
353
|
-
|
|
663
|
+
// Try to find the baseUrl from any available token
|
|
664
|
+
let baseUrl = null;
|
|
665
|
+
// Check users/ subdirectory first (new layout)
|
|
666
|
+
const usersDir = join(dirPath, "users");
|
|
667
|
+
if (existsSync(usersDir)) {
|
|
668
|
+
for (const userEntry of readdirSync(usersDir)) {
|
|
669
|
+
const userTokenPath = join(usersDir, userEntry, "token.json");
|
|
670
|
+
const userToken = readJsonFile(userTokenPath);
|
|
671
|
+
if (userToken?.baseUrl) {
|
|
672
|
+
baseUrl = userToken.baseUrl;
|
|
673
|
+
break;
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
// Fallback: legacy flat layout
|
|
678
|
+
if (!baseUrl) {
|
|
679
|
+
const token = readJsonFile(join(dirPath, "token.json"));
|
|
680
|
+
if (token?.baseUrl) {
|
|
681
|
+
baseUrl = token.baseUrl;
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
// Fallback: client.json
|
|
685
|
+
if (!baseUrl) {
|
|
686
|
+
const client = readJsonFile(join(dirPath, "client.json"));
|
|
687
|
+
if (client?.baseUrl) {
|
|
688
|
+
baseUrl = client.baseUrl;
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
if (!baseUrl)
|
|
354
692
|
continue;
|
|
693
|
+
const hasToken = existsSync(resolveFile(baseUrl, "token.json"));
|
|
694
|
+
const activeUser = getActiveUserRaw(baseUrl);
|
|
695
|
+
let displayName;
|
|
696
|
+
if (activeUser) {
|
|
697
|
+
const tok = loadUserTokenConfig(baseUrl, activeUser);
|
|
698
|
+
if (tok?.displayName)
|
|
699
|
+
displayName = tok.displayName;
|
|
355
700
|
}
|
|
356
701
|
items.push({
|
|
357
|
-
baseUrl
|
|
358
|
-
hasToken
|
|
359
|
-
isCurrent:
|
|
360
|
-
alias: getPlatformAlias(
|
|
702
|
+
baseUrl,
|
|
703
|
+
hasToken,
|
|
704
|
+
isCurrent: baseUrl === currentPlatform,
|
|
705
|
+
alias: getPlatformAlias(baseUrl) ?? undefined,
|
|
706
|
+
userId: activeUser ?? undefined,
|
|
707
|
+
displayName,
|
|
361
708
|
});
|
|
362
709
|
}
|
|
363
710
|
items.sort((a, b) => a.baseUrl.localeCompare(b.baseUrl));
|
|
@@ -365,12 +712,20 @@ export function listPlatforms() {
|
|
|
365
712
|
}
|
|
366
713
|
function loadPlatformConfig(baseUrl) {
|
|
367
714
|
ensureStoreReady();
|
|
368
|
-
return readJsonFile(
|
|
715
|
+
return readJsonFile(resolveFile(baseUrl, "config.json"));
|
|
369
716
|
}
|
|
370
717
|
function savePlatformConfig(baseUrl, config) {
|
|
371
718
|
ensureStoreReady();
|
|
372
|
-
|
|
373
|
-
|
|
719
|
+
const userId = getActiveUser(baseUrl);
|
|
720
|
+
if (userId) {
|
|
721
|
+
const dir = getUserDir(baseUrl, userId);
|
|
722
|
+
ensureDir(dir);
|
|
723
|
+
writeJsonFile(join(dir, "config.json"), config);
|
|
724
|
+
}
|
|
725
|
+
else {
|
|
726
|
+
ensurePlatformDir(baseUrl);
|
|
727
|
+
writeJsonFile(getPlatformFile(baseUrl, "config.json"), config);
|
|
728
|
+
}
|
|
374
729
|
}
|
|
375
730
|
export function loadPlatformBusinessDomain(baseUrl) {
|
|
376
731
|
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 {
|
|
62
|
+
export type { UserProfile } from "./config/store.js";
|
|
63
|
+
export { NO_AUTH_TOKEN, isNoAuth, saveNoAuthPlatform, 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 { NO_AUTH_TOKEN, isNoAuth, saveNoAuthPlatform, autoSelectBusinessDomain, getConfigDir, getCurrentPlatform, getActiveUser, setActiveUser, listUsers, listUserProfiles, resolveUserId, extractUserId, } from "./config/store.js";
|
|
50
|
+
// ── JWT utilities ─────────────────────────────────────────────────────────────
|
|
51
|
+
export { decodeJwtPayload, extractUserIdFromJwt } from "./config/jwt.js";
|
package/dist/kweaver.d.ts
CHANGED
|
@@ -37,6 +37,11 @@ export interface ConfigureOptions {
|
|
|
37
37
|
* config, preventing accidental cross-environment credential leaks.
|
|
38
38
|
*/
|
|
39
39
|
config?: boolean;
|
|
40
|
+
/**
|
|
41
|
+
* If false, use no-auth mode (no Authorization headers). Requires baseUrl or KWEAVER_BASE_URL.
|
|
42
|
+
* Incompatible with config=true.
|
|
43
|
+
*/
|
|
44
|
+
auth?: boolean;
|
|
40
45
|
/** Default BKN ID used by search() and weaver(). */
|
|
41
46
|
bknId?: string;
|
|
42
47
|
/** Default agent ID used by chat(). */
|