@kweaver-ai/kweaver-sdk 0.6.4 → 0.6.6

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.
@@ -1,18 +1,7 @@
1
1
  import { isNoAuth } from "../config/no-auth.js";
2
2
  import { autoSelectBusinessDomain, clearPlatformSession, deletePlatform, deleteUser, getActiveUser, getConfigDir, getCurrentPlatform, getPlatformAlias, hasPlatform, listPlatforms, listUserProfiles, loadClientConfig, loadTokenConfig, resolveBusinessDomain, resolvePlatformIdentifier, resolveUserId, saveNoAuthPlatform, setActiveUser, setCurrentPlatform, setPlatformAlias, } from "../config/store.js";
3
3
  import { decodeJwtPayload } from "../config/jwt.js";
4
- import { buildCopyCommand, formatHttpError, isStudiowebShellUnavailableError, normalizeBaseUrl, oauth2Login, oauth2PasswordSigninLogin, playwrightLogin, refreshTokenLogin, } from "../auth/oauth.js";
5
- /** True when the `playwright` npm package can be imported (browser binaries may still need `npx playwright install`). */
6
- async function isPlaywrightPackageResolvable() {
7
- try {
8
- const modName = "playwright";
9
- await import(/* webpackIgnore: true */ modName);
10
- return true;
11
- }
12
- catch {
13
- return false;
14
- }
15
- }
4
+ import { buildCopyCommand, formatHttpError, normalizeBaseUrl, oauth2Login, oauth2PasswordSigninLogin, promptForUsername, promptForPassword, refreshTokenLogin, } from "../auth/oauth.js";
16
5
  export async function runAuthCommand(args) {
17
6
  const target = args[0];
18
7
  const rest = args.slice(1);
@@ -39,24 +28,24 @@ Login options:
39
28
  Requires --client-id and --client-secret.
40
29
  Get these from the callback page after browser login or \`auth export\`.
41
30
  --port <n> Local callback port (default: 9010). Use when 9010 is occupied.
42
- --no-browser Do not open a browser; print the auth URL and prompt for the callback URL or code (stdin).
43
- Use on headless servers or when automatic browser launch fails.
44
- -u, --username Username (with -p: tries HTTP /oauth2/signin first when the Studio web shell is available)
45
- -p, --password Password (with -u: falls back to Playwright headless only when studioweb is unavailable and Playwright is installed)
46
- --http-signin With -u/-p: HTTP POST /oauth2/signin only (no Playwright fallback). Uses the built-in RSA public key.
47
- --playwright With -u/-p: force Playwright (skip HTTP sign-in). Without -u/-p: open Playwright for manual login.
31
+ --no-browser Do not open a browser. Without -u/-p: print the auth URL and prompt for the
32
+ callback URL or code (stdin). With -u and/or -p: route through HTTP sign-in
33
+ (any missing credential is prompted; password is hidden when stdin is a TTY).
34
+ -u, --username Username for HTTP /oauth2/signin (POST). If -p is omitted, password is prompted.
35
+ -p, --password Password for HTTP /oauth2/signin (POST). If -u is omitted, username is prompted.
36
+ --http-signin Force HTTP /oauth2/signin (no browser). Missing -u/-p are prompted from stdin.
48
37
  --insecure, -k Skip TLS certificate verification (self-signed / dev HTTPS only)
49
38
  --no-auth Save platform without OAuth (servers with no authentication). Same as detecting OAuth 404 during login.`);
50
39
  return 0;
51
40
  }
