@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.
- package/dist/bin.js +263 -15
- 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 (!
|
|
162
|
+
if (!stdout2.isTTY) {
|
|
158
163
|
console.log(`
|
|
159
164
|
\u25C6 ${message}
|
|
160
165
|
`);
|
|
161
166
|
return;
|
|
162
167
|
}
|
|
163
|
-
|
|
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
|
-
|
|
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 (
|
|
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: "",
|