archbyte 0.2.2 → 0.2.5

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 (48) hide show
  1. package/README.md +33 -5
  2. package/bin/archbyte.js +47 -7
  3. package/dist/agents/prompt-data.js +4 -4
  4. package/dist/agents/providers/router.js +1 -16
  5. package/dist/agents/runtime/types.d.ts +5 -1
  6. package/dist/agents/runtime/types.js +3 -3
  7. package/dist/agents/static/code-sampler.js +1 -4
  8. package/dist/agents/static/component-detector.js +1 -4
  9. package/dist/agents/static/event-detector.js +2 -1
  10. package/dist/agents/static/excluded-dirs.d.ts +3 -0
  11. package/dist/agents/static/excluded-dirs.js +11 -0
  12. package/dist/agents/static/file-tree-collector.js +1 -5
  13. package/dist/agents/tools/claude-code.js +2 -5
  14. package/dist/agents/tools/local-fs.js +2 -6
  15. package/dist/cli/analyze.d.ts +1 -0
  16. package/dist/cli/analyze.js +5 -5
  17. package/dist/cli/auth.js +8 -6
  18. package/dist/cli/config.d.ts +1 -0
  19. package/dist/cli/config.js +80 -61
  20. package/dist/cli/constants.d.ts +13 -0
  21. package/dist/cli/constants.js +20 -0
  22. package/dist/cli/diff.js +1 -1
  23. package/dist/cli/export.js +3 -3
  24. package/dist/cli/gate.js +3 -3
  25. package/dist/cli/generate.js +2 -1
  26. package/dist/cli/license-gate.js +5 -5
  27. package/dist/cli/mcp-server.d.ts +1 -0
  28. package/dist/cli/mcp-server.js +443 -0
  29. package/dist/cli/mcp.d.ts +1 -0
  30. package/dist/cli/mcp.js +102 -0
  31. package/dist/cli/patrol.js +4 -4
  32. package/dist/cli/run.d.ts +1 -0
  33. package/dist/cli/run.js +3 -7
  34. package/dist/cli/serve.js +3 -2
  35. package/dist/cli/setup.js +153 -72
  36. package/dist/cli/stats.js +1 -1
  37. package/dist/cli/ui.js +3 -3
  38. package/dist/cli/validate.js +2 -2
  39. package/dist/cli/version.d.ts +2 -0
  40. package/dist/cli/version.js +84 -0
  41. package/dist/cli/workflow.js +7 -7
  42. package/dist/cli/yaml-io.js +1 -1
  43. package/dist/server/src/index.js +52 -0
  44. package/package.json +4 -2
  45. package/ui/dist/assets/{index-Bl1r8zrI.css → index-0_XpUUZQ.css} +1 -1
  46. package/ui/dist/assets/index-BdfGbhpp.js +70 -0
  47. package/ui/dist/index.html +2 -2
  48. package/ui/dist/assets/index-CqbB6DOK.js +0 -70
@@ -1,9 +1,7 @@
1
1
  import * as fs from "fs";
2
- import * as path from "path";
3
2
  import chalk from "chalk";
