@kweaver-ai/kweaver-sdk 0.5.0 → 0.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/README.md +6 -1
  2. package/README.zh.md +5 -0
  3. package/dist/api/agent-chat.d.ts +1 -1
  4. package/dist/api/agent-chat.js +4 -4
  5. package/dist/api/agent-list.d.ts +35 -0
  6. package/dist/api/agent-list.js +86 -12
  7. package/dist/api/bkn-backend.d.ts +60 -0
  8. package/dist/api/bkn-backend.js +103 -10
  9. package/dist/api/conversations.d.ts +6 -3
  10. package/dist/api/conversations.js +26 -27
  11. package/dist/api/dataflow.js +1 -10
  12. package/dist/api/datasources.js +1 -10
  13. package/dist/api/dataviews.js +1 -10
  14. package/dist/api/headers.d.ts +9 -0
  15. package/dist/api/headers.js +25 -0
  16. package/dist/api/knowledge-networks.d.ts +41 -0
  17. package/dist/api/knowledge-networks.js +69 -22
  18. package/dist/api/ontology-query.d.ts +14 -1
  19. package/dist/api/ontology-query.js +63 -49
  20. package/dist/api/semantic-search.js +2 -12
  21. package/dist/api/skills.d.ts +141 -0
  22. package/dist/api/skills.js +216 -0
  23. package/dist/api/vega.d.ts +63 -0
  24. package/dist/api/vega.js +131 -10
  25. package/dist/auth/oauth.d.ts +5 -1
  26. package/dist/auth/oauth.js +293 -94
  27. package/dist/cli.js +29 -4
  28. package/dist/client.d.ts +3 -0
  29. package/dist/client.js +4 -0
  30. package/dist/commands/agent.d.ts +33 -1
  31. package/dist/commands/agent.js +721 -49
  32. package/dist/commands/auth.js +211 -21
  33. package/dist/commands/bkn-ops.d.ts +77 -0
  34. package/dist/commands/bkn-ops.js +1056 -0
  35. package/dist/commands/bkn-query.d.ts +14 -0
  36. package/dist/commands/bkn-query.js +370 -0
  37. package/dist/commands/bkn-schema.d.ts +135 -0
  38. package/dist/commands/bkn-schema.js +1461 -0
  39. package/dist/commands/bkn-utils.d.ts +36 -0
  40. package/dist/commands/bkn-utils.js +102 -0
  41. package/dist/commands/bkn.d.ts +7 -113
  42. package/dist/commands/bkn.js +175 -2429
  43. package/dist/commands/dataview.d.ts +7 -0
  44. package/dist/commands/dataview.js +38 -2
  45. package/dist/commands/ds.d.ts +1 -0
  46. package/dist/commands/ds.js +8 -1
  47. package/dist/commands/import-csv.d.ts +2 -0
  48. package/dist/commands/import-csv.js +3 -2
  49. package/dist/commands/skill.d.ts +26 -0
  50. package/dist/commands/skill.js +524 -0
  51. package/dist/commands/vega.js +371 -14
  52. package/dist/config/jwt.d.ts +6 -0
  53. package/dist/config/jwt.js +21 -0
  54. package/dist/config/store.d.ts +37 -5
  55. package/dist/config/store.js +363 -30
  56. package/dist/index.d.ts +6 -1
  57. package/dist/index.js +5 -1
  58. package/dist/resources/bkn.d.ts +4 -0
  59. package/dist/resources/bkn.js +4 -0
  60. package/dist/resources/conversations.d.ts +5 -2
  61. package/dist/resources/conversations.js +17 -3
  62. package/dist/resources/skills.d.ts +47 -0
  63. package/dist/resources/skills.js +47 -0
  64. package/dist/resources/vega.d.ts +11 -0
  65. package/dist/resources/vega.js +37 -1
  66. package/package.json +1 -1
