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 +8 -0
- package/README.zh-CN.md +8 -0
- package/dist/cli/awel-config.d.ts +6 -0
- package/dist/cli/awel-config.js +20 -0
- package/dist/cli/babel-setup.js +2 -19
- package/dist/cli/index.js +2 -0
- package/dist/cli/onboarding.d.ts +1 -0
- package/dist/cli/onboarding.js +58 -0
- package/dist/cli/providers/registry.d.ts +12 -0
- package/dist/cli/providers/registry.js +41 -0
- package/dist/host/host.js +2 -8
- package/package.json +1 -1
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,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
|
+
}
|
package/dist/cli/babel-setup.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { existsSync, readFileSync, writeFileSync
|
|
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:
|
|
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
|
|