opena2a-cli 0.7.2 → 0.8.1
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 +51 -603
- package/dist/adapters/docker.d.ts.map +1 -1
- package/dist/adapters/docker.js +5 -2
- package/dist/adapters/docker.js.map +1 -1
- package/dist/adapters/registry.d.ts.map +1 -1
- package/dist/adapters/registry.js +1 -0
- package/dist/adapters/registry.js.map +1 -1
- package/dist/adapters/types.d.ts +2 -0
- package/dist/adapters/types.d.ts.map +1 -1
- package/dist/commands/create/index.d.ts +21 -0
- package/dist/commands/create/index.d.ts.map +1 -0
- package/dist/commands/create/index.js +44 -0
- package/dist/commands/create/index.js.map +1 -0
- package/dist/commands/create/skill.d.ts +35 -0
- package/dist/commands/create/skill.d.ts.map +1 -0
- package/dist/commands/create/skill.js +250 -0
- package/dist/commands/create/skill.js.map +1 -0
- package/dist/commands/create/templates.d.ts +47 -0
- package/dist/commands/create/templates.d.ts.map +1 -0
- package/dist/commands/create/templates.js +392 -0
- package/dist/commands/create/templates.js.map +1 -0
- package/dist/commands/detect.js +1 -1
- package/dist/commands/detect.js.map +1 -1
- package/dist/commands/guard-harden.d.ts +15 -0
- package/dist/commands/guard-harden.d.ts.map +1 -0
- package/dist/commands/guard-harden.js +242 -0
- package/dist/commands/guard-harden.js.map +1 -0
- package/dist/commands/guard.d.ts +3 -1
- package/dist/commands/guard.d.ts.map +1 -1
- package/dist/commands/guard.js +25 -1
- package/dist/commands/guard.js.map +1 -1
- package/dist/commands/identity.d.ts +5 -0
- package/dist/commands/identity.d.ts.map +1 -1
- package/dist/commands/identity.js +515 -11
- package/dist/commands/identity.js.map +1 -1
- package/dist/commands/login.d.ts +24 -0
- package/dist/commands/login.d.ts.map +1 -0
- package/dist/commands/login.js +238 -0
- package/dist/commands/login.js.map +1 -0
- package/dist/commands/mcp-audit.d.ts.map +1 -1
- package/dist/commands/mcp-audit.js +4 -0
- package/dist/commands/mcp-audit.js.map +1 -1
- package/dist/index.js +124 -5
- package/dist/index.js.map +1 -1
- package/dist/lib/registry-client.d.ts +59 -0
- package/dist/lib/registry-client.d.ts.map +1 -0
- package/dist/lib/registry-client.js +169 -0
- package/dist/lib/registry-client.js.map +1 -0
- package/dist/semantic/command-index.json +27 -0
- package/dist/shield/status.d.ts.map +1 -1
- package/dist/shield/status.js +22 -1
- package/dist/shield/status.js.map +1 -1
- package/dist/util/aim-client.d.ts +141 -0
- package/dist/util/aim-client.d.ts.map +1 -0
- package/dist/util/aim-client.js +225 -0
- package/dist/util/aim-client.js.map +1 -0
- package/dist/util/auth.d.ts +23 -0
- package/dist/util/auth.d.ts.map +1 -0
- package/dist/util/auth.js +54 -0
- package/dist/util/auth.js.map +1 -0
- package/dist/util/server-url.d.ts +15 -0
- package/dist/util/server-url.d.ts.map +1 -0
- package/dist/util/server-url.js +45 -0
- package/dist/util/server-url.js.map +1 -0
- package/package.json +1 -1
|
@@ -37,6 +37,9 @@ exports.identity = identity;
|
|
|
37
37
|
const path = __importStar(require("path"));
|
|
38
38
|
const fs = __importStar(require("fs"));
|
|
39
39
|
const colors_js_1 = require("../util/colors.js");
|
|
40
|
+
const server_url_js_1 = require("../util/server-url.js");
|
|
41
|
+
const aim_client_js_1 = require("../util/aim-client.js");
|
|
42
|
+
const auth_js_1 = require("../util/auth.js");
|
|
40
43
|
const USAGE = [
|
|
41
44
|
'',
|
|
42
45
|
'Usage: opena2a identity <subcommand>',
|
|
@@ -64,8 +67,29 @@ const USAGE = [
|
|
|
64
67
|
' detach Remove cross-tool wiring',
|
|
65
68
|
' sync Sync events from enabled tools',
|
|
66
69
|
'',
|
|
70
|
+
'Server Integration',
|
|
71
|
+
' connect <url> Connect local identity to an AIM server',
|
|
72
|
+
' disconnect Remove server association (keep local identity)',
|
|
73
|
+
'',
|
|
74
|
+
'Server Flags (for create, list, trust, audit, log):',
|
|
75
|
+
' --server <url> AIM server URL (e.g. localhost:8080, cloud)',
|
|
76
|
+
' --api-key <key> AIM API key for authentication',
|
|
77
|
+
'',
|
|
67
78
|
].join('\n');
|
|
68
79
|
async function identity(options) {
|
|
80
|
+
// Normalize --json flag to format
|
|
81
|
+
if (options.json) {
|
|
82
|
+
options.format = 'json';
|
|
83
|
+
}
|
|
84
|
+
// Resolve --server URL when provided
|
|
85
|
+
if (options.server) {
|
|
86
|
+
options.server = (0, server_url_js_1.resolveServerUrl)(options.server);
|
|
87
|
+
const serverCommands = ['create', 'list', 'show', 'trust'];
|
|
88
|
+
if (!serverCommands.includes(options.subcommand)) {
|
|
89
|
+
process.stderr.write((0, colors_js_1.yellow)(`Warning: --server is not supported for "identity ${options.subcommand}". Operating in local mode.`) + '\n\n');
|
|
90
|
+
options.server = undefined;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
69
93
|
const sub = options.subcommand;
|
|
70
94
|
switch (sub) {
|
|
71
95
|
case 'list':
|
|
@@ -93,6 +117,10 @@ async function identity(options) {
|
|
|
93
117
|
return handleDetach(options);
|
|
94
118
|
case 'sync':
|
|
95
119
|
return handleSync(options);
|
|
120
|
+
case 'connect':
|
|
121
|
+
return handleConnect(options);
|
|
122
|
+
case 'disconnect':
|
|
123
|
+
return handleDisconnect(options);
|
|
96
124
|
default:
|
|
97
125
|
process.stderr.write(`Unknown identity subcommand: ${sub}\n`);
|
|
98
126
|
process.stderr.write(USAGE + '\n');
|
|
@@ -110,6 +138,96 @@ async function loadAimCore() {
|
|
|
110
138
|
}
|
|
111
139
|
}
|
|
112
140
|
// ---------------------------------------------------------------------------
|
|
141
|
+
// Server helpers
|
|
142
|
+
// ---------------------------------------------------------------------------
|
|
143
|
+
/**
|
|
144
|
+
* Build an AimClient from options. Returns null if no server is specified
|
|
145
|
+
* and no stored config exists.
|
|
146
|
+
*/
|
|
147
|
+
function getServerClient(options) {
|
|
148
|
+
const serverFlag = options.server;
|
|
149
|
+
const config = (0, aim_client_js_1.loadServerConfig)();
|
|
150
|
+
const apiKey = options.apiKey ?? config?.apiKey;
|
|
151
|
+
if (serverFlag) {
|
|
152
|
+
const url = (0, server_url_js_1.resolveServerUrl)(serverFlag);
|
|
153
|
+
// If no explicit API key, check global auth for matching server
|
|
154
|
+
if (!apiKey) {
|
|
155
|
+
const globalAuth = (0, auth_js_1.loadAuth)();
|
|
156
|
+
if (globalAuth && (0, auth_js_1.isAuthValid)(globalAuth) && globalAuth.serverUrl === url) {
|
|
157
|
+
return new aim_client_js_1.AimClient(url, { accessToken: globalAuth.accessToken });
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
return new aim_client_js_1.AimClient(url, { apiKey });
|
|
161
|
+
}
|
|
162
|
+
if (config?.serverUrl) {
|
|
163
|
+
return new aim_client_js_1.AimClient(config.serverUrl, { apiKey });
|
|
164
|
+
}
|
|
165
|
+
// No server flag and no stored config -- try global auth
|
|
166
|
+
const globalAuth = (0, auth_js_1.loadAuth)();
|
|
167
|
+
if (globalAuth && (0, auth_js_1.isAuthValid)(globalAuth)) {
|
|
168
|
+
return new aim_client_js_1.AimClient(globalAuth.serverUrl, { accessToken: globalAuth.accessToken });
|
|
169
|
+
}
|
|
170
|
+
return null;
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Check server health. Returns true if reachable, false otherwise.
|
|
174
|
+
* On failure, writes a clear error message.
|
|
175
|
+
*/
|
|
176
|
+
async function checkServerHealth(client, serverUrl) {
|
|
177
|
+
try {
|
|
178
|
+
await client.health();
|
|
179
|
+
return true;
|
|
180
|
+
}
|
|
181
|
+
catch (err) {
|
|
182
|
+
if (err instanceof aim_client_js_1.AimServerError && err.serverMessage) {
|
|
183
|
+
process.stderr.write(`Cannot connect to AIM server at ${serverUrl}. Verify the server is running.\n`);
|
|
184
|
+
process.stderr.write(` Detail: ${err.serverMessage}\n`);
|
|
185
|
+
}
|
|
186
|
+
else {
|
|
187
|
+
process.stderr.write(`Cannot connect to AIM server at ${serverUrl}. Verify the server is running.\n`);
|
|
188
|
+
}
|
|
189
|
+
return false;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Resolve the auth token from stored config.
|
|
194
|
+
*/
|
|
195
|
+
function getStoredAuth() {
|
|
196
|
+
const config = (0, aim_client_js_1.loadServerConfig)();
|
|
197
|
+
if (config) {
|
|
198
|
+
return {
|
|
199
|
+
token: config.accessToken ?? undefined,
|
|
200
|
+
apiKey: config.apiKey ?? undefined,
|
|
201
|
+
agentId: config.agentId,
|
|
202
|
+
serverUrl: config.serverUrl,
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
// Fallback: check global auth credentials from "opena2a login"
|
|
206
|
+
const globalAuth = (0, auth_js_1.loadAuth)();
|
|
207
|
+
if (globalAuth && (0, auth_js_1.isAuthValid)(globalAuth)) {
|
|
208
|
+
return { token: globalAuth.accessToken, serverUrl: globalAuth.serverUrl };
|
|
209
|
+
}
|
|
210
|
+
return {};
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Format server error for display.
|
|
214
|
+
*/
|
|
215
|
+
function formatServerError(err) {
|
|
216
|
+
if (err instanceof aim_client_js_1.AimServerError) {
|
|
217
|
+
if (err.statusCode === 401) {
|
|
218
|
+
return 'Authentication failed. Check your --api-key or run: opena2a identity connect <url>';
|
|
219
|
+
}
|
|
220
|
+
if (err.statusCode === 403) {
|
|
221
|
+
return 'Access denied. Your API key may lack required permissions.';
|
|
222
|
+
}
|
|
223
|
+
if (err.statusCode === 404) {
|
|
224
|
+
return 'Agent not found on server. It may have been deleted or never registered.';
|
|
225
|
+
}
|
|
226
|
+
return err.message;
|
|
227
|
+
}
|
|
228
|
+
return err instanceof Error ? err.message : String(err);
|
|
229
|
+
}
|
|
230
|
+
// ---------------------------------------------------------------------------
|
|
113
231
|
// list / show
|
|
114
232
|
// ---------------------------------------------------------------------------
|
|
115
233
|
async function handleList(options) {
|
|
@@ -132,8 +250,84 @@ async function handleList(options) {
|
|
|
132
250
|
return 0;
|
|
133
251
|
}
|
|
134
252
|
const id = aim.getIdentity();
|
|
253
|
+
// If server is configured, fetch enriched data
|
|
254
|
+
const client = getServerClient(options);
|
|
255
|
+
const serverConfig = (0, aim_client_js_1.loadServerConfig)();
|
|
256
|
+
const auth = getStoredAuth();
|
|
257
|
+
let serverAgent = null;
|
|
258
|
+
let serverAgentList = null;
|
|
259
|
+
const effectiveServerUrl = serverConfig?.serverUrl ?? auth.serverUrl;
|
|
260
|
+
// If we have a server client with auth, try to fetch server data
|
|
261
|
+
if (client && auth.token) {
|
|
262
|
+
try {
|
|
263
|
+
if (serverConfig?.agentId) {
|
|
264
|
+
// Fetch specific agent if we have a registered agent ID
|
|
265
|
+
serverAgent = await client.getAgent(auth.token, serverConfig.agentId);
|
|
266
|
+
}
|
|
267
|
+
else {
|
|
268
|
+
// No specific agent ID — list all agents from server
|
|
269
|
+
serverAgentList = await client.listAgents(auth.token);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
catch (err) {
|
|
273
|
+
if (!isJson) {
|
|
274
|
+
process.stderr.write((0, colors_js_1.yellow)(`Warning: Could not fetch server data: ${formatServerError(err)}`) + '\n');
|
|
275
|
+
process.stderr.write((0, colors_js_1.yellow)('Showing local identity only.') + '\n\n');
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
else if (client && serverConfig?.agentId && auth.apiKey) {
|
|
280
|
+
try {
|
|
281
|
+
const loginResp = await client.login({ name: id.agentName, apiKey: auth.apiKey });
|
|
282
|
+
serverAgent = await client.getAgent(loginResp.accessToken, serverConfig.agentId);
|
|
283
|
+
(0, aim_client_js_1.saveServerConfig)({ ...serverConfig, accessToken: loginResp.accessToken, refreshToken: loginResp.refreshToken });
|
|
284
|
+
}
|
|
285
|
+
catch (err) {
|
|
286
|
+
if (!isJson) {
|
|
287
|
+
process.stderr.write((0, colors_js_1.yellow)(`Warning: Could not fetch server data: ${formatServerError(err)}`) + '\n');
|
|
288
|
+
process.stderr.write((0, colors_js_1.yellow)('Showing local identity only.') + '\n\n');
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
// If server returned a list of agents (from global auth), show them
|
|
293
|
+
if (serverAgentList && serverAgentList.agents?.length > 0) {
|
|
294
|
+
if (isJson) {
|
|
295
|
+
process.stdout.write(JSON.stringify({
|
|
296
|
+
local: { ...id },
|
|
297
|
+
server: { url: effectiveServerUrl, agents: serverAgentList.agents, total: serverAgentList.total },
|
|
298
|
+
}, null, 2) + '\n');
|
|
299
|
+
return 0;
|
|
300
|
+
}
|
|
301
|
+
process.stdout.write((0, colors_js_1.bold)('Local Identity') + '\n');
|
|
302
|
+
process.stdout.write((0, colors_js_1.gray)('-'.repeat(50)) + '\n');
|
|
303
|
+
process.stdout.write(` Agent ID: ${(0, colors_js_1.cyan)(id.agentId)}\n`);
|
|
304
|
+
process.stdout.write(` Name: ${id.agentName}\n`);
|
|
305
|
+
process.stdout.write(` Public Key: ${(0, colors_js_1.dim)(id.publicKey.slice(0, 32) + '...')}\n`);
|
|
306
|
+
process.stdout.write(` Created: ${id.createdAt}\n`);
|
|
307
|
+
process.stdout.write((0, colors_js_1.gray)('-'.repeat(50)) + '\n\n');
|
|
308
|
+
process.stdout.write((0, colors_js_1.bold)(`Server Agents (${effectiveServerUrl})`) + '\n');
|
|
309
|
+
process.stdout.write((0, colors_js_1.gray)('-'.repeat(50)) + '\n');
|
|
310
|
+
for (const agent of serverAgentList.agents) {
|
|
311
|
+
const statusColor = agent.status === 'verified' ? colors_js_1.green : agent.status === 'active' ? colors_js_1.cyan : colors_js_1.yellow;
|
|
312
|
+
process.stdout.write(` ${(0, colors_js_1.cyan)(agent.name)} (${(0, colors_js_1.dim)(agent.id)})\n`);
|
|
313
|
+
process.stdout.write(` Status: ${statusColor(agent.status)} Trust: ${agent.trustScore}\n`);
|
|
314
|
+
}
|
|
315
|
+
process.stdout.write((0, colors_js_1.gray)('-'.repeat(50)) + '\n');
|
|
316
|
+
process.stdout.write((0, colors_js_1.dim)(` ${serverAgentList.total} agent(s) total`) + '\n');
|
|
317
|
+
return 0;
|
|
318
|
+
}
|
|
135
319
|
if (isJson) {
|
|
136
|
-
|
|
320
|
+
const result = { ...id };
|
|
321
|
+
if (serverAgent) {
|
|
322
|
+
result.server = {
|
|
323
|
+
id: serverAgent.id,
|
|
324
|
+
name: serverAgent.name,
|
|
325
|
+
trustScore: serverAgent.trustScore,
|
|
326
|
+
status: serverAgent.status,
|
|
327
|
+
serverUrl: effectiveServerUrl,
|
|
328
|
+
};
|
|
329
|
+
}
|
|
330
|
+
process.stdout.write(JSON.stringify(result, null, 2) + '\n');
|
|
137
331
|
return 0;
|
|
138
332
|
}
|
|
139
333
|
process.stdout.write((0, colors_js_1.bold)('Agent Identity') + '\n');
|
|
@@ -143,6 +337,19 @@ async function handleList(options) {
|
|
|
143
337
|
process.stdout.write(` Public Key: ${(0, colors_js_1.dim)(id.publicKey.slice(0, 32) + '...')}\n`);
|
|
144
338
|
process.stdout.write(` Created: ${id.createdAt}\n`);
|
|
145
339
|
process.stdout.write(` Data Dir: ${(0, colors_js_1.dim)(aim.getDataDir())}\n`);
|
|
340
|
+
if (serverAgent && serverConfig) {
|
|
341
|
+
process.stdout.write('\n' + (0, colors_js_1.bold)(' Server') + '\n');
|
|
342
|
+
process.stdout.write(` Server URL: ${(0, colors_js_1.cyan)(serverConfig.serverUrl)}\n`);
|
|
343
|
+
process.stdout.write(` Server ID: ${serverAgent.id}\n`);
|
|
344
|
+
process.stdout.write(` Status: ${serverAgent.status === 'verified' ? (0, colors_js_1.green)(serverAgent.status) : (0, colors_js_1.yellow)(serverAgent.status)}\n`);
|
|
345
|
+
process.stdout.write(` Trust Score: ${serverAgent.trustScore}\n`);
|
|
346
|
+
}
|
|
347
|
+
else if (serverConfig) {
|
|
348
|
+
process.stdout.write('\n' + (0, colors_js_1.bold)(' Server') + '\n');
|
|
349
|
+
process.stdout.write(` Server URL: ${(0, colors_js_1.cyan)(serverConfig.serverUrl)}\n`);
|
|
350
|
+
process.stdout.write(` Server ID: ${serverConfig.agentId}\n`);
|
|
351
|
+
process.stdout.write(` Status: ${(0, colors_js_1.dim)('offline or unreachable')}\n`);
|
|
352
|
+
}
|
|
146
353
|
process.stdout.write((0, colors_js_1.gray)('-'.repeat(50)) + '\n');
|
|
147
354
|
return 0;
|
|
148
355
|
}
|
|
@@ -162,13 +369,17 @@ async function handleCreate(options) {
|
|
|
162
369
|
if (!name) {
|
|
163
370
|
process.stderr.write('Missing required option: --name <name>\n');
|
|
164
371
|
process.stderr.write('Usage: opena2a identity create --name my-agent\n');
|
|
372
|
+
process.stderr.write(' opena2a identity create --name my-agent --server localhost:8080 --api-key <key>\n');
|
|
165
373
|
return 1;
|
|
166
374
|
}
|
|
167
375
|
const isJson = options.format === 'json';
|
|
168
376
|
try {
|
|
377
|
+
// If --server is provided, register on the server
|
|
378
|
+
if (options.server) {
|
|
379
|
+
return handleServerCreate(options, name, isJson);
|
|
380
|
+
}
|
|
381
|
+
// Local-only creation
|
|
169
382
|
const aim = new mod.AIMCore({ agentName: name });
|
|
170
|
-
// Check if identity file already exists BEFORE calling getIdentity
|
|
171
|
-
// (getIdentity creates one if missing, so we check the file directly)
|
|
172
383
|
const dataDir = aim.getDataDir();
|
|
173
384
|
const identityPath = await import('node:path').then(p => p.join(dataDir, 'identity.json'));
|
|
174
385
|
const { existsSync } = await import('node:fs');
|
|
@@ -197,6 +408,82 @@ async function handleCreate(options) {
|
|
|
197
408
|
return 1;
|
|
198
409
|
}
|
|
199
410
|
}
|
|
411
|
+
/**
|
|
412
|
+
* Register agent on the AIM server and store the result locally.
|
|
413
|
+
*/
|
|
414
|
+
async function handleServerCreate(options, name, isJson) {
|
|
415
|
+
const serverUrl = (0, server_url_js_1.resolveServerUrl)(options.server);
|
|
416
|
+
const apiKey = options.apiKey;
|
|
417
|
+
if (!apiKey) {
|
|
418
|
+
process.stderr.write('Missing required option: --api-key <key>\n');
|
|
419
|
+
process.stderr.write('The AIM server requires an API key for agent registration.\n');
|
|
420
|
+
process.stderr.write(`Usage: opena2a identity create --name ${name} --server ${options.server} --api-key <key>\n`);
|
|
421
|
+
return 1;
|
|
422
|
+
}
|
|
423
|
+
const client = new aim_client_js_1.AimClient(serverUrl);
|
|
424
|
+
// 1. Health check
|
|
425
|
+
if (!(await checkServerHealth(client, serverUrl))) {
|
|
426
|
+
return 1;
|
|
427
|
+
}
|
|
428
|
+
try {
|
|
429
|
+
// 2. Register on server
|
|
430
|
+
const resp = await client.register({ name, displayName: name, description: `Agent ${name} registered via OpenA2A CLI` }, apiKey);
|
|
431
|
+
// 3. Also create local identity via aim-core
|
|
432
|
+
const mod = await loadAimCore();
|
|
433
|
+
let localId = null;
|
|
434
|
+
if (mod) {
|
|
435
|
+
const aim = new mod.AIMCore({ agentName: name });
|
|
436
|
+
localId = aim.getIdentity();
|
|
437
|
+
}
|
|
438
|
+
// 4. Store server config locally
|
|
439
|
+
const config = {
|
|
440
|
+
serverUrl,
|
|
441
|
+
agentId: resp.agentId,
|
|
442
|
+
apiKey,
|
|
443
|
+
registeredAt: new Date().toISOString(),
|
|
444
|
+
};
|
|
445
|
+
(0, aim_client_js_1.saveServerConfig)(config);
|
|
446
|
+
if (isJson) {
|
|
447
|
+
process.stdout.write(JSON.stringify({
|
|
448
|
+
created: true,
|
|
449
|
+
server: {
|
|
450
|
+
id: resp.agentId,
|
|
451
|
+
name: resp.name,
|
|
452
|
+
displayName: resp.displayName,
|
|
453
|
+
publicKey: resp.publicKey,
|
|
454
|
+
trustScore: resp.trustScore,
|
|
455
|
+
status: resp.status,
|
|
456
|
+
serverUrl,
|
|
457
|
+
},
|
|
458
|
+
local: localId ? {
|
|
459
|
+
agentId: localId.agentId,
|
|
460
|
+
agentName: localId.agentName,
|
|
461
|
+
publicKey: localId.publicKey,
|
|
462
|
+
} : null,
|
|
463
|
+
}, null, 2) + '\n');
|
|
464
|
+
return 0;
|
|
465
|
+
}
|
|
466
|
+
process.stdout.write((0, colors_js_1.green)('Agent registered on AIM server') + '\n');
|
|
467
|
+
process.stdout.write((0, colors_js_1.gray)('-'.repeat(50)) + '\n');
|
|
468
|
+
process.stdout.write(` Server ID: ${(0, colors_js_1.cyan)(resp.agentId)}\n`);
|
|
469
|
+
process.stdout.write(` Name: ${resp.name}\n`);
|
|
470
|
+
process.stdout.write(` Display Name: ${resp.displayName}\n`);
|
|
471
|
+
process.stdout.write(` Public Key: ${(0, colors_js_1.dim)((resp.publicKey ?? '').slice(0, 32) + '...')}\n`);
|
|
472
|
+
process.stdout.write(` Trust Score: ${resp.trustScore}\n`);
|
|
473
|
+
process.stdout.write(` Status: ${resp.status === 'verified' ? (0, colors_js_1.green)(resp.status) : (0, colors_js_1.yellow)(resp.status)}\n`);
|
|
474
|
+
process.stdout.write(` Server URL: ${(0, colors_js_1.dim)(serverUrl)}\n`);
|
|
475
|
+
if (localId) {
|
|
476
|
+
process.stdout.write(`\n Local ID: ${(0, colors_js_1.dim)(localId.agentId)}\n`);
|
|
477
|
+
}
|
|
478
|
+
process.stdout.write((0, colors_js_1.gray)('-'.repeat(50)) + '\n');
|
|
479
|
+
process.stdout.write((0, colors_js_1.dim)('Server config stored in ~/.opena2a/aim-core/identities/server.json') + '\n');
|
|
480
|
+
return 0;
|
|
481
|
+
}
|
|
482
|
+
catch (err) {
|
|
483
|
+
process.stderr.write(`Failed to register on server: ${formatServerError(err)}\n`);
|
|
484
|
+
return 1;
|
|
485
|
+
}
|
|
486
|
+
}
|
|
200
487
|
// ---------------------------------------------------------------------------
|
|
201
488
|
// trust
|
|
202
489
|
// ---------------------------------------------------------------------------
|
|
@@ -208,6 +495,29 @@ async function handleTrust(options) {
|
|
|
208
495
|
try {
|
|
209
496
|
const aim = new mod.AIMCore({ agentName: 'default' });
|
|
210
497
|
aim.getIdentity(); // ensure identity exists
|
|
498
|
+
// If server is configured, fetch server-side trust data
|
|
499
|
+
const client = getServerClient(options);
|
|
500
|
+
const serverConfig = (0, aim_client_js_1.loadServerConfig)();
|
|
501
|
+
let serverAgent = null;
|
|
502
|
+
if (client && serverConfig?.agentId) {
|
|
503
|
+
try {
|
|
504
|
+
const auth = getStoredAuth();
|
|
505
|
+
if (auth.token) {
|
|
506
|
+
serverAgent = await client.getAgent(auth.token, serverConfig.agentId);
|
|
507
|
+
}
|
|
508
|
+
else if (auth.apiKey) {
|
|
509
|
+
const id = aim.getIdentity();
|
|
510
|
+
const loginResp = await client.login({ name: id.agentName, apiKey: auth.apiKey });
|
|
511
|
+
serverAgent = await client.getAgent(loginResp.accessToken, serverConfig.agentId);
|
|
512
|
+
(0, aim_client_js_1.saveServerConfig)({ ...serverConfig, accessToken: loginResp.accessToken, refreshToken: loginResp.refreshToken });
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
catch (err) {
|
|
516
|
+
if (!isJson) {
|
|
517
|
+
process.stderr.write((0, colors_js_1.yellow)(`Warning: Could not fetch server trust data: ${formatServerError(err)}`) + '\n\n');
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
}
|
|
211
521
|
// Auto-sync trust hints if a manifest exists (tools are attached)
|
|
212
522
|
const targetDir = path.resolve(options.dir ?? process.cwd());
|
|
213
523
|
let hasManifest = false;
|
|
@@ -222,11 +532,19 @@ async function handleTrust(options) {
|
|
|
222
532
|
}
|
|
223
533
|
}
|
|
224
534
|
catch {
|
|
225
|
-
// Identity module not available or manifest missing
|
|
535
|
+
// Identity module not available or manifest missing
|
|
226
536
|
}
|
|
227
537
|
const trust = aim.calculateTrust();
|
|
228
538
|
if (isJson) {
|
|
229
|
-
|
|
539
|
+
const result = { ...trust, attached: hasManifest };
|
|
540
|
+
if (serverAgent) {
|
|
541
|
+
result.server = {
|
|
542
|
+
trustScore: serverAgent.trustScore,
|
|
543
|
+
status: serverAgent.status,
|
|
544
|
+
serverUrl: serverConfig?.serverUrl,
|
|
545
|
+
};
|
|
546
|
+
}
|
|
547
|
+
process.stdout.write(JSON.stringify(result, null, 2) + '\n');
|
|
230
548
|
return 0;
|
|
231
549
|
}
|
|
232
550
|
const displayScore = trust.score ?? Math.round((trust.overall ?? 0) * 100);
|
|
@@ -235,6 +553,12 @@ async function handleTrust(options) {
|
|
|
235
553
|
process.stdout.write((0, colors_js_1.bold)('Trust Score') + '\n');
|
|
236
554
|
process.stdout.write((0, colors_js_1.gray)('-'.repeat(50)) + '\n');
|
|
237
555
|
process.stdout.write(` Score: ${gradeColor((0, colors_js_1.bold)(String(displayScore) + '/100'))} (${gradeColor(displayGrade)})\n`);
|
|
556
|
+
// Show server trust score if available
|
|
557
|
+
if (serverAgent) {
|
|
558
|
+
const serverScore = serverAgent.trustScore;
|
|
559
|
+
const serverColor = serverScore >= 80 ? colors_js_1.green : serverScore >= 60 ? colors_js_1.yellow : colors_js_1.red;
|
|
560
|
+
process.stdout.write(` Server: ${serverColor((0, colors_js_1.bold)(String(serverScore) + '/100'))} ${(0, colors_js_1.dim)('(from AIM server)')}\n`);
|
|
561
|
+
}
|
|
238
562
|
process.stdout.write('\n');
|
|
239
563
|
process.stdout.write((0, colors_js_1.bold)(' Factors:') + '\n');
|
|
240
564
|
for (const [factor, value] of Object.entries(trust.factors)) {
|
|
@@ -248,7 +572,6 @@ async function handleTrust(options) {
|
|
|
248
572
|
if (trust.calculatedAt) {
|
|
249
573
|
process.stdout.write((0, colors_js_1.dim)(` Calculated: ${trust.calculatedAt}`) + '\n');
|
|
250
574
|
}
|
|
251
|
-
// Show improvement suggestions for factors at 0%
|
|
252
575
|
const zeroFactors = Object.entries(trust.factors).filter(([, v]) => v === 0);
|
|
253
576
|
if (zeroFactors.length > 0) {
|
|
254
577
|
process.stdout.write('\n' + (0, colors_js_1.bold)(' How to improve:') + '\n');
|
|
@@ -323,6 +646,62 @@ async function handleAudit(options) {
|
|
|
323
646
|
return 1;
|
|
324
647
|
const isJson = options.format === 'json';
|
|
325
648
|
const limit = options.limit ?? 10;
|
|
649
|
+
// If server is configured, fetch server-side audit logs
|
|
650
|
+
const client = getServerClient(options);
|
|
651
|
+
const serverConfig = (0, aim_client_js_1.loadServerConfig)();
|
|
652
|
+
if (client && serverConfig?.agentId) {
|
|
653
|
+
try {
|
|
654
|
+
const auth = getStoredAuth();
|
|
655
|
+
let token = auth.token;
|
|
656
|
+
if (!token && auth.apiKey) {
|
|
657
|
+
const aim = new mod.AIMCore({ agentName: 'default' });
|
|
658
|
+
const id = aim.getIdentity();
|
|
659
|
+
const loginResp = await client.login({ name: id.agentName, apiKey: auth.apiKey });
|
|
660
|
+
token = loginResp.accessToken;
|
|
661
|
+
(0, aim_client_js_1.saveServerConfig)({ ...serverConfig, accessToken: loginResp.accessToken, refreshToken: loginResp.refreshToken });
|
|
662
|
+
}
|
|
663
|
+
if (token) {
|
|
664
|
+
const resp = await client.getAuditLogs(token, serverConfig.agentId, { pageSize: limit });
|
|
665
|
+
if (isJson) {
|
|
666
|
+
process.stdout.write(JSON.stringify({
|
|
667
|
+
source: 'server',
|
|
668
|
+
serverUrl: serverConfig.serverUrl,
|
|
669
|
+
total: resp.total,
|
|
670
|
+
auditLogs: resp.auditLogs,
|
|
671
|
+
}, null, 2) + '\n');
|
|
672
|
+
return 0;
|
|
673
|
+
}
|
|
674
|
+
process.stdout.write((0, colors_js_1.bold)(`Server Audit Log (${resp.auditLogs.length} of ${resp.total})`) + '\n');
|
|
675
|
+
process.stdout.write((0, colors_js_1.dim)(` Server: ${serverConfig.serverUrl}`) + '\n');
|
|
676
|
+
process.stdout.write((0, colors_js_1.gray)('-'.repeat(70)) + '\n');
|
|
677
|
+
if (resp.auditLogs.length === 0) {
|
|
678
|
+
process.stdout.write((0, colors_js_1.dim)(' No server audit events recorded yet.') + '\n');
|
|
679
|
+
}
|
|
680
|
+
else {
|
|
681
|
+
for (const e of resp.auditLogs) {
|
|
682
|
+
const ts = (e.createdAt ?? '').slice(0, 19).replace('T', ' ');
|
|
683
|
+
process.stdout.write(` ${(0, colors_js_1.dim)(ts)} ${(e.action ?? '').padEnd(16)} ${(e.resource ?? '').padEnd(16)} ${(0, colors_js_1.dim)(e.details ?? '')}\n`);
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
process.stdout.write((0, colors_js_1.gray)('-'.repeat(70)) + '\n');
|
|
687
|
+
// Also show local audit events if --verbose
|
|
688
|
+
if (options.verbose) {
|
|
689
|
+
process.stdout.write('\n');
|
|
690
|
+
return showLocalAudit(mod, limit, isJson);
|
|
691
|
+
}
|
|
692
|
+
return 0;
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
catch (err) {
|
|
696
|
+
if (!isJson) {
|
|
697
|
+
process.stderr.write((0, colors_js_1.yellow)(`Warning: Could not fetch server audit logs: ${formatServerError(err)}`) + '\n');
|
|
698
|
+
process.stderr.write((0, colors_js_1.yellow)('Showing local audit log.') + '\n\n');
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
return showLocalAudit(mod, limit, isJson);
|
|
703
|
+
}
|
|
704
|
+
async function showLocalAudit(mod, limit, isJson) {
|
|
326
705
|
try {
|
|
327
706
|
const aim = new mod.AIMCore({ agentName: 'default' });
|
|
328
707
|
const events = aim.readAuditLog({ limit });
|
|
@@ -398,6 +777,136 @@ async function handleLog(options) {
|
|
|
398
777
|
}
|
|
399
778
|
}
|
|
400
779
|
// ---------------------------------------------------------------------------
|
|
780
|
+
// connect -- connect existing local identity to an AIM server
|
|
781
|
+
// ---------------------------------------------------------------------------
|
|
782
|
+
async function handleConnect(options) {
|
|
783
|
+
const mod = await loadAimCore();
|
|
784
|
+
if (!mod)
|
|
785
|
+
return 1;
|
|
786
|
+
// The URL comes from the positional arg or --server flag
|
|
787
|
+
const rawUrl = options.args?.[0] ?? options.server;
|
|
788
|
+
if (!rawUrl) {
|
|
789
|
+
process.stderr.write('Missing server URL.\n');
|
|
790
|
+
process.stderr.write('Usage: opena2a identity connect <url> --api-key <key>\n');
|
|
791
|
+
process.stderr.write(' opena2a identity connect localhost:8080 --api-key <key>\n');
|
|
792
|
+
return 1;
|
|
793
|
+
}
|
|
794
|
+
const apiKey = options.apiKey;
|
|
795
|
+
if (!apiKey) {
|
|
796
|
+
process.stderr.write('Missing required option: --api-key <key>\n');
|
|
797
|
+
process.stderr.write('The AIM server requires an API key for agent registration.\n');
|
|
798
|
+
return 1;
|
|
799
|
+
}
|
|
800
|
+
const serverUrl = (0, server_url_js_1.resolveServerUrl)(rawUrl);
|
|
801
|
+
const client = new aim_client_js_1.AimClient(serverUrl);
|
|
802
|
+
const isJson = options.format === 'json';
|
|
803
|
+
// 1. Health check
|
|
804
|
+
if (!(await checkServerHealth(client, serverUrl))) {
|
|
805
|
+
return 1;
|
|
806
|
+
}
|
|
807
|
+
try {
|
|
808
|
+
// 2. Load local identity
|
|
809
|
+
const aim = new mod.AIMCore({ agentName: 'default' });
|
|
810
|
+
const identityFile = path.join(aim.getDataDir(), 'identity.json');
|
|
811
|
+
if (!fs.existsSync(identityFile)) {
|
|
812
|
+
process.stderr.write('No local identity found. Create one first: opena2a identity create --name my-agent\n');
|
|
813
|
+
return 1;
|
|
814
|
+
}
|
|
815
|
+
const id = aim.getIdentity();
|
|
816
|
+
// 3. Register on server
|
|
817
|
+
const resp = await client.register({ name: id.agentName, displayName: id.agentName, description: `Agent ${id.agentName} registered via OpenA2A CLI` }, apiKey);
|
|
818
|
+
// 4. Store server config
|
|
819
|
+
const config = {
|
|
820
|
+
serverUrl,
|
|
821
|
+
agentId: resp.agentId,
|
|
822
|
+
apiKey,
|
|
823
|
+
registeredAt: new Date().toISOString(),
|
|
824
|
+
};
|
|
825
|
+
(0, aim_client_js_1.saveServerConfig)(config);
|
|
826
|
+
// 5. Log the connect event
|
|
827
|
+
aim.logEvent({
|
|
828
|
+
action: 'identity.connect',
|
|
829
|
+
target: serverUrl,
|
|
830
|
+
result: 'allowed',
|
|
831
|
+
plugin: 'opena2a-cli',
|
|
832
|
+
});
|
|
833
|
+
if (isJson) {
|
|
834
|
+
process.stdout.write(JSON.stringify({
|
|
835
|
+
connected: true,
|
|
836
|
+
localAgentId: id.agentId,
|
|
837
|
+
serverAgentId: resp.agentId,
|
|
838
|
+
serverUrl,
|
|
839
|
+
trustScore: resp.trustScore,
|
|
840
|
+
status: resp.status,
|
|
841
|
+
}, null, 2) + '\n');
|
|
842
|
+
return 0;
|
|
843
|
+
}
|
|
844
|
+
process.stdout.write((0, colors_js_1.green)('Connected to AIM server') + '\n');
|
|
845
|
+
process.stdout.write((0, colors_js_1.gray)('-'.repeat(50)) + '\n');
|
|
846
|
+
process.stdout.write(` Local ID: ${(0, colors_js_1.cyan)(id.agentId)}\n`);
|
|
847
|
+
process.stdout.write(` Server ID: ${(0, colors_js_1.cyan)(resp.agentId)}\n`);
|
|
848
|
+
process.stdout.write(` Server URL: ${(0, colors_js_1.dim)(serverUrl)}\n`);
|
|
849
|
+
process.stdout.write(` Trust Score: ${resp.trustScore}\n`);
|
|
850
|
+
process.stdout.write(` Status: ${resp.status === 'verified' ? (0, colors_js_1.green)(resp.status) : (0, colors_js_1.yellow)(resp.status)}\n`);
|
|
851
|
+
process.stdout.write((0, colors_js_1.gray)('-'.repeat(50)) + '\n');
|
|
852
|
+
process.stdout.write((0, colors_js_1.dim)('Future commands will automatically use this server.') + '\n');
|
|
853
|
+
process.stdout.write((0, colors_js_1.dim)('Disconnect with: opena2a identity disconnect') + '\n');
|
|
854
|
+
return 0;
|
|
855
|
+
}
|
|
856
|
+
catch (err) {
|
|
857
|
+
process.stderr.write(`Failed to connect to server: ${formatServerError(err)}\n`);
|
|
858
|
+
return 1;
|
|
859
|
+
}
|
|
860
|
+
}
|
|
861
|
+
// ---------------------------------------------------------------------------
|
|
862
|
+
// disconnect -- remove server association
|
|
863
|
+
// ---------------------------------------------------------------------------
|
|
864
|
+
async function handleDisconnect(options) {
|
|
865
|
+
const isJson = options.format === 'json';
|
|
866
|
+
const config = (0, aim_client_js_1.loadServerConfig)();
|
|
867
|
+
if (!config) {
|
|
868
|
+
if (isJson) {
|
|
869
|
+
process.stdout.write(JSON.stringify({ disconnected: false, reason: 'no server configured' }, null, 2) + '\n');
|
|
870
|
+
}
|
|
871
|
+
else {
|
|
872
|
+
process.stdout.write('No server connection configured.\n');
|
|
873
|
+
}
|
|
874
|
+
return 0;
|
|
875
|
+
}
|
|
876
|
+
const removed = (0, aim_client_js_1.removeServerConfig)();
|
|
877
|
+
// Log the disconnect event
|
|
878
|
+
try {
|
|
879
|
+
const mod = await loadAimCore();
|
|
880
|
+
if (mod) {
|
|
881
|
+
const aim = new mod.AIMCore({ agentName: 'default' });
|
|
882
|
+
aim.logEvent({
|
|
883
|
+
action: 'identity.disconnect',
|
|
884
|
+
target: config.serverUrl,
|
|
885
|
+
result: 'allowed',
|
|
886
|
+
plugin: 'opena2a-cli',
|
|
887
|
+
});
|
|
888
|
+
}
|
|
889
|
+
}
|
|
890
|
+
catch {
|
|
891
|
+
// Non-critical
|
|
892
|
+
}
|
|
893
|
+
if (isJson) {
|
|
894
|
+
process.stdout.write(JSON.stringify({
|
|
895
|
+
disconnected: removed,
|
|
896
|
+
serverUrl: config.serverUrl,
|
|
897
|
+
agentId: config.agentId,
|
|
898
|
+
}, null, 2) + '\n');
|
|
899
|
+
}
|
|
900
|
+
else {
|
|
901
|
+
process.stdout.write((0, colors_js_1.green)('Disconnected from AIM server') + '\n');
|
|
902
|
+
process.stdout.write(` Server URL: ${(0, colors_js_1.dim)(config.serverUrl)}\n`);
|
|
903
|
+
process.stdout.write(` Server ID: ${(0, colors_js_1.dim)(config.agentId)}\n`);
|
|
904
|
+
process.stdout.write((0, colors_js_1.dim)('\n Local identity and audit log are preserved.') + '\n');
|
|
905
|
+
process.stdout.write((0, colors_js_1.dim)(' Only the server association was removed.') + '\n');
|
|
906
|
+
}
|
|
907
|
+
return 0;
|
|
908
|
+
}
|
|
909
|
+
// ---------------------------------------------------------------------------
|
|
401
910
|
// policy -- show or load capability policy
|
|
402
911
|
// ---------------------------------------------------------------------------
|
|
403
912
|
async function handlePolicy(options) {
|
|
@@ -654,11 +1163,9 @@ async function handleAttach(options) {
|
|
|
654
1163
|
shield: false,
|
|
655
1164
|
};
|
|
656
1165
|
if (options.all) {
|
|
657
|
-
// Enable all
|
|
658
1166
|
enabledTools = { secretless: true, configguard: true, arp: true, hma: true, shield: true };
|
|
659
1167
|
}
|
|
660
1168
|
else if (options.tools) {
|
|
661
|
-
// Enable specific tools, merge with existing
|
|
662
1169
|
const requested = options.tools.split(',').map(t => t.trim().toLowerCase());
|
|
663
1170
|
const knownTools = ['secretless', 'configguard', 'guard', 'arp', 'hma', 'hackmyagent', 'shield'];
|
|
664
1171
|
const unknown = requested.filter(t => !knownTools.includes(t));
|
|
@@ -684,11 +1191,9 @@ async function handleAttach(options) {
|
|
|
684
1191
|
}
|
|
685
1192
|
}
|
|
686
1193
|
else if (existing) {
|
|
687
|
-
// Re-attach with existing config
|
|
688
1194
|
enabledTools = existing.tools;
|
|
689
1195
|
}
|
|
690
1196
|
else {
|
|
691
|
-
// First attach with no flags — enable all by default
|
|
692
1197
|
enabledTools = { secretless: true, configguard: true, arp: true, hma: true, shield: true };
|
|
693
1198
|
}
|
|
694
1199
|
// 3. Collect trust hints from enabled tools
|
|
@@ -705,7 +1210,6 @@ async function handleAttach(options) {
|
|
|
705
1210
|
process.stdout.write((0, colors_js_1.bold)(' Requested tools: ') + options.tools + '\n\n');
|
|
706
1211
|
}
|
|
707
1212
|
process.stdout.write((0, colors_js_1.bold)(' Tool Detection:') + '\n');
|
|
708
|
-
// Show all tools (not just enabled ones) so user sees the full picture
|
|
709
1213
|
const allToolNames = [
|
|
710
1214
|
{ key: 'secretless', label: 'Secretless' },
|
|
711
1215
|
{ key: 'configguard', label: 'ConfigGuard' },
|