@techdigger/humanode-agentlink-cli 0.2.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 +93 -0
- package/REGISTRATION.md +302 -0
- package/dist/commands/authorize-link.d.ts +19 -0
- package/dist/commands/authorize-link.js +30 -0
- package/dist/commands/init.d.ts +33 -0
- package/dist/commands/init.js +127 -0
- package/dist/commands/link.d.ts +38 -0
- package/dist/commands/link.js +82 -0
- package/dist/commands/status.d.ts +30 -0
- package/dist/commands/status.js +68 -0
- package/dist/help.d.ts +5 -0
- package/dist/help.js +103 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.js +185 -0
- package/dist/lib/core.d.ts +92 -0
- package/dist/lib/core.js +435 -0
- package/dist/lib/telemetry.d.ts +13 -0
- package/dist/lib/telemetry.js +123 -0
- package/dist/templates.d.ts +12 -0
- package/dist/templates.js +281 -0
- package/package.json +42 -0
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { buildEmbeddedHostedLinkUrl, createAgentLinkConsent, createLinkSession, } from '@techdigger/humanode-agentlink';
|
|
2
|
+
import { CliError, NETWORKS, assertNoExtraPositionals, defaultDeadline, formatJson, parseFlags, parseFutureDeadline, parseRequiredAddress, resolveLinkerUrl, resolveNetwork, resolvePrivateKey, resolveRegistryAddress, resolveRpcUrl, openUrlInBrowser, } from '../lib/core.js';
|
|
3
|
+
export function parseLinkOptions(argv, env, now, overrides = {}) {
|
|
4
|
+
const parsed = parseFlags(argv);
|
|
5
|
+
assertNoExtraPositionals('link', parsed.positionals);
|
|
6
|
+
return {
|
|
7
|
+
owner: parseRequiredAddress('owner', parsed.values.owner),
|
|
8
|
+
registry: resolveRegistryAddress(parsed.values.registry, env),
|
|
9
|
+
network: resolveNetwork(parsed.values.network, env),
|
|
10
|
+
deadline: parsed.values.deadline ? parseFutureDeadline(parsed.values.deadline, now) : defaultDeadline(now),
|
|
11
|
+
privateKey: resolvePrivateKey(parsed.values['private-key'], env, overrides.privateKey),
|
|
12
|
+
rpcUrl: resolveRpcUrl(parsed.values['rpc-url'], env),
|
|
13
|
+
linkerUrl: resolveLinkerUrl(parsed.values['linker-url'], env),
|
|
14
|
+
open: parsed.flags.has('open'),
|
|
15
|
+
json: parsed.flags.has('json'),
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
export async function runLink(options, deps = {}) {
|
|
19
|
+
const now = deps.now ?? new Date();
|
|
20
|
+
const createConsent = deps.createAgentLinkConsent ?? createAgentLinkConsent;
|
|
21
|
+
const createSession = deps.createLinkSession ?? createLinkSession;
|
|
22
|
+
const buildUrl = deps.buildLinkUrl ?? buildEmbeddedHostedLinkUrl;
|
|
23
|
+
const openUrl = deps.openUrl ?? openUrlInBrowser;
|
|
24
|
+
try {
|
|
25
|
+
const consent = await createConsent({
|
|
26
|
+
owner: options.owner,
|
|
27
|
+
registry: options.registry,
|
|
28
|
+
deadline: options.deadline,
|
|
29
|
+
network: options.network,
|
|
30
|
+
privateKey: options.privateKey,
|
|
31
|
+
rpcUrl: options.rpcUrl,
|
|
32
|
+
});
|
|
33
|
+
const session = await createSession({
|
|
34
|
+
platformAccountId: 'local-cli',
|
|
35
|
+
network: options.network,
|
|
36
|
+
registry: options.registry,
|
|
37
|
+
consent,
|
|
38
|
+
expiresAt: new Date(Number(options.deadline) * 1000).toISOString(),
|
|
39
|
+
}, { now });
|
|
40
|
+
const linkerUrl = buildUrl(options.linkerUrl, session);
|
|
41
|
+
let opened = false;
|
|
42
|
+
if (options.open) {
|
|
43
|
+
await openUrl(linkerUrl);
|
|
44
|
+
opened = true;
|
|
45
|
+
}
|
|
46
|
+
return {
|
|
47
|
+
agent: consent.agent,
|
|
48
|
+
owner: options.owner,
|
|
49
|
+
network: options.network,
|
|
50
|
+
registry: options.registry,
|
|
51
|
+
deadline: options.deadline.toString(),
|
|
52
|
+
linkerUrl,
|
|
53
|
+
opened,
|
|
54
|
+
consent,
|
|
55
|
+
session,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
catch (error) {
|
|
59
|
+
throw new CliError(error instanceof Error ? error.message : String(error));
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
export function formatLinkOutput(result, json = false) {
|
|
63
|
+
if (json) {
|
|
64
|
+
return formatJson(result);
|
|
65
|
+
}
|
|
66
|
+
const lines = [
|
|
67
|
+
`Agent: ${result.agent}`,
|
|
68
|
+
`Owner: ${result.owner}`,
|
|
69
|
+
`Network: ${NETWORKS[result.network].label}`,
|
|
70
|
+
`Registry: ${result.registry}`,
|
|
71
|
+
`Deadline: ${result.deadline}`,
|
|
72
|
+
'',
|
|
73
|
+
'Open this linker URL and connect the owner wallet to finish the on-chain link:',
|
|
74
|
+
result.linkerUrl,
|
|
75
|
+
'',
|
|
76
|
+
'The CLI does not submit linkAgent(...) itself. The owner still completes the transaction in the linker UI.',
|
|
77
|
+
];
|
|
78
|
+
if (result.opened) {
|
|
79
|
+
lines.push('', 'The CLI asked your OS to open the linker in your default browser.');
|
|
80
|
+
}
|
|
81
|
+
return lines.join('\n');
|
|
82
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { Address } from 'viem';
|
|
2
|
+
import { createBiomapperQueryClient } from '@techdigger/humanode-agentlink';
|
|
3
|
+
import { type EnvSource, type NetworkName } from '../lib/core.js';
|
|
4
|
+
export interface StatusOptions {
|
|
5
|
+
registry: Address;
|
|
6
|
+
agent: Address;
|
|
7
|
+
network: NetworkName;
|
|
8
|
+
rpcUrl?: string;
|
|
9
|
+
json: boolean;
|
|
10
|
+
}
|
|
11
|
+
export interface StatusOptionOverrides {
|
|
12
|
+
privateKey?: string;
|
|
13
|
+
}
|
|
14
|
+
export interface StatusResult {
|
|
15
|
+
agent: Address;
|
|
16
|
+
network: NetworkName;
|
|
17
|
+
registry: Address;
|
|
18
|
+
linked: boolean;
|
|
19
|
+
active: boolean;
|
|
20
|
+
owner: Address | null;
|
|
21
|
+
generationPtr: string | null;
|
|
22
|
+
message: string;
|
|
23
|
+
biomapperAppUrl: string;
|
|
24
|
+
}
|
|
25
|
+
export interface StatusDeps {
|
|
26
|
+
createBiomapperQueryClient: typeof createBiomapperQueryClient;
|
|
27
|
+
}
|
|
28
|
+
export declare function parseStatusOptions(argv: string[], env: EnvSource, overrides?: StatusOptionOverrides): StatusOptions;
|
|
29
|
+
export declare function runStatus(options: StatusOptions, deps?: Partial<StatusDeps>): Promise<StatusResult>;
|
|
30
|
+
export declare function formatStatusOutput(result: StatusResult, json?: boolean): string;
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { createBiomapperQueryClient } from '@techdigger/humanode-agentlink';
|
|
2
|
+
import { CliError, NETWORKS, assertNoExtraPositionals, deriveAgentAddress, formatJson, parseFlags, resolveAgentAddress, resolveNetwork, resolvePrivateKey, resolveRegistryAddress, resolveRpcUrl, } from '../lib/core.js';
|
|
3
|
+
export function parseStatusOptions(argv, env, overrides = {}) {
|
|
4
|
+
const parsed = parseFlags(argv);
|
|
5
|
+
assertNoExtraPositionals('status', parsed.positionals);
|
|
6
|
+
let agent;
|
|
7
|
+
if (parsed.values.agent) {
|
|
8
|
+
agent = resolveAgentAddress(parsed.values.agent, undefined, env);
|
|
9
|
+
}
|
|
10
|
+
else {
|
|
11
|
+
const privateKey = resolvePrivateKey(parsed.values['private-key'], env, overrides.privateKey);
|
|
12
|
+
agent = deriveAgentAddress(privateKey);
|
|
13
|
+
}
|
|
14
|
+
return {
|
|
15
|
+
registry: resolveRegistryAddress(parsed.values.registry, env),
|
|
16
|
+
agent,
|
|
17
|
+
network: resolveNetwork(parsed.values.network, env),
|
|
18
|
+
rpcUrl: resolveRpcUrl(parsed.values['rpc-url'], env),
|
|
19
|
+
json: parsed.flags.has('json'),
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
export async function runStatus(options, deps = {}) {
|
|
23
|
+
const createQueryClient = deps.createBiomapperQueryClient ?? createBiomapperQueryClient;
|
|
24
|
+
try {
|
|
25
|
+
const queryClient = createQueryClient({
|
|
26
|
+
network: options.network,
|
|
27
|
+
registryAddress: options.registry,
|
|
28
|
+
rpcUrl: options.rpcUrl,
|
|
29
|
+
});
|
|
30
|
+
const result = await queryClient.checkAgentStatus({
|
|
31
|
+
agentAddress: options.agent,
|
|
32
|
+
});
|
|
33
|
+
return {
|
|
34
|
+
agent: options.agent,
|
|
35
|
+
network: options.network,
|
|
36
|
+
registry: options.registry,
|
|
37
|
+
linked: result.linked,
|
|
38
|
+
active: result.active,
|
|
39
|
+
owner: result.owner,
|
|
40
|
+
generationPtr: result.generationPtr,
|
|
41
|
+
message: result.message,
|
|
42
|
+
biomapperAppUrl: NETWORKS[options.network].biomapperAppUrl,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
catch (error) {
|
|
46
|
+
throw new CliError(error instanceof Error ? error.message : String(error));
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
export function formatStatusOutput(result, json = false) {
|
|
50
|
+
if (json) {
|
|
51
|
+
return formatJson(result);
|
|
52
|
+
}
|
|
53
|
+
const lines = [
|
|
54
|
+
`Agent: ${result.agent}`,
|
|
55
|
+
`Network: ${NETWORKS[result.network].label}`,
|
|
56
|
+
`Registry: ${result.registry}`,
|
|
57
|
+
'',
|
|
58
|
+
];
|
|
59
|
+
if (!result.linked) {
|
|
60
|
+
lines.push('Status: Not linked', '', 'This agent is not linked to any owner.', 'Run "agentlink link --owner 0xOwner --open" to generate a linker URL.');
|
|
61
|
+
return lines.join('\n');
|
|
62
|
+
}
|
|
63
|
+
lines.push(`Status: ${result.active ? 'Active' : 'Inactive'}`, `Owner: ${result.owner}`, `Generation: ${result.generationPtr ?? 'n/a'}`);
|
|
64
|
+
if (!result.active) {
|
|
65
|
+
lines.push('', 'This link is still stored, but the linked owner wallet is not biomapped in the current generation.', `If that same wallet becomes biomapped again at ${result.biomapperAppUrl}, the link becomes active again automatically.`, 'Then re-check with "agentlink status".');
|
|
66
|
+
}
|
|
67
|
+
return lines.join('\n');
|
|
68
|
+
}
|
package/dist/help.d.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export declare function getRootHelpText(): string;
|
|
2
|
+
export declare function getAuthorizeLinkHelpText(): string;
|
|
3
|
+
export declare function getStatusHelpText(): string;
|
|
4
|
+
export declare function getLinkHelpText(): string;
|
|
5
|
+
export declare function getInitHelpText(): string;
|
package/dist/help.js
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
export function getRootHelpText() {
|
|
2
|
+
return `Usage: agentlink <command> [options]
|
|
3
|
+
|
|
4
|
+
Commands:
|
|
5
|
+
init Scaffold a Biomapper-ready agent starter
|
|
6
|
+
link Generate consent and open a self-contained local/dev linker URL
|
|
7
|
+
authorize-link Generate a one-time EIP-712 consent blob for manual linking
|
|
8
|
+
status Check whether an agent is linked and active
|
|
9
|
+
|
|
10
|
+
Run agentlink <command> --help for command-specific options.
|
|
11
|
+
|
|
12
|
+
Environment:
|
|
13
|
+
AGENT_PRIVATE_KEY Local/dev secret source; prefer stdin or secret managers in production
|
|
14
|
+
BIOMAPPER_NETWORK Default network (base | base-sepolia)
|
|
15
|
+
BIOMAPPER_REGISTRY Default BiomapperAgentRegistry address
|
|
16
|
+
BIOMAPPER_RPC_URL Optional RPC URL override
|
|
17
|
+
BIOMAPPER_LINKER_URL Default linker URL (defaults to http://localhost:5173/)
|
|
18
|
+
AGENTLINK_TELEMETRY Optional override for the saved CLI telemetry choice (on | off)
|
|
19
|
+
AGENTLINK_POSTHOG_KEY Optional PostHog API key for CLI telemetry
|
|
20
|
+
AGENTLINK_POSTHOG_HOST Optional PostHog host override (defaults to https://us.i.posthog.com)
|
|
21
|
+
AGENTLINK_SOURCE Optional distribution source label for telemetry events
|
|
22
|
+
AGENTLINK_CAMPAIGN Optional campaign label for telemetry events
|
|
23
|
+
AGENTLINK_CONTENT_ID Optional content identifier for telemetry events
|
|
24
|
+
`;
|
|
25
|
+
}
|
|
26
|
+
export function getAuthorizeLinkHelpText() {
|
|
27
|
+
return `Usage:
|
|
28
|
+
agentlink authorize-link --owner <address> [options]
|
|
29
|
+
|
|
30
|
+
Description:
|
|
31
|
+
Generate a one-time EIP-712 consent blob that authorizes a biomapped owner wallet
|
|
32
|
+
to link an agent wallet in BiomapperAgentRegistry.
|
|
33
|
+
|
|
34
|
+
Options:
|
|
35
|
+
--owner Biomapped owner wallet that will call linkAgent
|
|
36
|
+
--registry BiomapperAgentRegistry contract address (falls back to BIOMAPPER_REGISTRY)
|
|
37
|
+
--deadline Unix timestamp in seconds for signature expiry
|
|
38
|
+
--network base | base-sepolia (default: BIOMAPPER_NETWORK or base-sepolia)
|
|
39
|
+
--private-key Insecure/dev-only: agent private key as 0x-prefixed hex. Falls back to AGENT_PRIVATE_KEY
|
|
40
|
+
--private-key-stdin Read the agent private key from stdin instead of argv
|
|
41
|
+
--rpc-url Optional RPC URL override for the selected network
|
|
42
|
+
--help Show this help message
|
|
43
|
+
`;
|
|
44
|
+
}
|
|
45
|
+
export function getStatusHelpText() {
|
|
46
|
+
return `Usage:
|
|
47
|
+
agentlink status [options]
|
|
48
|
+
|
|
49
|
+
Description:
|
|
50
|
+
Query the BiomapperAgentRegistry to check if an agent is linked and whether
|
|
51
|
+
the link is currently active (owner is biomapped in the current generation).
|
|
52
|
+
|
|
53
|
+
Options:
|
|
54
|
+
--registry BiomapperAgentRegistry contract address (falls back to BIOMAPPER_REGISTRY)
|
|
55
|
+
--agent Agent wallet address to check. If omitted, derived from the private key input
|
|
56
|
+
--network base | base-sepolia (default: BIOMAPPER_NETWORK or base-sepolia)
|
|
57
|
+
--private-key Insecure/dev-only: agent private key (used to derive agent address if --agent is omitted)
|
|
58
|
+
--private-key-stdin Read the agent private key from stdin instead of argv
|
|
59
|
+
--rpc-url Optional RPC URL override for the selected network
|
|
60
|
+
--json Output raw JSON instead of human-readable text
|
|
61
|
+
--help Show this help message
|
|
62
|
+
`;
|
|
63
|
+
}
|
|
64
|
+
export function getLinkHelpText() {
|
|
65
|
+
return `Usage:
|
|
66
|
+
agentlink link --owner <address> [options]
|
|
67
|
+
|
|
68
|
+
Description:
|
|
69
|
+
Generate agent consent, embed it in a self-contained local/dev linker URL, and optionally
|
|
70
|
+
open the browser so the owner can complete linking with their wallet.
|
|
71
|
+
|
|
72
|
+
Options:
|
|
73
|
+
--owner Biomapped owner wallet address
|
|
74
|
+
--registry BiomapperAgentRegistry contract address (falls back to BIOMAPPER_REGISTRY)
|
|
75
|
+
--network base | base-sepolia (default: BIOMAPPER_NETWORK or base-sepolia)
|
|
76
|
+
--deadline Unix timestamp in seconds for signature and session expiry (default: 24 hours from now)
|
|
77
|
+
--private-key Insecure/dev-only: agent private key as 0x-prefixed hex. Falls back to AGENT_PRIVATE_KEY
|
|
78
|
+
--private-key-stdin Read the agent private key from stdin instead of argv
|
|
79
|
+
--rpc-url Optional RPC URL override for the selected network
|
|
80
|
+
--linker-url Linker base URL (default: BIOMAPPER_LINKER_URL or http://localhost:5173/)
|
|
81
|
+
--open Open the generated linker URL in the default browser
|
|
82
|
+
--json Output consent, session, and linker URL as JSON
|
|
83
|
+
--help Show this help message
|
|
84
|
+
`;
|
|
85
|
+
}
|
|
86
|
+
export function getInitHelpText() {
|
|
87
|
+
return `Usage:
|
|
88
|
+
agentlink init [dir] [options]
|
|
89
|
+
|
|
90
|
+
Description:
|
|
91
|
+
Scaffold a Biomapper-ready TypeScript starter for LangChain, Vercel AI SDK, or MCP.
|
|
92
|
+
|
|
93
|
+
Options:
|
|
94
|
+
--template langchain | vercel-ai-sdk | mcp
|
|
95
|
+
--network base | base-sepolia (default: BIOMAPPER_NETWORK or base-sepolia)
|
|
96
|
+
--registry BiomapperAgentRegistry contract address (falls back to BIOMAPPER_REGISTRY)
|
|
97
|
+
--package-manager npm | pnpm | yarn | bun (default: npm)
|
|
98
|
+
--package-source npm | local (default: local when running from this repo, otherwise npm)
|
|
99
|
+
--install Install dependencies after writing files
|
|
100
|
+
--yes Use defaults and skip prompts
|
|
101
|
+
--help Show this help message
|
|
102
|
+
`;
|
|
103
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { createAgentLinkConsent, createBiomapperQueryClient, createLinkSession } from '@techdigger/humanode-agentlink';
|
|
3
|
+
export interface CliRuntime {
|
|
4
|
+
cwd: string;
|
|
5
|
+
env: Record<string, string | undefined>;
|
|
6
|
+
now: Date;
|
|
7
|
+
stdout: (text: string) => void;
|
|
8
|
+
stderr: (text: string) => void;
|
|
9
|
+
createAgentLinkConsent: typeof createAgentLinkConsent;
|
|
10
|
+
createBiomapperQueryClient: typeof createBiomapperQueryClient;
|
|
11
|
+
createLinkSession: typeof createLinkSession;
|
|
12
|
+
buildLinkUrl: (baseUrl: string, session: Awaited<ReturnType<typeof createLinkSession>>) => string;
|
|
13
|
+
openUrl: (url: string) => Promise<void>;
|
|
14
|
+
readStdin: () => Promise<string>;
|
|
15
|
+
}
|
|
16
|
+
export declare function runCli(argv?: string[], overrides?: Partial<CliRuntime>): Promise<number>;
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { realpathSync } from 'node:fs';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
4
|
+
import { captureCliTelemetry } from './lib/telemetry.js';
|
|
5
|
+
import { formatInitOutput, parseInitOptions, runInit } from './commands/init.js';
|
|
6
|
+
import { formatLinkOutput, parseLinkOptions, runLink } from './commands/link.js';
|
|
7
|
+
import { formatStatusOutput, parseStatusOptions, runStatus } from './commands/status.js';
|
|
8
|
+
import { parseAuthorizeLinkOptions, runAuthorizeLink } from './commands/authorize-link.js';
|
|
9
|
+
import { CliError, formatJson, loadEnvFiles, openUrlInBrowser, parseFlags, readAllFromStdin } from './lib/core.js';
|
|
10
|
+
import { getAuthorizeLinkHelpText, getInitHelpText, getLinkHelpText, getRootHelpText, getStatusHelpText, } from './help.js';
|
|
11
|
+
import { buildEmbeddedHostedLinkUrl, createAgentLinkConsent, createBiomapperQueryClient, createLinkSession, } from '@techdigger/humanode-agentlink';
|
|
12
|
+
function writeLine(write, text) {
|
|
13
|
+
write(text.endsWith('\n') ? text : `${text}\n`);
|
|
14
|
+
}
|
|
15
|
+
export async function runCli(argv = process.argv.slice(2), overrides = {}) {
|
|
16
|
+
const cwd = overrides.cwd ?? process.cwd();
|
|
17
|
+
const env = overrides.env ?? (await loadEnvFiles(cwd));
|
|
18
|
+
const now = overrides.now ?? new Date();
|
|
19
|
+
const stdout = overrides.stdout ?? (text => process.stdout.write(text));
|
|
20
|
+
const stderr = overrides.stderr ?? (text => process.stderr.write(text));
|
|
21
|
+
const runtime = {
|
|
22
|
+
cwd,
|
|
23
|
+
env,
|
|
24
|
+
now,
|
|
25
|
+
stdout,
|
|
26
|
+
stderr,
|
|
27
|
+
createAgentLinkConsent: overrides.createAgentLinkConsent ?? createAgentLinkConsent,
|
|
28
|
+
createBiomapperQueryClient: overrides.createBiomapperQueryClient ?? createBiomapperQueryClient,
|
|
29
|
+
createLinkSession: overrides.createLinkSession ?? createLinkSession,
|
|
30
|
+
buildLinkUrl: overrides.buildLinkUrl ?? buildEmbeddedHostedLinkUrl,
|
|
31
|
+
openUrl: overrides.openUrl ?? openUrlInBrowser,
|
|
32
|
+
readStdin: overrides.readStdin ?? readAllFromStdin,
|
|
33
|
+
};
|
|
34
|
+
const [command, ...args] = argv;
|
|
35
|
+
try {
|
|
36
|
+
if (!command || command === '--help' || command === '-h') {
|
|
37
|
+
writeLine(runtime.stdout, getRootHelpText());
|
|
38
|
+
return 0;
|
|
39
|
+
}
|
|
40
|
+
switch (command) {
|
|
41
|
+
case 'authorize-link':
|
|
42
|
+
if (args.includes('--help') || args.includes('-h')) {
|
|
43
|
+
writeLine(runtime.stdout, getAuthorizeLinkHelpText());
|
|
44
|
+
return 0;
|
|
45
|
+
}
|
|
46
|
+
const authorizePrivateKey = await readPrivateKeyFromStdin(args, runtime);
|
|
47
|
+
writeLine(runtime.stdout, formatJson(await runAuthorizeLink(parseAuthorizeLinkOptions(args, runtime.env, runtime.now, {
|
|
48
|
+
privateKey: authorizePrivateKey,
|
|
49
|
+
}), {
|
|
50
|
+
createAgentLinkConsent: runtime.createAgentLinkConsent,
|
|
51
|
+
})));
|
|
52
|
+
await captureCliTelemetry({
|
|
53
|
+
eventName: 'agentlink_cli_command_completed',
|
|
54
|
+
stage: 'consent-generated',
|
|
55
|
+
contentId: 'cli:authorize-link',
|
|
56
|
+
properties: {
|
|
57
|
+
command: 'authorize-link',
|
|
58
|
+
},
|
|
59
|
+
}, runtime.env);
|
|
60
|
+
return 0;
|
|
61
|
+
case 'status': {
|
|
62
|
+
if (args.includes('--help') || args.includes('-h')) {
|
|
63
|
+
writeLine(runtime.stdout, getStatusHelpText());
|
|
64
|
+
return 0;
|
|
65
|
+
}
|
|
66
|
+
const parsedArgs = parseFlags(args);
|
|
67
|
+
const statusPrivateKey = parsedArgs.values.agent
|
|
68
|
+
? undefined
|
|
69
|
+
: await readPrivateKeyFromStdin(args, runtime);
|
|
70
|
+
const options = parseStatusOptions(args, runtime.env, {
|
|
71
|
+
privateKey: statusPrivateKey,
|
|
72
|
+
});
|
|
73
|
+
const result = await runStatus(options, {
|
|
74
|
+
createBiomapperQueryClient: runtime.createBiomapperQueryClient,
|
|
75
|
+
});
|
|
76
|
+
writeLine(runtime.stdout, formatStatusOutput(result, options.json));
|
|
77
|
+
await captureCliTelemetry({
|
|
78
|
+
eventName: 'agentlink_cli_command_completed',
|
|
79
|
+
stage: result.active
|
|
80
|
+
? 'active-status-confirmed'
|
|
81
|
+
: result.linked
|
|
82
|
+
? 'linked-status-confirmed'
|
|
83
|
+
: 'status-checked',
|
|
84
|
+
network: result.network,
|
|
85
|
+
contentId: 'cli:status',
|
|
86
|
+
properties: {
|
|
87
|
+
command: 'status',
|
|
88
|
+
linked: result.linked,
|
|
89
|
+
active: result.active,
|
|
90
|
+
},
|
|
91
|
+
}, runtime.env);
|
|
92
|
+
return 0;
|
|
93
|
+
}
|
|
94
|
+
case 'link': {
|
|
95
|
+
if (args.includes('--help') || args.includes('-h')) {
|
|
96
|
+
writeLine(runtime.stdout, getLinkHelpText());
|
|
97
|
+
return 0;
|
|
98
|
+
}
|
|
99
|
+
const linkPrivateKey = await readPrivateKeyFromStdin(args, runtime);
|
|
100
|
+
const options = parseLinkOptions(args, runtime.env, runtime.now, {
|
|
101
|
+
privateKey: linkPrivateKey,
|
|
102
|
+
});
|
|
103
|
+
const result = await runLink(options, {
|
|
104
|
+
now: runtime.now,
|
|
105
|
+
createAgentLinkConsent: runtime.createAgentLinkConsent,
|
|
106
|
+
createLinkSession: runtime.createLinkSession,
|
|
107
|
+
buildLinkUrl: runtime.buildLinkUrl,
|
|
108
|
+
openUrl: runtime.openUrl,
|
|
109
|
+
});
|
|
110
|
+
writeLine(runtime.stdout, formatLinkOutput(result, options.json));
|
|
111
|
+
await captureCliTelemetry({
|
|
112
|
+
eventName: 'agentlink_cli_command_completed',
|
|
113
|
+
stage: 'link-generated',
|
|
114
|
+
network: result.network,
|
|
115
|
+
contentId: 'cli:link',
|
|
116
|
+
properties: {
|
|
117
|
+
command: 'link',
|
|
118
|
+
opened: result.opened,
|
|
119
|
+
},
|
|
120
|
+
}, runtime.env);
|
|
121
|
+
return 0;
|
|
122
|
+
}
|
|
123
|
+
case 'init': {
|
|
124
|
+
if (args.includes('--help') || args.includes('-h')) {
|
|
125
|
+
writeLine(runtime.stdout, getInitHelpText());
|
|
126
|
+
return 0;
|
|
127
|
+
}
|
|
128
|
+
const result = await runInit(parseInitOptions(args, runtime.env), {
|
|
129
|
+
cwd: runtime.cwd,
|
|
130
|
+
env: runtime.env,
|
|
131
|
+
});
|
|
132
|
+
writeLine(runtime.stdout, formatInitOutput(result, runtime.cwd));
|
|
133
|
+
await captureCliTelemetry({
|
|
134
|
+
eventName: 'agentlink_cli_command_completed',
|
|
135
|
+
stage: 'starter-generated',
|
|
136
|
+
network: result.network,
|
|
137
|
+
contentId: `cli:init:${result.template}`,
|
|
138
|
+
properties: {
|
|
139
|
+
command: 'init',
|
|
140
|
+
template: result.template,
|
|
141
|
+
packageManager: result.packageManager,
|
|
142
|
+
packageSource: result.packageSource,
|
|
143
|
+
},
|
|
144
|
+
}, runtime.env);
|
|
145
|
+
return 0;
|
|
146
|
+
}
|
|
147
|
+
default:
|
|
148
|
+
throw new CliError(`Unknown command "${command}". Run "agentlink --help" for available commands.`);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
catch (error) {
|
|
152
|
+
writeLine(runtime.stderr, error instanceof Error ? error.message : String(error));
|
|
153
|
+
return 1;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
async function readPrivateKeyFromStdin(argv, runtime) {
|
|
157
|
+
const parsed = parseFlags(argv);
|
|
158
|
+
if (!parsed.flags.has('private-key-stdin')) {
|
|
159
|
+
return undefined;
|
|
160
|
+
}
|
|
161
|
+
if (parsed.values['private-key']) {
|
|
162
|
+
throw new CliError('Pass either --private-key or --private-key-stdin, not both.');
|
|
163
|
+
}
|
|
164
|
+
const privateKey = (await runtime.readStdin()).trim();
|
|
165
|
+
if (!privateKey) {
|
|
166
|
+
throw new CliError('Missing agent private key on stdin. Pipe a 32-byte hex key into --private-key-stdin.');
|
|
167
|
+
}
|
|
168
|
+
return privateKey;
|
|
169
|
+
}
|
|
170
|
+
function isExecutedDirectly() {
|
|
171
|
+
if (!process.argv[1]) {
|
|
172
|
+
return false;
|
|
173
|
+
}
|
|
174
|
+
try {
|
|
175
|
+
return realpathSync(process.argv[1]) === realpathSync(fileURLToPath(import.meta.url));
|
|
176
|
+
}
|
|
177
|
+
catch {
|
|
178
|
+
return false;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
if (isExecutedDirectly()) {
|
|
182
|
+
runCli().then(code => {
|
|
183
|
+
process.exitCode = code;
|
|
184
|
+
});
|
|
185
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { type Address, type Hex } from 'viem';
|
|
2
|
+
export declare const NETWORKS: {
|
|
3
|
+
readonly base: {
|
|
4
|
+
readonly label: "Base mainnet";
|
|
5
|
+
readonly biomapperAppUrl: string;
|
|
6
|
+
};
|
|
7
|
+
readonly 'base-sepolia': {
|
|
8
|
+
readonly label: "Base Sepolia";
|
|
9
|
+
readonly biomapperAppUrl: string;
|
|
10
|
+
};
|
|
11
|
+
};
|
|
12
|
+
export type NetworkName = keyof typeof NETWORKS;
|
|
13
|
+
export declare const TEMPLATE_NAMES: readonly ["langchain", "vercel-ai-sdk", "mcp"];
|
|
14
|
+
export type TemplateName = (typeof TEMPLATE_NAMES)[number];
|
|
15
|
+
export declare const PACKAGE_MANAGERS: readonly ["npm", "pnpm", "yarn", "bun"];
|
|
16
|
+
export type PackageManager = (typeof PACKAGE_MANAGERS)[number];
|
|
17
|
+
export declare const PACKAGE_SOURCE_NAMES: readonly ["npm", "local"];
|
|
18
|
+
export type PackageSourceName = (typeof PACKAGE_SOURCE_NAMES)[number];
|
|
19
|
+
export interface EnvSource {
|
|
20
|
+
[key: string]: string | undefined;
|
|
21
|
+
}
|
|
22
|
+
export interface ParsedFlags {
|
|
23
|
+
positionals: string[];
|
|
24
|
+
values: Record<string, string>;
|
|
25
|
+
flags: Set<string>;
|
|
26
|
+
}
|
|
27
|
+
export interface Prompter {
|
|
28
|
+
text(input: {
|
|
29
|
+
message: string;
|
|
30
|
+
defaultValue?: string;
|
|
31
|
+
allowEmpty?: boolean;
|
|
32
|
+
}): Promise<string>;
|
|
33
|
+
select<T extends string>(input: {
|
|
34
|
+
message: string;
|
|
35
|
+
options: ReadonlyArray<{
|
|
36
|
+
value: T;
|
|
37
|
+
label: string;
|
|
38
|
+
}>;
|
|
39
|
+
defaultValue: T;
|
|
40
|
+
}): Promise<T>;
|
|
41
|
+
confirm(input: {
|
|
42
|
+
message: string;
|
|
43
|
+
defaultValue?: boolean;
|
|
44
|
+
}): Promise<boolean>;
|
|
45
|
+
close(): void;
|
|
46
|
+
}
|
|
47
|
+
export interface GeneratedFile {
|
|
48
|
+
path: string;
|
|
49
|
+
content: string;
|
|
50
|
+
}
|
|
51
|
+
export interface HumanodePackageSpecs {
|
|
52
|
+
agentlink: string;
|
|
53
|
+
langchain: string;
|
|
54
|
+
aiSdk: string;
|
|
55
|
+
mcp: string;
|
|
56
|
+
cli: string;
|
|
57
|
+
}
|
|
58
|
+
export declare class CliError extends Error {
|
|
59
|
+
constructor(message: string);
|
|
60
|
+
}
|
|
61
|
+
export declare function loadEnvFiles(cwd: string, baseEnv?: EnvSource): Promise<EnvSource>;
|
|
62
|
+
export declare function parseFlags(argv: string[]): ParsedFlags;
|
|
63
|
+
export declare function assertNoExtraPositionals(command: string, positionals: string[]): void;
|
|
64
|
+
export declare function resolveNetwork(value: string | undefined, env: EnvSource): NetworkName;
|
|
65
|
+
export declare function parseRequiredAddress(name: string, value: string | undefined): Address;
|
|
66
|
+
export declare function parseAddressOrPlaceholder(name: string, value: string | undefined, fallback: string): string;
|
|
67
|
+
export declare function resolveRegistryAddress(value: string | undefined, env: EnvSource): Address;
|
|
68
|
+
export declare function resolveRpcUrl(value: string | undefined, env: EnvSource): string | undefined;
|
|
69
|
+
export declare function resolveLinkerUrl(value: string | undefined, env: EnvSource): string;
|
|
70
|
+
export declare function normalizePrivateKey(value: string | undefined): Hex;
|
|
71
|
+
export declare function resolvePrivateKey(value: string | undefined, env: EnvSource, override?: string): Hex;
|
|
72
|
+
export declare function deriveAgentAddress(privateKey: Hex): Address;
|
|
73
|
+
export declare function resolveAgentAddress(agent: string | undefined, privateKey: string | undefined, env: EnvSource): Address;
|
|
74
|
+
export declare function parseFutureDeadline(value: string | undefined, now: Date): bigint;
|
|
75
|
+
export declare function defaultDeadline(now: Date, hours?: number): bigint;
|
|
76
|
+
export declare function formatJson(value: unknown): string;
|
|
77
|
+
export declare function getPackageManagerInstallCommand(packageManager: PackageManager): string;
|
|
78
|
+
export declare function getPackageManagerRunCommand(packageManager: PackageManager, script: string): string;
|
|
79
|
+
export declare function resolvePackageSource(value: string | undefined): PackageSourceName | undefined;
|
|
80
|
+
export declare function resolveHumanodePackageSpecs(targetDir: string, requestedSource?: PackageSourceName): Promise<{
|
|
81
|
+
packageSource: PackageSourceName;
|
|
82
|
+
packages: HumanodePackageSpecs;
|
|
83
|
+
}>;
|
|
84
|
+
export declare function toPackageName(value: string): string;
|
|
85
|
+
export declare function ensureTargetDirectoryIsWritable(targetDir: string): Promise<void>;
|
|
86
|
+
export declare function writeGeneratedFiles(targetDir: string, files: GeneratedFile[]): Promise<void>;
|
|
87
|
+
export declare function createConsolePrompter(): Prompter;
|
|
88
|
+
export declare function openUrlInBrowser(url: string): Promise<void>;
|
|
89
|
+
export declare function readAllFromStdin(): Promise<string>;
|
|
90
|
+
export declare function installProjectDependencies(packageManager: PackageManager, cwd: string): Promise<void>;
|
|
91
|
+
export declare function resolvePackageManager(value: string | undefined): PackageManager | undefined;
|
|
92
|
+
export declare function resolveTemplateName(value: string | undefined): TemplateName | undefined;
|