4
- const CONFIG_DIR = path.join(process.env.HOME ?? process.env.USERPROFILE ?? ".", ".archbyte");
5
- const CONFIG_PATH = path.join(CONFIG_DIR, "config.json");
6
- const VALID_PROVIDERS = ["anthropic", "openai", "google", "ollama"];
3
+ import { CONFIG_DIR, CONFIG_PATH } from "./constants.js";
4
+ const VALID_PROVIDERS = ["anthropic", "openai", "google"];
7
5
  export async function handleConfig(options) {
8
6
  const [action, key, value] = options.args;
9
7
  if (!action || action === "show") {
@@ -13,7 +11,7 @@ export async function handleConfig(options) {
13
11
  if (action === "set") {
14
12
  if (!key || !value) {
15
13
  console.error(chalk.red("Usage: archbyte config set <key> <value>"));
16
- console.error(chalk.gray(" Keys: provider, api-key, ollama-url"));
14
+ console.error(chalk.gray(" Keys: provider, api-key, model"));
17
15
  process.exit(1);
18
16
  }
19
17
  setConfig(key, value);
@@ -32,10 +30,10 @@ export async function handleConfig(options) {
32
30
  return;
33
31
  }
34
32
  console.error(chalk.red(`Unknown action: ${action}`));
35
- console.error(chalk.gray(" archbyte config showshow current config"));
36
- console.error(chalk.gray(" archbyte config set <k> <v>set a config value"));
37
- console.error(chalk.gray(" archbyte config get <k>get a config value"));
38
- console.error(chalk.gray(" archbyte config pathshow config file path"));
33
+ console.error(chalk.gray(" archbyte config show show current config"));
34
+ console.error(chalk.gray(" archbyte config set <k> <v> set a config value"));
35
+ console.error(chalk.gray(" archbyte config get <k> get a config value"));
36
+ console.error(chalk.gray(" archbyte config path show config file path"));
39
37
  process.exit(1);
40
38
  }
41
39
  function loadConfig() {
@@ -57,75 +55,105 @@ function saveConfig(config) {
57
55
  }
58
56
  function showConfig() {
59
57
  const config = loadConfig();
58
+ const profiles = (config.profiles ?? {});
60
59
  console.log();
61
60
  console.log(chalk.bold.cyan("ArchByte Configuration"));
62
61
  console.log(chalk.gray(`Config file: ${CONFIG_PATH}`));
63
62
  console.log();
64
- if (!config.provider && !config.apiKey) {
65
- console.log(chalk.yellow("No configuration found. Set up with:"));
66
- console.log(chalk.gray(" archbyte config set provider anthropic"));
67
- console.log(chalk.gray(" archbyte config set api-key sk-ant-..."));
68
- console.log();
69
- console.log(chalk.gray("Or use environment variables:"));
70
- console.log(chalk.gray(" export ARCHBYTE_PROVIDER=anthropic"));
71
- console.log(chalk.gray(" export ARCHBYTE_API_KEY=sk-ant-..."));
63
+ const configured = Object.keys(profiles).filter((p) => profiles[p]?.apiKey);
64
+ if (!config.provider && !configured.length && !config.apiKey) {
65
+ console.log(chalk.yellow("No configuration found. Run:"));
66
+ console.log(chalk.gray(" archbyte init"));
72
67
  return;
73
68
  }
74
69
  console.log(` ${chalk.bold("provider")}: ${config.provider ?? chalk.gray("not set")}`);
75
- console.log(` ${chalk.bold("api-key")}: ${config.apiKey ? maskKey(config.apiKey) : chalk.gray("not set")}`);
76
- if (config.model) {
77
- console.log(` ${chalk.bold("model")}: ${config.model}`);
70
+ // Show profiles
71
+ if (configured.length > 0) {
72
+ console.log();
73
+ for (const name of configured) {
74
+ const p = profiles[name];
75
+ const active = name === config.provider ? chalk.green(" (active)") : "";
76
+ console.log(` ${chalk.bold(name)}${active}`);
77
+ console.log(` api-key: ${p.apiKey ? maskKey(p.apiKey) : chalk.gray("not set")}`);
78
+ if (p.model)
79
+ console.log(` model: ${p.model}`);
80
+ }
78
81
  }
79
- if (config.ollamaBaseUrl) {
80
- console.log(` ${chalk.bold("ollama-url")}: ${config.ollamaBaseUrl}`);
82
+ else if (config.apiKey) {
83
+ // Legacy flat config
84
+ console.log(` ${chalk.bold("api-key")}: ${maskKey(config.apiKey)}`);
85
+ if (config.model) {
86
+ console.log(` ${chalk.bold("model")}: ${config.model}`);
87
+ }
81
88
  }
82
89
  console.log();
83
90
  }
84
91
  function setConfig(key, value) {
85
92
  const config = loadConfig();
93
+ if (!config.profiles)
94
+ config.profiles = {};
95
+ const profiles = config.profiles;
86
96
  switch (key) {
87
97
  case "provider": {
88
98
  if (!VALID_PROVIDERS.includes(value)) {
89
99
  console.error(chalk.red(`Invalid provider: ${value}. Must be: ${VALID_PROVIDERS.join(", ")}`));
90
100
  process.exit(1);
91
101
  }
92
- const oldProvider = config.provider;
93
102
  config.provider = value;
94
- // Clear model override when switching providers model names are provider-specific
95
- if (oldProvider && oldProvider !== value && config.model) {
96
- delete config.model;
97
- console.log(chalk.yellow(`Cleared model override (was for ${oldProvider})`));
103
+ // Check if this provider has a stored profile
104
+ if (profiles[value]?.apiKey) {
105
+ console.log(chalk.green(`Switched to ${value} (credentials on file)`));
106
+ }
107
+ else {
108
+ console.log(chalk.yellow(`Switched to ${value}. Run archbyte init to configure API key.`));
98
109
  }
99
110
  break;
100
111
  }
101
112
  case "api-key":
102
113
  case "apiKey":
103
- case "key":
104
- config.apiKey = value;
114
+ case "key": {
115
+ const activeProvider = config.provider;
116
+ if (!activeProvider) {
117
+ console.error(chalk.red("Set a provider first: archbyte config set provider <name>"));
118
+ process.exit(1);
119
+ }
120
+ if (!profiles[activeProvider])
121
+ profiles[activeProvider] = {};
122
+ profiles[activeProvider].apiKey = value;
105
123
  break;
106
- case "model":
124
+ }
125
+ case "model": {
126
+ const activeProvider2 = config.provider;
127
+ if (!activeProvider2) {
128
+ console.error(chalk.red("Set a provider first: archbyte config set provider <name>"));
129
+ process.exit(1);
130
+ }
131
+ if (!profiles[activeProvider2])
132
+ profiles[activeProvider2] = {};
107
133
  if (value === "" || value === "default") {
108
- delete config.model;
134
+ delete profiles[activeProvider2].model;
109
135
  saveConfig(config);
110
136
  console.log(chalk.green("Cleared model override (will use provider defaults)"));
111
137
  return;
112
138
  }
113
- config.model = value;
114
- break;
115
- case "ollama-url":
116
- case "ollamaUrl":
117
- config.ollamaBaseUrl = value;
139
+ profiles[activeProvider2].model = value;
118
140
  break;
141
+ }
119
142
  default:
120
143
  console.error(chalk.red(`Unknown config key: ${key}`));
121
- console.error(chalk.gray(" Valid keys: provider, api-key, model, ollama-url"));
144
+ console.error(chalk.gray(" Valid keys: provider, api-key, model"));
122
145
  process.exit(1);
123
146
  }
124
147
  saveConfig(config);
125
- console.log(chalk.green(`Set ${key} = ${key.includes("key") ? maskKey(value) : value}`));
148
+ if (key !== "provider") {
149
+ console.log(chalk.green(`Set ${key} = ${key.includes("key") ? maskKey(value) : value}`));
150
+ }
126
151
  }
127
152
  function getConfig(key) {
128
153
  const config = loadConfig();
154
+ const profiles = (config.profiles ?? {});
155
+ const active = config.provider;
156
+ const profile = active ? profiles[active] : undefined;
129
157
  switch (key) {
130
158
  case "provider":
131
159
  console.log(config.provider ?? "");
@@ -133,14 +161,10 @@ function getConfig(key) {
133
161
  case "api-key":
134
162
  case "apiKey":
135
163
  case "key":
136
- console.log(config.apiKey ?? "");
164
+ console.log(profile?.apiKey ?? config.apiKey ?? "");
137
165
  break;
138
166
  case "model":
139
- console.log(config.model ?? "");
140
- break;
141
- case "ollama-url":
142
- case "ollamaUrl":
143
- console.log(config.ollamaBaseUrl ?? "");
167
+ console.log(profile?.model ?? config.model ?? "");
144
168
  break;
145
169
  default:
146
170
  console.error(chalk.red(`Unknown config key: ${key}`));
@@ -154,12 +178,21 @@ function maskKey(key) {
154
178
  }
155
179
  /**
156
180
  * Resolve the full ArchByteConfig from config file + env vars.
181
+ * Supports profiles (new) and legacy flat config (backward compat).
157
182
  * Env vars override config file.
158
183
  */
159
184
  export function resolveConfig() {
160
185
  const config = loadConfig();
161
186
  const provider = process.env.ARCHBYTE_PROVIDER ?? config.provider;
162
- const apiKey = process.env.ARCHBYTE_API_KEY ?? config.apiKey;
187
+ // Reject unknown providers (e.g. legacy "ollama")
188
+ if (provider && !VALID_PROVIDERS.includes(provider)) {
189
+ return null;
190
+ }
191
+ // Resolve API key + model from profiles first, then legacy flat keys
192
+ const profiles = (config.profiles ?? {});
193
+ const profile = provider ? profiles[provider] : undefined;
194
+ const apiKey = process.env.ARCHBYTE_API_KEY ?? profile?.apiKey ?? config.apiKey;
195
+ const model = process.env.ARCHBYTE_MODEL ?? profile?.model ?? config.model;
163
196
  // Auto-detect from known env vars if nothing explicit
164
197
  if (!provider && !apiKey) {
165
198
  if (process.env.ANTHROPIC_API_KEY) {
@@ -176,25 +209,11 @@ export function resolveConfig() {
176
209
  }
177
210
  return null;
178
211
  }
179
- if (!provider)
180
- return null;
181
- // Ollama doesn't need an API key
182
- if (provider === "ollama") {
183
- return {
184
- provider: "ollama",
185
- apiKey: "",
186
- model: process.env.ARCHBYTE_MODEL ?? config.model,
187
- ollamaBaseUrl: process.env.OLLAMA_BASE_URL ??
188
- config.ollamaBaseUrl ??
189
- "http://localhost:11434",
190
- };
191
- }
192
- if (!apiKey)
212
+ if (!provider || !apiKey)
193
213
  return null;
194
214
  return {
195
215
  provider,
196
216
  apiKey,
197
- model: process.env.ARCHBYTE_MODEL ?? config.model,
198
- ollamaBaseUrl: config.ollamaBaseUrl,
217
+ model: model,
199
218
  };
200
219
  }
@@ -0,0 +1,13 @@
1
+ export declare const API_BASE: string;
2
+ export declare const SITE_URL = "https://archbyte.heartbyte.io";
3
+ export declare const DEFAULT_PORT = 3847;
4
+ export declare const CLI_CALLBACK_PORT = 19274;
5
+ export declare const CONFIG_DIR: string;
6
+ export declare const CONFIG_PATH: string;
7
+ export declare const CREDENTIALS_PATH: string;
8
+ /** Project-local .archbyte directory name */
9
+ export declare const PROJECT_DIR = ".archbyte";
10
+ /** Timeout for non-critical network checks (license, version, gate) */
11
+ export declare const NETWORK_TIMEOUT_MS = 5000;
12
+ /** Timeout for OAuth callback server */
13
+ export declare const OAUTH_TIMEOUT_MS = 60000;
@@ -0,0 +1,20 @@
1
+ // Shared constants for the ArchByte CLI.
2
+ // Single source of truth for URLs, ports, paths, and timeouts.
3
+ import * as path from "path";
4
+ // ─── API ───
5
+ export const API_BASE = process.env.ARCHBYTE_API_URL ?? "https://api.heartbyte.io";
6
+ export const SITE_URL = "https://archbyte.heartbyte.io";
7
+ // ─── Ports ───
8
+ export const DEFAULT_PORT = 3847;
9
+ export const CLI_CALLBACK_PORT = 19274;
10
+ // ─── Paths ───
11
+ export const CONFIG_DIR = path.join(process.env.HOME ?? process.env.USERPROFILE ?? ".", ".archbyte");
12
+ export const CONFIG_PATH = path.join(CONFIG_DIR, "config.json");
13
+ export const CREDENTIALS_PATH = path.join(CONFIG_DIR, "credentials.json");
14
+ /** Project-local .archbyte directory name */
15
+ export const PROJECT_DIR = ".archbyte";
16
+ // ─── Timeouts ───
17
+ /** Timeout for non-critical network checks (license, version, gate) */
18
+ export const NETWORK_TIMEOUT_MS = 5000;
19
+ /** Timeout for OAuth callback server */
20
+ export const OAUTH_TIMEOUT_MS = 60_000;
package/dist/cli/diff.js CHANGED
@@ -17,7 +17,7 @@ export async function handleDiff(options) {
17
17
  const currentArch = loadArchitectureFile(currentPath);
18
18
  const projectName = process.cwd().split("/").pop() || "project";
19
19
  console.log();
20
- console.log(chalk.bold.cyan(`⚡ ArchByte Diff ${projectName}`));
20
+ console.log(chalk.bold.cyan(`⚡ ArchByte Diff: ${projectName}`));
21
21
  console.log(chalk.gray(` Baseline: ${options.baseline}`));
22
22
  console.log(chalk.gray(` Current: ${options.current || ".archbyte/architecture.json"}`));
23
23
  console.log();
@@ -104,7 +104,7 @@ function exportMarkdown(arch) {
104
104
  const realNodes = arch.nodes;
105
105
  const projectName = process.cwd().split("/").pop() || "project";
106
106
  const lines = [];
107
- lines.push(`# Architecture ${projectName}`);
107
+ lines.push(`# Architecture: ${projectName}`);
108
108
  lines.push("");
109
109
  lines.push(`> Generated by ArchByte on ${new Date().toISOString().slice(0, 10)}`);
110
110
  lines.push("");
@@ -127,7 +127,7 @@ function exportMarkdown(arch) {
127
127
  for (const node of realNodes) {
128
128
  const tech = node.techStack && node.techStack.length > 0
129
129
  ? node.techStack.join(", ")
130
- : "";
130
+ : "-";
131
131
  lines.push(`| ${node.label} | ${node.type} | ${node.layer} | ${tech} |`);
132
132
  }
133
133
  lines.push("");
@@ -145,7 +145,7 @@ function exportMarkdown(arch) {
145
145
  for (const edge of arch.edges) {
146
146
  const src = nodeMap.get(edge.source)?.label || edge.source;
147
147
  const tgt = nodeMap.get(edge.target)?.label || edge.target;
148
- const label = edge.label ? ` ${edge.label}` : "";
148
+ const label = edge.label ? `: ${edge.label}` : "";
149
149
  lines.push(`- **${src}** → **${tgt}**${label}`);
150
150
  }
151
151
  }
package/dist/cli/gate.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { loadCredentials, cacheVerifiedTier, resetOfflineActions, checkOfflineAction } from "./auth.js";
2
- const API_BASE = process.env.ARCHBYTE_API_URL ?? "https://api.heartbyte.io";
2
+ import { API_BASE, NETWORK_TIMEOUT_MS } from "./constants.js";
3
3
  /**
4
4
  * Auth gate command for Claude Code integration.
5
5
  *
@@ -48,7 +48,7 @@ async function checkGate(action) {
48
48
  "Content-Type": "application/json",
49
49
  },
50
50
  body: JSON.stringify({ action }),
51
- signal: AbortSignal.timeout(5000),
51
+ signal: AbortSignal.timeout(NETWORK_TIMEOUT_MS),
52
52
  });
53
53
  if (res.status === 401) {
54
54
  const result = {
@@ -120,7 +120,7 @@ async function recordScan() {
120
120
  "Content-Type": "application/json",
121
121
  },
122
122
  body: JSON.stringify({}),
123
- signal: AbortSignal.timeout(5000),
123
+ signal: AbortSignal.timeout(NETWORK_TIMEOUT_MS),
124
124
  });
125
125
  console.log(JSON.stringify({ recorded: true }));
126
126
  }
@@ -4,6 +4,7 @@ import chalk from "chalk";
4
4
  import { generateArchitecture } from "../server/src/generator/index.js";
5
5
  import { loadSpec, specToAnalysis } from "./yaml-io.js";
6
6
  import { spinner } from "./ui.js";
7
+ import { DEFAULT_PORT } from "./constants.js";
7
8
  /**
8
9
  * Generate excalidraw diagram from analysis JSON
9
10
  */
@@ -233,7 +234,7 @@ export async function handleGenerate(options) {
233
234
  console.log();
234
235
  console.log(chalk.bold("Next steps:"));
235
236
  console.log(chalk.gray(` 1. Run ${chalk.cyan("archbyte serve")} to start the visualization server`));
236
- console.log(chalk.gray(` 2. Open ${chalk.cyan("http://localhost:3847")} to view and adjust the diagram`));
237
+ console.log(chalk.gray(` 2. Open ${chalk.cyan(`http://localhost:${DEFAULT_PORT}`)} to view and adjust the diagram`));
237
238
  console.log();
238
239
  }
239
240
  catch (error) {
@@ -1,6 +1,6 @@
1
1
  import chalk from "chalk";
2
2
  import { loadCredentials, cacheVerifiedTier, resetOfflineActions, checkOfflineAction } from "./auth.js";
3
- const API_BASE = process.env.ARCHBYTE_API_URL ?? "https://api.heartbyte.io";
3
+ import { API_BASE, NETWORK_TIMEOUT_MS } from "./constants.js";
4
4
  /**
5
5
  * Pre-flight license check. Must be called before scan/analyze/generate.
6
6
  *
@@ -22,10 +22,10 @@ export async function requireLicense(action) {
22
22
  console.error();
23
23
  console.error(chalk.red("Authentication required."));
24
24
  console.error();
25
- console.error(chalk.gray("Sign in to use ArchByte:"));
25
+ console.error(chalk.gray("Sign in or create a free account:"));
26
26
  console.error(chalk.gray(" archbyte login"));
27
27
  console.error();
28
- console.error(chalk.gray("Basic tier includes unlimited scans."));
28
+ console.error(chalk.gray("Free tier includes unlimited scans. No credit card required."));
29
29
  process.exit(1);
30
30
  }
31
31
  // Token expired locally
@@ -44,7 +44,7 @@ export async function requireLicense(action) {
44
44
  "Content-Type": "application/json",
45
45
  },
46
46
  body: JSON.stringify({ action }),
47
- signal: AbortSignal.timeout(5000),
47
+ signal: AbortSignal.timeout(NETWORK_TIMEOUT_MS),
48
48
  });
49
49
  if (res.status === 401) {
50
50
  console.error();
@@ -112,7 +112,7 @@ export async function recordUsage(meta) {
112
112
  "Content-Type": "application/json",
113
113
  },
114
114
  body: JSON.stringify(meta),
115
- signal: AbortSignal.timeout(5000),
115
+ signal: AbortSignal.timeout(NETWORK_TIMEOUT_MS),
116
116
  });
117
117
  }
118
118
  catch {
@@ -0,0 +1 @@
1
+ export declare function startMcpServer(): Promise<void>;