@amodalai/amodal 0.1.11 → 0.1.13

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.1.11",
3
+ "version": "0.1.13",
4
4
  "description": "Amodal CLI",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -30,16 +30,9 @@
30
30
  "semver": "^7.6.0",
31
31
  "yargs": "^17.7.2",
32
32
  "zod": "^4.3.6",
33
- "@amodalai/core": "0.1.11",
34
- "@amodalai/runtime": "0.1.11"
35
- },
36
- "peerDependencies": {
37
- "@amodalai/runtime-app": "0.1.11"
38
- },
39
- "peerDependenciesMeta": {
40
- "@amodalai/runtime-app": {
41
- "optional": true
42
- }
33
+ "@amodalai/core": "0.1.13",
34
+ "@amodalai/runtime": "0.1.13",
35
+ "@amodalai/runtime-app": "0.1.13"
43
36
  },
44
37
  "devDependencies": {
45
38
  "@types/node": "^20.11.24",
@@ -0,0 +1,58 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2025 Amodal Labs, Inc.
4
+ * SPDX-License-Identifier: MIT
5
+ */
6
+
7
+ import {readFile} from 'node:fs/promises';
8
+ import * as path from 'node:path';
9
+ import {
10
+ fetchAdminAgent,
11
+ getAdminAgentVersion,
12
+ } from '@amodalai/core';
13
+ import {findRepoRoot} from '../shared/repo-discovery.js';
14
+
15
+ /**
16
+ * Update the global admin agent cache from the registry.
17
+ * Called by `amodal update --admin-agent`.
18
+ */
19
+ export async function updateAdminAgentCommand(): Promise<number> {
20
+ // Check if amodal.json overrides the admin agent
21
+ let repoPath: string | undefined;
22
+ try {
23
+ repoPath = findRepoRoot();
24
+ } catch {
25
+ // Not in a repo — that's fine
26
+ }
27
+
28
+ if (repoPath) {
29
+ try {
30
+ const configRaw = await readFile(path.join(repoPath, 'amodal.json'), 'utf-8');
31
+ const parsed: unknown = JSON.parse(configRaw);
32
+ if (parsed && typeof parsed === 'object' && 'adminAgent' in parsed) {
33
+
34
+ const adminPath = (parsed as Record<string, unknown>)['adminAgent'];
35
+ if (typeof adminPath === 'string') {
36
+ process.stderr.write(`[update] Admin agent is overridden in amodal.json (adminAgent: "${adminPath}").\n`);
37
+ process.stderr.write('[update] The global cache is not used. Update your local copy directly.\n');
38
+ return 1;
39
+ }
40
+ }
41
+ } catch {
42
+ // No config or parse error — proceed
43
+ }
44
+ }
45
+
46
+ process.stderr.write('[update] Fetching latest admin agent from registry...\n');
47
+ try {
48
+ const dir = await fetchAdminAgent();
49
+ const version = await getAdminAgentVersion(dir);
50
+ process.stderr.write(`[update] Admin agent updated to v${version ?? 'unknown'}\n`);
51
+ process.stderr.write(`[update] Cached at ${dir}\n`);
52
+ return 0;
53
+ } catch (err) {
54
+ const msg = err instanceof Error ? err.message : String(err);
55
+ process.stderr.write(`[update] Admin agent update failed: ${msg}\n`);
56
+ return 1;
57
+ }
58
+ }
@@ -6,6 +6,7 @@
6
6
 
7
7
  import type {CommandModule} from 'yargs';
8
8
  import {existsSync} from 'node:fs';
9
+ import {createRequire} from 'node:module';
9
10
  import path from 'node:path';
10
11
  import {fileURLToPath} from 'node:url';
11
12
  import {createLocalServer} from '@amodalai/runtime';
@@ -42,15 +43,19 @@ export async function runDev(options: DevOptions = {}): Promise<void> {
42
43
  try {
43
44
  let staticAppDir: string | undefined;
44
45
 
45
- // Use pre-built static assets for the SPA for the SPA.
46
+ // Use pre-built static assets for the SPA.
46
47
  // Vite dev middleware is only used inside the monorepo with `pnpm dev`.
47
48
  const scriptDir = path.dirname(fileURLToPath(import.meta.url));
48
49
  const candidates = [
49
50
  // esbuild bundle: bundle/app/
50
51
  path.resolve(scriptDir, 'app'),
51
- // global/local install: <pkg root>/node_modules/@amodalai/runtime-app/dist/
52
- path.resolve(scriptDir, '..', '..', '..', 'node_modules', '@amodalai', 'runtime-app', 'dist'),
53
52
  ];
53
+
54
+ // Resolve @amodalai/runtime-app via Node module resolution (works regardless of install layout)
55
+ const require = createRequire(import.meta.url);
56
+ const runtimeAppPkg = require.resolve('@amodalai/runtime-app/package.json');
57
+ candidates.push(path.join(path.dirname(runtimeAppPkg), 'dist'));
58
+
54
59
  for (const dir of candidates) {
55
60
  if (existsSync(path.join(dir, 'index.html'))) {
56
61
  process.stderr.write('[dev] Serving pre-built runtime app\n');
@@ -34,7 +34,6 @@ import {evalCommand} from './eval.js';
34
34
  import {experimentCommand} from './experiment.js';
35
35
  import {testQueryCommand} from './test-query.js';
36
36
  import {automationsCommand} from './automations.js';
37
-
38
37
  /**
39
38
  * All amodal subcommands for flat registration on the root yargs instance.
40
39
  */
@@ -148,21 +148,49 @@ export async function runUpdate(options: UpdateOptions = {}): Promise<number> {
148
148
 
149
149
  export const updateCommand: CommandModule = {
150
150
  command: 'update [name]',
151
- describe: 'Update installed packages',
151
+ describe: 'Update packages. Use --all for all packages, --admin-agent for the admin agent, or specify a name.',
152
152
  builder: (yargs) =>
153
153
  yargs
154
154
  .positional('name', {type: 'string', describe: 'Package name to update'})
155
+ .option('all', {type: 'boolean', default: false, describe: 'Update all installed packages'})
156
+ .option('admin-agent', {type: 'boolean', default: false, describe: 'Update the global admin agent cache'})
155
157
  .option('latest', {type: 'boolean', default: false, describe: 'Allow major version updates'})
156
158
  .option('dry-run', {type: 'boolean', default: false, describe: 'Show what would be updated'}),
157
159
  handler: async (argv) => {
158
- const code = await runUpdate({
159
- // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
160
- name: argv['name'] as string | undefined,
161
- // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
162
- latest: argv['latest'] as boolean,
163
- // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
164
- dryRun: argv['dryRun'] as boolean,
165
- });
166
- process.exit(code);
160
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
161
+ const name = argv['name'] as string | undefined;
162
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
163
+ const all = argv['all'] as boolean;
164
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
165
+ const adminAgent = argv['adminAgent'] as boolean;
166
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
167
+ const latest = argv['latest'] as boolean;
168
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
169
+ const dryRun = argv['dryRun'] as boolean;
170
+
171
+ // Must specify at least one target
172
+ if (!name && !all && !adminAgent) {
173
+ process.stderr.write('Usage: amodal update <name> | --all | --admin-agent\n');
174
+ process.stderr.write(' <name> Update a specific package\n');
175
+ process.stderr.write(' --all Update all installed packages\n');
176
+ process.stderr.write(' --admin-agent Update the global admin agent\n');
177
+ process.stderr.write(' Flags can be combined: amodal update --all --admin-agent\n');
178
+ process.exit(1);
179
+ }
180
+
181
+ let failures = 0;
182
+
183
+ // Update admin agent if requested
184
+ if (adminAgent) {
185
+ const {updateAdminAgentCommand} = await import('./admin.js');
186
+ failures += await updateAdminAgentCommand();
187
+ }
188
+
189
+ // Update packages if requested
190
+ if (name || all) {
191
+ failures += await runUpdate({name, latest, dryRun});
192
+ }
193
+
194
+ process.exit(failures > 0 ? 1 : 0);
167
195
  },
168
196
  };
@@ -227,8 +227,34 @@ export async function runConnectionPreflight(repoPath: string): Promise<Prefligh
227
227
 
228
228
  const results: LiveTestResult[] = [];
229
229
 
230
+ // Split connections by protocol
231
+ const restConnections: Array<[string, typeof repo.connections extends Map<string, infer V> ? V : never]> = [];
232
+ const mcpFromConnections: Record<string, {transport: 'stdio' | 'sse' | 'http'; command?: string; args?: string[]; env?: Record<string, string>; url?: string; headers?: Record<string, string>}> = {};
233
+
234
+ for (const [name, conn] of repo.connections) {
235
+ if (conn.spec.protocol === 'mcp') {
236
+ // Resolve env: references in headers
237
+ const resolvedHeaders: Record<string, string> = {};
238
+ if (conn.spec.headers) {
239
+ for (const [k, v] of Object.entries(conn.spec.headers)) {
240
+ resolvedHeaders[k] = v.startsWith('env:') ? (envVars.get(v.slice(4)) ?? process.env[v.slice(4)] ?? '') : v;
241
+ }
242
+ }
243
+ mcpFromConnections[name] = {
244
+ transport: conn.spec.transport ?? 'stdio',
245
+ command: conn.spec.command,
246
+ args: conn.spec.args,
247
+ env: conn.spec.env,
248
+ url: conn.spec.url,
249
+ headers: Object.keys(resolvedHeaders).length > 0 ? resolvedHeaders : undefined,
250
+ };
251
+ } else {
252
+ restConnections.push([name, conn]);
253
+ }
254
+ }
255
+
230
256
  // Test REST connections in parallel
231
- const restPromises = [...repo.connections.entries()].map(([name, conn]) =>
257
+ const restPromises = restConnections.map(([name, conn]) =>
232
258
  testRestConnection(name, conn.spec, envVars),
233
259
  );
234
260
  const restResults = await Promise.allSettled(restPromises);
@@ -238,9 +264,17 @@ export async function runConnectionPreflight(repoPath: string): Promise<Prefligh
238
264
  }
239
265
  }
240
266
 
241
- // Test MCP servers
242
- if (repo.mcpServers && Object.keys(repo.mcpServers).length > 0) {
243
- const mcpResults = await testMcpServers(repo.mcpServers);
267
+ // Test MCP connections (from protocol:mcp connections + legacy mcp.servers)
268
+ const allMcpConfigs = {...mcpFromConnections};
269
+ if (repo.mcpServers) {
270
+ for (const [name, config] of Object.entries(repo.mcpServers)) {
271
+ if (!allMcpConfigs[name]) {
272
+ allMcpConfigs[name] = config;
273
+ }
274
+ }
275
+ }
276
+ if (Object.keys(allMcpConfigs).length > 0) {
277
+ const mcpResults = await testMcpServers(allMcpConfigs);
244
278
  results.push(...mcpResults);
245
279
  }
246
280