awel 0.1.1 → 0.1.2

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/README.md CHANGED
@@ -12,9 +12,17 @@ AI-powered development overlay for Next.js. Awel runs a proxy in front of your d
12
12
  # Skip if you're already in a Next.js app
13
13
  npx create-next-app@latest my-app && cd my-app
14
14
 
15
+ # Set up at least one AI provider (pick one):
16
+ export ANTHROPIC_API_KEY="sk-ant-..." # Anthropic API
17
+ export OPENAI_API_KEY="sk-..." # OpenAI
18
+ export GOOGLE_GENERATIVE_AI_API_KEY="..." # Google AI
19
+ # Or install the Claude CLI: https://docs.anthropic.com/en/docs/claude-code
20
+
15
21
  npx awel dev
16
22
  ```
17
23
 
24
+ Awel needs at least one configured provider to function. See [Supported Models](#supported-models) for the full list.
25
+
18
26
  This starts Awel on port 3001 and proxies your Next.js dev server on port 3000. Open `http://localhost:3001` to see your app with the Awel overlay.
19
27
 
20
28
  ### Options
package/README.zh-CN.md CHANGED
@@ -12,9 +12,17 @@
12
12
  # 如果你已经在一个 Next.js 项目中,可以跳过这一步
13
13
  npx create-next-app@latest my-app && cd my-app
14
14
 
15
+ # 至少配置一个 AI 服务商(任选其一):
16
+ export ANTHROPIC_API_KEY="sk-ant-..." # Anthropic API
17
+ export OPENAI_API_KEY="sk-..." # OpenAI
18
+ export GOOGLE_GENERATIVE_AI_API_KEY="..." # Google AI
19
+ # 或安装 Claude CLI:https://docs.anthropic.com/en/docs/claude-code
20
+
15
21
  npx awel dev
16
22
  ```
17
23
 
24
+ Awel 需要至少一个已配置的服务商才能运行。完整列表见[支持的模型](#支持的模型)。
25
+
18
26
  Awel 会在端口 3001 启动,并代理运行在端口 3000 的 Next.js 开发服务器。打开 `http://localhost:3001` 即可看到带有 Awel 浮层的应用。
19
27
 
20
28
  ### 选项
@@ -0,0 +1,6 @@
1
+ export interface AwelConfig {
2
+ babelPlugin?: boolean;
3
+ onboarded?: boolean;
4
+ }
5
+ export declare function readAwelConfig(projectCwd: string): AwelConfig;
6
+ export declare function writeAwelConfig(projectCwd: string, config: AwelConfig): void;
@@ -0,0 +1,20 @@
1
+ import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'fs';
2
+ import { join } from 'path';
3
+ export function readAwelConfig(projectCwd) {
4
+ const configPath = join(projectCwd, '.awel', 'config.json');
5
+ if (!existsSync(configPath))
6
+ return {};
7
+ try {
8
+ return JSON.parse(readFileSync(configPath, 'utf-8'));
9
+ }
10
+ catch {
11
+ return {};
12
+ }
13
+ }
14
+ export function writeAwelConfig(projectCwd, config) {
15
+ const dir = join(projectCwd, '.awel');
16
+ if (!existsSync(dir)) {
17
+ mkdirSync(dir, { recursive: true });
18
+ }
19
+ writeFileSync(join(dir, 'config.json'), JSON.stringify(config, null, 2) + '\n', 'utf-8');
20
+ }
@@ -1,6 +1,7 @@
1
- import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'fs';
1
+ import { existsSync, readFileSync, writeFileSync } from 'fs';
2
2
  import { join, dirname } from 'path';
3
3
  import { fileURLToPath } from 'url';
4
+ import { readAwelConfig, writeAwelConfig } from './awel-config.js';
4
5
  const __dirname = dirname(fileURLToPath(import.meta.url));