@@ -0,0 +1,216 @@
1
+ import { spawnSync } from "node:child_process";
2
+ import { Buffer } from "node:buffer";
3
+ import { existsSync, mkdirSync, readdirSync, renameSync, rmSync, writeFileSync } from "node:fs";
4
+ import { basename, resolve } from "node:path";
5
+ import { HttpError, fetchTextOrThrow } from "../utils/http.js";
6
+ const SKILL_API_PREFIX = "/api/agent-operator-integration/v1";
7
+ function buildHeaders(accessToken, businessDomain) {
8
+ return {
9
+ accept: "application/json, text/plain, */*",
10
+ authorization: `Bearer ${accessToken}`,
11
+ token: accessToken,
12
+ "x-business-domain": businessDomain,
13
+ "x-language": "zh-cn",
14
+ };
15
+ }
16
+ function baseHeaders(opts) {
17
+ return buildHeaders(opts.accessToken, opts.businessDomain ?? "bd_public");
18
+ }
19
+ function buildUrl(baseUrl, path) {
20
+ return `${baseUrl.replace(/\/+$/, "")}${path}`;
21
+ }
22
+ function unwrapEnvelope(raw) {
23
+ const parsed = JSON.parse(raw);
24
+ if (parsed && typeof parsed === "object" && "data" in parsed) {
25
+ return parsed.data;
26
+ }
27
+ return parsed;
28
+ }
29
+ function appendCommonListParams(url, opts) {
30
+ if (opts.page !== undefined)
31
+ url.searchParams.set("page", String(opts.page));
32
+ if (opts.pageSize !== undefined)
33
+ url.searchParams.set("page_size", String(opts.pageSize));
34
+ if (opts.sortBy)
35
+ url.searchParams.set("sort_by", opts.sortBy);
36
+ if (opts.sortOrder)
37
+ url.searchParams.set("sort_order", opts.sortOrder);
38
+ if (opts.all !== undefined)
39
+ url.searchParams.set("all", String(opts.all));
40
+ if (opts.name)
41
+ url.searchParams.set("name", opts.name);
42
+ if (opts.source)
43
+ url.searchParams.set("source", opts.source);
44
+ }
45
+ function parseContentDisposition(value) {
46
+ if (!value)
47
+ return undefined;
48
+ const utf8Match = /filename\*=UTF-8''([^;]+)/i.exec(value);
49
+ if (utf8Match?.[1]) {
50
+ return decodeURIComponent(utf8Match[1]);
51
+ }
52
+ const plainMatch = /filename="?([^";]+)"?/i.exec(value);
53
+ return plainMatch?.[1];
54
+ }
55
+ async function fetchBytesOrThrow(input, init) {
56
+ const response = await fetch(input, init);
57
+ const body = new Uint8Array(await response.arrayBuffer());
58
+ if (!response.ok) {
59
+ throw new HttpError(response.status, response.statusText, new TextDecoder().decode(body));
60
+ }
61
+ return { response, body };
62
+ }
63
+ export async function listSkills(options) {
64
+ const url = new URL(buildUrl(options.baseUrl, `${SKILL_API_PREFIX}/skills`));
65
+ appendCommonListParams(url, options);
66
+ if (options.status)
67
+ url.searchParams.set("status", options.status);
68
+ if (options.createUser)
69
+ url.searchParams.set("create_user", options.createUser);
70
+ const { body } = await fetchTextOrThrow(url, { headers: baseHeaders(options) });
71
+ return unwrapEnvelope(body);
72
+ }
73
+ export async function listSkillMarket(options) {
74
+ const url = new URL(buildUrl(options.baseUrl, `${SKILL_API_PREFIX}/skills/market`));
75
+ appendCommonListParams(url, options);
76
+ const { body } = await fetchTextOrThrow(url, { headers: baseHeaders(options) });
77
+ return unwrapEnvelope(body);
78
+ }
79
+ export async function getSkill(options) {
80
+ const url = buildUrl(options.baseUrl, `${SKILL_API_PREFIX}/skills/${encodeURIComponent(options.skillId)}`);
81
+ const { body } = await fetchTextOrThrow(url, { headers: baseHeaders(options) });
82
+ return unwrapEnvelope(body);
83
+ }
84
+ export async function deleteSkill(options) {
85
+ const url = buildUrl(options.baseUrl, `${SKILL_API_PREFIX}/skills/${encodeURIComponent(options.skillId)}`);
86
+ const { body } = await fetchTextOrThrow(url, { method: "DELETE", headers: baseHeaders(options) });
87
+ return unwrapEnvelope(body);
88
+ }
89
+ export async function updateSkillStatus(options) {
90
+ const url = buildUrl(options.baseUrl, `${SKILL_API_PREFIX}/skills/${encodeURIComponent(options.skillId)}/status`);
91
+ const { body } = await fetchTextOrThrow(url, {
92
+ method: "PUT",
93
+ headers: { ...baseHeaders(options), "content-type": "application/json" },
94
+ body: JSON.stringify({ status: options.status }),
95
+ });
96
+ return unwrapEnvelope(body);
97
+ }
98
+ export async function registerSkillContent(options) {
99
+ const url = buildUrl(options.baseUrl, `${SKILL_API_PREFIX}/skills`);
100
+ const payload = {
101
+ file_type: "content",
102
+ file: options.content,
103
+ };
104
+ if (options.source)
105
+ payload.source = options.source;
106
+ if (options.extendInfo)
107
+ payload.extend_info = options.extendInfo;
108
+ const { body } = await fetchTextOrThrow(url, {
109
+ method: "POST",
110
+ headers: { ...baseHeaders(options), "content-type": "application/json" },
111
+ body: JSON.stringify(payload),
112
+ });
113
+ return unwrapEnvelope(body);
114
+ }
115
+ export async function registerSkillZip(options) {
116
+ const url = buildUrl(options.baseUrl, `${SKILL_API_PREFIX}/skills`);
117
+ const form = new FormData();
118
+ form.set("file_type", "zip");
119
+ form.set("file", new Blob([Buffer.from(options.bytes)]), options.filename);
120
+ if (options.source)
121
+ form.set("source", options.source);
122
+ if (options.extendInfo)
123
+ form.set("extend_info", JSON.stringify(options.extendInfo));
124
+ const { body } = await fetchTextOrThrow(url, {
125
+ method: "POST",
126
+ headers: baseHeaders(options),
127
+ body: form,
128
+ });
129
+ return unwrapEnvelope(body);
130
+ }
131
+ export async function getSkillContentIndex(options) {
132
+ const url = buildUrl(options.baseUrl, `${SKILL_API_PREFIX}/skills/${encodeURIComponent(options.skillId)}/content`);
133
+ const { body } = await fetchTextOrThrow(url, { headers: baseHeaders(options) });
134
+ return unwrapEnvelope(body);
135
+ }
136
+ export async function fetchSkillContent(options) {
137
+ const index = await getSkillContentIndex(options);
138
+ const { body } = await fetchTextOrThrow(index.url);
139
+ return body;
140
+ }
141
+ export async function readSkillFile(options) {
142
+ const url = buildUrl(options.baseUrl, `${SKILL_API_PREFIX}/skills/${encodeURIComponent(options.skillId)}/files/read`);
143
+ const { body } = await fetchTextOrThrow(url, {
144
+ method: "POST",
145
+ headers: { ...baseHeaders(options), "content-type": "application/json" },
146
+ body: JSON.stringify({ rel_path: options.relPath }),
147
+ });
148
+ return unwrapEnvelope(body);
149
+ }
150
+ export async function fetchSkillFile(options) {
151
+ const file = await readSkillFile(options);
152
+ const { body } = await fetchBytesOrThrow(file.url);
153
+ return body;
154
+ }
155
+ export async function downloadSkill(options) {
156
+ const url = buildUrl(options.baseUrl, `${SKILL_API_PREFIX}/skills/${encodeURIComponent(options.skillId)}/download`);
157
+ const { response, body } = await fetchBytesOrThrow(url, { headers: baseHeaders(options) });
158
+ const serverName = parseContentDisposition(response.headers.get("content-disposition"));
159
+ return {
160
+ fileName: basename(serverName || `${options.skillId}.zip`),
161
+ bytes: body,
162
+ };
163
+ }
164
+ export function installSkillArchive(options) {
165
+ const targetDir = resolve(options.directory);
166
+ const existed = existsSync(targetDir);
167
+ if (existed) {
168
+ const entries = readdirSync(targetDir);
169
+ if (entries.length > 0) {
170
+ if (!options.force) {
171
+ throw new Error(`Install target is not empty: ${targetDir}. Use --force to replace it.`);
172
+ }
173
+ }
174
+ }
175
+ const parentDir = resolve(targetDir, "..");
176
+ mkdirSync(parentDir, { recursive: true });
177
+ const archivePath = resolve(parentDir, `${basename(targetDir)}.zip`);
178
+ const stagingDir = resolve(parentDir, `.${basename(targetDir)}.tmp-${process.pid}-${Date.now()}`);
179
+ const backupDir = existed ? resolve(parentDir, `.${basename(targetDir)}.bak-${process.pid}-${Date.now()}`) : undefined;
180
+ mkdirSync(stagingDir, { recursive: true });
181
+ writeFileSync(archivePath, options.bytes);
182
+ try {
183
+ const result = spawnSync("unzip", ["-oq", archivePath, "-d", stagingDir], {
184
+ encoding: "utf8",
185
+ });
186
+ if (result.error) {
187
+ throw result.error;
188
+ }
189
+ if (result.status !== 0) {
190
+ throw new Error(result.stderr || `unzip exited with status ${result.status}`);
191
+ }
192
+ if (existsSync(targetDir)) {
193
+ renameSync(targetDir, backupDir);
194
+ }
195
+ renameSync(stagingDir, targetDir);
196
+ if (backupDir && existsSync(backupDir)) {
197
+ rmSync(backupDir, { recursive: true, force: true });
198
+ }
199
+ return { directory: targetDir };
200
+ }
201
+ catch (error) {
202
+ rmSync(stagingDir, { recursive: true, force: true });
203
+ if (backupDir && existsSync(backupDir) && !existsSync(targetDir)) {
204
+ renameSync(backupDir, targetDir);
205
+ }
206
+ throw new Error(error instanceof Error
207
+ ? `Skill install failed: ${error.message}`
208
+ : `Skill install failed: ${String(error)}`);
209
+ }
210
+ finally {
211
+ rmSync(archivePath, { force: true });
212
+ if (backupDir && existsSync(backupDir)) {
213
+ rmSync(backupDir, { recursive: true, force: true });
214
+ }
215
+ }
216
+ }
@@ -174,6 +174,69 @@ export interface ListVegaDiscoverTasksOptions {
174
174
  businessDomain?: string;
175
175
  }
