archbyte 0.3.1 → 0.3.3

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/bin/archbyte.js CHANGED
@@ -36,9 +36,10 @@ program
36
36
  .version(PKG_VERSION)
37
37
  .addHelpText('after', `
38
38
  Quick start:
39
- $ archbyte login Sign in
40
- $ archbyte init Configure your model provider
41
- $ archbyte run Analyze generate → serve
39
+ 1. Sign up at https://archbyte.heartbyte.io
40
+ 2. $ archbyte login Sign in from the CLI
41
+ 3. $ archbyte init Configure your model provider
42
+ 4. $ archbyte run Analyze → generate → serve
42
43
 
43
44
  https://archbyte.heartbyte.io
44
45
  Support: archbyte@heartbyte.io
@@ -5,6 +5,5 @@ export declare class GoogleProvider implements LLMProvider {
5
5
  constructor(apiKey: string);
6
6
  chat(params: ChatParams): Promise<LLMResponse>;
7
7
  stream(params: ChatParams): AsyncIterable<LLMChunk>;
8
- private toGeminiContents;
9
- private toGeminiFunctionDecl;
8
+ private toContents;
10
9
  }
@@ -1,43 +1,47 @@
1
- import { GoogleGenerativeAI, SchemaType, } from "@google/generative-ai";
1
+ import { GoogleGenAI } from "@google/genai";
2
2
  export class GoogleProvider {
3
3
  name = "google";
4
4
  client;
5
5
  constructor(apiKey) {
6
- this.client = new GoogleGenerativeAI(apiKey);
6
+ this.client = new GoogleGenAI({ apiKey });
7
7
  }
8
8
  async chat(params) {
9
- const model = this.client.getGenerativeModel({
10
- model: params.model ?? "gemini-2.5-pro",
11
- systemInstruction: params.system,
12
- ...(params.tools?.length
13
- ? {
14
- tools: [
15
- {
16
- functionDeclarations: params.tools.map((t) => this.toGeminiFunctionDecl(t)),
17
- },
18
- ],
19
- }
20
- : {}),
9
+ const model = params.model ?? "gemini-2.5-pro";
10
+ const response = await this.client.models.generateContent({
11
+ model,
12
+ contents: this.toContents(params.messages),
13
+ config: {
14
+ systemInstruction: params.system,
15
+ maxOutputTokens: params.maxTokens ?? 8192,
16
+ ...(params.tools?.length
17
+ ? {
18
+ tools: [
19
+ {
20
+ functionDeclarations: params.tools.map((t) => ({
21
+ name: t.name,
22
+ description: t.description,
23
+ parameters: t.input_schema,
24
+ })),
25
+ },
26
+ ],
27
+ }
28
+ : {}),
29
+ },
21
30
  });
22
- const contents = this.toGeminiContents(params.messages);
23
- const result = await model.generateContent({ contents });
24
- const response = result.response;
25
31
  const content = [];
26
32
  let hasToolCalls = false;
27
- for (const candidate of response.candidates ?? []) {
28
- for (const part of candidate.content?.parts ?? []) {
29
- if (part.text) {
30
- content.push({ type: "text", text: part.text });
31
- }
32
- if (part.functionCall) {
33
- hasToolCalls = true;
34
- content.push({
35
- type: "tool_use",
36
- id: `call_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`,
37
- name: part.functionCall.name,
38
- input: part.functionCall.args ?? {},
39
- });
40
- }
33
+ if (response.text) {
34
+ content.push({ type: "text", text: response.text });
35
+ }
36
+ if (response.functionCalls) {
37
+ for (const fc of response.functionCalls) {
38
+ hasToolCalls = true;
39
+ content.push({
40
+ type: "tool_use",
41
+ id: fc.id ?? `call_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`,
42
+ name: fc.name ?? "",
43
+ input: fc.args ?? {},
44
+ });
41
45
  }
42
46
  }
43
47
  return {
@@ -50,44 +54,49 @@ export class GoogleProvider {
50
54
  };
51
55
  }
52
56
  async *stream(params) {
53
- const model = this.client.getGenerativeModel({
54
- model: params.model ?? "gemini-2.5-pro",
55
- systemInstruction: params.system,
56
- ...(params.tools?.length
57
- ? {
58
- tools: [
59
- {
60
- functionDeclarations: params.tools.map((t) => this.toGeminiFunctionDecl(t)),
61
- },
62
- ],
63
- }
64
- : {}),
65
- });
66
- const contents = this.toGeminiContents(params.messages);
67
- const result = await model.generateContentStream({ contents });
68
- for await (const chunk of result.stream) {
69
- for (const candidate of chunk.candidates ?? []) {
70
- for (const part of candidate.content?.parts ?? []) {
71
- if (part.text) {
72
- yield { type: "text", text: part.text };
73
- }
74
- if (part.functionCall) {
75
- yield {
76
- type: "tool_use_start",
77
- toolName: part.functionCall.name,
78
- toolId: `call_${Date.now()}`,
79
- };
80
- yield {
81
- type: "tool_use_input",
82
- input: JSON.stringify(part.functionCall.args ?? {}),
83
- };
57
+ const model = params.model ?? "gemini-2.5-pro";
58
+ const stream = await this.client.models.generateContentStream({
59
+ model,
60
+ contents: this.toContents(params.messages),
61
+ config: {
62
+ systemInstruction: params.system,
63
+ maxOutputTokens: params.maxTokens ?? 8192,
64
+ ...(params.tools?.length
65
+ ? {
66
+ tools: [
67
+ {
68
+ functionDeclarations: params.tools.map((t) => ({
69
+ name: t.name,
70
+ description: t.description,
71
+ parameters: t.input_schema,
72
+ })),
73
+ },
74
+ ],
84
75
  }
76
+ : {}),
77
+ },
78
+ });
79
+ for await (const chunk of stream) {
80
+ if (chunk.text) {
81
+ yield { type: "text", text: chunk.text };
82
+ }
83
+ if (chunk.functionCalls) {
84
+ for (const fc of chunk.functionCalls) {
85
+ yield {
86
+ type: "tool_use_start",
87
+ toolName: fc.name ?? "",
88
+ toolId: fc.id ?? `call_${Date.now()}`,
89
+ };
90
+ yield {
91
+ type: "tool_use_input",
92
+ input: JSON.stringify(fc.args ?? {}),
93
+ };
85
94
  }
86
95
  }
87
96
  }
88
97
  yield { type: "done" };
89
98
  }
90
- toGeminiContents(messages) {
99
+ toContents(messages) {
91
100
  return messages.map((m) => {
92
101
  const parts = [];
93
102
  if (typeof m.content === "string") {
@@ -122,15 +131,4 @@ export class GoogleProvider {
122
131
  };
123
132
  });
124
133
  }
125
- toGeminiFunctionDecl(tool) {
126
- return {
127
- name: tool.name,
128
- description: tool.description,
129
- parameters: {
130
- type: SchemaType.OBJECT,
131
- properties: (tool.input_schema.properties ?? {}),
132
- required: tool.input_schema.required ?? [],
133
- },
134
- };
135
- }
136
134
  }
@@ -9,6 +9,8 @@ interface AnalyzeOptions {
9
9
  dryRun?: boolean;
10
10
  force?: boolean;
11
11
  dir?: string;
12
+ /** When true, skip "archbyte serve" in the Next steps (e.g. called from `archbyte run`) */
13
+ skipServeHint?: boolean;
12
14
  }
13
15
  export declare function handleAnalyze(options: AnalyzeOptions): Promise<void>;
14
16
  /**
@@ -63,7 +63,7 @@ export async function handleAnalyze(options) {
63
63
  progress.update(2, "Generating diagram...");
64
64
  await autoGenerate(rootDir, options);
65
65
  progress.done("Analysis complete");
66
- printSummary(analysis, duration, "static");
66
+ printSummary(analysis, duration, "static", { skipServeHint: options.skipServeHint });
67
67
  return;
68
68
  }
69
69
  // ─── Default mode: static context → LLM pipeline ───
@@ -248,7 +248,7 @@ export async function handleAnalyze(options) {
248
248
  writeSpec(rootDir, fbSpec);
249
249
  writeScanMetadata(rootDir, duration, "static-fallback");
250
250
  await autoGenerate(rootDir, options);
251
- printSummary(analysis, duration, "static");
251
+ printSummary(analysis, duration, "static", { skipServeHint: options.skipServeHint });
252
252
  }
253
253
  catch (fallbackErr) {
254
254
  console.error(chalk.red(` Static fallback also failed: ${fallbackErr instanceof Error ? fallbackErr.message : String(fallbackErr)}`));
@@ -292,7 +292,7 @@ export async function handleAnalyze(options) {
292
292
  agentCount: 5,
293
293
  durationMs: duration,
294
294
  });
295
- printSummary(analysis, duration, "pipeline");
295
+ printSummary(analysis, duration, "pipeline", { skipServeHint: options.skipServeHint });
296
296
  }
297
297
  // ─── Shared helpers ───
298
298
  function writeAnalysisStatus(rootDir, status, error) {
@@ -352,7 +352,7 @@ async function autoGenerate(rootDir, options) {
352
352
  console.error(chalk.gray("You can run 'archbyte generate' manually."));
353
353
  }
354
354
  }
355
- function printSummary(analysis, durationMs, mode) {
355
+ function printSummary(analysis, durationMs, mode, options) {
356
356
  const components = analysis.components ?? [];
357
357
  const connections = analysis.connections ?? [];
358
358
  console.log();
@@ -363,7 +363,9 @@ function printSummary(analysis, durationMs, mode) {
363
363
  console.log(chalk.gray(` Connections: ${connections.length}`));
364
364
  console.log();
365
365
  console.log(chalk.bold("Next"));
366
- console.log(` ${chalk.cyan("archbyte serve")} Open the interactive diagram`);
366
+ if (!options?.skipServeHint) {
367
+ console.log(` ${chalk.cyan("archbyte serve")} Open the interactive diagram`);
368
+ }
367
369
  console.log(` ${chalk.cyan("archbyte validate")} Check architecture fitness rules ${chalk.yellow("[Pro]")}`);
368
370
  console.log(` ${chalk.cyan("archbyte patrol")} Continuous architecture monitoring ${chalk.yellow("[Pro]")}`);
369
371
  console.log();
package/dist/cli/auth.js CHANGED
@@ -7,7 +7,9 @@ import { CONFIG_DIR, CREDENTIALS_PATH, API_BASE, CLI_CALLBACK_PORT, OAUTH_TIMEOU
7
7
  export async function handleLogin(provider) {
8
8
  console.log();
9
9
  console.log(chalk.bold.cyan("ArchByte Login"));
10
- console.log(chalk.gray("Sign in or create a free account to get started."));
10
+ console.log();
11
+ console.log(chalk.gray(" Don't have an account yet? Sign up first:"));
12
+ console.log(` ${chalk.bold.cyan("https://archbyte.heartbyte.io")}`);
11
13
  console.log();
12
14
  const existing = loadCredentials();
13
15
  if (existing && !isExpired(existing)) {
@@ -35,7 +37,6 @@ export async function handleLogin(provider) {
35
37
  }
36
38
  const selectedProvider = provider ?? "github";
37
39
  console.log(chalk.gray(`Opening browser for ${selectedProvider} sign-in...`));
38
- console.log(chalk.gray("Don't have an account? One will be created automatically."));
39
40
  console.log();
40
41
  try {
41
42
  const credentials = await startOAuthFlow(selectedProvider);
package/dist/cli/run.js CHANGED
@@ -13,6 +13,7 @@ export async function handleRun(options) {
13
13
  force: options.force,
14
14
  dryRun: options.dryRun,
15
15
  dir: options.dir,
16
+ skipServeHint: true,
16
17
  });
17
18
  if (options.dryRun)
18
19
  return;
package/dist/cli/serve.js CHANGED
@@ -75,7 +75,9 @@ export async function handleServe(options) {
75
75
  // Try to start, handle port conflicts interactively
76
76
  while (true) {
77
77
  try {
78
- console.log(chalk.green(`UI: http://localhost:${port}`));
78
+ const url = `http://localhost:${port}`;
79
+ console.log();
80
+ console.log(chalk.bold.green(` ➜ ${chalk.underline(url)}`));
79
81
  console.log();
80
82
  await startServer({
81
83
  name: projectName,
package/dist/cli/setup.js CHANGED
@@ -143,10 +143,14 @@ async function validateProviderSilent(providerName, apiKey, model) {
143
143
  return true;
144
144
  }
145
145
  catch (err) {
146
+ const msg = err instanceof Error ? err.message : String(err);
146
147
  if (process.env.ARCHBYTE_DEBUG) {
147
- const msg = err instanceof Error ? err.message : String(err);
148
148
  console.log(chalk.red(`\n Validation error: ${msg}`));
149
149
  }
150
+ // Quota/rate limit = key is valid, just exhausted
151
+ if (msg.includes("429") || msg.includes("RESOURCE_EXHAUSTED") || msg.includes("rate_limit") || msg.includes("quota")) {
152
+ return "quota";
153
+ }
150
154
  return false;
151
155
  }
152
156
  }
@@ -308,12 +312,17 @@ export async function handleSetup() {
308
312
  const validationModel = profiles[provider].model ?? resolveModel(provider, "fast");
309
313
  // Validate provider with spinner
310
314
  const s = spinner(`Validating credentials with ${validationModel}`);
311
- let isValid = await validateProviderSilent(provider, apiKey, validationModel);
312
- s.stop(isValid ? "valid" : "invalid", isValid ? "green" : "red");
313
- // Retry loop on failure
314
- if (!isValid) {
315
+ let result = await validateProviderSilent(provider, apiKey, validationModel);
316
+ if (result === "quota") {
317
+ s.stop("valid (quota exceeded key accepted)", "yellow");
318
+ }
319
+ else {
320
+ s.stop(result ? "valid" : "invalid", result ? "green" : "red");
321
+ }
322
+ // Retry loop on failure (not for quota — that means the key works)
323
+ if (result === false) {
315
324
  let retries = 0;
316
- while (!isValid && retries < 2) {
325
+ while (result === false && retries < 2) {
317
326
  if (!await confirm(" Retry?"))
318
327
  break;
319
328
  retries++;
@@ -323,10 +332,15 @@ export async function handleSetup() {
323
332
  console.log(chalk.green(` ✓ API key: ${maskKey(newKey)}`));
324
333
  }
325
334
  const s2 = spinner(`Validating credentials with ${validationModel}`);
326
- isValid = await validateProviderSilent(provider, profiles[provider].apiKey, validationModel);
327
- s2.stop(isValid ? "valid" : "invalid", isValid ? "green" : "red");
335
+ result = await validateProviderSilent(provider, profiles[provider].apiKey, validationModel);
336
+ if (result === "quota") {
337
+ s2.stop("valid (quota exceeded — key accepted)", "yellow");
338
+ }
339
+ else {
340
+ s2.stop(result ? "valid" : "invalid", result ? "green" : "red");
341
+ }
328
342
  }
329
- if (!isValid) {
343
+ if (result === false) {
330
344
  console.log(chalk.yellow(" Warning: credentials unverified. Saving anyway."));
331
345
  }
332
346
  }
@@ -393,7 +407,10 @@ export async function handleSetup() {
393
407
  if (hasClaude || hasCodex) {
394
408
  console.log(" " + chalk.cyan("archbyte mcp install") + " Use from your AI tool");
395
409
  }
396
- console.log();
410
+ if (result === false) {
411
+ console.log(chalk.yellow(" Warning: credentials unverified. Saving anyway."));
412
+ console.log();
413
+ }
397
414
  }
398
415
  function writeArchbyteReadme(archbyteDir) {
399
416
  const readmePath = path.join(archbyteDir, "README.md");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "archbyte",
3
- "version": "0.3.1",
3
+ "version": "0.3.3",
4
4
  "description": "ArchByte - See what agents build. As they build it.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -36,7 +36,7 @@
36
36
  },
37
37
  "dependencies": {
38
38
  "@anthropic-ai/sdk": "^0.74.0",
39
- "@google/generative-ai": "^0.24.1",
39
+ "@google/genai": "^1.41.0",
40
40
  "@modelcontextprotocol/sdk": "^1.26.0",
41
41
  "chalk": "^5.3.0",
42
42
  "chokidar": "^3.5.3",