aai-gateway 0.5.2 → 0.6.0
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 +102 -103
- package/dist/cli.js +18 -18
- package/dist/cli.js.map +1 -1
- package/dist/index.js +14 -25
- package/dist/index.js.map +1 -1
- package/dist/{server-DQ0nbmQB.js → server-C-9LuKWE.js} +2049 -2078
- package/dist/server-C-9LuKWE.js.map +1 -0
- package/package.json +1 -1
- package/dist/server-DQ0nbmQB.js.map +0 -1
package/README.md
CHANGED
|
@@ -4,32 +4,32 @@
|
|
|
4
4
|
|
|
5
5
|
AAI Gateway turns many apps, agents, skills, and MCP servers into one MCP server.
|
|
6
6
|
|
|
7
|
-
##
|
|
7
|
+
## Core Values
|
|
8
8
|
|
|
9
|
-
###
|
|
9
|
+
### Value 1: Natural Language-Driven Tool Integration
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
After installing the AAI Gateway MCP, you can quickly integrate any other MCP or skill through natural language descriptions, and control other AI Agent tools (including Claude Code, Codex, OpenCode, etc.).
|
|
12
12
|
|
|
13
|
-
AAI Gateway
|
|
13
|
+
AAI Gateway also integrates a search tool that helps you search for official and secure MCPs and skills from authoritative, mainstream websites, and install them with a single sentence. Control of other AI Agent tools is done via Agent Client Protocol (ACP), including session management.
|
|
14
14
|
|
|
15
|
-
###
|
|
15
|
+
### Value 2: Progressive Disclosure Strategy
|
|
16
16
|
|
|
17
|
-
AAI Gateway
|
|
17
|
+
AAI Gateway does not dump all tool descriptions into the LLM context at once. Instead, it employs a progressive disclosure strategy:
|
|
18
18
|
|
|
19
|
-
**MCP Server
|
|
19
|
+
**MCP Server Level**: Only the overall description of the MCP Server is exposed initially. When the LLM determines that a specific tool needs to be used, it returns tool usage guidance first. The Agent then calls the unified `aai:exec` to execute based on that guidance. `aai:exec` accepts `appId`, `tool`, and `tool args` as parameters.
|
|
20
20
|
|
|
21
|
-
**MCP /
|
|
21
|
+
**MCP / Skill Description Level**: Two tiers of disclosure are provided:
|
|
22
22
|
|
|
23
|
-
- `summary` —
|
|
24
|
-
- `keywords` —
|
|
23
|
+
- `summary` — Natural language description; good for automatic triggering
|
|
24
|
+
- `keywords` — Compact keyword set; further reduces context overhead
|
|
25
25
|
|
|
26
|
-
|
|
26
|
+
This allows OpenClaw (a popular personal assistant application) and similar tools that require many tools and skills to still run smoothly.
|
|
27
27
|
|
|
28
|
-
##
|
|
28
|
+
## How To Use
|
|
29
29
|
|
|
30
|
-
### 1.
|
|
30
|
+
### 1. Install AAI Gateway MCP
|
|
31
31
|
|
|
32
|
-
|
|
32
|
+
You do not need to preinstall `aai-gateway`. Simply register it as a user-level MCP server and launch it via `npx`.
|
|
33
33
|
|
|
34
34
|
#### Claude Code
|
|
35
35
|
|
|
@@ -45,7 +45,7 @@ codex mcp add aai-gateway -- npx -y aai-gateway
|
|
|
45
45
|
|
|
46
46
|
#### OpenCode
|
|
47
47
|
|
|
48
|
-
|
|
48
|
+
Add to `~/.config/opencode/opencode.json`:
|
|
49
49
|
|
|
50
50
|
```json
|
|
51
51
|
{
|
|
@@ -60,47 +60,46 @@ codex mcp add aai-gateway -- npx -y aai-gateway
|
|
|
60
60
|
}
|
|
61
61
|
```
|
|
62
62
|
|
|
63
|
-
### 2.
|
|
63
|
+
### 2. Search and Install MCP or Skill
|
|
64
64
|
|
|
65
|
-
|
|
65
|
+
If you don't know which MCP or skill to install, just ask your AI tool to search for what you need using AAI Gateway (e.g., "please search for a filesystem MCP" or "find me a git commit skill").
|
|
66
66
|
|
|
67
|
-
|
|
67
|
+
The search will:
|
|
68
68
|
|
|
69
|
-
-
|
|
70
|
-
-
|
|
71
|
-
-
|
|
72
|
-
-
|
|
73
|
-
- 将确认的项目路由到 `mcp:import` 或 `skill:import` 流程
|
|
69
|
+
- Convert your request into search keywords
|
|
70
|
+
- Recommend safer authoritative sources to search first
|
|
71
|
+
- Normalize search results into a shortlist for your confirmation
|
|
72
|
+
- Route confirmed items into the import flow
|
|
74
73
|
|
|
75
|
-
|
|
74
|
+
**Recommended Search Source Order**:
|
|
76
75
|
|
|
77
|
-
1.
|
|
78
|
-
2.
|
|
79
|
-
3.
|
|
76
|
+
1. Official catalogs: `modelcontextprotocol/registry`, `modelcontextprotocol/servers`, `openai/skills`
|
|
77
|
+
2. Community-curated lists: `punkpeye/awesome-mcp-servers`, `ComposioHQ/awesome-claude-skills`
|
|
78
|
+
3. Higher-scrutiny sources: Open marketplaces like ClawHub (use with extra caution)
|
|
80
79
|
|
|
81
|
-
>
|
|
80
|
+
> Note: The recommended list is a starting point, not a hard allowlist. Do not casually suggest tools from unknown websites. For marketplace platforms, also verify the maintainer's identity, repository activity, README quality, and license visibility.
|
|
82
81
|
|
|
83
|
-
### 3.
|
|
82
|
+
### 3. Import an MCP Server
|
|
84
83
|
|
|
85
|
-
|
|
84
|
+
Main workflow: Copy a mainstream MCP config snippet into your AI tool and ask it to import that server through AAI Gateway.
|
|
86
85
|
|
|
87
|
-
AI
|
|
86
|
+
The AI tool will:
|
|
88
87
|
|
|
89
|
-
1.
|
|
90
|
-
2.
|
|
91
|
-
3.
|
|
88
|
+
1. Read the MCP config you pasted
|
|
89
|
+
2. Ask you to choose an exposure mode
|
|
90
|
+
3. Call `mcp:import`
|
|
92
91
|
|
|
93
|
-
AAI Gateway
|
|
92
|
+
AAI Gateway keeps import parameters consistent with standard MCP config shapes:
|
|
94
93
|
|
|
95
|
-
- stdio MCP
|
|
96
|
-
- remote MCP
|
|
94
|
+
- stdio MCP: `command`, `args`, `env`, `cwd`
|
|
95
|
+
- remote MCP: `url`, optional `transport`, optional `headers`
|
|
97
96
|
|
|
98
|
-
|
|
97
|
+
Choose an exposure mode before import:
|
|
99
98
|
|
|
100
|
-
- `summary
|
|
101
|
-
- `keywords
|
|
99
|
+
- `summary`: Easier automatic triggering
|
|
100
|
+
- `keywords`: Leaves room for more tools, but usually needs more explicit keyword mentions
|
|
102
101
|
|
|
103
|
-
**stdio MCP
|
|
102
|
+
**stdio MCP Example**:
|
|
104
103
|
|
|
105
104
|
```json
|
|
106
105
|
{
|
|
@@ -109,7 +108,7 @@ AAI Gateway 保持导入参数与标准 MCP 配置格式一致:
|
|
|
109
108
|
}
|
|
110
109
|
```
|
|
111
110
|
|
|
112
|
-
**Remote Streamable HTTP MCP
|
|
111
|
+
**Remote Streamable HTTP MCP Example**:
|
|
113
112
|
|
|
114
113
|
```json
|
|
115
114
|
{
|
|
@@ -117,7 +116,7 @@ AAI Gateway 保持导入参数与标准 MCP 配置格式一致:
|
|
|
117
116
|
}
|
|
118
117
|
```
|
|
119
118
|
|
|
120
|
-
**Remote SSE MCP
|
|
119
|
+
**Remote SSE MCP Example**:
|
|
121
120
|
|
|
122
121
|
```json
|
|
123
122
|
{
|
|
@@ -126,23 +125,23 @@ AAI Gateway 保持导入参数与标准 MCP 配置格式一致:
|
|
|
126
125
|
}
|
|
127
126
|
```
|
|
128
127
|
|
|
129
|
-
|
|
128
|
+
After import, AAI Gateway returns:
|
|
130
129
|
|
|
131
|
-
-
|
|
132
|
-
-
|
|
133
|
-
-
|
|
134
|
-
-
|
|
130
|
+
- The generated app id
|
|
131
|
+
- The generated `keywords`
|
|
132
|
+
- The generated `summary`
|
|
133
|
+
- The guide tool name: `app:<id>`
|
|
135
134
|
|
|
136
|
-
>
|
|
135
|
+
> **Important**: Restart your AI tool before using the newly imported tool. After restart, the imported app will appear as `app:<id>`. Use `aai:exec` to actually run the imported app's operations.
|
|
137
136
|
|
|
138
|
-
### 4.
|
|
137
|
+
### 4. Import a Skill
|
|
139
138
|
|
|
140
|
-
|
|
139
|
+
Skills are imported through the AI tool as well. Just tell the AI tool to import a skill using AAI Gateway, then provide either:
|
|
141
140
|
|
|
142
|
-
-
|
|
143
|
-
-
|
|
141
|
+
- A local skill path
|
|
142
|
+
- A remote skill root URL that exposes `SKILL.md`
|
|
144
143
|
|
|
145
|
-
|
|
144
|
+
**Local Skill Example**:
|
|
146
145
|
|
|
147
146
|
```json
|
|
148
147
|
{
|
|
@@ -150,7 +149,7 @@ AAI Gateway 保持导入参数与标准 MCP 配置格式一致:
|
|
|
150
149
|
}
|
|
151
150
|
```
|
|
152
151
|
|
|
153
|
-
|
|
152
|
+
**Remote Skill Example**:
|
|
154
153
|
|
|
155
154
|
```json
|
|
156
155
|
{
|
|
@@ -158,23 +157,23 @@ AAI Gateway 保持导入参数与标准 MCP 配置格式一致:
|
|
|
158
157
|
}
|
|
159
158
|
```
|
|
160
159
|
|
|
161
|
-
|
|
160
|
+
Like MCP import, skill import returns `app id`, `keywords`, `summary`, and the `app:<id>` guide tool name.
|
|
162
161
|
|
|
163
|
-
|
|
162
|
+
Restart your AI tool after import.
|
|
164
163
|
|
|
165
|
-
### 5.
|
|
164
|
+
### 5. Supported ACP Agents
|
|
166
165
|
|
|
167
|
-
AAI Gateway
|
|
166
|
+
AAI Gateway can also control app-like agents through ACP.
|
|
168
167
|
|
|
169
|
-
|
|
168
|
+
Currently supported ACP agent types:
|
|
170
169
|
|
|
171
170
|
- OpenCode
|
|
172
171
|
- Claude Code
|
|
173
172
|
- Codex
|
|
174
173
|
|
|
175
|
-
##
|
|
174
|
+
## How It Works
|
|
176
175
|
|
|
177
|
-
###
|
|
176
|
+
### Architecture
|
|
178
177
|
|
|
179
178
|
```
|
|
180
179
|
┌─────────────────────────────────────────────────────────────┐
|
|
@@ -213,19 +212,19 @@ AAI Gateway 还能通过 ACP 控制类应用的 Agent。
|
|
|
213
212
|
└─────────────────────────────────────────────────────────────┘
|
|
214
213
|
```
|
|
215
214
|
|
|
216
|
-
###
|
|
215
|
+
### Unified Abstraction: Agent App
|
|
217
216
|
|
|
218
|
-
AAI Gateway
|
|
217
|
+
AAI Gateway unifies MCPs, Skills, ACP Agents, and CLI tools into **Agent Apps**.
|
|
219
218
|
|
|
220
|
-
|
|
219
|
+
To integrate an app with AAI Gateway, simply provide an app descriptor file (`aai.json`). The descriptor tells AAI Gateway:
|
|
221
220
|
|
|
222
|
-
-
|
|
223
|
-
-
|
|
224
|
-
-
|
|
221
|
+
- What the app is
|
|
222
|
+
- How to connect to it
|
|
223
|
+
- How to expose it at low context cost
|
|
225
224
|
|
|
226
|
-
###
|
|
225
|
+
### Descriptor Examples
|
|
227
226
|
|
|
228
|
-
#### MCP Server
|
|
227
|
+
#### MCP Server Descriptor
|
|
229
228
|
|
|
230
229
|
```json
|
|
231
230
|
{
|
|
@@ -250,7 +249,7 @@ AAI Gateway 将 MCP、技能、ACP Agent、CLI 工具统一抽象为 **Agent App
|
|
|
250
249
|
}
|
|
251
250
|
```
|
|
252
251
|
|
|
253
|
-
#### Skill
|
|
252
|
+
#### Skill Descriptor
|
|
254
253
|
|
|
255
254
|
```json
|
|
256
255
|
{
|
|
@@ -274,7 +273,7 @@ AAI Gateway 将 MCP、技能、ACP Agent、CLI 工具统一抽象为 **Agent App
|
|
|
274
273
|
}
|
|
275
274
|
```
|
|
276
275
|
|
|
277
|
-
#### ACP Agent
|
|
276
|
+
#### ACP Agent Descriptor
|
|
278
277
|
|
|
279
278
|
```json
|
|
280
279
|
{
|
|
@@ -298,7 +297,7 @@ AAI Gateway 将 MCP、技能、ACP Agent、CLI 工具统一抽象为 **Agent App
|
|
|
298
297
|
}
|
|
299
298
|
```
|
|
300
299
|
|
|
301
|
-
#### CLI
|
|
300
|
+
#### CLI Tool Descriptor
|
|
302
301
|
|
|
303
302
|
```json
|
|
304
303
|
{
|
|
@@ -322,53 +321,53 @@ AAI Gateway 将 MCP、技能、ACP Agent、CLI 工具统一抽象为 **Agent App
|
|
|
322
321
|
}
|
|
323
322
|
```
|
|
324
323
|
|
|
325
|
-
##
|
|
324
|
+
## Pre-bundling Apps in AAI Gateway
|
|
326
325
|
|
|
327
|
-
###
|
|
326
|
+
### Submit a Pull Request
|
|
328
327
|
|
|
329
|
-
|
|
328
|
+
If you want AAI Gateway to ship with a descriptor for an app by default, open a PR.
|
|
330
329
|
|
|
331
|
-
PR
|
|
330
|
+
A PR should include:
|
|
332
331
|
|
|
333
|
-
1.
|
|
334
|
-
2.
|
|
335
|
-
3.
|
|
336
|
-
4.
|
|
332
|
+
1. The descriptor itself
|
|
333
|
+
2. A safe discovery rule that proves the app is actually installed
|
|
334
|
+
3. The connection config
|
|
335
|
+
4. An explanation of why the integration should be bundled
|
|
337
336
|
|
|
338
|
-
|
|
337
|
+
Built-in ACP agent descriptors live in:
|
|
339
338
|
|
|
340
339
|
- `src/discovery/descriptors/`
|
|
341
340
|
|
|
342
|
-
|
|
341
|
+
They are registered in:
|
|
343
342
|
|
|
344
343
|
- `src/discovery/agent-registry.ts`
|
|
345
344
|
|
|
346
|
-
|
|
345
|
+
Standard PR workflow:
|
|
347
346
|
|
|
348
|
-
1.
|
|
349
|
-
2.
|
|
350
|
-
3.
|
|
351
|
-
4.
|
|
347
|
+
1. Add the descriptor file
|
|
348
|
+
2. Add or update discovery checks
|
|
349
|
+
3. Register it in the appropriate discovery source
|
|
350
|
+
4. Update the README if the new integration is user-facing
|
|
352
351
|
|
|
353
|
-
|
|
352
|
+
If you're unsure whether an integration should be bundled, open an issue first to discuss.
|
|
354
353
|
|
|
355
|
-
###
|
|
354
|
+
### Where to Place Descriptors
|
|
356
355
|
|
|
357
|
-
AAI Gateway
|
|
356
|
+
AAI Gateway discovers apps from the following locations:
|
|
358
357
|
|
|
359
358
|
#### Web Apps
|
|
360
359
|
|
|
361
|
-
|
|
360
|
+
Publish at:
|
|
362
361
|
|
|
363
362
|
```
|
|
364
363
|
https://<your-host>/.well-known/aai.json
|
|
365
364
|
```
|
|
366
365
|
|
|
367
|
-
|
|
366
|
+
AAI Gateway fetches this path when the user calls `remote:discover`.
|
|
368
367
|
|
|
369
368
|
#### macOS Apps
|
|
370
369
|
|
|
371
|
-
|
|
370
|
+
Recommended locations:
|
|
372
371
|
|
|
373
372
|
- `<YourApp>.app/Contents/Resources/aai.json`
|
|
374
373
|
- `~/Library/Containers/<container>/Data/Library/Application Support/aai.json`
|
|
@@ -376,7 +375,7 @@ https://<your-host>/.well-known/aai.json
|
|
|
376
375
|
|
|
377
376
|
#### Linux Apps
|
|
378
377
|
|
|
379
|
-
|
|
378
|
+
Scanned locations:
|
|
380
379
|
|
|
381
380
|
- `/usr/share`
|
|
382
381
|
- `/usr/local/share`
|
|
@@ -384,20 +383,20 @@ https://<your-host>/.well-known/aai.json
|
|
|
384
383
|
|
|
385
384
|
#### Windows Apps
|
|
386
385
|
|
|
387
|
-
|
|
386
|
+
Scanned locations:
|
|
388
387
|
|
|
389
388
|
- `C:\Program Files`
|
|
390
389
|
- `C:\Program Files (x86)`
|
|
391
390
|
- `%LOCALAPPDATA%`
|
|
392
391
|
|
|
393
|
-
####
|
|
392
|
+
#### Descriptor Guidelines
|
|
394
393
|
|
|
395
|
-
-
|
|
396
|
-
- `app.name.default`
|
|
397
|
-
- `keywords`
|
|
398
|
-
- `summary`
|
|
399
|
-
-
|
|
400
|
-
-
|
|
394
|
+
- Keep descriptors small and practical
|
|
395
|
+
- Make `app.name.default` clear
|
|
396
|
+
- Keep `keywords` short and high-signal
|
|
397
|
+
- Make `summary` explain when the app should be used
|
|
398
|
+
- Put detailed capability data in the downstream protocol, not in the descriptor
|
|
399
|
+
- If your app already speaks MCP, keep the descriptor minimal and let MCP provide lazy tool details
|
|
401
400
|
|
|
402
401
|
## Disclaimer
|
|
403
402
|
|
package/dist/cli.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { A as logger, D as createDesktopDiscovery, O as getManagedAppDir, T as AAI_GATEWAY_VERSION, _ as isMcpAccess, a as buildMcpImportConfig, b as getMcpExecutor, c as importSkill, d as upsertSkillRegistryEntry, f as upsertMcpRegistryEntry, i as IMPORT_LIMITS, l as normalizeExposureInput, m as createSecureStorage, n as createGatewayServer, o as buildSkillImportSource, r as EXPOSURE_LIMITS, s as importMcpServer, u as validateImportHeaders, v as isSkillAccess } from "./server-
|
|
2
|
+
import { A as logger, D as createDesktopDiscovery, O as getManagedAppDir, T as AAI_GATEWAY_VERSION, _ as isMcpAccess, a as buildMcpImportConfig, b as getMcpExecutor, c as importSkill, d as upsertSkillRegistryEntry, f as upsertMcpRegistryEntry, i as IMPORT_LIMITS, l as normalizeExposureInput, m as createSecureStorage, n as createGatewayServer, o as buildSkillImportSource, r as EXPOSURE_LIMITS, s as importMcpServer, u as validateImportHeaders, v as isSkillAccess } from "./server-C-9LuKWE.js";
|
|
3
3
|
import { join } from "node:path";
|
|
4
4
|
import { existsSync, readFileSync } from "node:fs";
|
|
5
5
|
//#region src/cli.ts
|
|
@@ -154,8 +154,8 @@ function parseArgs(args) {
|
|
|
154
154
|
};
|
|
155
155
|
}
|
|
156
156
|
if (args[0] === "app" && args[1] === "config") {
|
|
157
|
-
const
|
|
158
|
-
if (!
|
|
157
|
+
const appId = args[2];
|
|
158
|
+
if (!appId) throw new Error("Usage: aai-gateway app config <app-id>");
|
|
159
159
|
let exposure;
|
|
160
160
|
let summary;
|
|
161
161
|
const keywords = [];
|
|
@@ -188,7 +188,7 @@ function parseArgs(args) {
|
|
|
188
188
|
return {
|
|
189
189
|
command: "app-config",
|
|
190
190
|
dev,
|
|
191
|
-
|
|
191
|
+
appId,
|
|
192
192
|
...exposure ? { exposure } : {},
|
|
193
193
|
...summary ? { summary } : {},
|
|
194
194
|
...keywords.length > 0 ? { keywords } : {}
|
|
@@ -283,7 +283,7 @@ async function runScan(dev) {
|
|
|
283
283
|
return;
|
|
284
284
|
}
|
|
285
285
|
for (const app of apps) {
|
|
286
|
-
console.log(`${app.
|
|
286
|
+
console.log(`${app.appId}`);
|
|
287
287
|
console.log(` Name: ${app.descriptor.app.name.default}`);
|
|
288
288
|
console.log(` Location: ${app.location ?? "(unknown)"}`);
|
|
289
289
|
console.log(` Protocol: ${app.descriptor.access.protocol}`);
|
|
@@ -312,10 +312,10 @@ async function runMcpImport(options) {
|
|
|
312
312
|
headers: options.headers
|
|
313
313
|
});
|
|
314
314
|
console.log(`Imported MCP app: ${result.descriptor.app.name.default}`);
|
|
315
|
-
console.log(`App ID: ${result.entry.
|
|
315
|
+
console.log(`App ID: ${result.entry.appId}`);
|
|
316
316
|
console.log(`Descriptor: ${result.entry.descriptorPath}`);
|
|
317
|
-
console.log(`Managed directory: ${getManagedAppDir(result.entry.
|
|
318
|
-
console.log(`Tool name after restart: app:${result.entry.
|
|
317
|
+
console.log(`Managed directory: ${getManagedAppDir(result.entry.appId)}`);
|
|
318
|
+
console.log(`Tool name after restart: app:${result.entry.appId}`);
|
|
319
319
|
console.log(`Keywords: ${result.descriptor.exposure.keywords.join(", ")}`);
|
|
320
320
|
console.log(`Summary: ${result.descriptor.exposure.summary}`);
|
|
321
321
|
console.log(`Exposure mode: ${options.exposure}`);
|
|
@@ -333,16 +333,16 @@ async function runSkillImport(options) {
|
|
|
333
333
|
url: source.url
|
|
334
334
|
});
|
|
335
335
|
console.log(`Imported skill: ${result.descriptor.app.name.default}`);
|
|
336
|
-
console.log(`App ID: ${result.
|
|
337
|
-
console.log(`Descriptor: ${join(getManagedAppDir(result.
|
|
336
|
+
console.log(`App ID: ${result.appId}`);
|
|
337
|
+
console.log(`Descriptor: ${join(getManagedAppDir(result.appId), "aai.json")}`);
|
|
338
338
|
console.log(`Skill directory: ${result.managedPath}`);
|
|
339
|
-
console.log(`Tool name after restart: app:${result.
|
|
339
|
+
console.log(`Tool name after restart: app:${result.appId}`);
|
|
340
340
|
console.log(`Keywords: ${result.descriptor.exposure.keywords.join(", ")}`);
|
|
341
341
|
console.log(`Summary: ${result.descriptor.exposure.summary}`);
|
|
342
342
|
console.log(`Exposure mode: ${options.exposure}`);
|
|
343
343
|
}
|
|
344
344
|
async function runAppConfig(options) {
|
|
345
|
-
const descriptorPath = resolveManagedDescriptorPath(options.
|
|
345
|
+
const descriptorPath = resolveManagedDescriptorPath(options.appId);
|
|
346
346
|
const descriptor = JSON.parse(readFileSync(descriptorPath, "utf-8"));
|
|
347
347
|
const nextExposure = normalizeAppConfigExposure(options, descriptor.exposure);
|
|
348
348
|
const nextDescriptor = {
|
|
@@ -350,17 +350,17 @@ async function runAppConfig(options) {
|
|
|
350
350
|
exposure: nextExposure
|
|
351
351
|
};
|
|
352
352
|
if (isMcpAccess(nextDescriptor.access)) await upsertMcpRegistryEntry({
|
|
353
|
-
|
|
353
|
+
appId: options.appId,
|
|
354
354
|
protocol: "mcp",
|
|
355
355
|
config: nextDescriptor.access.config
|
|
356
356
|
}, nextDescriptor);
|
|
357
357
|
else if (isSkillAccess(nextDescriptor.access)) await upsertSkillRegistryEntry({
|
|
358
|
-
|
|
358
|
+
appId: options.appId,
|
|
359
359
|
protocol: "skill",
|
|
360
360
|
config: nextDescriptor.access.config
|
|
361
361
|
}, nextDescriptor);
|
|
362
|
-
else throw new Error(`App '${options.
|
|
363
|
-
console.log(`Updated app: ${options.
|
|
362
|
+
else throw new Error(`App '${options.appId}' is not an imported MCP app or imported skill`);
|
|
363
|
+
console.log(`Updated app: ${options.appId}`);
|
|
364
364
|
if (options.exposure) console.log(`Exposure mode: ${options.exposure}`);
|
|
365
365
|
console.log(`Keywords: ${nextDescriptor.exposure.keywords.join(", ")}`);
|
|
366
366
|
console.log(`Summary: ${nextDescriptor.exposure.summary}`);
|
|
@@ -372,8 +372,8 @@ function normalizeAppConfigExposure(options, current) {
|
|
|
372
372
|
summary
|
|
373
373
|
});
|
|
374
374
|
}
|
|
375
|
-
function resolveManagedDescriptorPath(
|
|
376
|
-
const descriptorPath = join(getManagedAppDir(
|
|
375
|
+
function resolveManagedDescriptorPath(appId) {
|
|
376
|
+
const descriptorPath = join(getManagedAppDir(appId), "aai.json");
|
|
377
377
|
if (existsSync(descriptorPath)) return descriptorPath;
|
|
378
378
|
return descriptorPath;
|
|
379
379
|
}
|
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.js","names":[],"sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env node\n\nimport { existsSync, readFileSync } from 'node:fs';\nimport { join } from 'node:path';\n\nimport { createDesktopDiscovery } from './discovery/index.js';\nimport { getMcpExecutor } from './executors/mcp.js';\nimport {\n buildMcpImportConfig,\n buildSkillImportSource,\n type ExposureMode,\n EXPOSURE_LIMITS,\n IMPORT_LIMITS,\n importMcpServer,\n importSkill,\n normalizeExposureInput,\n validateImportHeaders,\n} from './mcp/importer.js';\nimport { createGatewayServer } from './mcp/server.js';\nimport { upsertMcpRegistryEntry } from './storage/mcp-registry.js';\nimport { getManagedAppDir } from './storage/paths.js';\nimport { createSecureStorage } from './storage/secure-storage/index.js';\nimport { upsertSkillRegistryEntry } from './storage/skill-registry.js';\nimport { isMcpAccess, isSkillAccess, type AaiJson } from './types/aai-json.js';\nimport { logger } from './utils/logger.js';\nimport { AAI_GATEWAY_VERSION } from './version.js';\n\ninterface CommonOptions {\n dev: boolean;\n}\n\ninterface ServeOptions extends CommonOptions {\n command: 'serve';\n}\n\ninterface ScanOptions extends CommonOptions {\n command: 'scan';\n}\n\ninterface ExposureOptions {\n exposure: ExposureMode;\n summary: string;\n keywords: string[];\n}\n\ninterface McpImportOptions extends CommonOptions, ExposureOptions {\n command: 'mcp-import';\n name?: string;\n transport?: 'streamable-http' | 'sse';\n url?: string;\n launchCommand?: string;\n timeout?: number;\n launchArgs: string[];\n launchEnv: Record<string, string>;\n launchCwd?: string;\n headers: Record<string, string>;\n}\n\ninterface SkillImportOptions extends CommonOptions, ExposureOptions {\n command: 'skill-import';\n path?: string;\n url?: string;\n}\n\ninterface AppConfigOptions extends CommonOptions {\n command: 'app-config';\n localId: string;\n exposure?: ExposureMode;\n keywords?: string[];\n summary?: string;\n}\n\ntype CliOptions = ServeOptions | ScanOptions | McpImportOptions | SkillImportOptions | AppConfigOptions;\n\nfunction parseKeyValue(value: string, flag: string): [string, string] {\n const index = value.indexOf('=');\n if (index === -1) {\n throw new Error(`${flag} expects KEY=VALUE`);\n }\n return [value.slice(0, index), value.slice(index + 1)];\n}\n\nfunction parsePositiveInteger(value: string, flag: string): number {\n const parsed = Number(value);\n if (!Number.isInteger(parsed) || parsed <= 0) {\n throw new Error(`${flag} expects a positive integer`);\n }\n return parsed;\n}\n\nfunction parseArgs(args: string[]): CliOptions {\n const dev = args.includes('--dev');\n if (args.includes('--scan')) {\n return { command: 'scan', dev };\n }\n\n if (args[0] === 'mcp' && args[1] === 'import') {\n const exposure = parseRequiredExposureArgs(args.slice(2));\n let name: string | undefined;\n let transport: 'streamable-http' | 'sse' | undefined;\n let url: string | undefined;\n let launchCommand: string | undefined;\n let timeout: number | undefined;\n let launchCwd: string | undefined;\n const launchArgs: string[] = [];\n const launchEnv: Record<string, string> = {};\n const headers: Record<string, string> = {};\n\n for (let i = 2; i < args.length; i += 1) {\n const arg = args[i];\n const next = args[i + 1];\n switch (arg) {\n case '--dev':\n break;\n case '--exposure':\n case '--summary':\n case '--keyword':\n i += 1;\n break;\n case '--name':\n name = next;\n i += 1;\n break;\n case '--transport':\n if (next !== 'streamable-http' && next !== 'sse') {\n throw new Error('--transport must be streamable-http or sse');\n }\n transport = next;\n i += 1;\n break;\n case '--url':\n url = next;\n i += 1;\n break;\n case '--command':\n launchCommand = next;\n i += 1;\n break;\n case '--timeout':\n timeout = parsePositiveInteger(next, '--timeout');\n i += 1;\n break;\n case '--arg':\n launchArgs.push(next);\n i += 1;\n break;\n case '--env': {\n const [key, value] = parseKeyValue(next, '--env');\n launchEnv[key] = value;\n i += 1;\n break;\n }\n case '--cwd':\n launchCwd = next;\n i += 1;\n break;\n case '--header': {\n const [key, value] = parseKeyValue(next, '--header');\n headers[key] = value;\n i += 1;\n break;\n }\n default:\n if (\n arg.startsWith('--') &&\n ![\n '--dev',\n '--exposure',\n '--summary',\n '--keyword',\n '--name',\n '--transport',\n '--url',\n '--command',\n '--timeout',\n '--arg',\n '--env',\n '--cwd',\n '--header',\n ].includes(arg)\n ) {\n throw new Error(`Unknown argument: ${arg}`);\n }\n }\n }\n\n return {\n command: 'mcp-import',\n dev,\n ...exposure,\n ...(name ? { name } : {}),\n transport,\n url,\n launchCommand,\n ...(timeout !== undefined ? { timeout } : {}),\n launchArgs,\n launchEnv,\n launchCwd,\n headers,\n };\n }\n\n if (args[0] === 'skill' && args[1] === 'import') {\n const exposure = parseRequiredExposureArgs(args.slice(2));\n let path: string | undefined;\n let url: string | undefined;\n for (let i = 2; i < args.length; i += 1) {\n const arg = args[i];\n const next = args[i + 1];\n switch (arg) {\n case '--dev':\n break;\n case '--exposure':\n case '--summary':\n case '--keyword':\n i += 1;\n break;\n case '--path':\n path = next;\n i += 1;\n break;\n case '--url':\n url = next;\n i += 1;\n break;\n default:\n if (arg.startsWith('--') && !['--path', '--url', '--dev', '--exposure', '--summary', '--keyword'].includes(arg)) {\n throw new Error(`Unknown argument: ${arg}`);\n }\n }\n }\n return { command: 'skill-import', dev, ...exposure, path, url };\n }\n\n if (args[0] === 'app' && args[1] === 'config') {\n const localId = args[2];\n if (!localId) {\n throw new Error('Usage: aai-gateway app config <app-id>');\n }\n\n let exposure: ExposureMode | undefined;\n let summary: string | undefined;\n const keywords: string[] = [];\n\n for (let i = 3; i < args.length; i += 1) {\n const arg = args[i];\n const next = args[i + 1];\n switch (arg) {\n case '--dev':\n break;\n case '--exposure':\n if (next !== 'summary' && next !== 'keywords') {\n throw new Error('--exposure must be summary or keywords');\n }\n exposure = next;\n i += 1;\n break;\n case '--summary':\n summary = next;\n i += 1;\n break;\n case '--keyword':\n keywords.push(next);\n i += 1;\n break;\n default:\n if (arg.startsWith('--') && !['--dev', '--exposure', '--summary', '--keyword'].includes(arg)) {\n throw new Error(`Unknown argument: ${arg}`);\n }\n }\n }\n\n return {\n command: 'app-config',\n dev,\n localId,\n ...(exposure ? { exposure } : {}),\n ...(summary ? { summary } : {}),\n ...(keywords.length > 0 ? { keywords } : {}),\n };\n }\n\n return { command: 'serve', dev };\n}\n\nfunction parseRequiredExposureArgs(args: string[]): ExposureOptions {\n let exposure: ExposureMode | undefined;\n let summary: string | undefined;\n const keywords: string[] = [];\n\n for (let i = 0; i < args.length; i += 1) {\n const arg = args[i];\n const next = args[i + 1];\n switch (arg) {\n case '--exposure':\n if (next !== 'summary' && next !== 'keywords') {\n throw new Error('--exposure must be summary or keywords');\n }\n exposure = next;\n i += 1;\n break;\n case '--summary':\n summary = next;\n i += 1;\n break;\n case '--keyword':\n keywords.push(next);\n i += 1;\n break;\n default:\n break;\n }\n }\n\n if (!exposure) {\n throw new Error('Import requires --exposure summary|keywords');\n }\n\n if (!summary) {\n throw new Error(`Import requires --summary (maximum ${EXPOSURE_LIMITS.summaryLength} characters)`);\n }\n\n if (keywords.length === 0) {\n throw new Error(`Import requires at least one --keyword (maximum ${EXPOSURE_LIMITS.keywordCount} total)`);\n }\n\n return {\n exposure,\n ...normalizeExposureInput({ keywords, summary }),\n };\n}\n\nfunction printHelp(): void {\n console.log(`\nAAI Gateway\n\nUsage:\n aai-gateway [options]\n aai-gateway mcp import [options]\n aai-gateway skill import [options]\n aai-gateway app config <app-id> [options]\n\nOptions:\n --scan Scan for desktop descriptors and exit\n --dev Enable development mode\n --version Show version\n --help, -h Show help\n\nShared metadata options:\n --exposure MODE Required for import. One of: summary, keywords\n --summary TEXT Required for import, max ${EXPOSURE_LIMITS.summaryLength} characters\n --keyword VALUE Required for import and repeatable, max ${EXPOSURE_LIMITS.keywordCount} items, each max ${EXPOSURE_LIMITS.keywordLength} characters\n\nMCP import options:\n --name TEXT Optional app name used for display and app id generation, max ${IMPORT_LIMITS.nameLength} chars\n --command CMD Import a local stdio MCP server, max ${IMPORT_LIMITS.commandLength} chars\n --timeout MS Optional MCP downstream inactivity timeout in milliseconds, default 60000\n --arg VALUE Repeatable stdio argument, max ${IMPORT_LIMITS.argCount} items, each max ${IMPORT_LIMITS.argLength} chars\n --env KEY=VALUE Repeatable stdio environment variable, max ${IMPORT_LIMITS.envCount} entries\n --cwd DIR Working directory for stdio launch, max ${IMPORT_LIMITS.cwdLength} chars\n --url URL Import a remote MCP server, max ${IMPORT_LIMITS.urlLength} chars\n --transport TYPE Remote transport: streamable-http or sse\n --header KEY=VALUE Repeatable remote header stored in secure storage, max ${IMPORT_LIMITS.headerCount} entries\n\nSkill import options:\n --path DIR Import a local skill directory, max ${IMPORT_LIMITS.pathLength} chars\n --url URL Import a remote skill root URL, max ${IMPORT_LIMITS.urlLength} chars\n\nApp config options:\n --exposure MODE Optional. Update the recorded exposure mode\n --summary TEXT Optional. Override the current summary\n --keyword VALUE Optional and repeatable. Replace the current keywords\n`);\n}\n\nasync function runScan(dev: boolean): Promise<void> {\n const discovery = createDesktopDiscovery();\n const apps = await discovery.scan({ devMode: dev });\n\n if (apps.length === 0) {\n console.log('No desktop descriptors found.');\n return;\n }\n\n for (const app of apps) {\n console.log(`${app.localId}`);\n console.log(` Name: ${app.descriptor.app.name.default}`);\n console.log(` Location: ${app.location ?? '(unknown)'}`);\n console.log(` Protocol: ${app.descriptor.access.protocol}`);\n console.log(` Summary: ${app.descriptor.exposure.summary}`);\n }\n}\n\nasync function runMcpImport(options: McpImportOptions): Promise<void> {\n const storage = createSecureStorage();\n const executor = getMcpExecutor();\n validateImportHeaders(options.headers);\n const config = buildMcpImportConfig({\n transport: options.transport,\n url: options.url,\n command: options.launchCommand,\n timeout: options.timeout,\n args: options.launchArgs,\n env: options.launchEnv,\n cwd: options.launchCwd,\n });\n\n const result = await importMcpServer(executor, storage, {\n name: options.name,\n exposureMode: options.exposure,\n keywords: options.keywords,\n summary: options.summary,\n config,\n headers: options.headers,\n });\n\n console.log(`Imported MCP app: ${result.descriptor.app.name.default}`);\n console.log(`App ID: ${result.entry.localId}`);\n console.log(`Descriptor: ${result.entry.descriptorPath}`);\n console.log(`Managed directory: ${getManagedAppDir(result.entry.localId)}`);\n console.log(`Tool name after restart: app:${result.entry.localId}`);\n console.log(`Keywords: ${result.descriptor.exposure.keywords.join(', ')}`);\n console.log(`Summary: ${result.descriptor.exposure.summary}`);\n console.log(`Exposure mode: ${options.exposure}`);\n}\n\nasync function runSkillImport(options: SkillImportOptions): Promise<void> {\n const source = buildSkillImportSource({\n path: options.path,\n url: options.url,\n });\n\n const result = await importSkill({\n exposureMode: options.exposure,\n keywords: options.keywords,\n summary: options.summary,\n path: source.path,\n url: source.url,\n });\n\n console.log(`Imported skill: ${result.descriptor.app.name.default}`);\n console.log(`App ID: ${result.localId}`);\n console.log(`Descriptor: ${join(getManagedAppDir(result.localId), 'aai.json')}`);\n console.log(`Skill directory: ${result.managedPath}`);\n console.log(`Tool name after restart: app:${result.localId}`);\n console.log(`Keywords: ${result.descriptor.exposure.keywords.join(', ')}`);\n console.log(`Summary: ${result.descriptor.exposure.summary}`);\n console.log(`Exposure mode: ${options.exposure}`);\n}\n\nasync function runAppConfig(options: AppConfigOptions): Promise<void> {\n const descriptorPath = resolveManagedDescriptorPath(options.localId);\n const descriptor = JSON.parse(readFileSync(descriptorPath, 'utf-8')) as AaiJson;\n\n const nextExposure = normalizeAppConfigExposure(options, descriptor.exposure);\n const nextDescriptor: AaiJson = {\n ...descriptor,\n exposure: nextExposure,\n };\n\n if (isMcpAccess(nextDescriptor.access)) {\n await upsertMcpRegistryEntry(\n {\n localId: options.localId,\n protocol: 'mcp',\n config: nextDescriptor.access.config,\n },\n nextDescriptor\n );\n } else if (isSkillAccess(nextDescriptor.access)) {\n await upsertSkillRegistryEntry(\n {\n localId: options.localId,\n protocol: 'skill',\n config: nextDescriptor.access.config,\n },\n nextDescriptor\n );\n } else {\n throw new Error(`App '${options.localId}' is not an imported MCP app or imported skill`);\n }\n\n console.log(`Updated app: ${options.localId}`);\n if (options.exposure) {\n console.log(`Exposure mode: ${options.exposure}`);\n }\n console.log(`Keywords: ${nextDescriptor.exposure.keywords.join(', ')}`);\n console.log(`Summary: ${nextDescriptor.exposure.summary}`);\n}\n\nfunction normalizeAppConfigExposure(\n options: AppConfigOptions,\n current: AaiJson['exposure']\n): AaiJson['exposure'] {\n const summary = options.summary ?? current.summary;\n const keywords = options.keywords ?? current.keywords;\n return normalizeExposureInput({ keywords, summary });\n}\n\nfunction resolveManagedDescriptorPath(localId: string): string {\n const descriptorPath = join(getManagedAppDir(localId), 'aai.json');\n if (existsSync(descriptorPath)) {\n return descriptorPath;\n }\n\n return descriptorPath;\n}\n\nasync function main(): Promise<void> {\n const args = process.argv.slice(2);\n\n if (args.includes('--version')) {\n console.log(AAI_GATEWAY_VERSION);\n return;\n }\n\n if (args.includes('--help') || args.includes('-h')) {\n printHelp();\n return;\n }\n\n const options = parseArgs(args);\n\n switch (options.command) {\n case 'scan':\n await runScan(options.dev);\n return;\n case 'mcp-import':\n await runMcpImport(options);\n return;\n case 'skill-import':\n await runSkillImport(options);\n return;\n case 'app-config':\n await runAppConfig(options);\n return;\n case 'serve': {\n const server = await createGatewayServer({ devMode: options.dev });\n await server.start();\n return;\n }\n }\n}\n\nmain().catch((err) => {\n logger.fatal({ err }, 'Fatal error');\n process.exit(1);\n});\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"cli.js","names":[],"sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env node\n\nimport { existsSync, readFileSync } from 'node:fs';\nimport { join } from 'node:path';\n\nimport { createDesktopDiscovery } from './discovery/index.js';\nimport { getMcpExecutor } from './executors/mcp.js';\nimport {\n buildMcpImportConfig,\n buildSkillImportSource,\n type ExposureMode,\n EXPOSURE_LIMITS,\n IMPORT_LIMITS,\n importMcpServer,\n importSkill,\n normalizeExposureInput,\n validateImportHeaders,\n} from './mcp/importer.js';\nimport { createGatewayServer } from './mcp/server.js';\nimport { upsertMcpRegistryEntry } from './storage/mcp-registry.js';\nimport { getManagedAppDir } from './storage/paths.js';\nimport { createSecureStorage } from './storage/secure-storage/index.js';\nimport { upsertSkillRegistryEntry } from './storage/skill-registry.js';\nimport { isMcpAccess, isSkillAccess, type AaiJson } from './types/aai-json.js';\nimport { logger } from './utils/logger.js';\nimport { AAI_GATEWAY_VERSION } from './version.js';\n\ninterface CommonOptions {\n dev: boolean;\n}\n\ninterface ServeOptions extends CommonOptions {\n command: 'serve';\n}\n\ninterface ScanOptions extends CommonOptions {\n command: 'scan';\n}\n\ninterface ExposureOptions {\n exposure: ExposureMode;\n summary: string;\n keywords: string[];\n}\n\ninterface McpImportOptions extends CommonOptions, ExposureOptions {\n command: 'mcp-import';\n name?: string;\n transport?: 'streamable-http' | 'sse';\n url?: string;\n launchCommand?: string;\n timeout?: number;\n launchArgs: string[];\n launchEnv: Record<string, string>;\n launchCwd?: string;\n headers: Record<string, string>;\n}\n\ninterface SkillImportOptions extends CommonOptions, ExposureOptions {\n command: 'skill-import';\n path?: string;\n url?: string;\n}\n\ninterface AppConfigOptions extends CommonOptions {\n command: 'app-config';\n appId: string;\n exposure?: ExposureMode;\n keywords?: string[];\n summary?: string;\n}\n\ntype CliOptions = ServeOptions | ScanOptions | McpImportOptions | SkillImportOptions | AppConfigOptions;\n\nfunction parseKeyValue(value: string, flag: string): [string, string] {\n const index = value.indexOf('=');\n if (index === -1) {\n throw new Error(`${flag} expects KEY=VALUE`);\n }\n return [value.slice(0, index), value.slice(index + 1)];\n}\n\nfunction parsePositiveInteger(value: string, flag: string): number {\n const parsed = Number(value);\n if (!Number.isInteger(parsed) || parsed <= 0) {\n throw new Error(`${flag} expects a positive integer`);\n }\n return parsed;\n}\n\nfunction parseArgs(args: string[]): CliOptions {\n const dev = args.includes('--dev');\n if (args.includes('--scan')) {\n return { command: 'scan', dev };\n }\n\n if (args[0] === 'mcp' && args[1] === 'import') {\n const exposure = parseRequiredExposureArgs(args.slice(2));\n let name: string | undefined;\n let transport: 'streamable-http' | 'sse' | undefined;\n let url: string | undefined;\n let launchCommand: string | undefined;\n let timeout: number | undefined;\n let launchCwd: string | undefined;\n const launchArgs: string[] = [];\n const launchEnv: Record<string, string> = {};\n const headers: Record<string, string> = {};\n\n for (let i = 2; i < args.length; i += 1) {\n const arg = args[i];\n const next = args[i + 1];\n switch (arg) {\n case '--dev':\n break;\n case '--exposure':\n case '--summary':\n case '--keyword':\n i += 1;\n break;\n case '--name':\n name = next;\n i += 1;\n break;\n case '--transport':\n if (next !== 'streamable-http' && next !== 'sse') {\n throw new Error('--transport must be streamable-http or sse');\n }\n transport = next;\n i += 1;\n break;\n case '--url':\n url = next;\n i += 1;\n break;\n case '--command':\n launchCommand = next;\n i += 1;\n break;\n case '--timeout':\n timeout = parsePositiveInteger(next, '--timeout');\n i += 1;\n break;\n case '--arg':\n launchArgs.push(next);\n i += 1;\n break;\n case '--env': {\n const [key, value] = parseKeyValue(next, '--env');\n launchEnv[key] = value;\n i += 1;\n break;\n }\n case '--cwd':\n launchCwd = next;\n i += 1;\n break;\n case '--header': {\n const [key, value] = parseKeyValue(next, '--header');\n headers[key] = value;\n i += 1;\n break;\n }\n default:\n if (\n arg.startsWith('--') &&\n ![\n '--dev',\n '--exposure',\n '--summary',\n '--keyword',\n '--name',\n '--transport',\n '--url',\n '--command',\n '--timeout',\n '--arg',\n '--env',\n '--cwd',\n '--header',\n ].includes(arg)\n ) {\n throw new Error(`Unknown argument: ${arg}`);\n }\n }\n }\n\n return {\n command: 'mcp-import',\n dev,\n ...exposure,\n ...(name ? { name } : {}),\n transport,\n url,\n launchCommand,\n ...(timeout !== undefined ? { timeout } : {}),\n launchArgs,\n launchEnv,\n launchCwd,\n headers,\n };\n }\n\n if (args[0] === 'skill' && args[1] === 'import') {\n const exposure = parseRequiredExposureArgs(args.slice(2));\n let path: string | undefined;\n let url: string | undefined;\n for (let i = 2; i < args.length; i += 1) {\n const arg = args[i];\n const next = args[i + 1];\n switch (arg) {\n case '--dev':\n break;\n case '--exposure':\n case '--summary':\n case '--keyword':\n i += 1;\n break;\n case '--path':\n path = next;\n i += 1;\n break;\n case '--url':\n url = next;\n i += 1;\n break;\n default:\n if (arg.startsWith('--') && !['--path', '--url', '--dev', '--exposure', '--summary', '--keyword'].includes(arg)) {\n throw new Error(`Unknown argument: ${arg}`);\n }\n }\n }\n return { command: 'skill-import', dev, ...exposure, path, url };\n }\n\n if (args[0] === 'app' && args[1] === 'config') {\n const appId = args[2];\n if (!appId) {\n throw new Error('Usage: aai-gateway app config <app-id>');\n }\n\n let exposure: ExposureMode | undefined;\n let summary: string | undefined;\n const keywords: string[] = [];\n\n for (let i = 3; i < args.length; i += 1) {\n const arg = args[i];\n const next = args[i + 1];\n switch (arg) {\n case '--dev':\n break;\n case '--exposure':\n if (next !== 'summary' && next !== 'keywords') {\n throw new Error('--exposure must be summary or keywords');\n }\n exposure = next;\n i += 1;\n break;\n case '--summary':\n summary = next;\n i += 1;\n break;\n case '--keyword':\n keywords.push(next);\n i += 1;\n break;\n default:\n if (arg.startsWith('--') && !['--dev', '--exposure', '--summary', '--keyword'].includes(arg)) {\n throw new Error(`Unknown argument: ${arg}`);\n }\n }\n }\n\n return {\n command: 'app-config',\n dev,\n appId,\n ...(exposure ? { exposure } : {}),\n ...(summary ? { summary } : {}),\n ...(keywords.length > 0 ? { keywords } : {}),\n };\n }\n\n return { command: 'serve', dev };\n}\n\nfunction parseRequiredExposureArgs(args: string[]): ExposureOptions {\n let exposure: ExposureMode | undefined;\n let summary: string | undefined;\n const keywords: string[] = [];\n\n for (let i = 0; i < args.length; i += 1) {\n const arg = args[i];\n const next = args[i + 1];\n switch (arg) {\n case '--exposure':\n if (next !== 'summary' && next !== 'keywords') {\n throw new Error('--exposure must be summary or keywords');\n }\n exposure = next;\n i += 1;\n break;\n case '--summary':\n summary = next;\n i += 1;\n break;\n case '--keyword':\n keywords.push(next);\n i += 1;\n break;\n default:\n break;\n }\n }\n\n if (!exposure) {\n throw new Error('Import requires --exposure summary|keywords');\n }\n\n if (!summary) {\n throw new Error(`Import requires --summary (maximum ${EXPOSURE_LIMITS.summaryLength} characters)`);\n }\n\n if (keywords.length === 0) {\n throw new Error(`Import requires at least one --keyword (maximum ${EXPOSURE_LIMITS.keywordCount} total)`);\n }\n\n return {\n exposure,\n ...normalizeExposureInput({ keywords, summary }),\n };\n}\n\nfunction printHelp(): void {\n console.log(`\nAAI Gateway\n\nUsage:\n aai-gateway [options]\n aai-gateway mcp import [options]\n aai-gateway skill import [options]\n aai-gateway app config <app-id> [options]\n\nOptions:\n --scan Scan for desktop descriptors and exit\n --dev Enable development mode\n --version Show version\n --help, -h Show help\n\nShared metadata options:\n --exposure MODE Required for import. One of: summary, keywords\n --summary TEXT Required for import, max ${EXPOSURE_LIMITS.summaryLength} characters\n --keyword VALUE Required for import and repeatable, max ${EXPOSURE_LIMITS.keywordCount} items, each max ${EXPOSURE_LIMITS.keywordLength} characters\n\nMCP import options:\n --name TEXT Optional app name used for display and app id generation, max ${IMPORT_LIMITS.nameLength} chars\n --command CMD Import a local stdio MCP server, max ${IMPORT_LIMITS.commandLength} chars\n --timeout MS Optional MCP downstream inactivity timeout in milliseconds, default 60000\n --arg VALUE Repeatable stdio argument, max ${IMPORT_LIMITS.argCount} items, each max ${IMPORT_LIMITS.argLength} chars\n --env KEY=VALUE Repeatable stdio environment variable, max ${IMPORT_LIMITS.envCount} entries\n --cwd DIR Working directory for stdio launch, max ${IMPORT_LIMITS.cwdLength} chars\n --url URL Import a remote MCP server, max ${IMPORT_LIMITS.urlLength} chars\n --transport TYPE Remote transport: streamable-http or sse\n --header KEY=VALUE Repeatable remote header stored in secure storage, max ${IMPORT_LIMITS.headerCount} entries\n\nSkill import options:\n --path DIR Import a local skill directory, max ${IMPORT_LIMITS.pathLength} chars\n --url URL Import a remote skill root URL, max ${IMPORT_LIMITS.urlLength} chars\n\nApp config options:\n --exposure MODE Optional. Update the recorded exposure mode\n --summary TEXT Optional. Override the current summary\n --keyword VALUE Optional and repeatable. Replace the current keywords\n`);\n}\n\nasync function runScan(dev: boolean): Promise<void> {\n const discovery = createDesktopDiscovery();\n const apps = await discovery.scan({ devMode: dev });\n\n if (apps.length === 0) {\n console.log('No desktop descriptors found.');\n return;\n }\n\n for (const app of apps) {\n console.log(`${app.appId}`);\n console.log(` Name: ${app.descriptor.app.name.default}`);\n console.log(` Location: ${app.location ?? '(unknown)'}`);\n console.log(` Protocol: ${app.descriptor.access.protocol}`);\n console.log(` Summary: ${app.descriptor.exposure.summary}`);\n }\n}\n\nasync function runMcpImport(options: McpImportOptions): Promise<void> {\n const storage = createSecureStorage();\n const executor = getMcpExecutor();\n validateImportHeaders(options.headers);\n const config = buildMcpImportConfig({\n transport: options.transport,\n url: options.url,\n command: options.launchCommand,\n timeout: options.timeout,\n args: options.launchArgs,\n env: options.launchEnv,\n cwd: options.launchCwd,\n });\n\n const result = await importMcpServer(executor, storage, {\n name: options.name,\n exposureMode: options.exposure,\n keywords: options.keywords,\n summary: options.summary,\n config,\n headers: options.headers,\n });\n\n console.log(`Imported MCP app: ${result.descriptor.app.name.default}`);\n console.log(`App ID: ${result.entry.appId}`);\n console.log(`Descriptor: ${result.entry.descriptorPath}`);\n console.log(`Managed directory: ${getManagedAppDir(result.entry.appId)}`);\n console.log(`Tool name after restart: app:${result.entry.appId}`);\n console.log(`Keywords: ${result.descriptor.exposure.keywords.join(', ')}`);\n console.log(`Summary: ${result.descriptor.exposure.summary}`);\n console.log(`Exposure mode: ${options.exposure}`);\n}\n\nasync function runSkillImport(options: SkillImportOptions): Promise<void> {\n const source = buildSkillImportSource({\n path: options.path,\n url: options.url,\n });\n\n const result = await importSkill({\n exposureMode: options.exposure,\n keywords: options.keywords,\n summary: options.summary,\n path: source.path,\n url: source.url,\n });\n\n console.log(`Imported skill: ${result.descriptor.app.name.default}`);\n console.log(`App ID: ${result.appId}`);\n console.log(`Descriptor: ${join(getManagedAppDir(result.appId), 'aai.json')}`);\n console.log(`Skill directory: ${result.managedPath}`);\n console.log(`Tool name after restart: app:${result.appId}`);\n console.log(`Keywords: ${result.descriptor.exposure.keywords.join(', ')}`);\n console.log(`Summary: ${result.descriptor.exposure.summary}`);\n console.log(`Exposure mode: ${options.exposure}`);\n}\n\nasync function runAppConfig(options: AppConfigOptions): Promise<void> {\n const descriptorPath = resolveManagedDescriptorPath(options.appId);\n const descriptor = JSON.parse(readFileSync(descriptorPath, 'utf-8')) as AaiJson;\n\n const nextExposure = normalizeAppConfigExposure(options, descriptor.exposure);\n const nextDescriptor: AaiJson = {\n ...descriptor,\n exposure: nextExposure,\n };\n\n if (isMcpAccess(nextDescriptor.access)) {\n await upsertMcpRegistryEntry(\n {\n appId: options.appId,\n protocol: 'mcp',\n config: nextDescriptor.access.config,\n },\n nextDescriptor\n );\n } else if (isSkillAccess(nextDescriptor.access)) {\n await upsertSkillRegistryEntry(\n {\n appId: options.appId,\n protocol: 'skill',\n config: nextDescriptor.access.config,\n },\n nextDescriptor\n );\n } else {\n throw new Error(`App '${options.appId}' is not an imported MCP app or imported skill`);\n }\n\n console.log(`Updated app: ${options.appId}`);\n if (options.exposure) {\n console.log(`Exposure mode: ${options.exposure}`);\n }\n console.log(`Keywords: ${nextDescriptor.exposure.keywords.join(', ')}`);\n console.log(`Summary: ${nextDescriptor.exposure.summary}`);\n}\n\nfunction normalizeAppConfigExposure(\n options: AppConfigOptions,\n current: AaiJson['exposure']\n): AaiJson['exposure'] {\n const summary = options.summary ?? current.summary;\n const keywords = options.keywords ?? current.keywords;\n return normalizeExposureInput({ keywords, summary });\n}\n\nfunction resolveManagedDescriptorPath(appId: string): string {\n const descriptorPath = join(getManagedAppDir(appId), 'aai.json');\n if (existsSync(descriptorPath)) {\n return descriptorPath;\n }\n\n return descriptorPath;\n}\n\nasync function main(): Promise<void> {\n const args = process.argv.slice(2);\n\n if (args.includes('--version')) {\n console.log(AAI_GATEWAY_VERSION);\n return;\n }\n\n if (args.includes('--help') || args.includes('-h')) {\n printHelp();\n return;\n }\n\n const options = parseArgs(args);\n\n switch (options.command) {\n case 'scan':\n await runScan(options.dev);\n return;\n case 'mcp-import':\n await runMcpImport(options);\n return;\n case 'skill-import':\n await runSkillImport(options);\n return;\n case 'app-config':\n await runAppConfig(options);\n return;\n case 'serve': {\n const server = await createGatewayServer({ devMode: options.dev });\n await server.start();\n return;\n }\n }\n}\n\nmain().catch((err) => {\n logger.fatal({ err }, 'Fatal error');\n process.exit(1);\n});\n"],"mappings":"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"}
|