@kweaver-ai/kweaver-sdk 0.4.7 → 0.4.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/dist/auth/oauth.d.ts +11 -2
- package/dist/auth/oauth.js +95 -63
- package/dist/cli.js +77 -24
- package/dist/commands/bkn.d.ts +36 -0
- package/dist/commands/bkn.js +194 -15
- package/dist/commands/call.js +7 -1
- package/package.json +2 -2
package/dist/auth/oauth.d.ts
CHANGED
|
@@ -13,12 +13,21 @@ export declare function oauth2Login(baseUrl: string, options?: {
|
|
|
13
13
|
scope?: string;
|
|
14
14
|
}): Promise<TokenConfig>;
|
|
15
15
|
/**
|
|
16
|
-
* Playwright
|
|
17
|
-
*
|
|
16
|
+
* Playwright-automated OAuth2 login.
|
|
17
|
+
*
|
|
18
|
+
* Uses the full OAuth2 authorization code flow (same as `oauth2Login`) but
|
|
19
|
+
* automates the browser interaction with Playwright. This produces a
|
|
20
|
+
* refresh_token so the CLI can auto-refresh without re-login.
|
|
21
|
+
*
|
|
22
|
+
* When `username` and `password` are provided the browser runs headless and
|
|
23
|
+
* fills the login form automatically. Otherwise it opens a visible browser
|
|
24
|
+
* window for manual login (same UX as the old cookie-based flow).
|
|
18
25
|
*/
|
|
19
26
|
export declare function playwrightLogin(baseUrl: string, options?: {
|
|
20
27
|
username?: string;
|
|
21
28
|
password?: string;
|
|
29
|
+
port?: number;
|
|
30
|
+
scope?: string;
|
|
22
31
|
}): Promise<TokenConfig>;
|
|
23
32
|
/**
|
|
24
33
|
* Exchange refresh_token for a new access token (OAuth2 password grant style, same as Python ConfigAuth).
|
package/dist/auth/oauth.js
CHANGED
|
@@ -161,10 +161,19 @@ async function exchangeCodeForToken(baseUrl, code, clientId, clientSecret, redir
|
|
|
161
161
|
return token;
|
|
162
162
|
}
|
|
163
163
|
/**
|
|
164
|
-
* Playwright
|
|
165
|
-
*
|
|
164
|
+
* Playwright-automated OAuth2 login.
|
|
165
|
+
*
|
|
166
|
+
* Uses the full OAuth2 authorization code flow (same as `oauth2Login`) but
|
|
167
|
+
* automates the browser interaction with Playwright. This produces a
|
|
168
|
+
* refresh_token so the CLI can auto-refresh without re-login.
|
|
169
|
+
*
|
|
170
|
+
* When `username` and `password` are provided the browser runs headless and
|
|
171
|
+
* fills the login form automatically. Otherwise it opens a visible browser
|
|
172
|
+
* window for manual login (same UX as the old cookie-based flow).
|
|
166
173
|
*/
|
|
167
174
|
export async function playwrightLogin(baseUrl, options) {
|
|
175
|
+
const { createServer } = await import("node:http");
|
|
176
|
+
const { randomBytes } = await import("node:crypto");
|
|
168
177
|
let chromium;
|
|
169
178
|
try {
|
|
170
179
|
const modName = "playwright";
|
|
@@ -174,71 +183,94 @@ export async function playwrightLogin(baseUrl, options) {
|
|
|
174
183
|
catch {
|
|
175
184
|
throw new Error("Playwright is not installed. Run:\n npm install playwright && npx playwright install chromium");
|
|
176
185
|
}
|
|
177
|
-
const
|
|
178
|
-
const
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
186
|
+
const base = normalizeBaseUrl(baseUrl);
|
|
187
|
+
const port = options?.port ?? DEFAULT_REDIRECT_PORT;
|
|
188
|
+
const scope = options?.scope ?? DEFAULT_SCOPE;
|
|
189
|
+
const redirectUri = `http://127.0.0.1:${port}/callback`;
|
|
190
|
+
const hasCredentials = !!(options?.username && options?.password);
|
|
191
|
+
// Step 1: Ensure registered OAuth2 client
|
|
192
|
+
let client = loadClientConfig(base);
|
|
193
|
+
if (!client?.clientId) {
|
|
194
|
+
client = await registerOAuth2Client(base, redirectUri, scope);
|
|
195
|
+
saveClientConfig(base, client);
|
|
196
|
+
}
|
|
197
|
+
// Step 2: Generate CSRF state
|
|
198
|
+
const state = randomBytes(12).toString("hex");
|
|
199
|
+
// Step 3: Build authorization URL
|
|
200
|
+
const authParams = new URLSearchParams({
|
|
201
|
+
redirect_uri: redirectUri,
|
|
202
|
+
"x-forwarded-prefix": "",
|
|
203
|
+
client_id: client.clientId,
|
|
204
|
+
scope,
|
|
205
|
+
response_type: "code",
|
|
206
|
+
state,
|
|
207
|
+
lang: "zh-cn",
|
|
208
|
+
product: "adp",
|
|
209
|
+
});
|
|
210
|
+
const authUrl = `${base}/oauth2/auth?${authParams.toString()}`;
|
|
211
|
+
// Step 4: Start local callback server to capture the authorization code
|
|
212
|
+
const code = await new Promise((resolve, reject) => {
|
|
213
|
+
const TIMEOUT_MS = hasCredentials ? 30_000 : 120_000;
|
|
214
|
+
const timeoutId = setTimeout(() => {
|
|
215
|
+
server.close();
|
|
216
|
+
browser?.close();
|
|
217
|
+
reject(new Error(`OAuth2 login timed out (${TIMEOUT_MS / 1000}s). No authorization code received.`));
|
|
218
|
+
}, TIMEOUT_MS);
|
|
219
|
+
const server = createServer((req, res) => {
|
|
220
|
+
const url = new URL(req.url ?? "/", `http://127.0.0.1:${port}`);
|
|
221
|
+
if (url.pathname === "/callback") {
|
|
222
|
+
const receivedState = url.searchParams.get("state");
|
|
223
|
+
const receivedCode = url.searchParams.get("code");
|
|
224
|
+
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
|
|
225
|
+
res.end("<html><body><h2>Login successful. You can close this tab.</h2></body></html>");
|
|
226
|
+
clearTimeout(timeoutId);
|
|
227
|
+
server.close();
|
|
228
|
+
browser?.close();
|
|
229
|
+
if (receivedState !== state) {
|
|
230
|
+
reject(new Error("OAuth2 state mismatch — possible CSRF attack."));
|
|
203
231
|
}
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
break;
|
|
207
|
-
// In headless mode, check for login error messages
|
|
208
|
-
if (hasCredentials) {
|
|
209
|
-
try {
|
|
210
|
-
const errorEl = await page.$(".ant-message-error, .ant-alert-error");
|
|
211
|
-
if (errorEl) {
|
|
212
|
-
const errorText = await errorEl.textContent();
|
|
213
|
-
throw new Error(`Login failed: ${errorText?.trim() || "unknown error"}`);
|
|
214
|
-
}
|
|
232
|
+
else if (!receivedCode) {
|
|
233
|
+
reject(new Error("No authorization code received in callback."));
|
|
215
234
|
}
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
throw e;
|
|
235
|
+
else {
|
|
236
|
+
resolve(receivedCode);
|
|
219
237
|
}
|
|
220
238
|
}
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
239
|
+
else {
|
|
240
|
+
res.writeHead(404);
|
|
241
|
+
res.end();
|
|
242
|
+
}
|
|
243
|
+
});
|
|
244
|
+
let browser;
|
|
245
|
+
server.listen(port, "127.0.0.1", async () => {
|
|
246
|
+
try {
|
|
247
|
+
browser = await chromium.launch({ headless: hasCredentials });
|
|
248
|
+
const context = await browser.newContext();
|
|
249
|
+
const page = await context.newPage();
|
|
250
|
+
// Navigate to OAuth2 auth URL — redirects to signin page
|
|
251
|
+
await page.goto(authUrl, { waitUntil: "networkidle", timeout: 30_000 });
|
|
252
|
+
if (hasCredentials) {
|
|
253
|
+
// Auto-fill credentials
|
|
254
|
+
await page.waitForSelector('input[name="account"]', { timeout: 10_000 });
|
|
255
|
+
await page.fill('input[name="account"]', options.username);
|
|
256
|
+
await page.fill('input[name="password"]', options.password);
|
|
257
|
+
await page.click("button.ant-btn-primary");
|
|
258
|
+
}
|
|
259
|
+
// else: visible browser — user logs in manually
|
|
260
|
+
// The OAuth2 callback will fire when login completes, resolving the promise above
|
|
261
|
+
}
|
|
262
|
+
catch (err) {
|
|
263
|
+
clearTimeout(timeoutId);
|
|
264
|
+
server.close();
|
|
265
|
+
browser?.close();
|
|
266
|
+
reject(err);
|
|
267
|
+
}
|
|
268
|
+
});
|
|
269
|
+
});
|
|
270
|
+
// Step 5: Exchange authorization code for tokens (includes refresh_token)
|
|
271
|
+
const token = await exchangeCodeForToken(base, code, client.clientId, client.clientSecret, redirectUri);
|
|
272
|
+
setCurrentPlatform(base);
|
|
273
|
+
return token;
|
|
242
274
|
}
|
|
243
275
|
function tokenNeedsRefresh(token) {
|
|
244
276
|
if (!token.expiresAt) {
|
package/dist/cli.js
CHANGED
|
@@ -11,47 +11,100 @@ function printHelp() {
|
|
|
11
11
|
console.log(`kweaver
|
|
12
12
|
|
|
13
13
|
Usage:
|
|
14
|
-
kweaver
|
|
15
|
-
kweaver
|
|
16
|
-
|
|
17
|
-
kweaver auth
|
|
14
|
+
kweaver --version | -V
|
|
15
|
+
kweaver --help | -h
|
|
16
|
+
|
|
17
|
+
kweaver auth <platform-url> [--alias name] [-u user] [-p pass] [--playwright]
|
|
18
|
+
kweaver auth login <platform-url> (alias for auth <url>)
|
|
19
|
+
kweaver auth status [platform-url|alias]
|
|
18
20
|
kweaver auth list
|
|
19
|
-
kweaver auth use <platform-url>
|
|
20
|
-
kweaver auth logout [platform-url]
|
|
21
|
-
kweaver auth delete <platform-url>
|
|
21
|
+
kweaver auth use <platform-url|alias>
|
|
22
|
+
kweaver auth logout [platform-url|alias]
|
|
23
|
+
kweaver auth delete <platform-url|alias>
|
|
22
24
|
kweaver token
|
|
23
|
-
|
|
24
|
-
kweaver
|
|
25
|
-
|
|
26
|
-
kweaver
|
|
27
|
-
|
|
25
|
+
|
|
26
|
+
kweaver call <url> [-X METHOD] [-H "Name: value"] [-d BODY] [--data-raw BODY]
|
|
27
|
+
[--url URL] [--pretty] [--verbose] [-bd value]
|
|
28
|
+
(alias: kweaver curl ...)
|
|
29
|
+
|
|
30
|
+
kweaver agent chat <agent_id> [-m "message"] [--version value] [--conversation-id id]
|
|
31
|
+
[--stream] [--no-stream] [--verbose] [-bd value]
|
|
32
|
+
kweaver agent list [--name X] [--limit N] [--offset N] [-bd value] [--pretty]
|
|
33
|
+
kweaver agent get <agent_id> [-bd value] [--pretty]
|
|
34
|
+
kweaver agent get-by-key <key> [-bd value] [--pretty]
|
|
35
|
+
kweaver agent sessions <agent_id> [-bd value] [--limit N] [--pretty]
|
|
36
|
+
kweaver agent history <agent_id> <session_id> [-bd value] [--limit N] [--pretty]
|
|
37
|
+
kweaver agent create [options]
|
|
38
|
+
kweaver agent update <agent_id> [options]
|
|
39
|
+
kweaver agent delete <agent_id> [-bd value]
|
|
40
|
+
kweaver agent publish <agent_id> [-bd value]
|
|
41
|
+
kweaver agent unpublish <agent_id> [-bd value]
|
|
42
|
+
|
|
43
|
+
kweaver ds list [--keyword X] [--type T] [-bd value] [--pretty]
|
|
28
44
|
kweaver ds get <id>
|
|
29
|
-
kweaver ds
|
|
45
|
+
kweaver ds delete <id> [-y]
|
|
46
|
+
kweaver ds tables <id> [--keyword X] [--pretty]
|
|
47
|
+
kweaver ds connect <db_type> <host> <port> <database> --account X --password Y [--schema S] [--name N]
|
|
48
|
+
|
|
30
49
|
kweaver bkn list [options]
|
|
31
50
|
kweaver bkn get <kn-id> [options]
|
|
32
|
-
kweaver bkn search <kn-id> <query> [
|
|
51
|
+
kweaver bkn search <kn-id> <query> [--max-concepts N] [--mode M] [--pretty] [-bd value]
|
|
33
52
|
kweaver bkn create [options]
|
|
53
|
+
kweaver bkn create-from-ds [options]
|
|
34
54
|
kweaver bkn update <kn-id> [options]
|
|
35
|
-
kweaver bkn delete <kn-id>
|
|
36
|
-
kweaver
|
|
37
|
-
kweaver
|
|
38
|
-
kweaver
|
|
39
|
-
kweaver
|
|
55
|
+
kweaver bkn delete <kn-id> [-y]
|
|
56
|
+
kweaver bkn build <kn-id> [--wait] [--no-wait] [--timeout N]
|
|
57
|
+
kweaver bkn validate <kn-id>
|
|
58
|
+
kweaver bkn export <kn-id>
|
|
59
|
+
kweaver bkn stats <kn-id>
|
|
60
|
+
kweaver bkn push <directory> [--branch main] [-bd value]
|
|
61
|
+
kweaver bkn pull <kn-id> [directory] [--branch main] [-bd value]
|
|
62
|
+
kweaver bkn object-type list|get|create|update|delete|query|properties <kn-id> ...
|
|
63
|
+
kweaver bkn relation-type list|get|create|update|delete <kn-id> ...
|
|
64
|
+
kweaver bkn subgraph <kn-id> <body-json>
|
|
65
|
+
kweaver bkn action-type list|query|execute <kn-id> ... [--wait] [--no-wait] [--timeout N]
|
|
66
|
+
kweaver bkn action-execution get <kn-id> <execution-id>
|
|
67
|
+
kweaver bkn action-log list|get|cancel <kn-id> ...
|
|
68
|
+
|
|
69
|
+
kweaver config set-bd <value>
|
|
70
|
+
kweaver config show
|
|
71
|
+
|
|
72
|
+
kweaver vega health|stats|inspect
|
|
73
|
+
kweaver vega catalog list|get|health|test-connection|discover|resources [options]
|
|
74
|
+
kweaver vega resource list|get|query|preview [options]
|
|
75
|
+
kweaver vega connector-type list|get [options]
|
|
76
|
+
|
|
77
|
+
kweaver context-loader config set|use|list|remove|show [options]
|
|
78
|
+
kweaver context-loader tools|resources|templates|prompts [--cursor]
|
|
79
|
+
kweaver context-loader resource <uri>
|
|
80
|
+
kweaver context-loader prompt <name> [--args json]
|
|
81
|
+
kweaver context-loader kn-search <query> [--only-schema]
|
|
82
|
+
kweaver context-loader kn-schema-search <query> [--max N]
|
|
83
|
+
kweaver context-loader query-object-instance|query-instance-subgraph|get-logic-properties|get-action-info ...
|
|
84
|
+
(alias: kweaver context ...)
|
|
40
85
|
|
|
41
86
|
Commands:
|
|
42
87
|
auth Login, list, inspect, and switch saved platform auth profiles
|
|
43
88
|
token Print the current access token, refreshing it first if needed
|
|
44
|
-
call
|
|
89
|
+
call (curl) Call an API with curl-style flags and auto-injected token headers
|
|
90
|
+
agent Agent CRUD, chat, sessions, history, publish/unpublish
|
|
45
91
|
ds Manage datasources (list, get, delete, tables, connect)
|
|
46
|
-
|
|
47
|
-
|
|
92
|
+
bkn Knowledge network (CRUD, build, validate, export, stats, push/pull,
|
|
93
|
+
object-type, relation-type, subgraph, action-type, action-execution, action-log)
|
|
48
94
|
config Per-platform configuration (business domain)
|
|
49
|
-
vega Vega observability
|
|
50
|
-
context-loader
|
|
95
|
+
vega Vega observability (catalog, resource, connector-type, health/stats/inspect)
|
|
96
|
+
context-loader Context-loader MCP (config, tools, resources, prompts, kn-search, query-*, etc.)
|
|
51
97
|
help Show this message`);
|
|
52
98
|
}
|
|
53
99
|
export async function run(argv) {
|
|
54
100
|
const [command, ...rest] = argv;
|
|
101
|
+
if (command === "--version" || command === "-V" || command === "version") {
|
|
102
|
+
const { createRequire } = await import("node:module");
|
|
103
|
+
const require = createRequire(import.meta.url);
|
|
104
|
+
const pkg = require("../package.json");
|
|
105
|
+
console.log(pkg.version);
|
|
106
|
+
return 0;
|
|
107
|
+
}
|
|
55
108
|
if (argv.length === 0 || !command || command === "--help" || command === "-h" || command === "help") {
|
|
56
109
|
printHelp();
|
|
57
110
|
return 0;
|
package/dist/commands/bkn.d.ts
CHANGED
|
@@ -64,6 +64,42 @@ export interface KnObjectTypeQueryOptions {
|
|
|
64
64
|
}
|
|
65
65
|
export declare function parseKnObjectTypeQueryArgs(args: string[]): KnObjectTypeQueryOptions;
|
|
66
66
|
export declare function runKnCommand(args: string[]): Promise<number>;
|
|
67
|
+
/** Fields merged via GET → modify → PUT (not raw body mode). */
|
|
68
|
+
export interface ObjectTypeMergeFields {
|
|
69
|
+
name?: string;
|
|
70
|
+
displayKey?: string;
|
|
71
|
+
addProperties: Record<string, unknown>[];
|
|
72
|
+
removeProperties: string[];
|
|
73
|
+
tags?: string[];
|
|
74
|
+
comment?: string;
|
|
75
|
+
icon?: string;
|
|
76
|
+
color?: string;
|
|
77
|
+
}
|
|
78
|
+
export type ObjectTypeUpdateParsed = {
|
|
79
|
+
mode: "body";
|
|
80
|
+
knId: string;
|
|
81
|
+
otId: string;
|
|
82
|
+
body: string;
|
|
83
|
+
businessDomain: string;
|
|
84
|
+
pretty: boolean;
|
|
85
|
+
} | {
|
|
86
|
+
mode: "merge";
|
|
87
|
+
knId: string;
|
|
88
|
+
otId: string;
|
|
89
|
+
merge: ObjectTypeMergeFields;
|
|
90
|
+
businessDomain: string;
|
|
91
|
+
pretty: boolean;
|
|
92
|
+
branch: string;
|
|
93
|
+
};
|
|
94
|
+
/** Prepare a GET response entry for PUT (drop read-only fields). */
|
|
95
|
+
export declare function stripObjectTypeForPut(entry: Record<string, unknown>): Record<string, unknown>;
|
|
96
|
+
/**
|
|
97
|
+
* Apply merge flags onto a stripped object-type object (mutates copy).
|
|
98
|
+
* - Add: property `name` not in list → append.
|
|
99
|
+
* - Update: property `name` exists → replace entry (same as add; CLI also accepts `--update-property`).
|
|
100
|
+
* - Delete: `--remove-property` removes by `name` before adds are applied.
|
|
101
|
+
*/
|
|
102
|
+
export declare function applyObjectTypeMerge(target: Record<string, unknown>, merge: ObjectTypeMergeFields): Record<string, unknown>;
|
|
67
103
|
export interface KnActionTypeExecuteOptions {
|
|
68
104
|
knId: string;
|
|
69
105
|
atId: string;
|
package/dist/commands/bkn.js
CHANGED
|
@@ -767,12 +767,84 @@ function parseObjectTypeCreateArgs(args) {
|
|
|
767
767
|
businessDomain = resolveBusinessDomain();
|
|
768
768
|
return { knId, body, businessDomain, branch, pretty };
|
|
769
769
|
}
|
|
770
|
-
|
|
770
|
+
const OBJECT_TYPE_PUT_STRIP_KEYS = new Set([
|
|
771
|
+
"status",
|
|
772
|
+
"creator",
|
|
773
|
+
"updater",
|
|
774
|
+
"create_time",
|
|
775
|
+
"update_time",
|
|
776
|
+
"module_type",
|
|
777
|
+
"kn_id",
|
|
778
|
+
]);
|
|
779
|
+
/** Prepare a GET response entry for PUT (drop read-only fields). */
|
|
780
|
+
export function stripObjectTypeForPut(entry) {
|
|
781
|
+
const out = { ...entry };
|
|
782
|
+
for (const k of OBJECT_TYPE_PUT_STRIP_KEYS) {
|
|
783
|
+
delete out[k];
|
|
784
|
+
}
|
|
785
|
+
return out;
|
|
786
|
+
}
|
|
787
|
+
/**
|
|
788
|
+
* Apply merge flags onto a stripped object-type object (mutates copy).
|
|
789
|
+
* - Add: property `name` not in list → append.
|
|
790
|
+
* - Update: property `name` exists → replace entry (same as add; CLI also accepts `--update-property`).
|
|
791
|
+
* - Delete: `--remove-property` removes by `name` before adds are applied.
|
|
792
|
+
*/
|
|
793
|
+
export function applyObjectTypeMerge(target, merge) {
|
|
794
|
+
if (merge.name !== undefined)
|
|
795
|
+
target.name = merge.name;
|
|
796
|
+
if (merge.displayKey !== undefined)
|
|
797
|
+
target.display_key = merge.displayKey;
|
|
798
|
+
if (merge.comment !== undefined)
|
|
799
|
+
target.comment = merge.comment;
|
|
800
|
+
if (merge.icon !== undefined)
|
|
801
|
+
target.icon = merge.icon;
|
|
802
|
+
if (merge.color !== undefined)
|
|
803
|
+
target.color = merge.color;
|
|
804
|
+
if (merge.tags !== undefined)
|
|
805
|
+
target.tags = merge.tags;
|
|
806
|
+
let props = target.data_properties;
|
|
807
|
+
if (!Array.isArray(props)) {
|
|
808
|
+
props = [];
|
|
809
|
+
}
|
|
810
|
+
else {
|
|
811
|
+
props = props.map((p) => p && typeof p === "object" && !Array.isArray(p) ? { ...p } : p);
|
|
812
|
+
}
|
|
813
|
+
const list = props;
|
|
814
|
+
for (const rm of merge.removeProperties) {
|
|
815
|
+
for (let j = list.length - 1; j >= 0; j -= 1) {
|
|
816
|
+
const n = list[j]?.name;
|
|
817
|
+
if (typeof n === "string" && n === rm)
|
|
818
|
+
list.splice(j, 1);
|
|
819
|
+
}
|
|
820
|
+
}
|
|
821
|
+
for (const add of merge.addProperties) {
|
|
822
|
+
const nm = add.name;
|
|
823
|
+
if (typeof nm !== "string" || !nm) {
|
|
824
|
+
throw new Error("--add-property / --update-property JSON must include a non-empty string \"name\" field.");
|
|
825
|
+
}
|
|
826
|
+
const idx = list.findIndex((p) => p?.name === nm);
|
|
827
|
+
if (idx >= 0)
|
|
828
|
+
list[idx] = add;
|
|
829
|
+
else
|
|
830
|
+
list.push(add);
|
|
831
|
+
}
|
|
832
|
+
target.data_properties = list;
|
|
833
|
+
return target;
|
|
834
|
+
}
|
|
835
|
+
/** Parse object-type update: raw JSON body OR merge flags (GET-merge-PUT). */
|
|
771
836
|
function parseObjectTypeUpdateArgs(args) {
|
|
772
837
|
let name;
|
|
773
838
|
let displayKey;
|
|
774
839
|
let businessDomain = "";
|
|
775
840
|
let pretty = true;
|
|
841
|
+
let branch = "main";
|
|
842
|
+
let comment;
|
|
843
|
+
let icon;
|
|
844
|
+
let color;
|
|
845
|
+
let tagsJson;
|
|
846
|
+
const addProperties = [];
|
|
847
|
+
const removeProperties = [];
|
|
776
848
|
const positional = [];
|
|
777
849
|
for (let i = 0; i < args.length; i += 1) {
|
|
778
850
|
const arg = args[i];
|
|
@@ -786,6 +858,35 @@ function parseObjectTypeUpdateArgs(args) {
|
|
|
786
858
|
displayKey = args[++i];
|
|
787
859
|
continue;
|
|
788
860
|
}
|
|
861
|
+
if ((arg === "--add-property" || arg === "--update-property") && args[i + 1]) {
|
|
862
|
+
const raw = args[++i];
|
|
863
|
+
addProperties.push(parseJsonObject(raw, `--add-property / --update-property must be valid JSON object: ${raw}`));
|
|
864
|
+
continue;
|
|
865
|
+
}
|
|
866
|
+
if (arg === "--remove-property" && args[i + 1]) {
|
|
867
|
+
removeProperties.push(args[++i]);
|
|
868
|
+
continue;
|
|
869
|
+
}
|
|
870
|
+
if (arg === "--tags" && args[i + 1]) {
|
|
871
|
+
tagsJson = args[++i];
|
|
872
|
+
continue;
|
|
873
|
+
}
|
|
874
|
+
if (arg === "--comment" && args[i + 1]) {
|
|
875
|
+
comment = args[++i];
|
|
876
|
+
continue;
|
|
877
|
+
}
|
|
878
|
+
if (arg === "--icon" && args[i + 1]) {
|
|
879
|
+
icon = args[++i];
|
|
880
|
+
continue;
|
|
881
|
+
}
|
|
882
|
+
if (arg === "--color" && args[i + 1]) {
|
|
883
|
+
color = args[++i];
|
|
884
|
+
continue;
|
|
885
|
+
}
|
|
886
|
+
if (arg === "--branch" && args[i + 1]) {
|
|
887
|
+
branch = args[++i];
|
|
888
|
+
continue;
|
|
889
|
+
}
|
|
789
890
|
if ((arg === "-bd" || arg === "--biz-domain") && args[i + 1]) {
|
|
790
891
|
businessDomain = args[++i];
|
|
791
892
|
continue;
|
|
@@ -797,21 +898,72 @@ function parseObjectTypeUpdateArgs(args) {
|
|
|
797
898
|
if (!arg.startsWith("-"))
|
|
798
899
|
positional.push(arg);
|
|
799
900
|
}
|
|
800
|
-
const [knId, otId] = positional;
|
|
901
|
+
const [knId, otId, maybeBody] = positional;
|
|
801
902
|
if (!knId || !otId) {
|
|
802
|
-
throw new Error("Usage: kweaver bkn object-type update <kn-id> <ot-id> [--name
|
|
903
|
+
throw new Error("Usage: kweaver bkn object-type update <kn-id> <ot-id> [ '<full-json-body>' ] [--name ...] [--add-property|--update-property '<json>' ...] [--remove-property <name> ...]");
|
|
904
|
+
}
|
|
905
|
+
const hasMergeFlags = name !== undefined ||
|
|
906
|
+
displayKey !== undefined ||
|
|
907
|
+
addProperties.length > 0 ||
|
|
908
|
+
removeProperties.length > 0 ||
|
|
909
|
+
tagsJson !== undefined ||
|
|
910
|
+
comment !== undefined ||
|
|
911
|
+
icon !== undefined ||
|
|
912
|
+
color !== undefined;
|
|
913
|
+
if (maybeBody !== undefined && maybeBody.trim().startsWith("{")) {
|
|
914
|
+
if (hasMergeFlags) {
|
|
915
|
+
throw new Error("Do not combine a raw JSON body with --name/--add-property/--update-property/--remove-property and other merge flags.");
|
|
916
|
+
}
|
|
917
|
+
if (!businessDomain)
|
|
918
|
+
businessDomain = resolveBusinessDomain();
|
|
919
|
+
return {
|
|
920
|
+
mode: "body",
|
|
921
|
+
knId,
|
|
922
|
+
otId,
|
|
923
|
+
body: maybeBody.trim(),
|
|
924
|
+
businessDomain,
|
|
925
|
+
pretty,
|
|
926
|
+
};
|
|
927
|
+
}
|
|
928
|
+
if (maybeBody !== undefined) {
|
|
929
|
+
throw new Error(`Unexpected third argument "${maybeBody}". For raw PUT body, pass a single JSON object starting with "{".`);
|
|
803
930
|
}
|
|
804
|
-
|
|
805
|
-
if (
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
931
|
+
let tags;
|
|
932
|
+
if (tagsJson !== undefined) {
|
|
933
|
+
try {
|
|
934
|
+
const t = JSON.parse(tagsJson);
|
|
935
|
+
if (!Array.isArray(t) || !t.every((x) => typeof x === "string")) {
|
|
936
|
+
throw new Error("invalid");
|
|
937
|
+
}
|
|
938
|
+
tags = t;
|
|
939
|
+
}
|
|
940
|
+
catch {
|
|
941
|
+
throw new Error(`--tags must be a JSON array of strings, e.g. '["足球","球员"]'`);
|
|
942
|
+
}
|
|
943
|
+
}
|
|
944
|
+
const merge = {
|
|
945
|
+
addProperties,
|
|
946
|
+
removeProperties,
|
|
947
|
+
...(name !== undefined ? { name } : {}),
|
|
948
|
+
...(displayKey !== undefined ? { displayKey } : {}),
|
|
949
|
+
...(tags !== undefined ? { tags } : {}),
|
|
950
|
+
...(comment !== undefined ? { comment } : {}),
|
|
951
|
+
...(icon !== undefined ? { icon } : {}),
|
|
952
|
+
...(color !== undefined ? { color } : {}),
|
|
953
|
+
};
|
|
954
|
+
if (merge.name === undefined &&
|
|
955
|
+
merge.displayKey === undefined &&
|
|
956
|
+
merge.addProperties.length === 0 &&
|
|
957
|
+
merge.removeProperties.length === 0 &&
|
|
958
|
+
merge.tags === undefined &&
|
|
959
|
+
merge.comment === undefined &&
|
|
960
|
+
merge.icon === undefined &&
|
|
961
|
+
merge.color === undefined) {
|
|
962
|
+
throw new Error("No update fields. Use --name, --display-key, --add-property (new), --update-property (same as add; replaces by name), --remove-property, --tags, --comment, --icon, --color, or pass a full JSON object as the third argument.");
|
|
811
963
|
}
|
|
812
964
|
if (!businessDomain)
|
|
813
965
|
businessDomain = resolveBusinessDomain();
|
|
814
|
-
return { knId, otId,
|
|
966
|
+
return { mode: "merge", knId, otId, merge, businessDomain, pretty, branch };
|
|
815
967
|
}
|
|
816
968
|
/** Parse object-type delete args: <kn-id> <ot-ids> [-y] */
|
|
817
969
|
function parseObjectTypeDeleteArgs(args) {
|
|
@@ -959,14 +1111,15 @@ async function runKnObjectTypeCommand(args) {
|
|
|
959
1111
|
console.log(`kweaver bkn object-type list <kn-id> [--pretty] [-bd value]
|
|
960
1112
|
kweaver bkn object-type get <kn-id> <ot-id> [--pretty] [-bd value]
|
|
961
1113
|
kweaver bkn object-type create <kn-id> --name X --dataview-id Y --primary-key Z --display-key W [--property '<json>' ...]
|
|
962
|
-
kweaver bkn object-type update <kn-id> <ot-id> [--name X] [--display-key Y]
|
|
1114
|
+
kweaver bkn object-type update <kn-id> <ot-id> [--name X] [--display-key Y] [--add-property|--update-property '<json>' ...] [--remove-property N ...] [--tags '["a","b"]'] [--comment S] [--icon I] [--color C] [--branch main]
|
|
1115
|
+
kweaver bkn object-type update <kn-id> <ot-id> '<full-json-body>'
|
|
963
1116
|
kweaver bkn object-type delete <kn-id> <ot-ids> [-y]
|
|
964
1117
|
kweaver bkn object-type query <kn-id> <ot-id> ['<json>'] [--limit <n>] [--search-after '<json-array>'] [--pretty] [-bd value]
|
|
965
1118
|
kweaver bkn object-type properties <kn-id> <ot-id> '<json>' [--pretty] [-bd value]
|
|
966
1119
|
|
|
967
1120
|
list: List object types (schema) from ontology-manager.
|
|
968
1121
|
get: Get single object type details.
|
|
969
|
-
create/update/delete: Schema CRUD (create requires dataview-id).
|
|
1122
|
+
create/update/delete: Schema CRUD (create requires dataview-id). update: merge flags (--add-property / --update-property / --remove-property, etc.) GET-merge-PUT; or full JSON as third arg.
|
|
970
1123
|
query: Query via ontology-query API. Default limit is 30 if not specified. Use --search-after for pagination.
|
|
971
1124
|
properties: Query instance properties by primary key.
|
|
972
1125
|
|
|
@@ -1009,12 +1162,37 @@ properties JSON format: {"_instance_identities":[{"<primary-key>":"<value>"}],"p
|
|
|
1009
1162
|
if (action === "update") {
|
|
1010
1163
|
const opts = parseObjectTypeUpdateArgs(rest);
|
|
1011
1164
|
const token = await ensureValidToken();
|
|
1165
|
+
let putBody;
|
|
1166
|
+
if (opts.mode === "body") {
|
|
1167
|
+
putBody = opts.body;
|
|
1168
|
+
}
|
|
1169
|
+
else {
|
|
1170
|
+
const raw = await getObjectType({
|
|
1171
|
+
baseUrl: token.baseUrl,
|
|
1172
|
+
accessToken: token.accessToken,
|
|
1173
|
+
knId: opts.knId,
|
|
1174
|
+
otId: opts.otId,
|
|
1175
|
+
businessDomain: opts.businessDomain,
|
|
1176
|
+
branch: opts.branch,
|
|
1177
|
+
});
|
|
1178
|
+
const parsed = JSON.parse(raw);
|
|
1179
|
+
const entryUnknown = parsed.entries;
|
|
1180
|
+
const entry = Array.isArray(entryUnknown) && entryUnknown.length > 0 && entryUnknown[0] && typeof entryUnknown[0] === "object"
|
|
1181
|
+
? entryUnknown[0]
|
|
1182
|
+
: parsed;
|
|
1183
|
+
if (!entry || typeof entry !== "object") {
|
|
1184
|
+
throw new Error("Unexpected object-type GET response shape.");
|
|
1185
|
+
}
|
|
1186
|
+
const stripped = stripObjectTypeForPut(entry);
|
|
1187
|
+
applyObjectTypeMerge(stripped, opts.merge);
|
|
1188
|
+
putBody = JSON.stringify(stripped);
|
|
1189
|
+
}
|
|
1012
1190
|
const body = await updateObjectType({
|
|
1013
1191
|
baseUrl: token.baseUrl,
|
|
1014
1192
|
accessToken: token.accessToken,
|
|
1015
1193
|
knId: opts.knId,
|
|
1016
1194
|
otId: opts.otId,
|
|
1017
|
-
body:
|
|
1195
|
+
body: putBody,
|
|
1018
1196
|
businessDomain: opts.businessDomain,
|
|
1019
1197
|
});
|
|
1020
1198
|
console.log(formatCallOutput(body, opts.pretty));
|
|
@@ -1097,7 +1275,8 @@ JSON: {"_instance_identities":[{"<primary-key>":"<value>"}],"properties":["prop1
|
|
|
1097
1275
|
catch (error) {
|
|
1098
1276
|
if (error instanceof Error && error.message === "help") {
|
|
1099
1277
|
console.log(`kweaver bkn object-type create <kn-id> --name X --dataview-id Y --primary-key Z --display-key W [--property '<json>' ...]
|
|
1100
|
-
kweaver bkn object-type update <kn-id> <ot-id> [--name X] [--display-key Y]
|
|
1278
|
+
kweaver bkn object-type update <kn-id> <ot-id> [--name X] [--display-key Y] [--add-property|--update-property '<json>' ...] [--remove-property N ...] [--tags '["a"]'] [--comment S] [--icon I] [--color C] [--branch main]
|
|
1279
|
+
kweaver bkn object-type update <kn-id> <ot-id> '<full-json-body>'
|
|
1101
1280
|
kweaver bkn object-type delete <kn-id> <ot-ids> [-y]`);
|
|
1102
1281
|
return 0;
|
|
1103
1282
|
}
|
package/dist/commands/call.js
CHANGED
|
@@ -125,7 +125,7 @@ Options:
|
|
|
125
125
|
<url> API path (e.g. /api/ontology-manager/v1/knowledge-networks)
|
|
126
126
|
-X, --request HTTP method (default: GET)
|
|
127
127
|
-H, --header Extra header (repeatable)
|
|
128
|
-
-d, --data
|
|
128
|
+
-d, --data, --data-raw JSON request body (sets Content-Type: application/json if not set)
|
|
129
129
|
-bd, --biz-domain Override x-business-domain (default: bd_public)
|
|
130
130
|
-v, --verbose Print request info to stderr
|
|
131
131
|
--pretty Pretty-print JSON output (default)`);
|
|
@@ -147,6 +147,12 @@ Options:
|
|
|
147
147
|
: invocation.url;
|
|
148
148
|
const headers = new Headers(invocation.headers);
|
|
149
149
|
injectAuthHeaders(headers, token.accessToken, invocation.businessDomain);
|
|
150
|
+
if (invocation.body !== undefined &&
|
|
151
|
+
invocation.body.length > 0 &&
|
|
152
|
+
!headers.has("content-type") &&
|
|
153
|
+
!headers.has("Content-Type")) {
|
|
154
|
+
headers.set("content-type", "application/json");
|
|
155
|
+
}
|
|
150
156
|
if (invocation.verbose) {
|
|
151
157
|
for (const line of formatVerboseRequest({ ...invocation, url, headers })) {
|
|
152
158
|
console.error(line);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kweaver-ai/kweaver-sdk",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.8",
|
|
4
4
|
"description": "KWeaver TypeScript SDK — CLI tool and programmatic API for knowledge networks and Decision Agents.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
"start": "node ./dist/cli.js",
|
|
29
29
|
"lint": "tsc --noEmit -p tsconfig.json",
|
|
30
30
|
"test": "node --import tsx --test test/*.test.ts",
|
|
31
|
-
"test:e2e": "node --import tsx --test test/e2e/**/*.test.ts",
|
|
31
|
+
"test:e2e": "node --import tsx test/e2e/ensure-token.ts && node --import tsx --test --test-concurrency=1 test/e2e/**/*.test.ts",
|
|
32
32
|
"prepublishOnly": "npm run build"
|
|
33
33
|
},
|
|
34
34
|
"keywords": [
|