@kweaver-ai/kweaver-sdk 0.4.11 → 0.4.13
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 +59 -3
- package/README.zh.md +40 -1
- package/dist/api/dataflow.d.ts +2 -0
- package/dist/api/dataflow.js +5 -3
- package/dist/api/dataviews.d.ts +98 -1
- package/dist/api/dataviews.js +167 -14
- package/dist/api/vega.js +1 -1
- package/dist/auth/oauth.d.ts +21 -0
- package/dist/auth/oauth.js +226 -54
- package/dist/cli.js +13 -0
- package/dist/client.d.ts +12 -0
- package/dist/client.js +16 -0
- package/dist/commands/agent.js +15 -15
- package/dist/commands/auth.js +80 -6
- package/dist/commands/bkn.d.ts +11 -0
- package/dist/commands/bkn.js +102 -59
- package/dist/commands/dataview.d.ts +1 -0
- package/dist/commands/dataview.js +303 -0
- package/dist/commands/vega.js +7 -7
- package/dist/index.d.ts +3 -0
- package/dist/index.js +2 -0
- package/dist/resources/dataflows.d.ts +17 -0
- package/dist/resources/dataflows.js +22 -0
- package/dist/resources/datasources.d.ts +52 -0
- package/dist/resources/datasources.js +54 -0
- package/dist/resources/dataviews.d.ts +37 -0
- package/dist/resources/dataviews.js +47 -0
- package/dist/resources/vega.d.ts +41 -0
- package/dist/resources/vega.js +80 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -90,6 +90,34 @@ const graph = await client.bkn.querySubgraph("bkn-id", { /* path spec */ });
|
|
|
90
90
|
await client.bkn.executeAction("bkn-id", "at-id", { /* params */ });
|
|
91
91
|
const logs = await client.bkn.listActionLogs("bkn-id");
|
|
92
92
|
|
|
93
|
+
// Data sources & data views
|
|
94
|
+
const dsList = await client.datasources.list();
|
|
95
|
+
const tables = await client.datasources.listTables("ds-id");
|
|
96
|
+
const viewId = await client.dataviews.create({ name: "v", datasourceId: "ds-id", table: "orders" });
|
|
97
|
+
const views = await client.dataviews.list({ datasourceId: "ds-id" });
|
|
98
|
+
const fuzzy = await client.dataviews.find("BOM", { wait: false });
|
|
99
|
+
const exact = await client.dataviews.find("orders", {
|
|
100
|
+
datasourceId: "ds-id",
|
|
101
|
+
exact: true,
|
|
102
|
+
wait: true,
|
|
103
|
+
});
|
|
104
|
+
const dv = await client.dataviews.get(viewId);
|
|
105
|
+
const queryRows = await client.dataviews.query(viewId, {
|
|
106
|
+
sql: "SELECT id, name FROM orders LIMIT 10",
|
|
107
|
+
limit: 10,
|
|
108
|
+
needTotal: true,
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
// Dataflow automation (CSV import pipeline, etc.)
|
|
112
|
+
const result = await client.dataflows.execute({
|
|
113
|
+
title: "import", trigger_config: { operator: "manual" },
|
|
114
|
+
steps: [{ id: "s1", title: "load", operator: "csv_import", parameters: {} }],
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
// Vega observability
|
|
118
|
+
const catalogs = await client.vega.listCatalogs();
|
|
119
|
+
const health = await client.vega.health();
|
|
120
|
+
|
|
93
121
|
// Context Loader (semantic search over a BKN via MCP)
|
|
94
122
|
const cl = client.contextLoader(mcpUrl, "bkn-id");
|
|
95
123
|
const results = await cl.search({ query: "hypertension treatment" });
|
|
@@ -98,16 +126,27 @@ const results = await cl.search({ query: "hypertension treatment" });
|
|
|
98
126
|
## CLI Reference
|
|
99
127
|
|
|
100
128
|
```
|
|
101
|
-
kweaver auth login <url> [--alias name] [-u user] [-p pass] [--playwright] [--insecure|-k]
|
|
129
|
+
kweaver auth login <url> [--alias name] [-u user] [-p pass] [--playwright] [--insecure|-k]
|
|
130
|
+
kweaver auth login <url> --client-id ID --client-secret S --refresh-token T (headless login)
|
|
131
|
+
kweaver auth export [url|alias] [--json] (export command to run on a headless host)
|
|
132
|
+
kweaver auth status/list/use/delete/logout
|
|
102
133
|
kweaver token
|
|
134
|
+
kweaver config show / set-bd <value>
|
|
135
|
+
kweaver ds list/get/delete/tables/connect
|
|
136
|
+
kweaver ds import-csv <ds_id> --files <glob> [--table-prefix <p>] [--batch-size 500]
|
|
137
|
+
kweaver dataview list/find/get/query/delete
|
|
103
138
|
kweaver bkn list/get/stats/export/create/update/delete
|
|
139
|
+
kweaver bkn create-from-ds <ds_id> --name <name> [--tables t1,t2] [--build]
|
|
140
|
+
kweaver bkn create-from-csv <ds_id> --files <glob> --name <name> [--build]
|
|
141
|
+
kweaver bkn validate/push/pull
|
|
104
142
|
kweaver bkn object-type list/get/create/update/delete/query/properties
|
|
105
143
|
kweaver bkn relation-type list/get/create/update/delete
|
|
106
144
|
kweaver bkn action-type list/query/execute
|
|
107
|
-
kweaver bkn subgraph
|
|
145
|
+
kweaver bkn subgraph / search
|
|
108
146
|
kweaver bkn action-execution get
|
|
109
147
|
kweaver bkn action-log list/get/cancel
|
|
110
|
-
kweaver agent list/get/chat/sessions/history
|
|
148
|
+
kweaver agent list/get/create/update/delete/chat/sessions/history/publish/unpublish
|
|
149
|
+
kweaver vega health/stats/inspect/catalog/resource/connector-type
|
|
111
150
|
kweaver context-loader config set/use/list/show
|
|
112
151
|
kweaver context-loader kn-search/query-object-instance/...
|
|
113
152
|
kweaver call <path> [-X METHOD] [-d BODY] [-H header]
|
|
@@ -148,6 +187,23 @@ If you encounter errors like `fetch failed`, `self-signed certificate`, or `UNAB
|
|
|
148
187
|
|
|
149
188
|
> **Security note:** All of the above disable HTTPS certificate verification and should only be used in development or internal network environments. Use trusted CA-signed certificates in production.
|
|
150
189
|
|
|
190
|
+
### Headless / Server Authentication
|
|
191
|
+
|
|
192
|
+
For servers or CI environments without a browser, log in on any machine that has one, then transfer credentials:
|
|
193
|
+
|
|
194
|
+
**Step 1 — Browser machine:** Run `kweaver auth login` as usual. The callback page displays a ready-to-copy command with `--client-id`, `--client-secret`, and `--refresh-token`. Alternatively, run `kweaver auth export` to print the same command.
|
|
195
|
+
|
|
196
|
+
**Step 2 — On the machine without a browser:** Run the pasted command there (SSH server, CI, etc.):
|
|
197
|
+
|
|
198
|
+
```bash
|
|
199
|
+
kweaver auth login https://your-platform \
|
|
200
|
+
--client-id abc123 \
|
|
201
|
+
--client-secret def456 \
|
|
202
|
+
--refresh-token ghi789
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
The SDK exchanges the refresh token for a new access token and saves it locally. Auto-refresh works normally from that point on.
|
|
206
|
+
|
|
151
207
|
## Using with AI Agents
|
|
152
208
|
|
|
153
209
|
Install the KWeaver skill for Claude Code, Cursor, or other AI coding agents:
|
package/README.zh.md
CHANGED
|
@@ -90,6 +90,23 @@ const graph = await client.bkn.querySubgraph("bkn-id", { /* 路径规格 */
|
|
|
90
90
|
await client.bkn.executeAction("bkn-id", "at-id", { /* 参数 */ });
|
|
91
91
|
const logs = await client.bkn.listActionLogs("bkn-id");
|
|
92
92
|
|
|
93
|
+
// 数据源与数据视图
|
|
94
|
+
const dsList = await client.datasources.list();
|
|
95
|
+
const viewId = await client.dataviews.create({ name: "v", datasourceId: "ds-id", table: "orders" });
|
|
96
|
+
const views = await client.dataviews.list({ datasourceId: "ds-id" });
|
|
97
|
+
const fuzzy = await client.dataviews.find("BOM", { wait: false });
|
|
98
|
+
const exact = await client.dataviews.find("orders", {
|
|
99
|
+
datasourceId: "ds-id",
|
|
100
|
+
exact: true,
|
|
101
|
+
wait: true,
|
|
102
|
+
});
|
|
103
|
+
const dv = await client.dataviews.get(viewId);
|
|
104
|
+
const queryRows = await client.dataviews.query(viewId, {
|
|
105
|
+
sql: "SELECT id, name FROM orders LIMIT 10",
|
|
106
|
+
limit: 10,
|
|
107
|
+
needTotal: true,
|
|
108
|
+
});
|
|
109
|
+
|
|
93
110
|
// Context Loader(通过 MCP 对 BKN 做语义搜索)
|
|
94
111
|
const cl = client.contextLoader(mcpUrl, "bkn-id");
|
|
95
112
|
const results = await cl.search({ query: "高血压 治疗" });
|
|
@@ -98,8 +115,13 @@ const results = await cl.search({ query: "高血压 治疗" });
|
|
|
98
115
|
## 命令速查
|
|
99
116
|
|
|
100
117
|
```
|
|
101
|
-
kweaver auth login <url> [--alias name] [-u user] [-p pass] [--playwright] [--insecure|-k]
|
|
118
|
+
kweaver auth login <url> [--alias name] [-u user] [-p pass] [--playwright] [--insecure|-k]
|
|
119
|
+
kweaver auth login <url> --client-id ID --client-secret S --refresh-token T (无浏览器登录)
|
|
120
|
+
kweaver auth export [url|alias] [--json] (导出在无浏览器机器上运行的命令)
|
|
121
|
+
kweaver auth status/list/use/delete/logout
|
|
102
122
|
kweaver token
|
|
123
|
+
kweaver ds list/get/delete/tables/connect
|
|
124
|
+
kweaver dataview list/find/get/query/delete
|
|
103
125
|
kweaver bkn list/get/stats/export/create/update/delete
|
|
104
126
|
kweaver bkn object-type list/get/create/update/delete/query/properties
|
|
105
127
|
kweaver bkn relation-type list/get/create/update/delete
|
|
@@ -148,6 +170,23 @@ kweaver call <path> [-X METHOD] [-d BODY] [-H header]
|
|
|
148
170
|
|
|
149
171
|
> **安全提示:** 以上方式均会跳过 HTTPS 证书校验,仅适用于开发/内网环境。生产环境请使用受信任的 CA 签发证书。
|
|
150
172
|
|
|
173
|
+
### 无浏览器 / 服务器端认证
|
|
174
|
+
|
|
175
|
+
适用于 SSH 远程服务器、CI 环境等无浏览器场景:
|
|
176
|
+
|
|
177
|
+
**第一步 — 有浏览器的机器:** 正常运行 `kweaver auth login`。登录成功后,回调页面会显示一条可复制的命令(含 `--client-id`、`--client-secret`、`--refresh-token`)。也可以用 `kweaver auth export` 查看。
|
|
178
|
+
|
|
179
|
+
**第二步 — 在没有浏览器的那台机器上:** 在 SSH 服务器、CI 等环境中执行下面这条命令:
|
|
180
|
+
|
|
181
|
+
```bash
|
|
182
|
+
kweaver auth login https://your-platform \
|
|
183
|
+
--client-id abc123 \
|
|
184
|
+
--client-secret def456 \
|
|
185
|
+
--refresh-token ghi789
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
SDK 会用 refresh token 换取新的 access token 并保存到本地,之后自动续期正常工作。
|
|
189
|
+
|
|
151
190
|
## 在 AI 智能体中使用
|
|
152
191
|
|
|
153
192
|
为 Claude Code、Cursor 等 AI 编程助手安装 KWeaver 技能:
|
package/dist/api/dataflow.d.ts
CHANGED
|
@@ -45,6 +45,8 @@ export interface PollDataflowOptions {
|
|
|
45
45
|
interval?: number;
|
|
46
46
|
/** Maximum time to wait in seconds. Default: 900 */
|
|
47
47
|
timeout?: number;
|
|
48
|
+
/** Test injection: override sleep function. */
|
|
49
|
+
_sleep?: (ms: number) => Promise<void>;
|
|
48
50
|
}
|
|
49
51
|
/**
|
|
50
52
|
* Poll GET /api/automation/v1/dag/{dagId}/results until the run is done.
|
package/dist/api/dataflow.js
CHANGED
|
@@ -69,10 +69,11 @@ export async function runDataflow(options) {
|
|
|
69
69
|
* Throws on "failed"/"error" status or timeout.
|
|
70
70
|
*/
|
|
71
71
|
export async function pollDataflowResults(options) {
|
|
72
|
-
const { baseUrl, accessToken, businessDomain = "bd_public", dagId, interval = 3, timeout = 900, } = options;
|
|
72
|
+
const { baseUrl, accessToken, businessDomain = "bd_public", dagId, interval = 3, timeout = 900, _sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms)), } = options;
|
|
73
73
|
const base = baseUrl.replace(/\/+$/, "");
|
|
74
74
|
const url = `${base}/api/automation/v1/dag/${encodeURIComponent(dagId)}/results`;
|
|
75
75
|
const deadlineMs = Date.now() + timeout * 1000;
|
|
76
|
+
let currentInterval = interval;
|
|
76
77
|
while (Date.now() < deadlineMs) {
|
|
77
78
|
const response = await fetch(url, {
|
|
78
79
|
method: "GET",
|
|
@@ -95,9 +96,10 @@ export async function pollDataflowResults(options) {
|
|
|
95
96
|
}
|
|
96
97
|
}
|
|
97
98
|
// Still running — wait before next poll
|
|
98
|
-
if (
|
|
99
|
-
await
|
|
99
|
+
if (currentInterval > 0) {
|
|
100
|
+
await _sleep(currentInterval * 1000);
|
|
100
101
|
}
|
|
102
|
+
currentInterval = Math.min(currentInterval * 2, 30);
|
|
101
103
|
}
|
|
102
104
|
throw new Error(`Dataflow polling timed out after ${timeout}s for DAG ${dagId}`);
|
|
103
105
|
}
|
package/dist/api/dataviews.d.ts
CHANGED
|
@@ -1,3 +1,30 @@
|
|
|
1
|
+
/** Field metadata returned by the data-views API. */
|
|
2
|
+
export interface ViewField {
|
|
3
|
+
name: string;
|
|
4
|
+
type: string;
|
|
5
|
+
display_name?: string;
|
|
6
|
+
comment?: string;
|
|
7
|
+
}
|
|
8
|
+
/** Normalized data view model (mdl-data-model). */
|
|
9
|
+
export interface DataView {
|
|
10
|
+
id: string;
|
|
11
|
+
name: string;
|
|
12
|
+
query_type: string;
|
|
13
|
+
datasource_id: string;
|
|
14
|
+
/** View type, e.g. "atomic" or "custom". */
|
|
15
|
+
type?: string;
|
|
16
|
+
/** Underlying data source engine, e.g. "mysql", "postgresql". */
|
|
17
|
+
data_source_type?: string;
|
|
18
|
+
/** Human-readable data source name. */
|
|
19
|
+
data_source_name?: string;
|
|
20
|
+
/** Full SQL expression stored in the view definition (Trino catalog.schema.table). */
|
|
21
|
+
sql_str?: string;
|
|
22
|
+
/** Fully-qualified table reference (catalog."schema"."table"). */
|
|
23
|
+
meta_table_name?: string;
|
|
24
|
+
/** Field metadata. Populated by `get`; absent (`undefined`) in `list` results. */
|
|
25
|
+
fields?: ViewField[];
|
|
26
|
+
}
|
|
27
|
+
export declare function parseDataView(raw: Record<string, unknown>): DataView;
|
|
1
28
|
export interface CreateDataViewOptions {
|
|
2
29
|
baseUrl: string;
|
|
3
30
|
accessToken: string;
|
|
@@ -11,10 +38,80 @@ export interface CreateDataViewOptions {
|
|
|
11
38
|
businessDomain?: string;
|
|
12
39
|
}
|
|
13
40
|
export declare function createDataView(options: CreateDataViewOptions): Promise<string>;
|
|
41
|
+
export interface ListDataViewsOptions {
|
|
42
|
+
baseUrl: string;
|
|
43
|
+
accessToken: string;
|
|
44
|
+
businessDomain?: string;
|
|
45
|
+
/** Filter by data source id. */
|
|
46
|
+
datasourceId?: string;
|
|
47
|
+
/** Server-side keyword filter (fuzzy). */
|
|
48
|
+
name?: string;
|
|
49
|
+
/** View type filter (e.g. atomic, custom). */
|
|
50
|
+
type?: string;
|
|
51
|
+
/** Max items; default -1 (all). */
|
|
52
|
+
limit?: number;
|
|
53
|
+
}
|
|
54
|
+
export declare function listDataViews(options: ListDataViewsOptions): Promise<DataView[]>;
|
|
55
|
+
export interface DeleteDataViewOptions {
|
|
56
|
+
baseUrl: string;
|
|
57
|
+
accessToken: string;
|
|
58
|
+
id: string;
|
|
59
|
+
businessDomain?: string;
|
|
60
|
+
}
|
|
61
|
+
export declare function deleteDataView(options: DeleteDataViewOptions): Promise<void>;
|
|
14
62
|
export interface GetDataViewOptions {
|
|
15
63
|
baseUrl: string;
|
|
16
64
|
accessToken: string;
|
|
17
65
|
id: string;
|
|
18
66
|
businessDomain?: string;
|
|
19
67
|
}
|
|
20
|
-
export declare function getDataView(options: GetDataViewOptions): Promise<
|
|
68
|
+
export declare function getDataView(options: GetDataViewOptions): Promise<DataView>;
|
|
69
|
+
export interface FindDataViewOptions {
|
|
70
|
+
baseUrl: string;
|
|
71
|
+
accessToken: string;
|
|
72
|
+
businessDomain?: string;
|
|
73
|
+
/** View name to search for (sent as keyword to server). */
|
|
74
|
+
name: string;
|
|
75
|
+
/** Filter by data source id. */
|
|
76
|
+
datasourceId?: string;
|
|
77
|
+
/** When true, apply client-side exact name match after keyword search (default false). */
|
|
78
|
+
exact?: boolean;
|
|
79
|
+
/** When true, poll until a result appears or timeout (default false). */
|
|
80
|
+
wait?: boolean;
|
|
81
|
+
/** Total wait budget in ms (default 30000). Only used when wait is true. */
|
|
82
|
+
timeoutMs?: number;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Find data views by name. Uses server-side keyword filtering; when `exact` is true,
|
|
86
|
+
* applies client-side `name ===` filter. Optional polling with exponential backoff.
|
|
87
|
+
*/
|
|
88
|
+
export declare function findDataView(options: FindDataViewOptions): Promise<DataView[]>;
|
|
89
|
+
/** Options for querying data view rows via mdl-uniquery (SQL / view definition). */
|
|
90
|
+
export interface QueryDataViewOptions {
|
|
91
|
+
baseUrl: string;
|
|
92
|
+
accessToken: string;
|
|
93
|
+
id: string;
|
|
94
|
+
sql?: string;
|
|
95
|
+
offset?: number;
|
|
96
|
+
limit?: number;
|
|
97
|
+
needTotal?: boolean;
|
|
98
|
+
outputFields?: string[];
|
|
99
|
+
filters?: Record<string, unknown>;
|
|
100
|
+
sort?: Array<Record<string, unknown>>;
|
|
101
|
+
businessDomain?: string;
|
|
102
|
+
}
|
|
103
|
+
/** Query result from mdl-uniquery data-views POST (shape varies by backend). */
|
|
104
|
+
export interface DataViewQueryResult {
|
|
105
|
+
columns?: Array<{
|
|
106
|
+
name: string;
|
|
107
|
+
type?: string;
|
|
108
|
+
vega_type?: string;
|
|
109
|
+
}>;
|
|
110
|
+
entries?: unknown;
|
|
111
|
+
total_count?: number;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Execute a query against a data view (POST /api/mdl-uniquery/v1/data-views/:id).
|
|
115
|
+
* When `sql` is omitted, the server uses the view's stored SQL definition.
|
|
116
|
+
*/
|
|
117
|
+
export declare function queryDataView(options: QueryDataViewOptions): Promise<DataViewQueryResult>;
|
package/dist/api/dataviews.js
CHANGED
|
@@ -24,6 +24,54 @@ function extractViewId(data) {
|
|
|
24
24
|
}
|
|
25
25
|
return null;
|
|
26
26
|
}
|
|
27
|
+
export function parseDataView(raw) {
|
|
28
|
+
const fieldsRaw = raw.fields;
|
|
29
|
+
let fields;
|
|
30
|
+
if (Array.isArray(fieldsRaw) && fieldsRaw.length > 0) {
|
|
31
|
+
fields = [];
|
|
32
|
+
for (const f of fieldsRaw) {
|
|
33
|
+
if (f && typeof f === "object") {
|
|
34
|
+
const fr = f;
|
|
35
|
+
fields.push({
|
|
36
|
+
name: String(fr.name ?? ""),
|
|
37
|
+
type: String(fr.type ?? "varchar"),
|
|
38
|
+
display_name: fr.display_name != null ? String(fr.display_name) : undefined,
|
|
39
|
+
comment: fr.comment != null ? String(fr.comment) : undefined,
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
const dv = {
|
|
45
|
+
id: String(raw.id ?? ""),
|
|
46
|
+
name: String(raw.name ?? ""),
|
|
47
|
+
query_type: String(raw.query_type ?? "SQL"),
|
|
48
|
+
datasource_id: String(raw.data_source_id ?? raw.group_id ?? ""),
|
|
49
|
+
};
|
|
50
|
+
if (raw.type != null)
|
|
51
|
+
dv.type = String(raw.type);
|
|
52
|
+
if (raw.data_source_type != null)
|
|
53
|
+
dv.data_source_type = String(raw.data_source_type);
|
|
54
|
+
if (raw.data_source_name != null)
|
|
55
|
+
dv.data_source_name = String(raw.data_source_name);
|
|
56
|
+
if (raw.sql_str != null)
|
|
57
|
+
dv.sql_str = String(raw.sql_str);
|
|
58
|
+
if (raw.meta_table_name != null)
|
|
59
|
+
dv.meta_table_name = String(raw.meta_table_name);
|
|
60
|
+
if (fields)
|
|
61
|
+
dv.fields = fields;
|
|
62
|
+
return dv;
|
|
63
|
+
}
|
|
64
|
+
function extractListPayload(data) {
|
|
65
|
+
if (Array.isArray(data))
|
|
66
|
+
return data;
|
|
67
|
+
if (data && typeof data === "object") {
|
|
68
|
+
const obj = data;
|
|
69
|
+
const items = obj.entries ?? obj.data;
|
|
70
|
+
if (Array.isArray(items))
|
|
71
|
+
return items;
|
|
72
|
+
}
|
|
73
|
+
return [];
|
|
74
|
+
}
|
|
27
75
|
export async function createDataView(options) {
|
|
28
76
|
const { baseUrl, accessToken, name, datasourceId, table, fields = [], businessDomain = "bd_public", } = options;
|
|
29
77
|
const viewId = createHash("md5").update(`${datasourceId}:${table}`).digest("hex").slice(0, 35);
|
|
@@ -84,26 +132,56 @@ export async function createDataView(options) {
|
|
|
84
132
|
return createdId ?? viewId;
|
|
85
133
|
}
|
|
86
134
|
async function findDataViewByName(options) {
|
|
87
|
-
const
|
|
135
|
+
const list = await listDataViews({
|
|
136
|
+
baseUrl: options.baseUrl,
|
|
137
|
+
accessToken: options.accessToken,
|
|
138
|
+
businessDomain: options.businessDomain,
|
|
139
|
+
name: options.name,
|
|
140
|
+
});
|
|
141
|
+
const match = list.find((e) => e.name === options.name && e.datasource_id === options.groupId);
|
|
142
|
+
return match?.id ?? null;
|
|
143
|
+
}
|
|
144
|
+
export async function listDataViews(options) {
|
|
145
|
+
const { baseUrl, accessToken, businessDomain = "bd_public", datasourceId, name, type, limit = 30, } = options;
|
|
146
|
+
const base = baseUrl.replace(/\/+$/, "");
|
|
88
147
|
const url = new URL(`${base}/api/mdl-data-model/v1/data-views`);
|
|
89
|
-
url.searchParams.set("
|
|
148
|
+
url.searchParams.set("limit", String(limit));
|
|
149
|
+
if (datasourceId)
|
|
150
|
+
url.searchParams.set("data_source_id", datasourceId);
|
|
151
|
+
if (name)
|
|
152
|
+
url.searchParams.set("keyword", name);
|
|
153
|
+
if (type)
|
|
154
|
+
url.searchParams.set("type", type);
|
|
90
155
|
const response = await fetch(url.toString(), {
|
|
91
156
|
method: "GET",
|
|
92
|
-
headers: buildHeaders(
|
|
157
|
+
headers: buildHeaders(accessToken, businessDomain),
|
|
93
158
|
});
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
159
|
+
const bodyText = await response.text();
|
|
160
|
+
if (!response.ok) {
|
|
161
|
+
throw new HttpError(response.status, response.statusText, bodyText);
|
|
162
|
+
}
|
|
163
|
+
const parsed = JSON.parse(bodyText);
|
|
164
|
+
const items = extractListPayload(parsed);
|
|
165
|
+
const out = [];
|
|
166
|
+
for (const item of items) {
|
|
167
|
+
if (item && typeof item === "object") {
|
|
168
|
+
out.push(parseDataView(item));
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
return out;
|
|
99
172
|
}
|
|
100
|
-
async function deleteDataView(options) {
|
|
101
|
-
const
|
|
102
|
-
const
|
|
103
|
-
|
|
173
|
+
export async function deleteDataView(options) {
|
|
174
|
+
const { baseUrl, accessToken, id, businessDomain = "bd_public" } = options;
|
|
175
|
+
const base = baseUrl.replace(/\/+$/, "");
|
|
176
|
+
const url = `${base}/api/mdl-data-model/v1/data-views/${encodeURIComponent(id)}`;
|
|
177
|
+
const response = await fetch(url, {
|
|
104
178
|
method: "DELETE",
|
|
105
|
-
headers: buildHeaders(
|
|
179
|
+
headers: buildHeaders(accessToken, businessDomain),
|
|
106
180
|
});
|
|
181
|
+
const bodyText = await response.text();
|
|
182
|
+
if (!response.ok) {
|
|
183
|
+
throw new HttpError(response.status, response.statusText, bodyText);
|
|
184
|
+
}
|
|
107
185
|
}
|
|
108
186
|
export async function getDataView(options) {
|
|
109
187
|
const { baseUrl, accessToken, id, businessDomain = "bd_public", } = options;
|
|
@@ -117,5 +195,80 @@ export async function getDataView(options) {
|
|
|
117
195
|
if (!response.ok) {
|
|
118
196
|
throw new HttpError(response.status, response.statusText, body);
|
|
119
197
|
}
|
|
120
|
-
|
|
198
|
+
let parsed = JSON.parse(body);
|
|
199
|
+
if (Array.isArray(parsed) && parsed.length > 0) {
|
|
200
|
+
parsed = parsed[0];
|
|
201
|
+
}
|
|
202
|
+
if (!parsed || typeof parsed !== "object") {
|
|
203
|
+
throw new HttpError(500, "Invalid response", body);
|
|
204
|
+
}
|
|
205
|
+
return parseDataView(parsed);
|
|
206
|
+
}
|
|
207
|
+
function sleepMs(ms) {
|
|
208
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Find data views by name. Uses server-side keyword filtering; when `exact` is true,
|
|
212
|
+
* applies client-side `name ===` filter. Optional polling with exponential backoff.
|
|
213
|
+
*/
|
|
214
|
+
export async function findDataView(options) {
|
|
215
|
+
const { baseUrl, accessToken, businessDomain = "bd_public", name, datasourceId, exact = false, wait = false, timeoutMs = 30_000, } = options;
|
|
216
|
+
const deadline = Date.now() + timeoutMs;
|
|
217
|
+
let attempt = 0;
|
|
218
|
+
while (true) {
|
|
219
|
+
const list = await listDataViews({
|
|
220
|
+
baseUrl,
|
|
221
|
+
accessToken,
|
|
222
|
+
businessDomain,
|
|
223
|
+
datasourceId,
|
|
224
|
+
name,
|
|
225
|
+
limit: -1,
|
|
226
|
+
});
|
|
227
|
+
const results = exact ? list.filter((v) => v.name === name) : list;
|
|
228
|
+
if (results.length > 0 || !wait || Date.now() >= deadline)
|
|
229
|
+
return results;
|
|
230
|
+
const delayMs = Math.min(5000, 1000 * 2 ** attempt);
|
|
231
|
+
attempt += 1;
|
|
232
|
+
await sleepMs(delayMs);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* Execute a query against a data view (POST /api/mdl-uniquery/v1/data-views/:id).
|
|
237
|
+
* When `sql` is omitted, the server uses the view's stored SQL definition.
|
|
238
|
+
*/
|
|
239
|
+
export async function queryDataView(options) {
|
|
240
|
+
const { baseUrl, accessToken, id, sql, offset = 0, limit = 50, needTotal = false, outputFields, filters, sort, businessDomain = "bd_public", } = options;
|
|
241
|
+
const base = baseUrl.replace(/\/+$/, "");
|
|
242
|
+
const url = `${base}/api/mdl-uniquery/v1/data-views/${encodeURIComponent(id)}`;
|
|
243
|
+
const body = {
|
|
244
|
+
offset,
|
|
245
|
+
limit,
|
|
246
|
+
need_total: needTotal,
|
|
247
|
+
};
|
|
248
|
+
if (sql !== undefined && sql !== "")
|
|
249
|
+
body.sql = sql;
|
|
250
|
+
if (outputFields !== undefined)
|
|
251
|
+
body.output_fields = outputFields;
|
|
252
|
+
if (filters !== undefined)
|
|
253
|
+
body.filters = filters;
|
|
254
|
+
if (sort !== undefined)
|
|
255
|
+
body.sort = sort;
|
|
256
|
+
const response = await fetch(url, {
|
|
257
|
+
method: "POST",
|
|
258
|
+
headers: {
|
|
259
|
+
...buildHeaders(accessToken, businessDomain),
|
|
260
|
+
"content-type": "application/json",
|
|
261
|
+
"x-http-method-override": "GET",
|
|
262
|
+
},
|
|
263
|
+
body: JSON.stringify(body),
|
|
264
|
+
});
|
|
265
|
+
const bodyText = await response.text();
|
|
266
|
+
if (!response.ok) {
|
|
267
|
+
throw new HttpError(response.status, response.statusText, bodyText);
|
|
268
|
+
}
|
|
269
|
+
const parsed = JSON.parse(bodyText);
|
|
270
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
271
|
+
return parsed;
|
|
272
|
+
}
|
|
273
|
+
return {};
|
|
121
274
|
}
|
package/dist/api/vega.js
CHANGED
|
@@ -190,7 +190,7 @@ export async function queryVegaResourceData(options) {
|
|
|
190
190
|
return body;
|
|
191
191
|
}
|
|
192
192
|
export async function previewVegaResource(options) {
|
|
193
|
-
const { baseUrl, accessToken, id, limit =
|
|
193
|
+
const { baseUrl, accessToken, id, limit = 50, businessDomain = "bd_public", } = options;
|
|
194
194
|
const base = baseUrl.replace(/\/+$/, "");
|
|
195
195
|
const url = new URL(`${base}${VEGA_BASE}/resources/${encodeURIComponent(id)}/preview`);
|
|
196
196
|
url.searchParams.set("limit", String(limit));
|
package/dist/auth/oauth.d.ts
CHANGED
|
@@ -1,4 +1,15 @@
|
|
|
1
1
|
import { type TokenConfig } from "../config/store.js";
|
|
2
|
+
/** POSIX shell single-quote escaping for copy-paste commands. */
|
|
3
|
+
export declare function shellQuoteForShell(value: string): string;
|
|
4
|
+
/**
|
|
5
|
+
* Build a one-line `kweaver auth login ...` command for headless / other machines.
|
|
6
|
+
* Omits `--client-secret` when empty (PKCE-only client); headless refresh may still require a confidential client.
|
|
7
|
+
*/
|
|
8
|
+
export declare function buildCopyCommand(baseUrl: string, clientId: string, clientSecret: string, refreshToken: string | undefined, tlsInsecure?: boolean): string;
|
|
9
|
+
/**
|
|
10
|
+
* HTML shown after successful OAuth callback with a copyable headless login command.
|
|
11
|
+
*/
|
|
12
|
+
export declare function buildCallbackHtml(copyCommand: string): string;
|
|
2
13
|
export declare function normalizeBaseUrl(value: string): string;
|
|
3
14
|
/**
|
|
4
15
|
* OAuth2 Authorization Code login flow.
|
|
@@ -34,6 +45,16 @@ export declare function playwrightLogin(baseUrl: string, options?: {
|
|
|
34
45
|
scope?: string;
|
|
35
46
|
tlsInsecure?: boolean;
|
|
36
47
|
}): Promise<TokenConfig>;
|
|
48
|
+
/**
|
|
49
|
+
* Log in on a headless machine using OAuth2 client credentials and a refresh token (no browser).
|
|
50
|
+
* Exchanges the refresh token for a new access token and persists ~/.kweaver/ state.
|
|
51
|
+
*/
|
|
52
|
+
export declare function refreshTokenLogin(baseUrl: string, options: {
|
|
53
|
+
clientId: string;
|
|
54
|
+
clientSecret: string;
|
|
55
|
+
refreshToken: string;
|
|
56
|
+
tlsInsecure?: boolean;
|
|
57
|
+
}): Promise<TokenConfig>;
|
|
37
58
|
/**
|
|
38
59
|
* Exchange refresh_token for a new access token (OAuth2 password grant style, same as Python ConfigAuth).
|
|
39
60
|
* Persists the new token to ~/.kweaver/ and returns it.
|