@amodalai/amodal 0.3.24 → 0.3.25

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@amodalai/amodal",
3
- "version": "0.3.24",
3
+ "version": "0.3.25",
4
4
  "description": "Amodal CLI",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -30,12 +30,12 @@
30
30
  "semver": "^7.6.0",
31
31
  "yargs": "^17.7.2",
32
32
  "zod": "^4.3.6",
33
- "@amodalai/types": "0.3.24",
34
- "@amodalai/core": "0.3.24",
35
- "@amodalai/db": "0.3.24",
36
- "@amodalai/runtime": "0.3.24",
37
- "@amodalai/studio": "0.3.24",
38
- "@amodalai/runtime-app": "0.3.24"
33
+ "@amodalai/types": "0.3.25",
34
+ "@amodalai/core": "0.3.25",
35
+ "@amodalai/db": "0.3.25",
36
+ "@amodalai/runtime": "0.3.25",
37
+ "@amodalai/studio": "0.3.25",
38
+ "@amodalai/runtime-app": "0.3.25"
39
39
  },
40
40
  "devDependencies": {
41
41
  "@types/node": "^20.11.24",
@@ -33,7 +33,7 @@ vi.mock('@amodalai/db', () => ({
33
33
  }));
34
34
 
35
35
  vi.mock('@amodalai/core', () => ({
36
- resolveAdminAgent: vi.fn().mockResolvedValue(null),
36
+ ensureAdminAgent: vi.fn().mockResolvedValue(null),
37
37
  }));
38
38
 
39
39
  vi.mock('../shared/connection-preflight.js', () => ({
@@ -12,7 +12,7 @@ import {spawn} from 'node:child_process';
12
12
  import path from 'node:path';
13
13
  import {fileURLToPath} from 'node:url';
14
14
  import {createLocalServer, initLogLevel, interceptConsole, log} from '@amodalai/runtime';
15
- import {resolveAdminAgent} from '@amodalai/core';
15
+ import {ensureAdminAgent} from '@amodalai/core';
16
16
  import {findRepoRoot} from '../shared/repo-discovery.js';
17
17
  import {findFreePort} from '../shared/find-free-port.js';
18
18
  import {runConnectionPreflight, printPreflightTable} from '../shared/connection-preflight.js';
@@ -253,10 +253,13 @@ async function spawnAdminAgent(opts: {
253
253
  studioUrl: string | null;
254
254
  repoPath: string;
255
255
  }): Promise<AdminSpawnResult | null> {
256
- const adminAgentPath = await resolveAdminAgent(opts.repoPath);
257
- if (!adminAgentPath) {
258
- log.info('admin_agent_not_available', {
259
- hint: 'No admin agent found at ~/.amodal/admin-agent/ or in amodal.json — skipped',
256
+ let adminAgentPath: string | null;
257
+ try {
258
+ adminAgentPath = await ensureAdminAgent(opts.repoPath);
259
+ } catch (err: unknown) {
260
+ log.warn('admin_agent_fetch_failed', {
261
+ error: err instanceof Error ? err.message : String(err),
262
+ hint: 'Could not download @amodalai/agent-admin — admin agent skipped',
260
263
  });
261
264
  return null;
262
265
  }
@@ -38,6 +38,13 @@ describe('runInit', () => {
38
38
  const gitignore = readFileSync(join(tempDir, '.gitignore'), 'utf-8');
39
39
  expect(gitignore).toContain('node_modules/');
40
40
  expect(gitignore).toContain('.env');
41
+
42
+ expect(existsSync(join(tempDir, '.env'))).toBe(true);
43
+ const env = readFileSync(join(tempDir, '.env'), 'utf-8');
44
+ expect(env).toContain('ANTHROPIC_API_KEY=');
45
+ expect(env).toContain('# OPENAI_API_KEY=');
46
+ expect(env).toContain('# GOOGLE_API_KEY=');
47
+ expect(env).toContain('# DATABASE_URL=');
41
48
  });
42
49
 
43
50
  it('should write valid config.json', async () => {
@@ -84,6 +91,10 @@ describe('runInit', () => {
84
91
  const config = JSON.parse(readFileSync(join(tempDir, 'amodal.json'), 'utf-8'));
85
92
  expect(config['models']['main']['provider']).toBe('openai');
86
93
  expect(config['models']['main']['model']).toBe('gpt-4o');
94
+
95
+ const env = readFileSync(join(tempDir, '.env'), 'utf-8');
96
+ expect(env).toContain('OPENAI_API_KEY=');
97
+ expect(env).toContain('# ANTHROPIC_API_KEY=');
87
98
  });
88
99
 
89
100
  it('should use Google provider', async () => {
@@ -56,6 +56,12 @@ export async function runInit(options: InitOptions = {}): Promise<void> {
56
56
  writeFileSync(gitignorePath, '.amodal/\nnode_modules/\n.env\n.env.*\n');
57
57
  }
58
58
 
59
+ // Write skeleton .env if it doesn't exist
60
+ const envPath = join(cwd, '.env');
61
+ if (!existsSync(envPath)) {
62
+ writeFileSync(envPath, generateEnvTemplate(provider));
63
+ }
64
+
59
65
  process.stderr.write(`[init] Created project "${name}" (${provider})\n`);
60
66
  process.stderr.write('[init] Next steps:\n');
61
67
  process.stderr.write(' 1. Add a connection: amodal connect <name>\n');
@@ -63,6 +69,45 @@ export async function runInit(options: InitOptions = {}): Promise<void> {
63
69
  process.stderr.write(' 3. Start dev server: amodal dev\n');
64
70
  }
65
71
 
72
+ /**
73
+ * Generates a skeleton .env with commented provider keys and common vars.
74
+ * The selected provider's key is uncommented; others are commented out.
75
+ */
76
+ function generateEnvTemplate(
77
+ provider: 'anthropic' | 'openai' | 'google',
78
+ ): string {
79
+ const lines = [
80
+ '# Amodal environment variables',
81
+ '# Only fill in what you need — most projects use a single provider.',
82
+ '# The runtime auto-detects your provider from whichever key is set.',
83
+ '',
84
+ '# LLM provider keys (uncomment the one you use)',
85
+ ];
86
+
87
+ const keys: Array<{key: string; provider: string}> = [
88
+ {key: 'ANTHROPIC_API_KEY', provider: 'anthropic'},
89
+ {key: 'OPENAI_API_KEY', provider: 'openai'},
90
+ {key: 'GOOGLE_API_KEY', provider: 'google'},
91
+ ];
92
+
93
+ for (const {key, provider: p} of keys) {
94
+ if (p === provider) {
95
+ lines.push(`${key}=`);
96
+ } else {
97
+ lines.push(`# ${key}=`);
98
+ }
99
+ }
100
+
101
+ lines.push(
102
+ '',
103
+ '# Database (required — Postgres connection for memory, stores, and sessions)',
104
+ '# DATABASE_URL=postgresql://localhost:5432/my_agent',
105
+ '',
106
+ );
107
+
108
+ return lines.join('\n');
109
+ }
110
+
66
111
  export const initCommand: CommandModule = {
67
112
  command: 'init',
68
113
  describe: 'Initialize a new amodal project',
@@ -19,14 +19,7 @@ export function generateConfigTemplate(options: ConfigTemplateOptions): string {
19
19
  google: 'gemini-2.0-flash',
20
20
  };
21
21
 
22
- const envKeyMap: Record<string, string> = {
23
- anthropic: 'ANTHROPIC_API_KEY',
24
- openai: 'OPENAI_API_KEY',
25
- google: 'GOOGLE_API_KEY',
26
- };
27
-
28
22
  const model = modelMap[options.provider] ?? modelMap['anthropic'];
29
- const envKey = envKeyMap[options.provider] ?? envKeyMap['anthropic'];
30
23
 
31
24
  const config = {
32
25
  name: options.name,
@@ -35,7 +28,6 @@ export function generateConfigTemplate(options: ConfigTemplateOptions): string {
35
28
  main: {
36
29
  provider: options.provider,
37
30
  model,
38
- apiKey: `\${${envKey}}`,
39
31
  },
40
32
  },
41
33
  };