@techdigger/humanode-agentlink-cli 0.3.3 → 0.3.5
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 +2 -0
- package/REGISTRATION.md +4 -2
- package/dist/commands/authorize-link.js +1 -1
- package/dist/commands/request.d.ts +23 -0
- package/dist/commands/request.js +59 -0
- package/dist/help.d.ts +1 -0
- package/dist/help.js +27 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +14 -1
- package/dist/lib/core.d.ts +2 -0
- package/dist/lib/core.js +7 -6
- package/dist/templates.js +8 -4
- package/package.json +4 -3
package/README.md
CHANGED
|
@@ -30,6 +30,8 @@ After the owner completes linking in the browser, check the result:
|
|
|
30
30
|
agentlink status
|
|
31
31
|
```
|
|
32
32
|
|
|
33
|
+
For the first protected-route test, start with a `free-trial` route before moving to stricter gating or more complex payment flows.
|
|
34
|
+
|
|
33
35
|
## Commands
|
|
34
36
|
|
|
35
37
|
### `agentlink init`
|
package/REGISTRATION.md
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
# Humanode Agentlink — Agent Developer Guide
|
|
2
2
|
|
|
3
|
-
Link your AI agent to a biomapped human and unlock
|
|
3
|
+
Link your AI agent to a biomapped human and unlock verified-human access on x402 APIs.
|
|
4
4
|
|
|
5
5
|
## What linking does
|
|
6
6
|
|
|
7
|
-
When your agent is linked to a biomapped owner, platforms using Humanode Agentlink can identify your agent as acting on behalf of a real human. This unlocks better
|
|
7
|
+
When your agent is linked to a biomapped owner, platforms using Humanode Agentlink can identify your agent as acting on behalf of a real human. This unlocks better route behavior: free tiers, free trials, or linked-only gating that are unavailable to unlinked agents.
|
|
8
8
|
|
|
9
9
|
## Prerequisites
|
|
10
10
|
|
|
@@ -112,6 +112,8 @@ Possible results:
|
|
|
112
112
|
|
|
113
113
|
Use `--json` for machine-readable output.
|
|
114
114
|
|
|
115
|
+
For the first protected-route test, start with a `free-trial` route. It is the fastest way to prove the linked-human path end to end.
|
|
116
|
+
|
|
115
117
|
## Manual consent flow
|
|
116
118
|
|
|
117
119
|
If you do not want the CLI to create a link session URL, use the lower-level `authorize-link` command:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { isAddress } from 'viem';
|
|
2
2
|
import { createAgentLinkConsent } from '@techdigger/humanode-agentlink';
|
|
3
|
-
import { CliError, assertNoExtraPositionals, createConsolePrompter, defaultDeadline, parseFlags, parseFutureDeadline,
|
|
3
|
+
import { CliError, assertNoExtraPositionals, createConsolePrompter, defaultDeadline, parseFlags, parseFutureDeadline, resolveNetwork, resolvePrivateKey, resolveRegistryAddress, resolveRpcUrl, } from '../lib/core.js';
|
|
4
4
|
export async function parseAuthorizeLinkOptions(argv, env, now, overrides = {}) {
|
|
5
5
|
const parsed = parseFlags(argv);
|
|
6
6
|
assertNoExtraPositionals('authorize-link', parsed.positionals);
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { Hex } from 'viem';
|
|
2
|
+
import { type EnvSource, type NetworkName } from '../lib/core.js';
|
|
3
|
+
export interface RequestOptions {
|
|
4
|
+
url: string;
|
|
5
|
+
method: string;
|
|
6
|
+
network: NetworkName;
|
|
7
|
+
count: number;
|
|
8
|
+
json: boolean;
|
|
9
|
+
privateKey: Hex;
|
|
10
|
+
}
|
|
11
|
+
export interface RequestResult {
|
|
12
|
+
request: number;
|
|
13
|
+
status: number;
|
|
14
|
+
body: string;
|
|
15
|
+
}
|
|
16
|
+
export interface RequestDeps {
|
|
17
|
+
fetchImpl: typeof fetch;
|
|
18
|
+
}
|
|
19
|
+
export declare function parseRequestOptions(argv: string[], env: EnvSource, overrides?: {
|
|
20
|
+
privateKey?: string;
|
|
21
|
+
}): RequestOptions;
|
|
22
|
+
export declare function runRequest(options: RequestOptions, deps?: Partial<RequestDeps>): Promise<RequestResult[]>;
|
|
23
|
+
export declare function formatRequestOutput(results: RequestResult[], json: boolean): string;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { buildAgentLinkHeader } from '@techdigger/humanode-agentlink';
|
|
2
|
+
import { CliError, assertNoExtraPositionals, parseFlags, resolveNetwork, resolvePrivateKey, } from '../lib/core.js';
|
|
3
|
+
export function parseRequestOptions(argv, env, overrides = {}) {
|
|
4
|
+
const parsed = parseFlags(argv);
|
|
5
|
+
if (parsed.positionals.length === 0) {
|
|
6
|
+
throw new CliError('Missing URL. Usage: agentlink request <url> [options]');
|
|
7
|
+
}
|
|
8
|
+
assertNoExtraPositionals('request', parsed.positionals.slice(1));
|
|
9
|
+
const url = parsed.positionals[0];
|
|
10
|
+
try {
|
|
11
|
+
new URL(url);
|
|
12
|
+
}
|
|
13
|
+
catch {
|
|
14
|
+
throw new CliError(`Invalid URL: "${url}"`);
|
|
15
|
+
}
|
|
16
|
+
const countRaw = parsed.values.count;
|
|
17
|
+
const count = countRaw ? parseInt(countRaw, 10) : 1;
|
|
18
|
+
if (isNaN(count) || count < 1) {
|
|
19
|
+
throw new CliError(`--count must be a positive integer, got "${countRaw}"`);
|
|
20
|
+
}
|
|
21
|
+
const network = resolveNetwork(parsed.values.network, env);
|
|
22
|
+
return {
|
|
23
|
+
url,
|
|
24
|
+
method: (parsed.values.method ?? 'GET').toUpperCase(),
|
|
25
|
+
network,
|
|
26
|
+
count,
|
|
27
|
+
json: parsed.flags.has('json'),
|
|
28
|
+
privateKey: resolvePrivateKey(parsed.values['private-key'], env, overrides.privateKey),
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
export async function runRequest(options, deps = {}) {
|
|
32
|
+
const fetchImpl = deps.fetchImpl ?? fetch;
|
|
33
|
+
const results = [];
|
|
34
|
+
for (let i = 1; i <= options.count; i++) {
|
|
35
|
+
const agentlinkHeader = await buildAgentLinkHeader(options.url, {
|
|
36
|
+
privateKey: options.privateKey,
|
|
37
|
+
network: options.network,
|
|
38
|
+
});
|
|
39
|
+
let response;
|
|
40
|
+
try {
|
|
41
|
+
response = await fetchImpl(new Request(options.url, {
|
|
42
|
+
method: options.method,
|
|
43
|
+
headers: { agentlink: agentlinkHeader },
|
|
44
|
+
}));
|
|
45
|
+
}
|
|
46
|
+
catch (error) {
|
|
47
|
+
throw new CliError(`Request ${i} failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
48
|
+
}
|
|
49
|
+
const body = await response.text();
|
|
50
|
+
results.push({ request: i, status: response.status, body });
|
|
51
|
+
}
|
|
52
|
+
return results;
|
|
53
|
+
}
|
|
54
|
+
export function formatRequestOutput(results, json) {
|
|
55
|
+
if (json) {
|
|
56
|
+
return JSON.stringify(results, null, 2);
|
|
57
|
+
}
|
|
58
|
+
return results.map(r => `Request ${r.request} ${r.status} ${r.body.slice(0, 200)}`).join('\n');
|
|
59
|
+
}
|
package/dist/help.d.ts
CHANGED
|
@@ -3,4 +3,5 @@ export declare function getKeystoreHelpText(): string;
|
|
|
3
3
|
export declare function getAuthorizeLinkHelpText(): string;
|
|
4
4
|
export declare function getStatusHelpText(): string;
|
|
5
5
|
export declare function getLinkHelpText(): string;
|
|
6
|
+
export declare function getRequestHelpText(): string;
|
|
6
7
|
export declare function getInitHelpText(): string;
|
package/dist/help.js
CHANGED
|
@@ -7,6 +7,7 @@ Commands:
|
|
|
7
7
|
link Generate consent and open a linker URL for owner approval
|
|
8
8
|
authorize-link Generate a one-time EIP-712 consent blob for manual linking
|
|
9
9
|
status Check whether an agent is linked and active
|
|
10
|
+
request Send a signed agentlink request to a protected endpoint
|
|
10
11
|
|
|
11
12
|
Flags:
|
|
12
13
|
--help, -h Show this help text
|
|
@@ -108,6 +109,32 @@ Options:
|
|
|
108
109
|
--help Show this help message
|
|
109
110
|
`;
|
|
110
111
|
}
|
|
112
|
+
export function getRequestHelpText() {
|
|
113
|
+
return `Usage:
|
|
114
|
+
agentlink request <url> [options]
|
|
115
|
+
|
|
116
|
+
Description:
|
|
117
|
+
Send a signed agentlink request to a URL. Builds a SIWE payload signed with
|
|
118
|
+
your agent wallet, attaches it as the "agentlink" header, and prints the
|
|
119
|
+
response. Use --count to repeat the request (e.g. to test free-trial limits).
|
|
120
|
+
|
|
121
|
+
Options:
|
|
122
|
+
--method HTTP method (default: GET)
|
|
123
|
+
--count Number of times to send the request (default: 1)
|
|
124
|
+
--network base | base-sepolia (default: BIOMAPPER_NETWORK or base-sepolia)
|
|
125
|
+
--private-key-stdin Read the agent private key from stdin
|
|
126
|
+
--private-key Dev-only: pass key directly as 0x-prefixed hex
|
|
127
|
+
--json Output results as JSON
|
|
128
|
+
--help Show this help message
|
|
129
|
+
|
|
130
|
+
Key resolution order: keystore (default) → --private-key-stdin → --private-key → AGENT_PRIVATE_KEY env
|
|
131
|
+
|
|
132
|
+
Examples:
|
|
133
|
+
agentlink request http://localhost:3000/data
|
|
134
|
+
agentlink request http://75.119.141.48:3000/data --count 4
|
|
135
|
+
agentlink request https://api.example.com/resource --method POST --network base
|
|
136
|
+
`;
|
|
137
|
+
}
|
|
111
138
|
export function getInitHelpText() {
|
|
112
139
|
return `Usage:
|
|
113
140
|
agentlink init [dir] [options]
|
package/dist/index.d.ts
CHANGED
|
@@ -13,5 +13,6 @@ export interface CliRuntime {
|
|
|
13
13
|
openUrl: (url: string) => Promise<void>;
|
|
14
14
|
readStdin: () => Promise<string>;
|
|
15
15
|
readPassword: (prompt: string) => Promise<string>;
|
|
16
|
+
fetchImpl: typeof fetch;
|
|
16
17
|
}
|
|
17
18
|
export declare function runCli(argv?: string[], overrides?: Partial<CliRuntime>): Promise<number>;
|
package/dist/index.js
CHANGED
|
@@ -6,10 +6,11 @@ import { formatInitOutput, parseInitOptions, runInit } from './commands/init.js'
|
|
|
6
6
|
import { formatLinkOutput, parseLinkOptions, runLink } from './commands/link.js';
|
|
7
7
|
import { formatStatusOutput, parseStatusOptions, runStatus } from './commands/status.js';
|
|
8
8
|
import { parseAuthorizeLinkOptions, runAuthorizeLink } from './commands/authorize-link.js';
|
|
9
|
+
import { formatRequestOutput, parseRequestOptions, runRequest } from './commands/request.js';
|
|
9
10
|
import { runKeystoreAddress, runKeystoreDelete, runKeystoreSet } from './commands/keystore.js';
|
|
10
11
|
import { CliError, formatJson, loadEnvFiles, openUrlInBrowser, parseFlags, readAllFromStdin, readPassword } from './lib/core.js';
|
|
11
12
|
import { decryptKeystore, keystoreExists, keystorePath } from './lib/keystore.js';
|
|
12
|
-
import { getAuthorizeLinkHelpText, getInitHelpText, getKeystoreHelpText, getLinkHelpText, getRootHelpText, getStatusHelpText, } from './help.js';
|
|
13
|
+
import { getAuthorizeLinkHelpText, getInitHelpText, getKeystoreHelpText, getLinkHelpText, getRequestHelpText, getRootHelpText, getStatusHelpText, } from './help.js';
|
|
13
14
|
import { VERSION } from './version.js';
|
|
14
15
|
import { buildEmbeddedHostedLinkUrl, createAgentLinkConsent, createBiomapperQueryClient, createLinkSession, } from '@techdigger/humanode-agentlink';
|
|
15
16
|
function writeLine(write, text) {
|
|
@@ -34,6 +35,7 @@ export async function runCli(argv = process.argv.slice(2), overrides = {}) {
|
|
|
34
35
|
openUrl: overrides.openUrl ?? openUrlInBrowser,
|
|
35
36
|
readStdin: overrides.readStdin ?? readAllFromStdin,
|
|
36
37
|
readPassword: overrides.readPassword ?? readPassword,
|
|
38
|
+
fetchImpl: overrides.fetchImpl ?? fetch,
|
|
37
39
|
};
|
|
38
40
|
const [command, ...args] = argv;
|
|
39
41
|
try {
|
|
@@ -183,6 +185,17 @@ export async function runCli(argv = process.argv.slice(2), overrides = {}) {
|
|
|
183
185
|
}, runtime.env);
|
|
184
186
|
return 0;
|
|
185
187
|
}
|
|
188
|
+
case 'request': {
|
|
189
|
+
if (args.includes('--help') || args.includes('-h')) {
|
|
190
|
+
writeLine(runtime.stdout, getRequestHelpText());
|
|
191
|
+
return 0;
|
|
192
|
+
}
|
|
193
|
+
const requestPrivateKey = await readPrivateKeyFromStdin(args, runtime);
|
|
194
|
+
const requestOptions = parseRequestOptions(args, runtime.env, { privateKey: requestPrivateKey });
|
|
195
|
+
const requestResults = await runRequest(requestOptions, { fetchImpl: runtime.fetchImpl });
|
|
196
|
+
writeLine(runtime.stdout, formatRequestOutput(requestResults, requestOptions.json));
|
|
197
|
+
return 0;
|
|
198
|
+
}
|
|
186
199
|
default:
|
|
187
200
|
throw new CliError(`Unknown command "${command}". Run "agentlink --help" for available commands.`);
|
|
188
201
|
}
|
package/dist/lib/core.d.ts
CHANGED
|
@@ -4,11 +4,13 @@ export declare const NETWORKS: {
|
|
|
4
4
|
readonly label: "Base mainnet";
|
|
5
5
|
readonly biomapperAppUrl: string;
|
|
6
6
|
readonly defaultRegistry: Address;
|
|
7
|
+
readonly chainId: 8453;
|
|
7
8
|
};
|
|
8
9
|
readonly 'base-sepolia': {
|
|
9
10
|
readonly label: "Base Sepolia";
|
|
10
11
|
readonly biomapperAppUrl: string;
|
|
11
12
|
readonly defaultRegistry: Address;
|
|
13
|
+
readonly chainId: 84532;
|
|
12
14
|
};
|
|
13
15
|
};
|
|
14
16
|
export type NetworkName = keyof typeof NETWORKS;
|
package/dist/lib/core.js
CHANGED
|
@@ -12,11 +12,13 @@ export const NETWORKS = {
|
|
|
12
12
|
label: 'Base mainnet',
|
|
13
13
|
biomapperAppUrl: BIOMAPPER_APP_URLS.base,
|
|
14
14
|
defaultRegistry: '0x', // populated at mainnet launch
|
|
15
|
+
chainId: 8453,
|
|
15
16
|
},
|
|
16
17
|
'base-sepolia': {
|
|
17
18
|
label: 'Base Sepolia',
|
|
18
19
|
biomapperAppUrl: BIOMAPPER_APP_URLS['base-sepolia'],
|
|
19
20
|
defaultRegistry: '0xc0fb26BaACe7E1BCb3aFFD547AD5f2cAc4A4F51b',
|
|
21
|
+
chainId: 84532,
|
|
20
22
|
},
|
|
21
23
|
};
|
|
22
24
|
export const TEMPLATE_NAMES = ['langchain', 'vercel-ai-sdk', 'mcp'];
|
|
@@ -239,17 +241,16 @@ export function getPackageManagerInstallCommand(packageManager) {
|
|
|
239
241
|
export function getPackageManagerRunCommand(packageManager, script) {
|
|
240
242
|
return `${PM_COMMANDS[packageManager].run} ${script}`;
|
|
241
243
|
}
|
|
242
|
-
const HUMANODE_PACKAGE_VERSION = '^0.2.0';
|
|
243
244
|
const HUMANODE_PACKAGE_META = {
|
|
244
|
-
agentlink: { dirName: 'agentlink', packageName: '@techdigger/humanode-agentlink', version:
|
|
245
|
+
agentlink: { dirName: 'agentlink', packageName: '@techdigger/humanode-agentlink', version: '^0.3.0' },
|
|
245
246
|
langchain: {
|
|
246
247
|
dirName: 'langchain',
|
|
247
248
|
packageName: '@techdigger/humanode-agentlink-langchain',
|
|
248
|
-
version:
|
|
249
|
+
version: '^0.1.0',
|
|
249
250
|
},
|
|
250
|
-
aiSdk: { dirName: 'ai-sdk', packageName: '@techdigger/humanode-agentlink-ai-sdk', version:
|
|
251
|
-
mcp: { dirName: 'mcp', packageName: '@techdigger/humanode-agentlink-mcp', version:
|
|
252
|
-
cli: { dirName: 'cli', packageName: '@techdigger/humanode-agentlink-cli', version:
|
|
251
|
+
aiSdk: { dirName: 'ai-sdk', packageName: '@techdigger/humanode-agentlink-ai-sdk', version: '^0.1.0' },
|
|
252
|
+
mcp: { dirName: 'mcp', packageName: '@techdigger/humanode-agentlink-mcp', version: '^0.1.0' },
|
|
253
|
+
cli: { dirName: 'cli', packageName: '@techdigger/humanode-agentlink-cli', version: '^0.3.5' },
|
|
253
254
|
};
|
|
254
255
|
async function pathExists(targetPath) {
|
|
255
256
|
try {
|
package/dist/templates.js
CHANGED
|
@@ -7,7 +7,7 @@ const MCP_SDK_VERSION = '^1.12.1';
|
|
|
7
7
|
const DOTENV_VERSION = '^17.3.1';
|
|
8
8
|
const TSX_VERSION = '^4.19.4';
|
|
9
9
|
const TYPESCRIPT_VERSION = '^5.8.3';
|
|
10
|
-
function buildCommonEnv(input
|
|
10
|
+
function buildCommonEnv(input) {
|
|
11
11
|
const lines = [
|
|
12
12
|
`BIOMAPPER_NETWORK=${input.network}`,
|
|
13
13
|
`BIOMAPPER_REGISTRY=${input.registry}`,
|
|
@@ -210,6 +210,10 @@ ${getPackageManagerRunCommand(input.packageManager, 'link')} -- --owner 0xOwner
|
|
|
210
210
|
\`\`\`bash
|
|
211
211
|
${getPackageManagerRunCommand(input.packageManager, 'status')}
|
|
212
212
|
\`\`\`
|
|
213
|
+
|
|
214
|
+
## Recommended First Route Test
|
|
215
|
+
|
|
216
|
+
After linking succeeds, start by pointing the agent at a \`free-trial\` route. That proves the linked-human path before you move to stricter gating or more advanced payment flows.
|
|
213
217
|
`;
|
|
214
218
|
}
|
|
215
219
|
function buildLangChainReadme(input) {
|
|
@@ -253,21 +257,21 @@ export function buildTemplateFiles(input) {
|
|
|
253
257
|
case 'langchain':
|
|
254
258
|
return [
|
|
255
259
|
...commonFiles,
|
|
256
|
-
{ path: '.env.local', content: buildCommonEnv(input
|
|
260
|
+
{ path: '.env.local', content: buildCommonEnv(input) },
|
|
257
261
|
{ path: 'README.md', content: buildLangChainReadme(input) },
|
|
258
262
|
{ path: 'src/index.ts', content: buildLangChainSource() },
|
|
259
263
|
];
|
|
260
264
|
case 'vercel-ai-sdk':
|
|
261
265
|
return [
|
|
262
266
|
...commonFiles,
|
|
263
|
-
{ path: '.env.local', content: buildCommonEnv(input
|
|
267
|
+
{ path: '.env.local', content: buildCommonEnv(input) },
|
|
264
268
|
{ path: 'README.md', content: buildAISDKReadme(input) },
|
|
265
269
|
{ path: 'src/index.ts', content: buildAISource() },
|
|
266
270
|
];
|
|
267
271
|
case 'mcp':
|
|
268
272
|
return [
|
|
269
273
|
...commonFiles,
|
|
270
|
-
{ path: '.env.local', content: buildCommonEnv(input
|
|
274
|
+
{ path: '.env.local', content: buildCommonEnv(input) },
|
|
271
275
|
{ path: 'README.md', content: buildMcpReadme(input) },
|
|
272
276
|
{ path: 'mcp.config.json', content: buildMcpConfig(input.packageManager) },
|
|
273
277
|
{ path: 'src/index.ts', content: buildMcpSource() },
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@techdigger/humanode-agentlink-cli",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.5",
|
|
4
4
|
"description": "Scaffold, link, and inspect Biomapper-ready agent projects.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -23,8 +23,9 @@
|
|
|
23
23
|
"cli": "tsx src/index.ts"
|
|
24
24
|
},
|
|
25
25
|
"dependencies": {
|
|
26
|
-
"@techdigger/humanode-agentlink": "^0.
|
|
26
|
+
"@techdigger/humanode-agentlink": "^0.3.0",
|
|
27
27
|
"posthog-node": "^5.21.2",
|
|
28
|
+
"siwe": "^2.3.2",
|
|
28
29
|
"viem": "^2.46.2"
|
|
29
30
|
},
|
|
30
31
|
"keywords": [
|
|
@@ -35,7 +36,7 @@
|
|
|
35
36
|
"mcp"
|
|
36
37
|
],
|
|
37
38
|
"devDependencies": {
|
|
38
|
-
"@types/node": "^24.
|
|
39
|
+
"@types/node": "^24.12.2",
|
|
39
40
|
"tsx": "^4.19.4",
|
|
40
41
|
"typescript": "^5.9.3"
|
|
41
42
|
}
|