archbyte 0.3.5 → 0.4.0

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/ui.js CHANGED
@@ -111,13 +111,12 @@ export function select(prompt, options) {
111
111
  cleanup();
112
112
  resolve(selected);
113
113
  }
114
- else if (data === "\x03") {
115
- // Ctrl+C only — clean exit
114
+ else if (data === "\x03" || data === "q") {
115
+ // Ctrl+C or q — clean exit
116
116
  cleanup();
117
117
  process.stdout.write("\n");
118
118
  process.exit(0);
119
119
  }
120
- // All other keys (including q, arrows, etc.) are ignored
121
120
  };
122
121
  function cleanup() {
123
122
  stdin.removeListener("data", onData);
@@ -202,8 +201,8 @@ export function confirm(prompt) {
202
201
  process.stdout.write("n\n");
203
202
  resolve(false);
204
203
  }
205
- else if (data === "\x03") {
206
- // Ctrl+C only
204
+ else if (data === "\x03" || data === "q") {
205
+ // Ctrl+C or q — clean exit
207
206
  process.stdout.write("\n");
208
207
  process.exit(0);
209
208
  }
@@ -216,3 +215,58 @@ export function confirm(prompt) {
216
215
  stdin.on("data", onData);
217
216
  });
218
217
  }
218
+ /**
219
+ * Text input prompt. Returns the entered string.
220
+ * Non-TTY fallback: returns empty string.
221
+ *
222
+ * @param mask - If true, replaces each character with * (for passwords).
223
+ */
224
+ export function textInput(prompt, opts) {
225
+ if (!process.stdout.isTTY) {
226
+ console.log(` ${prompt}: `);
227
+ return Promise.resolve("");
228
+ }
229
+ return new Promise((resolve) => {
230
+ process.stdout.write(` ${prompt}: `);
231
+ const stdin = process.stdin;
232
+ const wasRaw = stdin.isRaw;
233
+ stdin.setRawMode(true);
234
+ stdin.resume();
235
+ stdin.setEncoding("utf8");
236
+ let value = "";
237
+ const onData = (data) => {
238
+ if (data === "\r" || data === "\n") {
239
+ // Enter — submit
240
+ stdin.removeListener("data", onData);
241
+ stdin.setRawMode(wasRaw ?? false);
242
+ stdin.pause();
243
+ process.stdout.write("\n");
244
+ resolve(value);
245
+ }
246
+ else if (data === "\x03") {
247
+ // Ctrl+C
248
+ stdin.removeListener("data", onData);
249
+ stdin.setRawMode(wasRaw ?? false);
250
+ stdin.pause();
251
+ process.stdout.write("\n");
252
+ process.exit(0);
253
+ }
254
+ else if (data === "\x7f" || data === "\b") {
255
+ // Backspace
256
+ if (value.length > 0) {
257
+ value = value.slice(0, -1);
258
+ process.stdout.write("\b \b");
259
+ }
260
+ }
261
+ else if (data.startsWith("\x1b")) {
262
+ // Ignore escape sequences
263
+ }
264
+ else if (data >= " ") {
265
+ // Printable character
266
+ value += data;
267
+ process.stdout.write(opts?.mask ? "*" : data);
268
+ }
269
+ };
270
+ stdin.on("data", onData);
271
+ });
272
+ }
@@ -379,12 +379,21 @@ function createHttpServer() {
379
379
  }
380
380
  const params = new URL(url, `http://localhost:${config.port}`).searchParams;
381
381
  const format = params.get("format") || "mermaid";
382
- const supported = ["mermaid", "markdown", "json", "plantuml", "dot"];
382
+ const supported = ["mermaid", "markdown", "json", "plantuml", "dot", "html"];
383
383
  if (!supported.includes(format)) {
384
384
  res.writeHead(400, { "Content-Type": "application/json" });
385
385
  res.end(JSON.stringify({ error: `Unknown format: ${format}. Supported: ${supported.join(", ")}` }));
386
386
  return;
387
387
  }
