@bloque/cli 0.0.46 → 0.0.47

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 (2) hide show
  1. package/dist/bin.js +263 -15
  2. package/package.json +1 -1
package/dist/bin.js CHANGED
@@ -43,6 +43,9 @@ var SessionStore = class _SessionStore {
43
43
  };
44
44
 
45
45
  // src/ui/portal.ts
46
+ import { stdout as stdout2 } from "process";
47
+
48
+ // src/ui/shared.ts
46
49
  import { stdout } from "process";
47
50
  var ESC = "\x1B";
48
51
  var RESET = `${ESC}[0m`;
@@ -101,6 +104,14 @@ function generateStarField() {
101
104
  }
102
105
  return field;
103
106
  }
107
+ function writeLines(lines) {
108
+ stdout.write(lines.join("\n") + "\n");
109
+ }
110
+ function clearLines(count) {
111
+ stdout.write(`${ESC}[${count}A${ESC}[0J`);
112
+ }
113
+
114
+ // src/ui/portal.ts
104
115
  function renderFrame(radius, stars, shimmer = false) {
105
116
  const lines = [];
106
117
  for (let y = 0; y < HEIGHT; y++) {
@@ -116,10 +127,10 @@ function renderFrame(radius, stars, shimmer = false) {
116
127
  if (Math.abs(dist - r) < hitThreshold) {
117
128
  const t = 1 - r / MAX_RADIUS;
118
129
  const idx = Math.round(t * (PALETTE.length - 1));
119
- color = PALETTE[idx];
130
+ color = PALETTE[idx] ?? "";
120
131
  if (r === 0) {
121
132
  char = "\u2726";
122
- color = BOLD + PALETTE[PALETTE.length - 1];
133
+ color = BOLD + (PALETTE[PALETTE.length - 1] ?? "");
123
134
  } else if (r <= 2) {
124
135
  char = shimmer && Math.random() < 0.3 ? "\u2727" : "\u25E6";
125
136
  } else {
@@ -128,7 +139,7 @@ function renderFrame(radius, stars, shimmer = false) {
128
139
  break;
129
140
  }
130
141
  }
131
- if (char === " " && stars[y][x]) {
142
+ if (char === " " && stars[y]?.[x]) {
132
143
  const twinkle = shimmer && Math.random() < 0.3;
133
144
  color = twinkle ? MED_STAR : DIM_STAR;
134
145
  char = ".";
@@ -147,20 +158,14 @@ function renderSuccess(message) {
147
158
  ""
148
159
  ];
149
160
  }
150
- function writeLines(lines) {
151
- stdout.write(lines.join("\n") + "\n");
152
- }
153
- function clearLines(count) {
154
- stdout.write(`${ESC}[${count}A${ESC}[0J`);
155
- }
156
161
  async function portalAnimation(message) {
157
- if (!stdout.isTTY) {
162
+ if (!stdout2.isTTY) {
158
163
  console.log(`
159
164
  \u25C6 ${message}
160
165
  `);
161
166
  return;
162
167
  }
163
- stdout.write(HIDE_CURSOR);
168
+ stdout2.write(HIDE_CURSOR);
164
169
  const stars = generateStarField();
165
170
  try {
166
171
  const frame0 = renderFrame(0, stars);
@@ -192,7 +197,7 @@ async function portalAnimation(message) {
192
197
  const successLines = renderSuccess(message);
193
198
  writeLines(successLines);
194
199
  } finally {
195
- stdout.write(SHOW_CURSOR);
200
+ stdout2.write(SHOW_CURSOR);
196
201
  }
197
202
  }
198
203
 
@@ -2090,6 +2095,246 @@ import { execSync } from "child_process";
2090
2095
  import { Command as Command5 } from "commander";
2091
2096
  import { SDK as SDK3 } from "@bloque/sdk";
2092
2097
  import { checkbox, confirm, input as input2, password as password2, select as select2 } from "@inquirer/prompts";
2098
+
2099
+ // src/auth/web.ts
2100
+ import { exec } from "child_process";
2101
+ import crypto from "crypto";
2102
+ import http from "http";
2103
+
2104
+ // src/ui/beacon.ts
2105
+ import { stdout as stdout3 } from "process";
2106
+ var FRAME_MS = 80;
2107
+ var RING_CYCLE = MAX_RADIUS + 3;
2108
+ var RING_OFFSET = Math.floor(RING_CYCLE / 2);
2109
+ function renderBeaconFrame(frame, stars) {
2110
+ const ring1Radius = frame % RING_CYCLE;
2111
+ const ring2Radius = (frame + RING_OFFSET) % RING_CYCLE;
2112
+ const lines = [];
2113
+ for (let y = 0; y < HEIGHT; y++) {
2114
+ let line = "";
2115
+ for (let x = 0; x < WIDTH; x++) {
2116
+ const dx = (x - CX) / ASPECT;
2117
+ const dy = y - CY;
2118
+ const dist = Math.sqrt(dx * dx + dy * dy);
2119
+ let char = " ";
2120
+ let color = "";
2121
+ if (dist < 0.5) {
2122
+ char = "\u2726";
2123
+ color = BOLD + PALETTE[PALETTE.length - 1];
2124
+ } else {
2125
+ for (const ringR of [ring1Radius, ring2Radius]) {
2126
+ if (ringR > MAX_RADIUS) continue;
2127
+ const hitThreshold = ringR <= 2 ? 0.6 : 0.55;
2128
+ if (Math.abs(dist - ringR) < hitThreshold) {
2129
+ const fade = ringR / MAX_RADIUS;
2130
+ const idx = Math.round(fade * (PALETTE.length - 1));
2131
+ const reverseIdx = PALETTE.length - 1 - idx;
2132
+ color = PALETTE[reverseIdx];
2133
+ if (ringR <= 2) {
2134
+ char = Math.random() < 0.25 ? "\u2727" : "\u25E6";
2135
+ } else {
2136
+ char = Math.random() < 0.12 ? "\u2727" : "\xB7";
2137
+ }
2138
+ break;
2139
+ }
2140
+ }
2141
+ }
2142
+ if (char === " " && stars[y][x]) {
2143
+ const twinkle = Math.random() < 0.15;
2144
+ color = twinkle ? MED_STAR : DIM_STAR;
2145
+ char = ".";
2146
+ }
2147
+ line += char !== " " ? color + char + RESET : " ";
2148
+ }
2149
+ lines.push(line);
2150
+ }
2151
+ return lines;
2152
+ }
2153
+ function startBeaconAnimation() {
2154
+ if (!stdout3.isTTY) {
2155
+ return () => {
2156
+ };
2157
+ }
2158
+ const stars = generateStarField();
2159
+ let frame = 0;
2160
+ let running = true;
2161
+ stdout3.write(HIDE_CURSOR);
2162
+ const initial = renderBeaconFrame(frame, stars);
2163
+ writeLines(initial);
2164
+ frame++;
2165
+ const interval = setInterval(() => {
2166
+ if (!running) return;
2167
+ clearLines(HEIGHT);
2168
+ const lines = renderBeaconFrame(frame, stars);
2169
+ writeLines(lines);
2170
+ frame++;
2171
+ }, FRAME_MS);
2172
+ return () => {
2173
+ if (!running) return;
2174
+ running = false;
2175
+ clearInterval(interval);
2176
+ clearLines(HEIGHT);
2177
+ stdout3.write(SHOW_CURSOR);
2178
+ };
2179
+ }
2180
+
2181
+ // src/auth/web.ts
2182
+ var TIMEOUT_MS = 5 * 60 * 1e3;
2183
+ var MAX_PORT_RETRIES = 3;
2184
+ function resolveCopilotUrl(mode, host) {
2185
+ if (host) return host;
2186
+ if (process.env.BLOQUE_COPILOT_URL) return process.env.BLOQUE_COPILOT_URL;
2187
+ return mode === "sandbox" ? "https://copilot-dev.bloque.app" : "https://copilot.bloque.app";
2188
+ }
2189
+ function openBrowser(url) {
2190
+ const cmd = process.platform === "darwin" ? `open "${url}"` : process.platform === "win32" ? `start "" "${url}"` : `xdg-open "${url}"`;
2191
+ exec(cmd, () => {
2192
+ });
2193
+ }
2194
+ function setCorsHeaders(res, copilotUrl) {
2195
+ res.setHeader("Access-Control-Allow-Origin", copilotUrl);
2196
+ res.setHeader("Access-Control-Allow-Methods", "POST, OPTIONS");
2197
+ res.setHeader("Access-Control-Allow-Headers", "Content-Type");
2198
+ }
2199
+ function listenOnRandomPort(server) {
2200
+ return new Promise((resolve, reject) => {
2201
+ server.once("error", reject);
2202
+ server.listen(0, "127.0.0.1", () => {
2203
+ server.removeListener("error", reject);
2204
+ const addr = server.address();
2205
+ if (!addr || typeof addr === "string") {
2206
+ reject(new Error("Failed to bind server"));
2207
+ return;
2208
+ }
2209
+ resolve(addr.port);
2210
+ });
2211
+ });
2212
+ }
2213
+ function startCallbackServer(nonce, copilotUrl) {
2214
+ let resolveToken;
2215
+ const tokenPromise = new Promise((resolve) => {
2216
+ resolveToken = resolve;
2217
+ });
2218
+ const server = http.createServer((req, res) => {
2219
+ setCorsHeaders(res, copilotUrl);
2220
+ if (req.method === "OPTIONS") {
2221
+ res.writeHead(204);
2222
+ res.end();
2223
+ return;
2224
+ }
2225
+ if (req.method === "POST" && req.url === "/callback") {
2226
+ let body = "";
2227
+ req.on("data", (chunk) => {
2228
+ body += chunk;
2229
+ });
2230
+ req.on("end", () => {
2231
+ try {
2232
+ const data = JSON.parse(body);
2233
+ if (data.nonce !== nonce) {
2234
+ res.writeHead(403, { "Content-Type": "application/json" });
2235
+ res.end(
2236
+ JSON.stringify({
2237
+ error: "invalid_nonce",
2238
+ message: "Nonce does not match"
2239
+ })
2240
+ );
2241
+ return;
2242
+ }
2243
+ res.writeHead(200, { "Content-Type": "application/json" });
2244
+ res.end(JSON.stringify({ success: true }));
2245
+ resolveToken({ token: data.token, apiUrl: data.api_url });
2246
+ } catch {
2247
+ res.writeHead(400, { "Content-Type": "application/json" });
2248
+ res.end(JSON.stringify({ error: "invalid_body" }));
2249
+ }
2250
+ });
2251
+ return;
2252
+ }
2253
+ res.writeHead(404);
2254
+ res.end();
2255
+ });
2256
+ return { server, tokenPromise };
2257
+ }
2258
+ async function startWebAuth(mode, host) {
2259
+ const copilotUrl = resolveCopilotUrl(mode, host);
2260
+ const nonce = crypto.randomBytes(32).toString("hex");
2261
+ const { server, tokenPromise } = startCallbackServer(nonce, copilotUrl);
2262
+ let port;
2263
+ for (let attempt = 0; attempt < MAX_PORT_RETRIES; attempt++) {
2264
+ try {
2265
+ port = await listenOnRandomPort(server);
2266
+ break;
2267
+ } catch (err) {
2268
+ const isAddrInUse = err instanceof Error && "code" in err && err.code === "EADDRINUSE";
2269
+ if (!isAddrInUse || attempt === MAX_PORT_RETRIES - 1) {
2270
+ throw err;
2271
+ }
2272
+ }
2273
+ }
2274
+ if (port === void 0) {
2275
+ throw new Error("Failed to start local callback server");
2276
+ }
2277
+ const authUrl = `${copilotUrl}/cli/auth?port=${port}&nonce=${nonce}`;
2278
+ console.log("\n\u{1F511} Opening browser for authorization...\n");
2279
+ console.log(" Waiting for you to authorize in the browser.");
2280
+ console.log(" If the browser didn't open, visit:\n");
2281
+ console.log(` ${authUrl}
2282
+ `);
2283
+ openBrowser(authUrl);
2284
+ const stopBeacon = startBeaconAnimation();
2285
+ const cleanup = () => {
2286
+ stopBeacon();
2287
+ server.close();
2288
+ };
2289
+ const sigintHandler = () => {
2290
+ cleanup();
2291
+ process.exit(130);
2292
+ };
2293
+ process.on("SIGINT", sigintHandler);
2294
+ try {
2295
+ const result = await Promise.race([
2296
+ tokenPromise,
2297
+ new Promise(
2298
+ (_, reject) => setTimeout(() => reject(new Error("timeout")), TIMEOUT_MS)
2299
+ )
2300
+ ]);
2301
+ cleanup();
2302
+ process.removeListener("SIGINT", sigintHandler);
2303
+ const store6 = new SessionStore();
2304
+ const session = {
2305
+ accessToken: result.token,
2306
+ urn: "",
2307
+ origin: copilotUrl,
2308
+ mode,
2309
+ authType: "jwt",
2310
+ apiUrl: result.apiUrl,
2311
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
2312
+ };
2313
+ try {
2314
+ store6.save(session);
2315
+ } catch {
2316
+ console.warn(
2317
+ "\n \u26A0 Could not write credentials to ~/.bloque/session.json"
2318
+ );
2319
+ console.warn(" Printing token so you can save it manually:\n");
2320
+ console.log(result.token);
2321
+ }
2322
+ await portalAnimation(`Authenticated via ${copilotUrl}`);
2323
+ console.log(` Token stored in ~/.bloque/session.json`);
2324
+ console.log(" You can now use @bloque/cli commands.\n");
2325
+ return session;
2326
+ } catch (err) {
2327
+ cleanup();
2328
+ process.removeListener("SIGINT", sigintHandler);
2329
+ if (err instanceof Error && err.message === "timeout") {
2330
+ console.error("\n\u2717 Authorization timed out. Please try again.\n");
2331
+ process.exit(1);
2332
+ }
2333
+ throw err;
2334
+ }
2335
+ }
2336
+
2337
+ // src/commands/setup.ts
2093
2338
  var store5 = new SessionStore();
2094
2339
  var OTP_CHANNELS2 = [
2095
2340
  {
@@ -2267,11 +2512,14 @@ async function runOtpLogin(mode) {
2267
2512
  });
2268
2513
  await portalAnimation(`Connected as ${clients.urn ?? userAlias}`);
2269
2514
  }
2270
- var setupCommand = new Command5("setup").description("Set up Bloque MCP in your AI code agents").option("--jwt <token>", "JWT token for authentication (skips OTP)").option("--sandbox", "Use sandbox environment instead of production").action(async (opts) => {
2271
- const { jwt, sandbox } = opts;
2515
+ var setupCommand = new Command5("setup").description("Set up Bloque MCP in your AI code agents").option("--web", "Authenticate via browser").option("--host <url>", "Copilot app URL (only with --web)").option("--jwt <token>", "JWT token for authentication (skips OTP)").option("--sandbox", "Use sandbox environment instead of production").action(async (opts) => {
2516
+ const { web, host, jwt, sandbox } = opts;
2272
2517
  const mode = sandbox ? "sandbox" : "production";
2273
2518
  console.log("\n Bloque Setup Wizard\n");
2274
- if (jwt) {
2519
+ if (web) {
2520
+ const session = await startWebAuth(mode, host);
2521
+ store5.save(session);
2522
+ } else if (jwt) {
2275
2523
  store5.save({
2276
2524
  accessToken: jwt,
2277
2525
  urn: "",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bloque/cli",
3
- "version": "0.0.46",
3
+ "version": "0.0.47",
4
4
  "description": "Bloque CLI — authenticate and expose SDK capabilities as an MCP server",
5
5
  "type": "module",
6
6
  "bin": {