@gopherhole/cli 0.4.0 → 0.5.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 +13 -5
- package/dist/index.js +66 -15
- package/package.json +2 -2
- package/src/index.ts +69 -15
package/README.md
CHANGED
|
@@ -6,17 +6,17 @@ Connect AI agents to the world.
|
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
8
|
# Use with npx (no install required)
|
|
9
|
-
npx gopherhole init
|
|
9
|
+
npx @gopherhole/cli init
|
|
10
10
|
|
|
11
|
-
# Or install globally
|
|
12
|
-
npm install -g gopherhole
|
|
11
|
+
# Or install globally (exposes the `gopherhole` command)
|
|
12
|
+
npm install -g @gopherhole/cli
|
|
13
13
|
```
|
|
14
14
|
|
|
15
15
|
## Quick Start
|
|
16
16
|
|
|
17
17
|
```bash
|
|
18
18
|
# Initialize a new agent in your project
|
|
19
|
-
npx gopherhole init
|
|
19
|
+
npx @gopherhole/cli init
|
|
20
20
|
|
|
21
21
|
# This will:
|
|
22
22
|
# 1. Log you in (or create an account)
|
|
@@ -64,12 +64,20 @@ The CLI stores configuration in:
|
|
|
64
64
|
- **Linux**: `~/.config/gopherhole-nodejs/`
|
|
65
65
|
- **Windows**: `%APPDATA%/gopherhole-nodejs/`
|
|
66
66
|
|
|
67
|
+
## Related Packages
|
|
68
|
+
|
|
69
|
+
- **[@gopherhole/mcp](https://www.npmjs.com/package/@gopherhole/mcp)** — MCP
|
|
70
|
+
server for Claude Code, Cursor, Windsurf, and other MCP-compatible IDEs.
|
|
71
|
+
Use this to call GopherHole agents from your IDE without writing any code.
|
|
72
|
+
- **[@gopherhole/sdk](https://www.npmjs.com/package/@gopherhole/sdk)** —
|
|
73
|
+
TypeScript SDK for building agents that send/receive A2A messages,
|
|
74
|
+
discover other agents, and use shared workspaces.
|
|
75
|
+
|
|
67
76
|
## Links
|
|
68
77
|
|
|
69
78
|
- Website: https://gopherhole.ai
|
|
70
79
|
- Dashboard: https://gopherhole.ai/dashboard
|
|
71
80
|
- Docs: https://docs.gopherhole.ai
|
|
72
|
-
- GitHub: https://github.com/gopherhole
|
|
73
81
|
|
|
74
82
|
## License
|
|
75
83
|
|
package/dist/index.js
CHANGED
|
@@ -20,7 +20,7 @@ const brand = {
|
|
|
20
20
|
greenDark: chalk_1.default.hex('#16a34a'), // gopher-600 - emphasis
|
|
21
21
|
};
|
|
22
22
|
// Version
|
|
23
|
-
const VERSION = '0.
|
|
23
|
+
const VERSION = '0.4.1';
|
|
24
24
|
// ========== API KEY RESOLUTION ==========
|
|
25
25
|
// Precedence: --api-key flag > GOPHERHOLE_API_KEY env var > .env file in cwd
|
|
26
26
|
async function resolveApiKey(flagValue) {
|
|
@@ -871,6 +871,9 @@ ${chalk_1.default.bold('Examples:')}
|
|
|
871
871
|
$ gopherhole agents config agent-abc123 --no-auto-approve
|
|
872
872
|
$ gopherhole agents config agent-abc123 --price 0.01 --price-unit request
|
|
873
873
|
$ gopherhole agents config agent-abc123 --visibility public
|
|
874
|
+
$ gopherhole agents config agent-abc123 --alias support
|
|
875
|
+
$ gopherhole agents config agent-abc123 --email-enabled
|
|
876
|
+
$ gopherhole agents config agent-abc123 --no-email-enabled
|
|
874
877
|
`)
|
|
875
878
|
.option('--auto-approve', 'Enable auto-approve (instant access for marketplace)')
|
|
876
879
|
.option('--no-auto-approve', 'Disable auto-approve (require manual approval)')
|
|
@@ -881,6 +884,9 @@ ${chalk_1.default.bold('Examples:')}
|
|
|
881
884
|
.option('--category <category>', 'Set category')
|
|
882
885
|
.option('--tags <tags>', 'Set tags (comma-separated)')
|
|
883
886
|
.option('--description <text>', 'Update description')
|
|
887
|
+
.option('--alias <alias>', 'Set email alias (<alias>[.<tenant-slug>]@gopherhole.io). Triggers 30-day grace redirect from the previous alias if email is enabled')
|
|
888
|
+
.option('--email-enabled', 'Enable inbound + outbound email for this agent (requires an alias)')
|
|
889
|
+
.option('--no-email-enabled', 'Disable email for this agent (inbound rejects with 550, Postie refuses to send)')
|
|
884
890
|
.action(async (agentId, options) => {
|
|
885
891
|
const sessionId = config.get('sessionId');
|
|
886
892
|
if (!sessionId) {
|
|
@@ -921,26 +927,65 @@ ${chalk_1.default.bold('Examples:')}
|
|
|
921
927
|
if (options.description) {
|
|
922
928
|
body.description = options.description;
|
|
923
929
|
}
|
|
924
|
-
|
|
930
|
+
const aliasChange = options.alias;
|
|
931
|
+
const emailEnabledChange = options.emailEnabled;
|
|
932
|
+
if (Object.keys(body).length === 0 && aliasChange === undefined && emailEnabledChange === undefined) {
|
|
925
933
|
console.log(chalk_1.default.yellow('No changes specified.'));
|
|
926
934
|
console.log(chalk_1.default.gray('Use --help to see available options.'));
|
|
927
935
|
return;
|
|
928
936
|
}
|
|
929
937
|
const spinner = (0, ora_1.default)('Updating agent config...').start();
|
|
930
|
-
log('PATCH /agents/' + agentId, body);
|
|
931
938
|
try {
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
939
|
+
if (Object.keys(body).length > 0) {
|
|
940
|
+
log('PATCH /agents/' + agentId, body);
|
|
941
|
+
const res = await fetch(`${API_URL}/agents/${agentId}`, {
|
|
942
|
+
method: 'PATCH',
|
|
943
|
+
headers: {
|
|
944
|
+
'Content-Type': 'application/json',
|
|
945
|
+
'X-Session-ID': sessionId,
|
|
946
|
+
},
|
|
947
|
+
body: JSON.stringify(body),
|
|
948
|
+
});
|
|
949
|
+
if (!res.ok) {
|
|
950
|
+
const err = await res.json();
|
|
951
|
+
logError('config', err);
|
|
952
|
+
throw new Error(err.error || 'Failed to update agent');
|
|
953
|
+
}
|
|
954
|
+
}
|
|
955
|
+
// Alias rename has its own endpoint because it also writes the
|
|
956
|
+
// 30-day grace redirect into agent_alias_history.
|
|
957
|
+
if (aliasChange !== undefined) {
|
|
958
|
+
log('PATCH /agents/' + agentId + '/alias', { alias: aliasChange });
|
|
959
|
+
const res = await fetch(`${API_URL}/agents/${agentId}/alias`, {
|
|
960
|
+
method: 'PATCH',
|
|
961
|
+
headers: {
|
|
962
|
+
'Content-Type': 'application/json',
|
|
963
|
+
'X-Session-ID': sessionId,
|
|
964
|
+
},
|
|
965
|
+
body: JSON.stringify({ alias: aliasChange }),
|
|
966
|
+
});
|
|
967
|
+
if (!res.ok) {
|
|
968
|
+
const err = await res.json().catch(() => ({ error: `HTTP ${res.status}` }));
|
|
969
|
+
logError('alias', err);
|
|
970
|
+
throw new Error(err.error || 'Failed to set alias');
|
|
971
|
+
}
|
|
972
|
+
}
|
|
973
|
+
// Email toggle is a dedicated endpoint that enforces alias-first.
|
|
974
|
+
if (emailEnabledChange !== undefined) {
|
|
975
|
+
log('PATCH /agents/' + agentId + '/email-enabled', { enabled: emailEnabledChange });
|
|
976
|
+
const res = await fetch(`${API_URL}/agents/${agentId}/email-enabled`, {
|
|
977
|
+
method: 'PATCH',
|
|
978
|
+
headers: {
|
|
979
|
+
'Content-Type': 'application/json',
|
|
980
|
+
'X-Session-ID': sessionId,
|
|
981
|
+
},
|
|
982
|
+
body: JSON.stringify({ enabled: emailEnabledChange }),
|
|
983
|
+
});
|
|
984
|
+
if (!res.ok) {
|
|
985
|
+
const err = await res.json().catch(() => ({ error: `HTTP ${res.status}` }));
|
|
986
|
+
logError('email-enabled', err);
|
|
987
|
+
throw new Error(err.error || 'Failed to toggle email');
|
|
988
|
+
}
|
|
944
989
|
}
|
|
945
990
|
spinner.succeed('Agent config updated');
|
|
946
991
|
// Show what was changed
|
|
@@ -969,6 +1014,12 @@ ${chalk_1.default.bold('Examples:')}
|
|
|
969
1014
|
const desc = body.description;
|
|
970
1015
|
changes.push(` Description: ${chalk_1.default.gray(desc.slice(0, 50))}${desc.length > 50 ? '...' : ''}`);
|
|
971
1016
|
}
|
|
1017
|
+
if (aliasChange !== undefined) {
|
|
1018
|
+
changes.push(` Alias: ${brand.green(aliasChange)}`);
|
|
1019
|
+
}
|
|
1020
|
+
if (emailEnabledChange !== undefined) {
|
|
1021
|
+
changes.push(` Email: ${emailEnabledChange ? brand.green('enabled 📬') : chalk_1.default.gray('disabled')}`);
|
|
1022
|
+
}
|
|
972
1023
|
if (changes.length > 0) {
|
|
973
1024
|
console.log('');
|
|
974
1025
|
console.log(chalk_1.default.bold('Changes:'));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gopherhole/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
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.
|
|
25
|
+
"@gopherhole/sdk": "^0.7.2",
|
|
26
26
|
"chalk": "^5.3.0",
|
|
27
27
|
"commander": "^12.0.0",
|
|
28
28
|
"conf": "^12.0.0",
|
package/src/index.ts
CHANGED
|
@@ -20,7 +20,7 @@ const brand = {
|
|
|
20
20
|
};
|
|
21
21
|
|
|
22
22
|
// Version
|
|
23
|
-
const VERSION = '0.
|
|
23
|
+
const VERSION = '0.4.1';
|
|
24
24
|
|
|
25
25
|
// ========== API KEY RESOLUTION ==========
|
|
26
26
|
// Precedence: --api-key flag > GOPHERHOLE_API_KEY env var > .env file in cwd
|
|
@@ -968,6 +968,9 @@ ${chalk.bold('Examples:')}
|
|
|
968
968
|
$ gopherhole agents config agent-abc123 --no-auto-approve
|
|
969
969
|
$ gopherhole agents config agent-abc123 --price 0.01 --price-unit request
|
|
970
970
|
$ gopherhole agents config agent-abc123 --visibility public
|
|
971
|
+
$ gopherhole agents config agent-abc123 --alias support
|
|
972
|
+
$ gopherhole agents config agent-abc123 --email-enabled
|
|
973
|
+
$ gopherhole agents config agent-abc123 --no-email-enabled
|
|
971
974
|
`)
|
|
972
975
|
.option('--auto-approve', 'Enable auto-approve (instant access for marketplace)')
|
|
973
976
|
.option('--no-auto-approve', 'Disable auto-approve (require manual approval)')
|
|
@@ -978,6 +981,9 @@ ${chalk.bold('Examples:')}
|
|
|
978
981
|
.option('--category <category>', 'Set category')
|
|
979
982
|
.option('--tags <tags>', 'Set tags (comma-separated)')
|
|
980
983
|
.option('--description <text>', 'Update description')
|
|
984
|
+
.option('--alias <alias>', 'Set email alias (<alias>[.<tenant-slug>]@gopherhole.io). Triggers 30-day grace redirect from the previous alias if email is enabled')
|
|
985
|
+
.option('--email-enabled', 'Enable inbound + outbound email for this agent (requires an alias)')
|
|
986
|
+
.option('--no-email-enabled', 'Disable email for this agent (inbound rejects with 550, Postie refuses to send)')
|
|
981
987
|
.action(async (agentId, options) => {
|
|
982
988
|
const sessionId = config.get('sessionId') as string;
|
|
983
989
|
if (!sessionId) {
|
|
@@ -1021,29 +1027,71 @@ ${chalk.bold('Examples:')}
|
|
|
1021
1027
|
body.description = options.description;
|
|
1022
1028
|
}
|
|
1023
1029
|
|
|
1024
|
-
|
|
1030
|
+
const aliasChange: string | undefined = options.alias;
|
|
1031
|
+
const emailEnabledChange: boolean | undefined = options.emailEnabled;
|
|
1032
|
+
|
|
1033
|
+
if (Object.keys(body).length === 0 && aliasChange === undefined && emailEnabledChange === undefined) {
|
|
1025
1034
|
console.log(chalk.yellow('No changes specified.'));
|
|
1026
1035
|
console.log(chalk.gray('Use --help to see available options.'));
|
|
1027
1036
|
return;
|
|
1028
1037
|
}
|
|
1029
1038
|
|
|
1030
1039
|
const spinner = ora('Updating agent config...').start();
|
|
1031
|
-
log('PATCH /agents/' + agentId, body);
|
|
1032
1040
|
|
|
1033
1041
|
try {
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
+
if (Object.keys(body).length > 0) {
|
|
1043
|
+
log('PATCH /agents/' + agentId, body);
|
|
1044
|
+
const res = await fetch(`${API_URL}/agents/${agentId}`, {
|
|
1045
|
+
method: 'PATCH',
|
|
1046
|
+
headers: {
|
|
1047
|
+
'Content-Type': 'application/json',
|
|
1048
|
+
'X-Session-ID': sessionId,
|
|
1049
|
+
},
|
|
1050
|
+
body: JSON.stringify(body),
|
|
1051
|
+
});
|
|
1042
1052
|
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1053
|
+
if (!res.ok) {
|
|
1054
|
+
const err = await res.json();
|
|
1055
|
+
logError('config', err);
|
|
1056
|
+
throw new Error(err.error || 'Failed to update agent');
|
|
1057
|
+
}
|
|
1058
|
+
}
|
|
1059
|
+
|
|
1060
|
+
// Alias rename has its own endpoint because it also writes the
|
|
1061
|
+
// 30-day grace redirect into agent_alias_history.
|
|
1062
|
+
if (aliasChange !== undefined) {
|
|
1063
|
+
log('PATCH /agents/' + agentId + '/alias', { alias: aliasChange });
|
|
1064
|
+
const res = await fetch(`${API_URL}/agents/${agentId}/alias`, {
|
|
1065
|
+
method: 'PATCH',
|
|
1066
|
+
headers: {
|
|
1067
|
+
'Content-Type': 'application/json',
|
|
1068
|
+
'X-Session-ID': sessionId,
|
|
1069
|
+
},
|
|
1070
|
+
body: JSON.stringify({ alias: aliasChange }),
|
|
1071
|
+
});
|
|
1072
|
+
if (!res.ok) {
|
|
1073
|
+
const err = await res.json().catch(() => ({ error: `HTTP ${res.status}` }));
|
|
1074
|
+
logError('alias', err);
|
|
1075
|
+
throw new Error((err as { error?: string }).error || 'Failed to set alias');
|
|
1076
|
+
}
|
|
1077
|
+
}
|
|
1078
|
+
|
|
1079
|
+
// Email toggle is a dedicated endpoint that enforces alias-first.
|
|
1080
|
+
if (emailEnabledChange !== undefined) {
|
|
1081
|
+
log('PATCH /agents/' + agentId + '/email-enabled', { enabled: emailEnabledChange });
|
|
1082
|
+
const res = await fetch(`${API_URL}/agents/${agentId}/email-enabled`, {
|
|
1083
|
+
method: 'PATCH',
|
|
1084
|
+
headers: {
|
|
1085
|
+
'Content-Type': 'application/json',
|
|
1086
|
+
'X-Session-ID': sessionId,
|
|
1087
|
+
},
|
|
1088
|
+
body: JSON.stringify({ enabled: emailEnabledChange }),
|
|
1089
|
+
});
|
|
1090
|
+
if (!res.ok) {
|
|
1091
|
+
const err = await res.json().catch(() => ({ error: `HTTP ${res.status}` }));
|
|
1092
|
+
logError('email-enabled', err);
|
|
1093
|
+
throw new Error((err as { error?: string }).error || 'Failed to toggle email');
|
|
1094
|
+
}
|
|
1047
1095
|
}
|
|
1048
1096
|
|
|
1049
1097
|
spinner.succeed('Agent config updated');
|
|
@@ -1073,6 +1121,12 @@ ${chalk.bold('Examples:')}
|
|
|
1073
1121
|
const desc = body.description as string;
|
|
1074
1122
|
changes.push(` Description: ${chalk.gray(desc.slice(0, 50))}${desc.length > 50 ? '...' : ''}`);
|
|
1075
1123
|
}
|
|
1124
|
+
if (aliasChange !== undefined) {
|
|
1125
|
+
changes.push(` Alias: ${brand.green(aliasChange)}`);
|
|
1126
|
+
}
|
|
1127
|
+
if (emailEnabledChange !== undefined) {
|
|
1128
|
+
changes.push(` Email: ${emailEnabledChange ? brand.green('enabled 📬') : chalk.gray('disabled')}`);
|
|
1129
|
+
}
|
|
1076
1130
|
|
|
1077
1131
|
if (changes.length > 0) {
|
|
1078
1132
|
console.log('');
|