388
+ // HTML export requires Pro tier
389
+ if (format === "html") {
390
+ const license = loadLicenseInfo();
391
+ if (license.tier !== "premium") {
392
+ res.writeHead(403, { "Content-Type": "application/json" });
393
+ res.end(JSON.stringify({ error: "HTML export requires a Pro subscription." }));
394
+ return;
395
+ }
396
+ }
388
397
  const realNodes = arch.nodes;
389
398
  const nodeMap = new Map();
390
399
  for (const nd of realNodes)
@@ -515,6 +524,43 @@ function createHttpServer() {
515
524
  ext = "json";
516
525
  break;
517
526
  }
527
+ case "html": {
528
+ // Build self-contained interactive HTML file
529
+ const uiAssetsDir = path.join(UI_DIST, "assets");
530
+ if (!existsSync(uiAssetsDir)) {
531
+ res.writeHead(500, { "Content-Type": "application/json" });
532
+ res.end(JSON.stringify({ error: "UI build not found. Run: npm run build:ui" }));
533
+ return;
534
+ }
535
+ const { readdirSync } = await import("fs");
536
+ const uiAssetFiles = readdirSync(uiAssetsDir);
537
+ let cssInline = "";
538
+ for (const f of uiAssetFiles.filter((f) => f.endsWith(".css"))) {
539
+ cssInline += readFileSync(path.join(uiAssetsDir, f), "utf-8") + "\n";
540
+ }
541
+ let jsInline = "";
542
+ for (const f of uiAssetFiles.filter((f) => f.endsWith(".js"))) {
543
+ jsInline += readFileSync(path.join(uiAssetsDir, f), "utf-8") + "\n";
544
+ }
545
+ content = `<!DOCTYPE html>
546
+ <html lang="en" data-theme="dark">
547
+ <head>
548
+ <meta charset="UTF-8">
549
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
550
+ <title>${config.name} - ArchByte Architecture</title>
551
+ <style>${cssInline}</style>
552
+ </head>
553
+ <body>
554
+ <script>try{if(localStorage.getItem('archbyte-theme')==='light')document.documentElement.setAttribute('data-theme','light')}catch(e){}</script>
555
+ <div id="root"></div>
556
+ <script>window.__ARCHBYTE_DATA__ = ${JSON.stringify(arch)};</script>
557
+ <script type="module">${jsInline}</script>
558
+ </body>
559
+ </html>`;
560
+ contentType = "text/html";
561
+ ext = "html";
562
+ break;
563
+ }
518
564
  default: { // markdown
519
565
  const lines = [`# Architecture — ${config.name}`, "", `> Generated by ArchByte on ${new Date().toISOString().slice(0, 10)}`, ""];
520
566
  lines.push("## Summary", "");
@@ -1343,7 +1389,15 @@ function loadLicenseInfo() {
1343
1389
  const credPath = path.join(home, ".archbyte", "credentials.json");
1344
1390
  if (!existsSync(credPath))
1345
1391
  return defaults;
1346
- const creds = JSON.parse(readFileSync(credPath, "utf-8"));
1392
+ const raw = JSON.parse(readFileSync(credPath, "utf-8"));
1393
+ // Multi-account format (version 2): extract active account
1394
+ let creds;
1395
+ if (raw?.version === 2 && raw.accounts && typeof raw.active === "string") {
1396
+ creds = raw.accounts[raw.active] ?? {};
1397
+ }
1398
+ else {
1399
+ creds = raw;
1400
+ }
1347
1401
  if (!creds.token)
1348
1402
  return defaults;
1349
1403
  // Check expiry
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "archbyte",
3
- "version": "0.3.5",
3
+ "version": "0.4.0",
4
4
  "description": "ArchByte - See what agents build. As they build it.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -45,6 +45,9 @@
45
45
  "openai": "^6.19.0",
46
46
  "zod": "^4.3.6"
47
47
  },
48
+ "optionalDependencies": {
49
+ "@anthropic-ai/claude-agent-sdk": "^0.1.77"
50
+ },
48
51
  "devDependencies": {
49
52
  "@types/js-yaml": "^4.0.9",
50
53
  "@types/node": "^20.0.0",