52
41
  if (target === "login") {
53
42
  if (rest[0] === "--help" || rest[0] === "-h") {
54
- console.log(`kweaver auth login <platform-url> [--alias <name>] [--no-auth] [--no-browser] [-u user] [-p pass] [--http-signin] [--playwright] [--refresh-token T --client-id ID --client-secret S]`);
43
+ console.log(`kweaver auth login <platform-url> [--alias <name>] [--no-auth] [--no-browser] [-u user] [-p pass] [--http-signin] [--refresh-token T --client-id ID --client-secret S]`);
55
44
  return 0;
56
45
  }
57
46
  const url = rest[0];
58
47
  if (!url || url.startsWith("-")) {
59
- console.error("Usage: kweaver auth login <platform-url> [--alias <name>] [-u user] [-p pass] [--playwright]");
48
+ console.error("Usage: kweaver auth login <platform-url> [--alias <name>] [-u user] [-p pass]");
60
49
  return 1;
61
50
  }
62
51
  return runAuthCommand([url, ...rest.slice(1)]);
@@ -78,9 +67,8 @@ Login options:
78
67
  try {
79
68
  const normalizedTarget = normalizeBaseUrl(target);
80
69
  const alias = readOption(args, "--alias");
81
- const username = readOption(args, "--username") ?? readOption(args, "-u");
82
- const password = readOption(args, "--password") ?? readOption(args, "-p");
83
- const usePlaywright = args.includes("--playwright");
70
+ let username = readOption(args, "--username") ?? readOption(args, "-u");
71
+ let password = readOption(args, "--password") ?? readOption(args, "-p");
84
72
  const httpSignin = args.includes("--http-signin");
85
73
  const oauthProduct = readOption(args, "--oauth-product");
86
74
  const signinPublicKeyFile = readOption(args, "--signin-public-key-file");
@@ -101,7 +89,7 @@ Login options:
101
89
  "--http-signin",
102
90
  "--oauth-product",
103
91
  "--signin-public-key-file",
104
- "--playwright", "--insecure", "-k", "--no-auth", "--redirect-uri",
92
+ "--insecure", "-k", "--no-auth", "--redirect-uri",
105
93
  ]);
