@gopherhole/cli 0.6.2 → 0.6.3
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/dist/index.js +35 -3
- package/package.json +2 -2
- package/src/index.ts +30 -3
package/dist/index.js
CHANGED
|
@@ -82,10 +82,39 @@ function makeHubClient(apiKey, transport) {
|
|
|
82
82
|
const hubUrl = apiUrl.replace('https://', 'wss://').replace('http://', 'ws://') + '/ws';
|
|
83
83
|
return new sdk_1.GopherHole({ apiKey, hubUrl, autoReconnect: false, transport: transport || 'http' });
|
|
84
84
|
}
|
|
85
|
+
/** Load secrets for a target agent from .gopherhole/secrets/{alias}.json */
|
|
86
|
+
async function loadAgentSecrets(agentId) {
|
|
87
|
+
const fs = await import('fs');
|
|
88
|
+
const path = await import('path');
|
|
89
|
+
const filePath = path.join(process.cwd(), '.gopherhole', 'secrets', `${agentId}.json`);
|
|
90
|
+
if (!fs.existsSync(filePath))
|
|
91
|
+
return null;
|
|
92
|
+
try {
|
|
93
|
+
const raw = fs.readFileSync(filePath, 'utf-8');
|
|
94
|
+
const parsed = JSON.parse(raw);
|
|
95
|
+
if (typeof parsed !== 'object' || parsed === null)
|
|
96
|
+
return null;
|
|
97
|
+
const secrets = {};
|
|
98
|
+
for (const [k, v] of Object.entries(parsed)) {
|
|
99
|
+
if (typeof v === 'string')
|
|
100
|
+
secrets[k] = v;
|
|
101
|
+
}
|
|
102
|
+
return Object.keys(secrets).length > 0 ? secrets : null;
|
|
103
|
+
}
|
|
104
|
+
catch {
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
85
108
|
/** Send a message and poll until terminal state, return response text.
|
|
86
109
|
* Matches the MCP client pattern: sendText → poll getTask. */
|
|
87
|
-
async function askAgent(client, agentId, text) {
|
|
88
|
-
const
|
|
110
|
+
async function askAgent(client, agentId, text, opts) {
|
|
111
|
+
const config = {};
|
|
112
|
+
if (opts?.secrets)
|
|
113
|
+
config['x-gopherhole-secrets'] = opts.secrets;
|
|
114
|
+
if (opts?.ttl !== undefined)
|
|
115
|
+
config['x-ttl'] = opts.ttl;
|
|
116
|
+
const sendConfig = Object.keys(config).length > 0 ? config : undefined;
|
|
117
|
+
const task = await client.sendText(agentId, text, sendConfig);
|
|
89
118
|
const terminalStates = ['completed', 'failed', 'canceled', 'rejected'];
|
|
90
119
|
let current = task;
|
|
91
120
|
const start = Date.now();
|
|
@@ -1283,6 +1312,7 @@ ${chalk_1.default.bold('Examples:')}
|
|
|
1283
1312
|
console.log(chalk_1.default.gray('Run: gopherhole login'));
|
|
1284
1313
|
process.exit(1);
|
|
1285
1314
|
}
|
|
1315
|
+
const secrets = await loadAgentSecrets(agentId);
|
|
1286
1316
|
const spinner = (0, ora_1.default)(`Sending to ${agentId}...`).start();
|
|
1287
1317
|
log('POST /a2a SendMessage', { to: agentId, message });
|
|
1288
1318
|
try {
|
|
@@ -1300,6 +1330,7 @@ ${chalk_1.default.bold('Examples:')}
|
|
|
1300
1330
|
configuration: {
|
|
1301
1331
|
agentId,
|
|
1302
1332
|
...(cmdOpts.ttl !== undefined ? { 'x-ttl': cmdOpts.ttl } : {}),
|
|
1333
|
+
...(secrets ? { 'x-gopherhole-secrets': secrets } : {}),
|
|
1303
1334
|
},
|
|
1304
1335
|
},
|
|
1305
1336
|
id: 1,
|
|
@@ -2565,10 +2596,11 @@ ${chalk_1.default.bold('Examples:')}
|
|
|
2565
2596
|
process.exit(1);
|
|
2566
2597
|
}
|
|
2567
2598
|
log(`Transport: http (A2AClient is HTTP-only)`);
|
|
2599
|
+
const secrets = await loadAgentSecrets(agentId);
|
|
2568
2600
|
const spinner = (0, ora_1.default)(`Messaging ${agentId}...`).start();
|
|
2569
2601
|
try {
|
|
2570
2602
|
const client = makeAgentClient(apiKey);
|
|
2571
|
-
const response = await askAgent(client, agentId, text);
|
|
2603
|
+
const response = await askAgent(client, agentId, text, secrets ? { secrets } : undefined);
|
|
2572
2604
|
spinner.stop();
|
|
2573
2605
|
console.log(response || chalk_1.default.gray('(no response)'));
|
|
2574
2606
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gopherhole/cli",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.3",
|
|
4
4
|
"description": "GopherHole CLI - Connect AI agents to the world",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"author": "GopherHole",
|
|
23
23
|
"license": "MIT",
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@gopherhole/sdk": "^0.7.
|
|
25
|
+
"@gopherhole/sdk": "^0.7.5",
|
|
26
26
|
"chalk": "^5.3.0",
|
|
27
27
|
"commander": "^12.0.0",
|
|
28
28
|
"conf": "^12.0.0",
|
package/src/index.ts
CHANGED
|
@@ -85,10 +85,34 @@ function makeHubClient(apiKey: string, transport?: TransportMode): GopherHole {
|
|
|
85
85
|
return new GopherHole({ apiKey, hubUrl, autoReconnect: false, transport: transport || 'http' });
|
|
86
86
|
}
|
|
87
87
|
|
|
88
|
+
/** Load secrets for a target agent from .gopherhole/secrets/{alias}.json */
|
|
89
|
+
async function loadAgentSecrets(agentId: string): Promise<Record<string, string> | null> {
|
|
90
|
+
const fs = await import('fs');
|
|
91
|
+
const path = await import('path');
|
|
92
|
+
const filePath = path.join(process.cwd(), '.gopherhole', 'secrets', `${agentId}.json`);
|
|
93
|
+
if (!fs.existsSync(filePath)) return null;
|
|
94
|
+
try {
|
|
95
|
+
const raw = fs.readFileSync(filePath, 'utf-8');
|
|
96
|
+
const parsed = JSON.parse(raw);
|
|
97
|
+
if (typeof parsed !== 'object' || parsed === null) return null;
|
|
98
|
+
const secrets: Record<string, string> = {};
|
|
99
|
+
for (const [k, v] of Object.entries(parsed)) {
|
|
100
|
+
if (typeof v === 'string') secrets[k] = v;
|
|
101
|
+
}
|
|
102
|
+
return Object.keys(secrets).length > 0 ? secrets : null;
|
|
103
|
+
} catch {
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
88
108
|
/** Send a message and poll until terminal state, return response text.
|
|
89
109
|
* Matches the MCP client pattern: sendText → poll getTask. */
|
|
90
|
-
async function askAgent(client: A2AClient, agentId: string, text: string): Promise<string> {
|
|
91
|
-
const
|
|
110
|
+
async function askAgent(client: A2AClient, agentId: string, text: string, opts?: { secrets?: Record<string, string>; ttl?: number }): Promise<string> {
|
|
111
|
+
const config: Record<string, unknown> = {};
|
|
112
|
+
if (opts?.secrets) config['x-gopherhole-secrets'] = opts.secrets;
|
|
113
|
+
if (opts?.ttl !== undefined) config['x-ttl'] = opts.ttl;
|
|
114
|
+
const sendConfig = Object.keys(config).length > 0 ? config : undefined;
|
|
115
|
+
const task = await client.sendText(agentId, text, sendConfig as any);
|
|
92
116
|
|
|
93
117
|
const terminalStates = ['completed', 'failed', 'canceled', 'rejected'];
|
|
94
118
|
let current = task;
|
|
@@ -1418,6 +1442,7 @@ ${chalk.bold('Examples:')}
|
|
|
1418
1442
|
process.exit(1);
|
|
1419
1443
|
}
|
|
1420
1444
|
|
|
1445
|
+
const secrets = await loadAgentSecrets(agentId);
|
|
1421
1446
|
const spinner = ora(`Sending to ${agentId}...`).start();
|
|
1422
1447
|
log('POST /a2a SendMessage', { to: agentId, message });
|
|
1423
1448
|
|
|
@@ -1436,6 +1461,7 @@ ${chalk.bold('Examples:')}
|
|
|
1436
1461
|
configuration: {
|
|
1437
1462
|
agentId,
|
|
1438
1463
|
...(cmdOpts.ttl !== undefined ? { 'x-ttl': cmdOpts.ttl } : {}),
|
|
1464
|
+
...(secrets ? { 'x-gopherhole-secrets': secrets } : {}),
|
|
1439
1465
|
},
|
|
1440
1466
|
},
|
|
1441
1467
|
id: 1,
|
|
@@ -2795,10 +2821,11 @@ ${chalk.bold('Examples:')}
|
|
|
2795
2821
|
}
|
|
2796
2822
|
|
|
2797
2823
|
log(`Transport: http (A2AClient is HTTP-only)`);
|
|
2824
|
+
const secrets = await loadAgentSecrets(agentId);
|
|
2798
2825
|
const spinner = ora(`Messaging ${agentId}...`).start();
|
|
2799
2826
|
try {
|
|
2800
2827
|
const client = makeAgentClient(apiKey);
|
|
2801
|
-
const response = await askAgent(client, agentId, text);
|
|
2828
|
+
const response = await askAgent(client, agentId, text, secrets ? { secrets } : undefined);
|
|
2802
2829
|
spinner.stop();
|
|
2803
2830
|
console.log(response || chalk.gray('(no response)'));
|
|
2804
2831
|
} catch (err) {
|