5
6
  const BABEL_CONFIG_FILES = [
6
7
  'babel.config.js',
@@ -37,24 +38,6 @@ function findExistingBabelConfig(projectCwd) {
37
38
  return 'package.json';
38
39
  return null;
39
40
  }
40
- function readAwelConfig(projectCwd) {
41
- const configPath = join(projectCwd, '.awel', 'config.json');
42
- if (!existsSync(configPath))
43
- return {};
44
- try {
45
- return JSON.parse(readFileSync(configPath, 'utf-8'));
46
- }
47
- catch {
48
- return {};
49
- }
50
- }
51
- function writeAwelConfig(projectCwd, config) {
52
- const dir = join(projectCwd, '.awel');
53
- if (!existsSync(dir)) {
54
- mkdirSync(dir, { recursive: true });
55
- }
56
- writeFileSync(join(dir, 'config.json'), JSON.stringify(config, null, 2) + '\n', 'utf-8');
57
- }
58
41
  // ANSI 256-color helpers — darker shades that stay visible on light backgrounds
59
42
  const bold = (s) => `\x1b[1m${s}\x1b[22m`;
60
43
  const dim = (s) => `\x1b[2m${s}\x1b[22m`;
package/dist/cli/index.js CHANGED
@@ -3,6 +3,7 @@ import { startServer } from './server.js';
3
3
  import { AWEL_PORT, USER_APP_PORT } from './config.js';
4
4
  import { setVerbose } from './verbose.js';
5
5
  import { ensureBabelPlugin } from './babel-setup.js';
6
+ import { ensureProvider } from './onboarding.js';
6
7
  import { awel } from './logger.js';
7
8
  import { spawnDevServer } from './subprocess.js';
8
9
  program
@@ -18,6 +19,7 @@ program
18
19
  const targetPort = parseInt(options.port, 10);
19
20
  if (options.verbose)
20
21
  setVerbose(true);
22
+ await ensureProvider(process.cwd());
21
23
  await ensureBabelPlugin(process.cwd());
22
24
  awel.log('🌟 Starting Awel...');
23
25
  awel.log(` Target app port: ${targetPort}`);
@@ -0,0 +1 @@
1
+ export declare function ensureProvider(projectCwd: string): Promise<void>;
@@ -0,0 +1,58 @@
1
+ import { readAwelConfig } from './awel-config.js';
2
+ import { getAvailableProviders, PROVIDER_ENV_KEYS, PROVIDER_LABELS } from './providers/registry.js';
3
+ import { awel } from './logger.js';
4
+ function printSetupInstructions() {
5
+ awel.log('No LLM providers are configured.');
6
+ awel.log('Awel needs at least one AI provider to function.');
7
+ awel.log('');
8
+ awel.log('Set up a provider by exporting its API key:');
9
+ awel.log('');
10
+ // List all providers with their setup commands
11
+ for (const [provider, envKey] of Object.entries(PROVIDER_ENV_KEYS)) {
12
+ const label = PROVIDER_LABELS[provider] ?? provider;
13
+ awel.log(` ${label}`);
14
+ awel.log(` export ${envKey}="..."`);
15
+ awel.log('');
16
+ }
17
+ // Claude Code is special — no env var, needs CLI install
18
+ const label = PROVIDER_LABELS['claude-code'] ?? 'Claude Code';
19
+ awel.log(` ${label}`);
20
+ awel.log(' Install the Claude CLI: https://docs.anthropic.com/en/docs/claude-code');
21
+ awel.log('');
22
+ awel.log('Then run `awel dev` again.');
23
+ }
24
+ export async function ensureProvider(projectCwd) {
25
+ const config = readAwelConfig(projectCwd);
26
+ const providers = getAvailableProviders();
27
+ const available = providers.filter(p => p.available);
28
+ const isFirstRun = !config.onboarded;
29
+ if (isFirstRun && available.length > 0) {
30
+ // First run with providers available — show welcome
31
+ awel.log('');
32
+ awel.log('Welcome to Awel!');
33
+ awel.log('AI-powered development overlay for Next.js');
34
+ awel.log('');
35
+ awel.log(`\u2714 ${available.length} provider${available.length === 1 ? '' : 's'} available:`);
36
+ for (const p of available) {
37
+ awel.log(` \u25CF ${p.label}`);
38
+ }
39
+ awel.log('');
40
+ printSetupInstructions();
41
+ process.exit(1);
42
+ }
43
+ if (isFirstRun && available.length === 0) {
44
+ // First run with NO providers — show welcome + instructions, exit
45
+ awel.log('');
46
+ awel.log('Welcome to Awel!');
47
+ awel.log('AI-powered development overlay for Next.js');
48
+ awel.log('');
49
+ printSetupInstructions();
50
+ process.exit(1);
51
+ }
52
+ if (!isFirstRun && available.length === 0) {
53
+ // Subsequent run with NO providers — instructions only, exit
54
+ printSetupInstructions();
55
+ process.exit(1);
56
+ }
57
+ // Subsequent run with providers available — silent pass-through
58
+ }
@@ -1,5 +1,17 @@
1
1
  import type { StreamProvider, ModelDefinition } from './types.js';
2
2
  export declare const PROVIDER_ENV_KEYS: Record<string, string>;
3
+ export declare const PROVIDER_LABELS: Record<string, string>;
4
+ export interface ProviderAvailability {
5
+ provider: string;
6
+ label: string;
7
+ available: boolean;
8
+ envVar: string | null;
9
+ }
10
+ /**
11
+ * Returns deduplicated provider availability info.
12
+ * Checks each provider once (claude-code via binary check, others via env var).
13
+ */
14
+ export declare function getAvailableProviders(): ProviderAvailability[];
3
15
  export declare function resolveProvider(modelId: string): {
4
16
  provider: StreamProvider;
5
17
  modelProvider: string;
@@ -59,6 +59,47 @@ function isClaudeBinaryAvailable() {
59
59
  }
60
60
  return _claudeBinaryAvailable;
61
61
  }
62
+ // ─── Provider Availability ────────────────────────────────────
63
+ export const PROVIDER_LABELS = {
64
+ 'claude-code': 'Claude Code',
65
+ anthropic: 'Anthropic API',
66
+ openai: 'OpenAI',
67
+ 'google-ai': 'Google AI',
68
+ 'vercel-gateway': 'Vercel AI Gateway',
69
+ qwen: 'Qwen',
70
+ minimax: 'MiniMax',
71
+ };
72
+ /**
73
+ * Returns deduplicated provider availability info.
74
+ * Checks each provider once (claude-code via binary check, others via env var).
75
+ */
76
+ export function getAvailableProviders() {
77
+ const seen = new Set();
78
+ const result = [];
79
+ for (const model of MODEL_CATALOG) {
80
+ if (seen.has(model.provider))
81
+ continue;
82
+ seen.add(model.provider);
83
+ const label = PROVIDER_LABELS[model.provider] ?? model.provider;
84
+ if (model.provider === 'claude-code') {
85
+ result.push({
86
+ provider: model.provider,
87
+ label,
88
+ available: isClaudeBinaryAvailable(),
89
+ envVar: null,
90
+ });
91
+ continue;
92
+ }
93
+ const envKey = PROVIDER_ENV_KEYS[model.provider];
94
+ result.push({
95
+ provider: model.provider,
96
+ label,
97
+ available: envKey ? !!process.env[envKey] : true,
98
+ envVar: envKey ?? null,
99
+ });
100
+ }
101
+ return result;
102
+ }
62
103
  // ─── Provider Resolution ─────────────────────────────────────
63
104
  export function resolveProvider(modelId) {
64
105
  const model = MODEL_CATALOG.find(m => m.id === modelId);
package/dist/host/host.js CHANGED
@@ -37,7 +37,8 @@
37
37
  justify-content: center;
38
38
  gap: 8px;
39
39
  background: var(--bg);
40
- border: 1px solid var(--border);
40
+ border: none;
41
+ border-radius: 8px;
41
42
  color: var(--fg);
42
43
  font-family: inherit;
43
44
  font-size: 13px;
@@ -49,24 +50,18 @@
49
50
 
50
51
  .awel-trigger {
51
52
  padding: 10px 16px;
52
- border-radius: 8px 0 0 8px;
53
53
  }
54
54
 
55
55
  .awel-inspector-btn {
56
56
  padding: 10px 12px;
57
- border-radius: 0;
58
- border-left: none;
59
57
  }
60
58
 
61
59
  .awel-screenshot-btn {
62
60
  padding: 10px 12px;
63
- border-radius: 0 8px 8px 0;
64
- border-left: none;
65
61
  }
66
62
 
67
63
  .awel-trigger:hover, .awel-inspector-btn:hover, .awel-screenshot-btn:hover {
68
64
  background: var(--hover-bg);
69
- border-color: var(--hover-border);
70
65
  }
71
66
 
72
67
  .awel-trigger:active, .awel-inspector-btn:active, .awel-screenshot-btn:active {
@@ -75,7 +70,6 @@
75
70
 
76
71
  .awel-inspector-btn.active {
77
72
  background: var(--active-bg);
78
- border-color: var(--active-border);
79
73
  color: var(--fg);
80
74
  }
81
75
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "awel",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "bin": {