106
94
  const KNOWN_VALUE_FLAGS = new Set([
107
95
  "--alias", "--client-id", "--client-secret", "--refresh-token",
@@ -130,30 +118,34 @@ Login options:
130
118
  if (noAuth && noBrowser) {
131
119
  console.error("--no-auth does not require a browser; --no-browser is ignored.");
132
120
  }
133
- if (noAuth && (username || password || usePlaywright || httpSignin)) {
134
- console.error("--no-auth cannot be used with Playwright login, HTTP sign-in, or -u/-p.");
121
+ if (noAuth && (username || password || httpSignin)) {
122
+ console.error("--no-auth cannot be used with HTTP sign-in or -u/-p.");
135
123
  return 1;
136
124
  }
137
- if (noBrowser && (username || password || usePlaywright || httpSignin)) {
138
- console.error("--no-browser cannot be used with Playwright login, HTTP sign-in, or -u/-p.");
139
- return 1;
140
- }
141
- if (httpSignin && usePlaywright) {
142
- console.error("--http-signin cannot be used with --playwright.");
143
- return 1;
125
+ if (noBrowser && httpSignin) {
126
+ // HTTP sign-in already runs without a browser; --no-browser is a no-op signal here.
127
+ console.error("--http-signin already runs without a browser; --no-browser is redundant and ignored.");
144
128
  }
145
129
  if (httpSignin && refreshToken) {
146
130
  console.error("--http-signin cannot be used with --refresh-token.");
147
131
  return 1;
148
132
  }
149
- if (httpSignin && (!username || !password)) {
150
- console.error("--http-signin requires -u/--username and -p/--password.");
151
- return 1;
152
- }
153
133
  if (noBrowser && refreshToken) {
154
134
  console.error("--no-browser cannot be used with --refresh-token.");
155
135
  return 1;
156
136
  }
137
+ // Headless credential login: if the user signalled HTTP sign-in (--http-signin,
138
+ // or partial -u/-p, or --no-browser combined with -u/-p) but didn't provide both
139
+ // credentials inline, prompt for the missing one(s) on stderr. Password is read
140
+ // without echo when stdin is a TTY.
141
+ const wantsCredentialLogin = !noAuth && !refreshToken &&
142
+ (httpSignin || (noBrowser && (username || password)) || (!!username !== !!password));
143
+ if (wantsCredentialLogin) {
144
+ if (!username)
145
+ username = await promptForUsername("Username");
146
+ if (!password)
147
+ password = await promptForPassword("Password");
148
+ }
157
149
  let token;
158
150
  if (noAuth) {
159
151
  token = saveNoAuthPlatform(normalizedTarget, { tlsInsecure });
@@ -182,17 +174,9 @@ Login options:
182
174
  signinPublicKeyPemPath: signinPublicKeyFile ?? undefined,
183
175
  });
184
176
  }
185
- else if (username && password && usePlaywright) {
186
- console.log("Logging in (headless, Playwright)...");
187
- token = await playwrightLogin(normalizedTarget, {
188
- username,
189
- password,
190
- tlsInsecure,
191
- port: customPort,
192
- });
193
- }
194
177
  else if (username && password) {
195
- const signinOpts = {
178
+ console.log("Logging in (HTTP /oauth2/signin)...");
179
+ token = await oauth2PasswordSigninLogin(normalizedTarget, {
196
180
  username,
197
181
  password,
198
182
  tlsInsecure,
@@ -201,39 +185,6 @@ Login options:
201
185
  clientSecret: clientSecret ?? undefined,
202
186
  oauthProduct: oauthProduct ?? undefined,
203
187
  signinPublicKeyPemPath: signinPublicKeyFile ?? undefined,
204
- };
205
- console.log("Logging in (HTTP /oauth2/signin)...");
206
- try {
207
- token = await oauth2PasswordSigninLogin(normalizedTarget, signinOpts);
208
- }
209
- catch (err) {
210
- if (!isStudiowebShellUnavailableError(err)) {
211
- throw err;
212
- }
213
- const playwrightOk = await isPlaywrightPackageResolvable();
214
- if (playwrightOk) {
215
- process.stderr.write("Studio web sign-in shell is not available; falling back to Playwright headless login.\n");
216
- console.log("Logging in (headless, Playwright)...");
217
- token = await playwrightLogin(normalizedTarget, {
218
- username,
219
- password,
220
- tlsInsecure,
221
- port: customPort,
222
- });
223
- }
224
- else {
225
- console.error("Studio web sign-in shell is not available on this platform, and the Playwright package is not installed.");
226
- console.error("Install Playwright for headless browser login: npm install playwright && npx playwright install chromium");
227
- console.error("Alternatively, use OAuth without credentials:");
228
- console.error(` kweaver auth login ${normalizedTarget} --no-browser`);
229
- throw err;
230
- }
231
- }
232
- }
233
- else if (usePlaywright) {
234
- console.log("Opening browser for login (Playwright)...");
235
- token = await playwrightLogin(normalizedTarget, {
236
- tlsInsecure, port: customPort,
237
188
  });
238
189
  }
239
190
  else {
@@ -456,7 +407,7 @@ Login options:
456
407
  console.log(`Run \`kweaver auth login ${logoutTarget}\` to sign in again.`);
457
408
  return 0;
458
409
  }
459
- console.error("Usage: kweaver auth login <platform-url> [--alias <name>] [-u user] [-p pass] [--playwright]");
410
+ console.error("Usage: kweaver auth login <platform-url> [--alias <name>] [-u user] [-p pass]");
460
411
  console.error(" kweaver auth whoami [platform-url|alias] [--json]");
461
412
  console.error(" kweaver auth export [platform-url|alias] [--json]");
462
413
  console.error(" kweaver auth status [platform-url|alias]");
@@ -49,6 +49,7 @@ export declare function parseKnCreateFromCsvArgs(args: string[]): {
49
49
  batchSize: number;
50
50
  tables: string[];
51
51
  build: boolean;
52
+ recreate: boolean;
52
53
  timeout: number;
53
54
  businessDomain: string;
54
55
  };
@@ -706,6 +706,7 @@ Options:
706
706
  --tables <a,b> Tables to include in KN (default: all imported)
707
707
  --build (default) Build after creation
708
708
  --no-build Skip build
709
+ --recreate Use "insert" mode on first batch (only effective for new tables)
709
710
  --timeout <n> Build timeout in seconds (default: 300)
710
711
  -bd, --biz-domain Business domain (default: bd_public)`;
711
712
  export function parseKnCreateFromCsvArgs(args) {
@@ -716,6 +717,7 @@ export function parseKnCreateFromCsvArgs(args) {
716
717
  let batchSize = 500;
717
718
  let tablesStr = "";
718
719
  let build = true;
720
+ let recreate = false;
719
721
  let timeout = 300;
720
722
  let businessDomain = "";
721
723
  for (let i = 0; i < args.length; i += 1) {
@@ -752,6 +754,10 @@ export function parseKnCreateFromCsvArgs(args) {
752
754
  build = false;
753
755
  continue;
754
756
  }
757
+ if (arg === "--recreate") {
758
+ recreate = true;
759
+ continue;
760
+ }
755
761
  if (arg === "--timeout" && args[i + 1]) {
756
762
  timeout = parseInt(args[++i], 10);
757
763
  if (Number.isNaN(timeout) || timeout < 1)
@@ -772,7 +778,7 @@ export function parseKnCreateFromCsvArgs(args) {
772
778
  }
773
779
  if (!businessDomain)
774
780
  businessDomain = resolveBusinessDomain();
775
- return { dsId, files, name, tablePrefix, batchSize, tables, build, timeout, businessDomain };
781
+ return { dsId, files, name, tablePrefix, batchSize, tables, build, recreate, timeout, businessDomain };
776
782
  }
777
783
  export async function runKnCreateFromCsvCommand(args) {
778
784
  let options;
@@ -795,6 +801,7 @@ export async function runKnCreateFromCsvCommand(args) {
795
801
  "--table-prefix", options.tablePrefix,
796
802
  "--batch-size", String(options.batchSize),
797
803
  "-bd", options.businessDomain,
804
+ ...(options.recreate ? ["--recreate"] : []),
798
805
  ];
799
806
  const importResult = await runDsImportCsv(importArgs);
800
807
  if (importResult.code !== 0) {
@@ -1,8 +1,18 @@
1
+ export type FormField = {
2
+ name: string;
3
+ kind: "string";
4
+ value: string;
5
+ } | {
6
+ name: string;
7
+ kind: "file";
8
+ path: string;
9
+ };
1
10
  export interface CallInvocation {
2
11
  url: string;
3
12
  method: string;
4
13
  headers: Headers;
5
14
  body?: string;
15
+ formFields?: FormField[];
6
16
  pretty: boolean;
7
17
  verbose: boolean;
8
18
  businessDomain: string;
@@ -1,3 +1,5 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import { basename } from "node:path";
1
3
  import { ensureValidToken, formatHttpError, with401RefreshRetry } from "../auth/oauth.js";
2
4
  import { isNoAuth } from "../config/no-auth.js";
3
5
  import { HttpError } from "../utils/http.js";
@@ -6,6 +8,7 @@ export function parseCallArgs(args) {
6
8
  const headers = new Headers();
7
9
  let method = "GET";
8
10
  let body;
11
+ const formFields = [];
9
12
  let url;
10
13
  let pretty = true;
11
14
  let verbose = false;
@@ -40,6 +43,26 @@ export function parseCallArgs(args) {
40
43
  index += 1;
41
44
  continue;
42
45
  }
46
+ if (arg === "-F" || arg === "--form") {
47
+ const raw = args[index + 1];
48
+ if (!raw)
49
+ throw new Error("Missing value for -F flag");
50
+ const eq = raw.indexOf("=");
51
+ if (eq === -1)
52
+ throw new Error(`Invalid -F format: ${raw} (expected key=value or key=@path)`);
53
+ const name = raw.slice(0, eq);
54
+ const rhs = raw.slice(eq + 1);
55
+ if (rhs.startsWith("@")) {
56
+ formFields.push({ name, kind: "file", path: rhs.slice(1) });
57
+ }
58
+ else {
59
+ formFields.push({ name, kind: "string", value: rhs });
60
+ }
61
+ if (method === "GET")
62
+ method = "POST";
63
+ index += 1;
64
+ continue;
65
+ }
43
66
  if (arg === "--pretty") {
44
67
  pretty = true;
45
68
  continue;
@@ -70,9 +93,21 @@ export function parseCallArgs(args) {
70
93
  if (!url) {
71
94
  throw new Error("Missing request URL");
72
95
  }
96
+ if (formFields.length > 0 && body !== undefined) {
97
+ throw new Error("-F and -d are mutually exclusive");
98
+ }
73
99
  if (!businessDomain)
74
100
  businessDomain = resolveBusinessDomain();
75
- return { url, method, headers, body, pretty, verbose, businessDomain };
101
+ return {
102
+ url,
103
+ method,
104
+ headers,
105
+ body,
106
+ formFields: formFields.length > 0 ? formFields : undefined,
107
+ pretty,
108
+ verbose,
109
+ businessDomain,
110
+ };
76
111
  }
77
112
  function injectAuthHeaders(headers, accessToken, businessDomain) {
78
113
  if (!isNoAuth(accessToken)) {
@@ -115,12 +150,17 @@ export function formatVerboseRequest(invocation) {
115
150
  for (const [name, value] of entries) {
116
151
  lines.push(` ${name}: ${value}`);
117
152
  }
118
- lines.push(`Body: ${invocation.body ? "present" : "empty"}`);
153
+ const bodyDesc = invocation.formFields && invocation.formFields.length > 0
154
+ ? `multipart (${invocation.formFields.length} field${invocation.formFields.length > 1 ? "s" : ""})`
155
+ : invocation.body
156
+ ? "present"
157
+ : "empty";
158
+ lines.push(`Body: ${bodyDesc}`);
119
159
  return lines;
120
160
  }
121
161
  export async function runCallCommand(args) {
122
162
  if (args.length === 0 || args[0] === "--help" || args[0] === "-h") {
123
- console.log(`kweaver call <url> [-X METHOD] [-H "Name: value"] [-d BODY] [--pretty] [--verbose] [-bd value]
163
+ console.log(`kweaver call <url> [-X METHOD] [-H "Name: value"] [-d BODY] [-F key=value] [--pretty] [--verbose] [-bd value]
124
164
 
125
165
  Call an API with curl-style flags and auto-injected token headers.
126
166
 
@@ -129,6 +169,7 @@ Options:
129
169
  -X, --request HTTP method (default: GET)
130
170
  -H, --header Extra header (repeatable)
131
171
  -d, --data, --data-raw JSON request body (sets Content-Type: application/json if not set)
172
+ -F, --form Multipart form field. -F key=value or -F key=@/path/to/file. Repeatable. Mutually exclusive with -d.
132
173
  -bd, --biz-domain Override x-business-domain (default: bd_public)
133
174
  -v, --verbose Print request info to stderr
134
175
  --pretty Pretty-print JSON output (default)`);
@@ -150,7 +191,22 @@ Options:
150
191
  : invocation.url;
151
192
  const headers = new Headers(invocation.headers);
152
193
  injectAuthHeaders(headers, token.accessToken, invocation.businessDomain);
153
- if (invocation.body !== undefined &&
194
+ let requestBody = invocation.body;
195
+ if (invocation.formFields && invocation.formFields.length > 0) {
196
+ const form = new FormData();
197
+ for (const field of invocation.formFields) {
198
+ if (field.kind === "string") {
199
+ form.append(field.name, field.value);
200
+ }
201
+ else {
202
+ const buf = await readFile(field.path);
203
+ form.append(field.name, new Blob([buf]), basename(field.path));
204
+ }
205
+ }
206
+ requestBody = form;
207
+ // do not set content-type — fetch sets multipart boundary
208
+ }
209
+ else if (invocation.body !== undefined &&
154
210
  invocation.body.length > 0 &&
155
211
  !headers.has("content-type") &&
156
212
  !headers.has("Content-Type")) {
@@ -164,7 +220,7 @@ Options:
164
220
  const response = await fetch(url, {
165
221
  method: invocation.method,
166
222
  headers,
167
- body: invocation.body,
223
+ body: requestBody,
168
224
  });
169
225
  const rawText = await response.text();
170
226
  const text = stripSseDoneMarker(rawText, response.headers.get("content-type"));
@@ -466,7 +466,7 @@ export async function runDsImportCsv(args) {
466
466
  process.stderr.write(`${elapsed}s\n`);
467
467
  }
468
468
  catch (err) {
469
- const msg = err instanceof Error ? err.message : String(err);
469
+ const msg = formatHttpError(err);
470
470
  process.stderr.write(`FAILED\n`);
471
471
  console.error(`[${tableName}] batch ${batchLabel} error: ${msg}`);
472
472
  batchFailed = true;
@@ -19,7 +19,7 @@ export interface DagBodyOptions {
19
19
  tableExist: boolean;
20
20
  data: Array<Record<string, string | null>>;
21
21
  fieldMappings: FieldMapping[];
22
- /** When true on the first batch (`tableExist` false), use overwrite to drop/recreate table before import. */
22
+ /** When true on the first batch (`tableExist` false), use "insert" to force table recreation. */
23
23
  recreate?: boolean;
24
24
  }
25
25
  /**
@@ -82,7 +82,9 @@ export function buildFieldMappings(headers) {
82
82
  export function buildDagBody(options) {
83
83
  const { datasourceId, datasourceType, tableName, tableExist, data, fieldMappings, recreate } = options;
84
84
  const ts = Date.now();
85
- const operateType = tableExist ? "append" : recreate ? "overwrite" : "append";
85
+ // "insert" creates/replaces the table; "append" adds rows to an existing table.
86
+ // With --recreate, use "insert" on first batch to force table recreation when schema changed.
87
+ const operateType = tableExist ? "append" : recreate ? "insert" : "append";
86
88
  const triggerStep = {
87
89
  id: "step-trigger",
88
90
  title: "Trigger",
@@ -0,0 +1,16 @@
1
+ export declare function runToolCommand(args: string[]): Promise<number>;
2
+ export interface ToolUploadOptions {
3
+ boxId: string;
4
+ filePath: string;
5
+ metadataType: "openapi";
6
+ businessDomain: string;
7
+ pretty: boolean;
8
+ }
9
+ export declare function parseToolUploadArgs(args: string[]): ToolUploadOptions;
10
+ export interface ToolStatusOptions {
11
+ boxId: string;
12
+ toolIds: string[];
13
+ status: "enabled" | "disabled";
14
+ businessDomain: string;
15
+ }
16
+ export declare function parseToolStatusArgs(args: string[], status: "enabled" | "disabled"): ToolStatusOptions;
@@ -0,0 +1,208 @@
1
+ import { access } from "node:fs/promises";
2
+ import { ensureValidToken, formatHttpError, with401RefreshRetry } from "../auth/oauth.js";
3
+ import { listTools, setToolStatuses, uploadTool } from "../api/toolboxes.js";
4
+ import { formatCallOutput } from "./call.js";
5
+ import { resolveBusinessDomain } from "../config/store.js";
6
+ const HELP = `kweaver tool
7
+
8
+ Subcommands:
9
+ upload --toolbox <box-id> <openapi-spec-path> [--metadata-type openapi]
10
+ Upload an OpenAPI spec file as a tool
11
+ list --toolbox <box-id> List tools in a toolbox
12
+ enable --toolbox <box-id> <tool-id>... Enable one or more tools
13
+ disable --toolbox <box-id> <tool-id>... Disable one or more tools
14
+
15
+ Options:
16
+ -bd, --biz-domain <s> Business domain (default: bd_public)
17
+ --pretty Pretty-print JSON (default)
18
+ --compact Single-line JSON (pipeline-friendly)`;
19
+ export async function runToolCommand(args) {
20
+ const [subcommand, ...rest] = args;
21
+ if (!subcommand || subcommand === "--help" || subcommand === "-h") {
22
+ console.log(HELP);
23
+ return 0;
24
+ }
25
+ const dispatch = () => {
26
+ if (subcommand === "upload")
27
+ return runToolUpload(rest);
28
+ if (subcommand === "list")
29
+ return runToolList(rest);
30
+ if (subcommand === "enable")
31
+ return runToolStatus(rest, "enabled");
32
+ if (subcommand === "disable")
33
+ return runToolStatus(rest, "disabled");
34
+ return Promise.resolve(-1);
35
+ };
36
+ try {
37
+ return await with401RefreshRetry(async () => {
38
+ const code = await dispatch();
39
+ if (code === -1) {
40
+ console.error(`Unknown tool subcommand: ${subcommand}`);
41
+ return 1;
42
+ }
43
+ return code;
44
+ });
45
+ }
46
+ catch (error) {
47
+ console.error(formatHttpError(error));
48
+ return 1;
49
+ }
50
+ }
51
+ export function parseToolUploadArgs(args) {
52
+ let boxId = "";
53
+ let filePath = "";
54
+ let metadataType = "openapi";
55
+ let businessDomain = "";
56
+ let pretty = true;
57
+ for (let i = 0; i < args.length; i += 1) {
58
+ const a = args[i];
59
+ if (a === "--toolbox" && args[i + 1]) {
60
+ boxId = args[++i];
61
+ continue;
62
+ }
63
+ if (a === "--metadata-type" && args[i + 1]) {
64
+ const val = args[++i];
65
+ if (val !== "openapi") {
66
+ throw new Error(`Unsupported --metadata-type: ${val} (only "openapi" is supported)`);
67
+ }
68
+ metadataType = val;
69
+ continue;
70
+ }
71
+ if ((a === "-bd" || a === "--biz-domain") && args[i + 1]) {
72
+ businessDomain = args[++i];
73
+ continue;
74
+ }
75
+ if (a === "--pretty") {
76
+ pretty = true;
77
+ continue;
78
+ }
79
+ if (a === "--compact") {
80
+ pretty = false;
81
+ continue;
82
+ }
83
+ if (!a.startsWith("-") && !filePath) {
84
+ filePath = a;
85
+ continue;
86
+ }
87
+ }
88
+ if (!boxId)
89
+ throw new Error("Missing required flag: --toolbox");
90
+ if (!filePath)
91
+ throw new Error("Missing required positional argument: <file-path>");
92
+ if (!businessDomain)
93
+ businessDomain = resolveBusinessDomain();
94
+ return { boxId, filePath, metadataType, businessDomain, pretty };
95
+ }
96
+ async function runToolUpload(args) {
97
+ let opts;
98
+ try {
99
+ opts = parseToolUploadArgs(args);
100
+ }
101
+ catch (e) {
102
+ console.error(e instanceof Error ? e.message : String(e));
103
+ return 1;
104
+ }
105
+ try {
106
+ await access(opts.filePath);
107
+ }
108
+ catch {
109
+ console.error(`File not found: ${opts.filePath}`);
110
+ return 1;
111
+ }
112
+ const token = await ensureValidToken();
113
+ const body = await uploadTool({
114
+ baseUrl: token.baseUrl,
115
+ accessToken: token.accessToken,
116
+ businessDomain: opts.businessDomain,
117
+ boxId: opts.boxId,
118
+ filePath: opts.filePath,
119
+ metadataType: opts.metadataType,
120
+ });
121
+ console.log(formatCallOutput(body, opts.pretty));
122
+ return 0;
123
+ }
124
+ // ── list ──────────────────────────────────────────────────────────────────────
125
+ async function runToolList(args) {
126
+ let boxId = "";
127
+ let businessDomain = "";
128
+ let pretty = true;
129
+ for (let i = 0; i < args.length; i += 1) {
130
+ const a = args[i];
131
+ if (a === "--toolbox" && args[i + 1]) {
132
+ boxId = args[++i];
133
+ continue;
134
+ }
135
+ if ((a === "-bd" || a === "--biz-domain") && args[i + 1]) {
136
+ businessDomain = args[++i];
137
+ continue;
138
+ }
139
+ if (a === "--pretty") {
140
+ pretty = true;
141
+ continue;
142
+ }
143
+ if (a === "--compact") {
144
+ pretty = false;
145
+ continue;
146
+ }
147
+ }
148
+ if (!boxId) {
149
+ console.error("Missing required flag: --toolbox");
150
+ return 1;
151
+ }
152
+ if (!businessDomain)
153
+ businessDomain = resolveBusinessDomain();
154
+ const token = await ensureValidToken();
155
+ const body = await listTools({
156
+ baseUrl: token.baseUrl,
157
+ accessToken: token.accessToken,
158
+ businessDomain,
159
+ boxId,
160
+ });
161
+ console.log(formatCallOutput(body, pretty));
162
+ return 0;
163
+ }
164
+ export function parseToolStatusArgs(args, status) {
165
+ let boxId = "";
166
+ let businessDomain = "";
167
+ const toolIds = [];
168
+ for (let i = 0; i < args.length; i += 1) {
169
+ const a = args[i];
170
+ if (a === "--toolbox" && args[i + 1]) {
171
+ boxId = args[++i];
172
+ continue;
173
+ }
174
+ if ((a === "-bd" || a === "--biz-domain") && args[i + 1]) {
175
+ businessDomain = args[++i];
176
+ continue;
177
+ }
178
+ if (!a.startsWith("-"))
179
+ toolIds.push(a);
180
+ }
181
+ if (!boxId)
182
+ throw new Error("Missing required flag: --toolbox");
183
+ if (toolIds.length === 0)
184
+ throw new Error("Missing tool id(s)");
185
+ if (!businessDomain)
186
+ businessDomain = resolveBusinessDomain();
187
+ return { boxId, toolIds, status, businessDomain };
188
+ }
189
+ async function runToolStatus(args, status) {
190
+ let opts;
191
+ try {
192
+ opts = parseToolStatusArgs(args, status);
193
+ }
194
+ catch (e) {
195
+ console.error(e instanceof Error ? e.message : String(e));
196
+ return 1;
197
+ }
198
+ const token = await ensureValidToken();
199
+ await setToolStatuses({
200
+ baseUrl: token.baseUrl,
201
+ accessToken: token.accessToken,
202
+ businessDomain: opts.businessDomain,
203
+ boxId: opts.boxId,
204
+ updates: opts.toolIds.map((toolId) => ({ toolId, status: opts.status })),
205
+ });
206
+ console.error(`${status === "enabled" ? "Enabled" : "Disabled"} ${opts.toolIds.length} tool(s) in toolbox ${opts.boxId}`);
207
+ return 0;
208
+ }
@@ -0,0 +1,14 @@
1
+ export declare function runToolboxCommand(args: string[]): Promise<number>;
2
+ export interface ToolboxCreateOptions {
3
+ name: string;
4
+ serviceUrl: string;
5
+ description: string;
6
+ businessDomain: string;
7
+ pretty: boolean;
8
+ }
9
+ export declare function parseToolboxCreateArgs(args: string[]): ToolboxCreateOptions;
10
+ export interface ToolboxSetStatusOptions {
11
+ boxId: string;
12
+ businessDomain: string;
13
+ }
14
+ export declare function parseToolboxSetStatusArgs(args: string[]): ToolboxSetStatusOptions;