176
176
  export declare function listVegaDiscoverTasks(options: ListVegaDiscoverTasksOptions): Promise<string>;
177
+ export interface CreateVegaDatasetDocsOptions {
178
+ baseUrl: string;
179
+ accessToken: string;
180
+ id: string;
181
+ body: string;
182
+ businessDomain?: string;
183
+ }
184
+ export declare function createVegaDatasetDocs(options: CreateVegaDatasetDocsOptions): Promise<string>;
185
+ export interface UpdateVegaDatasetDocsOptions {
186
+ baseUrl: string;
187
+ accessToken: string;
188
+ id: string;
189
+ body: string;
190
+ businessDomain?: string;
191
+ }
192
+ export declare function updateVegaDatasetDocs(options: UpdateVegaDatasetDocsOptions): Promise<string>;
193
+ export interface DeleteVegaDatasetDocsOptions {
194
+ baseUrl: string;
195
+ accessToken: string;
196
+ id: string;
197
+ docIds: string;
198
+ businessDomain?: string;
199
+ }
200
+ export declare function deleteVegaDatasetDocs(options: DeleteVegaDatasetDocsOptions): Promise<string>;
201
+ export interface DeleteVegaDatasetDocsQueryOptions {
202
+ baseUrl: string;
203
+ accessToken: string;
204
+ id: string;
205
+ body: string;
206
+ businessDomain?: string;
207
+ }
208
+ export declare function deleteVegaDatasetDocsQuery(options: DeleteVegaDatasetDocsQueryOptions): Promise<string>;
209
+ export interface BuildVegaDatasetOptions {
210
+ baseUrl: string;
211
+ accessToken: string;
212
+ id: string;
213
+ mode?: string;
214
+ businessDomain?: string;
215
+ }
216
+ export declare function buildVegaDataset(options: BuildVegaDatasetOptions): Promise<string>;
217
+ export interface GetVegaDatasetBuildStatusOptions {
218
+ baseUrl: string;
219
+ accessToken: string;
220
+ id: string;
221
+ taskId: string;
222
+ businessDomain?: string;
223
+ }
224
+ export declare function getVegaDatasetBuildStatus(options: GetVegaDatasetBuildStatusOptions): Promise<string>;
225
+ export interface ExecuteVegaQueryOptions {
226
+ baseUrl: string;
227
+ accessToken: string;
228
+ body: string;
229
+ businessDomain?: string;
230
+ }
231
+ export declare function executeVegaQuery(options: ExecuteVegaQueryOptions): Promise<string>;
232
+ export interface ListAllVegaResourcesOptions {
233
+ baseUrl: string;
234
+ accessToken: string;
235
+ limit?: number;
236
+ offset?: number;
237
+ businessDomain?: string;
238
+ }
239
+ export declare function listAllVegaResources(options: ListAllVegaResourcesOptions): Promise<string>;
177
240
  export interface GetVegaDiscoverTaskOptions {
178
241
  baseUrl: string;
179
242
  accessToken: string;
package/dist/api/vega.js CHANGED
@@ -1,15 +1,6 @@
1
1
  import { HttpError } from "../utils/http.js";
2
+ import { buildHeaders } from "./headers.js";
2
3
  const VEGA_BASE = "/api/vega-backend/v1";
3
- function buildHeaders(accessToken, businessDomain) {
4
- return {
5
- accept: "application/json, text/plain, */*",
6
- "accept-language": "zh-cn",
7
- authorization: `Bearer ${accessToken}`,
8
- token: accessToken,
9
- "x-business-domain": businessDomain,
10
- "x-language": "zh-cn",
11
- };
12
- }
13
4
  export async function vegaHealth(options) {
14
5
  const { baseUrl, accessToken, businessDomain = "bd_public", } = options;
15
6
  const base = baseUrl.replace(/\/+$/, "");
@@ -221,6 +212,7 @@ export async function queryVegaResourceData(options) {
221
212
  headers: {
222
213
  ...buildHeaders(accessToken, businessDomain),
223
214
  "content-type": "application/json",
215
+ "x-http-method-override": "GET",
224
216
  },
225
217
  body: requestBody,
226
218
  });
@@ -374,6 +366,135 @@ export async function listVegaDiscoverTasks(options) {
374
366
  }
375
367
  return body;
376
368
  }
