@blinq_ai/widget 0.1.0 → 0.1.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.
package/dist/cli.js CHANGED
@@ -1,21 +1,54 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/cli.ts
4
+ import { execFile } from "child_process";
4
5
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
5
6
  import { homedir } from "os";
6
7
  import { join } from "path";
7
- var command = process.argv[2];
8
- var DEFAULT_API_BASE_URL = process.env.BLINQ_API_BASE_URL ?? "https://api.blinq.kz";
8
+ import { promisify } from "util";
9
+ var execFileAsync = promisify(execFile);
10
+ var args = process.argv.slice(2);
11
+ var command = readCommand(args);
12
+ var DEFAULT_API_BASE_URL = readOption(args, "--api-base-url") ?? process.env.BLINQ_API_BASE_URL ?? "https://app.blinq.kz";
9
13
  var SESSION_DIR = join(homedir(), ".blinq");
10
14
  var SESSION_PATH = join(SESSION_DIR, "widget-cli.json");
11
15
  function printUsage() {
12
16
  console.log("Usage:");
13
- console.log(" npx @blinq_ai/widget login");
17
+ console.log(" npx @blinq_ai/widget login [--api-base-url https://app.blinq.kz]");
14
18
  console.log(" npx @blinq_ai/widget init");
19
+ console.log("");
20
+ console.log("Environment:");
21
+ console.log(" BLINQ_API_BASE_URL=https://app.blinq.kz");
15
22
  }
16
23
  function normalizeBaseUrl(url) {
17
24
  return url.replace(/\/$/, "");
18
25
  }
26
+ function readCommand(values) {
27
+ for (let index = 0; index < values.length; index += 1) {
28
+ const value = values[index];
29
+ if (value === "--api-base-url") {
30
+ index += 1;
31
+ continue;
32
+ }
33
+ if (value.startsWith("--api-base-url=") || value.startsWith("-")) {
34
+ continue;
35
+ }
36
+ return value;
37
+ }
38
+ return void 0;
39
+ }
40
+ function readOption(values, name) {
41
+ for (let index = 0; index < values.length; index += 1) {
42
+ const value = values[index];
43
+ if (value === name) {
44
+ return values[index + 1];
45
+ }
46
+ if (value.startsWith(`${name}=`)) {
47
+ return value.slice(name.length + 1);
48
+ }
49
+ }
50
+ return void 0;
51
+ }
19
52
  function ensureSessionDir() {
20
53
  mkdirSync(SESSION_DIR, { recursive: true });
21
54
  }
@@ -33,6 +66,62 @@ function loadSession() {
33
66
  async function sleep(ms) {
34
67
  await new Promise((resolve) => setTimeout(resolve, ms));
35
68
  }
69
+ function errorMessage(error) {
70
+ if (!(error instanceof Error)) {
71
+ return String(error);
72
+ }
73
+ const cause = "cause" in error ? error.cause : null;
74
+ if (cause instanceof Error) {
75
+ return `${error.message}: ${cause.message}`;
76
+ }
77
+ return error.message;
78
+ }
79
+ function responseErrorMessage(statusCode, body) {
80
+ let message = `Request failed with status ${statusCode}`;
81
+ try {
82
+ const payload = JSON.parse(body);
83
+ message = payload.detail ?? payload.message ?? message;
84
+ } catch {
85
+ }
86
+ return message;
87
+ }
88
+ async function curlJsonFetch(url, options, headers) {
89
+ const method = String(options.method ?? (options.body ? "POST" : "GET")).toUpperCase();
90
+ const curlArgs = [
91
+ "-sS",
92
+ "-L",
93
+ "--connect-timeout",
94
+ "15",
95
+ "--max-time",
96
+ "60",
97
+ "-w",
98
+ "\n%{http_code}",
99
+ "-X",
100
+ method
101
+ ];
102
+ headers.forEach((value, key) => {
103
+ curlArgs.push("-H", `${key}: ${value}`);
104
+ });
105
+ if (options.body) {
106
+ curlArgs.push("--data-binary", String(options.body));
107
+ }
108
+ curlArgs.push(url);
109
+ try {
110
+ const { stdout } = await execFileAsync("curl", curlArgs, {
111
+ encoding: "utf8",
112
+ maxBuffer: 10 * 1024 * 1024
113
+ });
114
+ const statusSeparator = stdout.lastIndexOf("\n");
115
+ const body = statusSeparator >= 0 ? stdout.slice(0, statusSeparator) : stdout;
116
+ const statusCode = Number(statusSeparator >= 0 ? stdout.slice(statusSeparator + 1) : "0");
117
+ if (!Number.isFinite(statusCode) || statusCode < 200 || statusCode >= 300) {
118
+ throw new Error(responseErrorMessage(statusCode, body));
119
+ }
120
+ return JSON.parse(body);
121
+ } catch (error) {
122
+ throw new Error(errorMessage(error));
123
+ }
124
+ }
36
125
  async function jsonFetch(url, options = {}, accessToken) {
37
126
  const headers = new Headers(options.headers);
38
127
  if (!headers.has("Content-Type") && options.body) {
@@ -41,18 +130,37 @@ async function jsonFetch(url, options = {}, accessToken) {
41
130
  if (accessToken) {
42
131
  headers.set("Authorization", `Bearer ${accessToken}`);
43
132
  }
44
- const response = await fetch(url, {
45
- ...options,
46
- headers
47
- });
48
- if (!response.ok) {
49
- let message = `Request failed with status ${response.status}`;
50
- try {
51
- const payload = await response.json();
52
- message = payload.detail ?? payload.message ?? message;
53
- } catch {
133
+ let response;
134
+ try {
135
+ response = await fetch(url, {
136
+ ...options,
137
+ headers
138
+ });
139
+ } catch (error) {
140
+ if (process.env.BLINQ_CLI_DISABLE_CURL_FALLBACK !== "1") {
141
+ try {
142
+ return await curlJsonFetch(url, options, headers);
143
+ } catch (curlError) {
144
+ throw new Error(
145
+ [
146
+ `Could not reach ${url}.`,
147
+ `Node fetch failed: ${errorMessage(error)}.`,
148
+ `curl fallback failed: ${errorMessage(curlError)}.`,
149
+ "If you use a custom deployment, pass --api-base-url or set BLINQ_API_BASE_URL."
150
+ ].join(" ")
151
+ );
152
+ }
54
153
  }
55
- throw new Error(message);
154
+ throw new Error(
155
+ [
156
+ `Could not reach ${url}.`,
157
+ `Node fetch failed: ${errorMessage(error)}.`,
158
+ "If you use a custom deployment, pass --api-base-url or set BLINQ_API_BASE_URL."
159
+ ].join(" ")
160
+ );
161
+ }
162
+ if (!response.ok) {
163
+ throw new Error(responseErrorMessage(response.status, await response.text()));
56
164
  }
57
165
  return await response.json();
58
166
  }