369
+ export async function createVegaDatasetDocs(options) {
370
+ const { baseUrl, accessToken, id, body: requestBody, businessDomain = "bd_public" } = options;
371
+ const base = baseUrl.replace(/\/+$/, "");
372
+ const url = `${base}${VEGA_BASE}/resources/dataset/${encodeURIComponent(id)}/docs`;
373
+ const response = await fetch(url, {
374
+ method: "POST",
375
+ headers: {
376
+ ...buildHeaders(accessToken, businessDomain),
377
+ "content-type": "application/json",
378
+ },
379
+ body: requestBody,
380
+ });
381
+ const body = await response.text();
382
+ if (!response.ok)
383
+ throw new HttpError(response.status, response.statusText, body);
384
+ return body;
385
+ }
386
+ export async function updateVegaDatasetDocs(options) {
387
+ const { baseUrl, accessToken, id, body: requestBody, businessDomain = "bd_public" } = options;
388
+ const base = baseUrl.replace(/\/+$/, "");
389
+ const url = `${base}${VEGA_BASE}/resources/dataset/${encodeURIComponent(id)}/docs`;
390
+ const response = await fetch(url, {
391
+ method: "PUT",
392
+ headers: {
393
+ ...buildHeaders(accessToken, businessDomain),
394
+ "content-type": "application/json",
395
+ },
396
+ body: requestBody,
397
+ });
398
+ const body = await response.text();
399
+ if (!response.ok)
400
+ throw new HttpError(response.status, response.statusText, body);
401
+ return body;
402
+ }
403
+ export async function deleteVegaDatasetDocs(options) {
404
+ const { baseUrl, accessToken, id, docIds, businessDomain = "bd_public" } = options;
405
+ const base = baseUrl.replace(/\/+$/, "");
406
+ const url = `${base}${VEGA_BASE}/resources/dataset/${encodeURIComponent(id)}/docs/${encodeURIComponent(docIds)}`;
407
+ const response = await fetch(url, {
408
+ method: "DELETE",
409
+ headers: buildHeaders(accessToken, businessDomain),
410
+ });
411
+ const body = await response.text();
412
+ if (!response.ok)
413
+ throw new HttpError(response.status, response.statusText, body);
414
+ return body;
415
+ }
416
+ export async function deleteVegaDatasetDocsQuery(options) {
417
+ const { baseUrl, accessToken, id, body: requestBody, businessDomain = "bd_public" } = options;
418
+ const base = baseUrl.replace(/\/+$/, "");
419
+ const url = `${base}${VEGA_BASE}/resources/dataset/${encodeURIComponent(id)}/docs/query`;
420
+ const response = await fetch(url, {
421
+ method: "POST",
422
+ headers: {
423
+ ...buildHeaders(accessToken, businessDomain),
424
+ "content-type": "application/json",
425
+ "x-http-method-override": "DELETE",
426
+ },
427
+ body: requestBody,
428
+ });
429
+ const body = await response.text();
430
+ if (!response.ok)
431
+ throw new HttpError(response.status, response.statusText, body);
432
+ return body;
433
+ }
434
+ export async function buildVegaDataset(options) {
435
+ const { baseUrl, accessToken, id, mode = "full", businessDomain = "bd_public" } = options;
436
+ const base = baseUrl.replace(/\/+$/, "");
437
+ const url = `${base}${VEGA_BASE}/resources/${encodeURIComponent(id)}/build`;
438
+ const response = await fetch(url, {
439
+ method: "POST",
440
+ headers: {
441
+ ...buildHeaders(accessToken, businessDomain),
442
+ "content-type": "application/json",
443
+ },
444
+ body: JSON.stringify({ mode }),
445
+ });
446
+ const body = await response.text();
447
+ if (!response.ok)
448
+ throw new HttpError(response.status, response.statusText, body);
449
+ return body;
450
+ }
451
+ export async function getVegaDatasetBuildStatus(options) {
452
+ const { baseUrl, accessToken, id, taskId, businessDomain = "bd_public" } = options;
453
+ const base = baseUrl.replace(/\/+$/, "");
454
+ const url = `${base}${VEGA_BASE}/resources/dataset/${encodeURIComponent(id)}/build/${encodeURIComponent(taskId)}`;
455
+ const response = await fetch(url, {
456
+ method: "GET",
457
+ headers: buildHeaders(accessToken, businessDomain),
458
+ });
459
+ const body = await response.text();
460
+ if (!response.ok)
461
+ throw new HttpError(response.status, response.statusText, body);
462
+ return body;
463
+ }
464
+ export async function executeVegaQuery(options) {
465
+ const { baseUrl, accessToken, body: requestBody, businessDomain = "bd_public" } = options;
466
+ const base = baseUrl.replace(/\/+$/, "");
467
+ const url = `${base}${VEGA_BASE}/query/execute`;
468
+ const response = await fetch(url, {
469
+ method: "POST",
470
+ headers: {
471
+ ...buildHeaders(accessToken, businessDomain),
472
+ "content-type": "application/json",
473
+ },
474
+ body: requestBody,
475
+ });
476
+ const body = await response.text();
477
+ if (!response.ok)
478
+ throw new HttpError(response.status, response.statusText, body);
479
+ return body;
480
+ }
481
+ export async function listAllVegaResources(options) {
482
+ const { baseUrl, accessToken, limit, offset, businessDomain = "bd_public" } = options;
483
+ const base = baseUrl.replace(/\/+$/, "");
484
+ const url = new URL(`${base}${VEGA_BASE}/resources/list`);
485
+ if (limit !== undefined)
486
+ url.searchParams.set("limit", String(limit));
487
+ if (offset !== undefined)
488
+ url.searchParams.set("offset", String(offset));
489
+ const response = await fetch(url.toString(), {
490
+ method: "GET",
491
+ headers: buildHeaders(accessToken, businessDomain),
492
+ });
493
+ const body = await response.text();
494
+ if (!response.ok)
495
+ throw new HttpError(response.status, response.statusText, body);
496
+ return body;
497
+ }
377
498
  export async function getVegaDiscoverTask(options) {
378
499
  const { baseUrl, accessToken, id, businessDomain = "bd_public" } = options;
379
500
  const base = baseUrl.replace(/\/+$/, "");
@@ -15,12 +15,14 @@ export declare function normalizeBaseUrl(value: string): string;
15
15
  * OAuth2 Authorization Code login flow.
16
16
  * 1. Register client (if not already registered), OR use a provided client ID
17
17
  * 2. Open browser to /oauth2/auth
18
- * 3. Receive authorization code via local HTTP callback
18
+ * 3. Receive authorization code via local HTTP callback (or manual paste for non-localhost)
19
19
  * 4. Exchange code for access_token + refresh_token
20
20
  * 5. Save token.json + client.json to ~/.kweaver/
21
21
  */
22
22
  export declare function oauth2Login(baseUrl: string, options?: {
23
23
  port?: number;
24
+ /** Full redirect URI override (e.g. "http://127.0.0.1:8080/callback" or a remote URL). */
25
+ redirectUri?: string;
24
26
  scope?: string;
25
27
  clientId?: string;
26
28
  clientSecret?: string;
@@ -42,6 +44,8 @@ export declare function playwrightLogin(baseUrl: string, options?: {
42
44
  username?: string;
43
45
  password?: string;
44
46
  port?: number;
47
+ /** Full redirect URI override. */
48
+ redirectUri?: string;
45
49
  scope?: string;
46
50
  tlsInsecure?: boolean;
47
51
  }): Promise<TokenConfig>;