@techdigger/humanode-agentlink-cli 0.2.0 → 0.3.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/REGISTRATION.md CHANGED
@@ -1,10 +1,10 @@
1
- # Biomapper Link — Agent Developer Guide
1
+ # Humanode Agentlink — Agent Developer Guide
2
2
 
3
3
  Link your AI agent to a biomapped human and unlock free or discounted access to x402 APIs.
4
4
 
5
5
  ## What linking does
6
6
 
7
- When your agent is linked to a biomapped owner, platforms using Biomapper Link can identify your agent as acting on behalf of a real human. This unlocks better pricing: free tiers, free trials, or discounts that are unavailable to unlinked agents.
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 pricing: free tiers, free trials, or discounts that are unavailable to unlinked agents.
8
8
 
9
9
  ## Prerequisites
10
10
 
@@ -30,20 +30,21 @@ export async function runInit(options, depsInput = {}) {
30
30
  const cwd = depsInput.cwd ?? process.cwd();
31
31
  const env = depsInput.env ?? process.env;
32
32
  const installDependencies = depsInput.installProjectDependencies ?? installProjectDependencies;
33
- const prompter = depsInput.prompter ?? createConsolePrompter();
33
+ let _prompter = depsInput.prompter;
34
+ const getPrompter = () => (_prompter ??= createConsolePrompter());
34
35
  try {
35
- const rawDirectory = await resolveInteractiveValue(options.directory, options.yes, () => DEFAULT_PROJECT_DIR, () => prompter.text({
36
+ const rawDirectory = await resolveInteractiveValue(options.directory, options.yes, () => DEFAULT_PROJECT_DIR, () => getPrompter().text({
36
37
  message: 'Project directory',
37
38
  defaultValue: DEFAULT_PROJECT_DIR,
38
39
  }));
39
40
  const targetDir = path.resolve(cwd, rawDirectory);
40
41
  await ensureTargetDirectoryIsWritable(targetDir);
41
- const template = await resolveInteractiveValue(options.template, options.yes, () => 'langchain', () => prompter.select({
42
+ const template = await resolveInteractiveValue(options.template, options.yes, () => 'langchain', () => getPrompter().select({
42
43
  message: 'Starter template',
43
44
  options: TEMPLATE_NAMES.map(value => ({ value, label: value })),
44
45
  defaultValue: 'langchain',
45
46
  }));
46
- const network = await resolveInteractiveValue(options.network, options.yes, () => resolveNetwork(undefined, env), () => prompter.select({
47
+ const network = await resolveInteractiveValue(options.network, options.yes, () => resolveNetwork(undefined, env), () => getPrompter().select({
47
48
  message: 'Biomapper network',
48
49
  options: [
49
50
  { value: 'base-sepolia', label: 'base-sepolia' },
@@ -51,11 +52,11 @@ export async function runInit(options, depsInput = {}) {
51
52
  ],
52
53
  defaultValue: resolveNetwork(undefined, env),
53
54
  }));
54
- const registry = await resolveInteractiveValue(options.registry, options.yes, () => env.BIOMAPPER_REGISTRY ?? DEFAULT_REGISTRY, async () => parseAddressOrPlaceholder('registry', await prompter.text({
55
+ const registry = await resolveInteractiveValue(options.registry, options.yes, () => env.BIOMAPPER_REGISTRY ?? DEFAULT_REGISTRY, async () => parseAddressOrPlaceholder('registry', await getPrompter().text({
55
56
  message: 'Biomapper registry address',
56
57
  defaultValue: env.BIOMAPPER_REGISTRY ?? DEFAULT_REGISTRY,
57
58
  }), DEFAULT_REGISTRY));
58
- const packageManager = await resolveInteractiveValue(options.packageManager, options.yes, () => 'npm', () => prompter.select({
59
+ const packageManager = await resolveInteractiveValue(options.packageManager, options.yes, () => 'npm', () => getPrompter().select({
59
60
  message: 'Package manager',
60
61
  options: PACKAGE_MANAGERS.map(value => ({ value, label: value })),
61
62
  defaultValue: 'npm',
@@ -65,7 +66,7 @@ export async function runInit(options, depsInput = {}) {
65
66
  ? true
66
67
  : options.yes
67
68
  ? false
68
- : await prompter.confirm({
69
+ : await getPrompter().confirm({
69
70
  message: 'Install dependencies now',
70
71
  defaultValue: true,
71
72
  });
@@ -102,7 +103,7 @@ export async function runInit(options, depsInput = {}) {
102
103
  }
103
104
  finally {
104
105
  if (!depsInput.prompter) {
105
- prompter?.close();
106
+ _prompter?.close();
106
107
  }
107
108
  }
108
109
  }
@@ -0,0 +1,3 @@
1
+ export declare function runKeystoreSet(): Promise<string>;
2
+ export declare function runKeystoreDelete(): Promise<void>;
3
+ export declare function runKeystoreAddress(): Promise<string>;
@@ -0,0 +1,37 @@
1
+ import { privateKeyToAccount } from 'viem/accounts';
2
+ import { CliError, normalizePrivateKey, readPassword } from '../lib/core.js';
3
+ import { decryptKeystore, deleteKeystore, encryptAndSave, keystoreExists, keystorePath } from '../lib/keystore.js';
4
+ export async function runKeystoreSet() {
5
+ const exists = await keystoreExists();
6
+ if (exists) {
7
+ throw new CliError(`A keystore already exists at ${keystorePath()}.\nRun "agentlink keystore delete" first to replace it.`);
8
+ }
9
+ const rawKey = await readPassword('Agent private key (hidden): ');
10
+ if (!rawKey.trim()) {
11
+ throw new CliError('No private key provided.');
12
+ }
13
+ const privateKey = normalizePrivateKey(rawKey.trim());
14
+ const address = privateKeyToAccount(privateKey).address;
15
+ const password = await readPassword('Keystore password (hidden): ');
16
+ if (!password) {
17
+ throw new CliError('Password cannot be empty.');
18
+ }
19
+ const confirm = await readPassword('Confirm password (hidden): ');
20
+ if (password !== confirm) {
21
+ throw new CliError('Passwords do not match.');
22
+ }
23
+ await encryptAndSave(privateKey, password);
24
+ return address;
25
+ }
26
+ export async function runKeystoreDelete() {
27
+ const exists = await keystoreExists();
28
+ if (!exists) {
29
+ throw new CliError('No keystore found.');
30
+ }
31
+ await deleteKeystore();
32
+ }
33
+ export async function runKeystoreAddress() {
34
+ const password = await readPassword('Keystore password (hidden): ');
35
+ const privateKey = normalizePrivateKey(await decryptKeystore(password));
36
+ return privateKeyToAccount(privateKey).address;
37
+ }
package/dist/help.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  export declare function getRootHelpText(): string;
2
+ export declare function getKeystoreHelpText(): string;
2
3
  export declare function getAuthorizeLinkHelpText(): string;
3
4
  export declare function getStatusHelpText(): string;
4
5
  export declare function getLinkHelpText(): string;
package/dist/help.js CHANGED
@@ -2,19 +2,21 @@ export function getRootHelpText() {
2
2
  return `Usage: agentlink <command> [options]
3
3
 
4
4
  Commands:
5
+ keystore Manage the encrypted agent private key keystore
5
6
  init Scaffold a Biomapper-ready agent starter
6
- link Generate consent and open a self-contained local/dev linker URL
7
+ link Generate consent and open a linker URL for owner approval
7
8
  authorize-link Generate a one-time EIP-712 consent blob for manual linking
8
9
  status Check whether an agent is linked and active
9
10
 
11
+ Flags:
12
+ --help, -h Show this help text
13
+
10
14
  Run agentlink <command> --help for command-specific options.
11
15
 
12
16
  Environment:
13
- AGENT_PRIVATE_KEY Local/dev secret source; prefer stdin or secret managers in production
14
17
  BIOMAPPER_NETWORK Default network (base | base-sepolia)
15
18
  BIOMAPPER_REGISTRY Default BiomapperAgentRegistry address
16
19
  BIOMAPPER_RPC_URL Optional RPC URL override
17
- BIOMAPPER_LINKER_URL Default linker URL (defaults to http://localhost:5173/)
18
20
  AGENTLINK_TELEMETRY Optional override for the saved CLI telemetry choice (on | off)
19
21
  AGENTLINK_POSTHOG_KEY Optional PostHog API key for CLI telemetry
20
22
  AGENTLINK_POSTHOG_HOST Optional PostHog host override (defaults to https://us.i.posthog.com)
@@ -23,6 +25,25 @@ Environment:
23
25
  AGENTLINK_CONTENT_ID Optional content identifier for telemetry events
24
26
  `;
25
27
  }
28
+ export function getKeystoreHelpText() {
29
+ return `Usage: agentlink keystore <subcommand>
30
+
31
+ Description:
32
+ Manage an AES-256-GCM encrypted keystore for your agent private key.
33
+ The key is stored at ~/.agentlink/keystore.json, readable only by your user.
34
+ All commands that need the private key unlock it automatically at runtime.
35
+
36
+ Subcommands:
37
+ set Store your agent private key (prompts for key + password, never logged)
38
+ delete Remove the keystore
39
+ address Show the agent address derived from the stored key (prompts for password)
40
+
41
+ Examples:
42
+ agentlink keystore set
43
+ agentlink keystore address
44
+ agentlink keystore delete
45
+ `;
46
+ }
26
47
  export function getAuthorizeLinkHelpText() {
27
48
  return `Usage:
28
49
  agentlink authorize-link --owner <address> [options]
@@ -36,10 +57,12 @@ Options:
36
57
  --registry BiomapperAgentRegistry contract address (falls back to BIOMAPPER_REGISTRY)
37
58
  --deadline Unix timestamp in seconds for signature expiry
38
59
  --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
60
+ --private-key-stdin Read the agent private key from stdin (for CI/secret managers)
61
+ --private-key Dev-only: pass key directly as 0x-prefixed hex. Prefer keystore or stdin.
41
62
  --rpc-url Optional RPC URL override for the selected network
42
63
  --help Show this help message
64
+
65
+ Key resolution order: keystore (default) → --private-key-stdin → --private-key → AGENT_PRIVATE_KEY env
43
66
  `;
44
67
  }
45
68
  export function getStatusHelpText() {
@@ -52,13 +75,15 @@ Description:
52
75
 
53
76
  Options:
54
77
  --registry BiomapperAgentRegistry contract address (falls back to BIOMAPPER_REGISTRY)
55
- --agent Agent wallet address to check. If omitted, derived from the private key input
78
+ --agent Agent wallet address to check (skips key lookup entirely)
56
79
  --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
80
+ --private-key-stdin Read the agent private key from stdin (for CI/secret managers)
81
+ --private-key Dev-only: pass key directly as 0x-prefixed hex
59
82
  --rpc-url Optional RPC URL override for the selected network
60
83
  --json Output raw JSON instead of human-readable text
61
84
  --help Show this help message
85
+
86
+ Key resolution order: keystore (default) → --private-key-stdin → --private-key → AGENT_PRIVATE_KEY env
62
87
  `;
63
88
  }
64
89
  export function getLinkHelpText() {
@@ -74,10 +99,10 @@ Options:
74
99
  --registry BiomapperAgentRegistry contract address (falls back to BIOMAPPER_REGISTRY)
75
100
  --network base | base-sepolia (default: BIOMAPPER_NETWORK or base-sepolia)
76
101
  --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
102
+ --private-key-stdin Read the agent private key from stdin (for CI/secret managers)
103
+ --private-key Dev-only: pass key directly as 0x-prefixed hex
79
104
  --rpc-url Optional RPC URL override for the selected network
80
- --linker-url Linker base URL (default: BIOMAPPER_LINKER_URL or http://localhost:5173/)
105
+ --linker-url Linker base URL override (default: https://agent-kit-biomapper.vercel.app/)
81
106
  --open Open the generated linker URL in the default browser
82
107
  --json Output consent, session, and linker URL as JSON
83
108
  --help Show this help message
package/dist/index.d.ts CHANGED
@@ -12,5 +12,6 @@ export interface CliRuntime {
12
12
  buildLinkUrl: (baseUrl: string, session: Awaited<ReturnType<typeof createLinkSession>>) => string;
13
13
  openUrl: (url: string) => Promise<void>;
14
14
  readStdin: () => Promise<string>;
15
+ readPassword: (prompt: string) => Promise<string>;
15
16
  }
16
17
  export declare function runCli(argv?: string[], overrides?: Partial<CliRuntime>): Promise<number>;
package/dist/index.js CHANGED
@@ -6,8 +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 { CliError, formatJson, loadEnvFiles, openUrlInBrowser, parseFlags, readAllFromStdin } from './lib/core.js';
10
- import { getAuthorizeLinkHelpText, getInitHelpText, getLinkHelpText, getRootHelpText, getStatusHelpText, } from './help.js';
9
+ import { runKeystoreAddress, runKeystoreDelete, runKeystoreSet } from './commands/keystore.js';
10
+ import { CliError, formatJson, loadEnvFiles, openUrlInBrowser, parseFlags, readAllFromStdin, readPassword } from './lib/core.js';
11
+ import { decryptKeystore, keystoreExists, keystorePath } from './lib/keystore.js';
12
+ import { getAuthorizeLinkHelpText, getInitHelpText, getKeystoreHelpText, getLinkHelpText, getRootHelpText, getStatusHelpText, } from './help.js';
13
+ import { VERSION } from './version.js';
11
14
  import { buildEmbeddedHostedLinkUrl, createAgentLinkConsent, createBiomapperQueryClient, createLinkSession, } from '@techdigger/humanode-agentlink';
12
15
  function writeLine(write, text) {
13
16
  write(text.endsWith('\n') ? text : `${text}\n`);
@@ -30,6 +33,7 @@ export async function runCli(argv = process.argv.slice(2), overrides = {}) {
30
33
  buildLinkUrl: overrides.buildLinkUrl ?? buildEmbeddedHostedLinkUrl,
31
34
  openUrl: overrides.openUrl ?? openUrlInBrowser,
32
35
  readStdin: overrides.readStdin ?? readAllFromStdin,
36
+ readPassword: overrides.readPassword ?? readPassword,
33
37
  };
34
38
  const [command, ...args] = argv;
35
39
  try {
@@ -37,7 +41,42 @@ export async function runCli(argv = process.argv.slice(2), overrides = {}) {
37
41
  writeLine(runtime.stdout, getRootHelpText());
38
42
  return 0;
39
43
  }
44
+ if (command === '--version' || command === '-v') {
45
+ writeLine(runtime.stdout, VERSION);
46
+ return 0;
47
+ }
40
48
  switch (command) {
49
+ case 'keystore': {
50
+ const [subcommand, ...subArgs] = args;
51
+ if (!subcommand || subcommand === '--help' || subcommand === '-h') {
52
+ writeLine(runtime.stdout, getKeystoreHelpText());
53
+ return 0;
54
+ }
55
+ switch (subcommand) {
56
+ case 'set': {
57
+ if (subArgs.includes('--help') || subArgs.includes('-h')) {
58
+ writeLine(runtime.stdout, getKeystoreHelpText());
59
+ return 0;
60
+ }
61
+ const address = await runKeystoreSet();
62
+ writeLine(runtime.stdout, `Keystore saved at ${keystorePath()}`);
63
+ writeLine(runtime.stdout, `Agent address: ${address}`);
64
+ return 0;
65
+ }
66
+ case 'delete': {
67
+ await runKeystoreDelete();
68
+ writeLine(runtime.stdout, 'Keystore deleted.');
69
+ return 0;
70
+ }
71
+ case 'address': {
72
+ const address = await runKeystoreAddress();
73
+ writeLine(runtime.stdout, `Agent address: ${address}`);
74
+ return 0;
75
+ }
76
+ default:
77
+ throw new CliError(`Unknown keystore subcommand "${subcommand}". Run "agentlink keystore --help" for available subcommands.`);
78
+ }
79
+ }
41
80
  case 'authorize-link':
42
81
  if (args.includes('--help') || args.includes('-h')) {
43
82
  writeLine(runtime.stdout, getAuthorizeLinkHelpText());
@@ -155,17 +194,26 @@ export async function runCli(argv = process.argv.slice(2), overrides = {}) {
155
194
  }
156
195
  async function readPrivateKeyFromStdin(argv, runtime) {
157
196
  const parsed = parseFlags(argv);
158
- if (!parsed.flags.has('private-key-stdin')) {
159
- return undefined;
197
+ if (parsed.flags.has('private-key-stdin')) {
198
+ if (parsed.values['private-key']) {
199
+ throw new CliError('Pass either --private-key or --private-key-stdin, not both.');
200
+ }
201
+ const privateKey = (await runtime.readStdin()).trim();
202
+ if (!privateKey) {
203
+ throw new CliError('Missing agent private key on stdin. Pipe a 32-byte hex key into --private-key-stdin.');
204
+ }
205
+ return privateKey;
160
206
  }
161
- if (parsed.values['private-key']) {
162
- throw new CliError('Pass either --private-key or --private-key-stdin, not both.');
207
+ // If an explicit key source is already present, let resolvePrivateKey handle it
208
+ if (parsed.values['private-key'] || runtime.env.AGENT_PRIVATE_KEY) {
209
+ return undefined;
163
210
  }
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.');
211
+ // Fall back to keystore
212
+ if (await keystoreExists()) {
213
+ const password = await runtime.readPassword('Keystore password: ');
214
+ return await decryptKeystore(password);
167
215
  }
168
- return privateKey;
216
+ return undefined;
169
217
  }
170
218
  function isExecutedDirectly() {
171
219
  if (!process.argv[1]) {
@@ -67,6 +67,7 @@ export declare function parseAddressOrPlaceholder(name: string, value: string |
67
67
  export declare function resolveRegistryAddress(value: string | undefined, env: EnvSource): Address;
68
68
  export declare function resolveRpcUrl(value: string | undefined, env: EnvSource): string | undefined;
69
69
  export declare function resolveLinkerUrl(value: string | undefined, env: EnvSource): string;
70
+ export declare function readPassword(prompt: string): Promise<string>;
70
71
  export declare function normalizePrivateKey(value: string | undefined): Hex;
71
72
  export declare function resolvePrivateKey(value: string | undefined, env: EnvSource, override?: string): Hex;
72
73
  export declare function deriveAgentAddress(privateKey: Hex): Address;
package/dist/lib/core.js CHANGED
@@ -134,7 +134,46 @@ export function resolveRpcUrl(value, env) {
134
134
  return value ?? env.BIOMAPPER_RPC_URL;
135
135
  }
136
136
  export function resolveLinkerUrl(value, env) {
137
- return value ?? env.BIOMAPPER_LINKER_URL ?? 'http://localhost:5173/';
137
+ return value ?? env.BIOMAPPER_LINKER_URL ?? 'https://agent-kit-biomapper.vercel.app/';
138
+ }
139
+ export function readPassword(prompt) {
140
+ return new Promise((resolve, reject) => {
141
+ if (!process.stdin.isTTY) {
142
+ reject(new CliError('Cannot read keystore password: not a TTY. Set AGENT_PRIVATE_KEY or use --private-key-stdin in non-interactive environments.'));
143
+ return;
144
+ }
145
+ process.stdout.write(prompt);
146
+ process.stdin.setRawMode(true);
147
+ process.stdin.resume();
148
+ process.stdin.setEncoding('utf8');
149
+ let password = '';
150
+ const onData = (char) => {
151
+ switch (char) {
152
+ case '\n':
153
+ case '\r':
154
+ process.stdin.setRawMode(false);
155
+ process.stdin.pause();
156
+ process.stdin.removeListener('data', onData);
157
+ process.stdout.write('\n');
158
+ resolve(password);
159
+ break;
160
+ case '\u0003':
161
+ // Ctrl+C
162
+ process.stdin.setRawMode(false);
163
+ process.stdin.pause();
164
+ process.stdin.removeListener('data', onData);
165
+ reject(new CliError('Cancelled.'));
166
+ break;
167
+ case '\u007F':
168
+ // Backspace
169
+ password = password.slice(0, -1);
170
+ break;
171
+ default:
172
+ password += char;
173
+ }
174
+ };
175
+ process.stdin.on('data', onData);
176
+ });
138
177
  }
139
178
  export function normalizePrivateKey(value) {
140
179
  if (!value) {
@@ -0,0 +1,5 @@
1
+ export declare function keystorePath(): string;
2
+ export declare function keystoreExists(): Promise<boolean>;
3
+ export declare function encryptAndSave(privateKey: string, password: string): Promise<void>;
4
+ export declare function decryptKeystore(password: string): Promise<string>;
5
+ export declare function deleteKeystore(): Promise<void>;
@@ -0,0 +1,81 @@
1
+ import { createCipheriv, createDecipheriv, randomBytes, scryptSync } from 'node:crypto';
2
+ import { mkdir, readFile, unlink, writeFile } from 'node:fs/promises';
3
+ import os from 'node:os';
4
+ import path from 'node:path';
5
+ const KEYSTORE_VERSION = 1;
6
+ const SCRYPT_N = 16384;
7
+ const SCRYPT_R = 8;
8
+ const SCRYPT_P = 1;
9
+ const KEY_LEN = 32;
10
+ export function keystorePath() {
11
+ return path.join(os.homedir(), '.agentlink', 'keystore.json');
12
+ }
13
+ export async function keystoreExists() {
14
+ try {
15
+ await readFile(keystorePath());
16
+ return true;
17
+ }
18
+ catch {
19
+ return false;
20
+ }
21
+ }
22
+ function deriveKey(password, salt) {
23
+ return scryptSync(password, salt, KEY_LEN, { N: SCRYPT_N, r: SCRYPT_R, p: SCRYPT_P });
24
+ }
25
+ export async function encryptAndSave(privateKey, password) {
26
+ const salt = randomBytes(32);
27
+ const iv = randomBytes(12);
28
+ const key = deriveKey(password, salt);
29
+ const cipher = createCipheriv('aes-256-gcm', key, iv);
30
+ const ciphertext = Buffer.concat([cipher.update(privateKey, 'utf8'), cipher.final()]);
31
+ const tag = cipher.getAuthTag();
32
+ const data = {
33
+ version: KEYSTORE_VERSION,
34
+ salt: salt.toString('hex'),
35
+ iv: iv.toString('hex'),
36
+ tag: tag.toString('hex'),
37
+ ciphertext: ciphertext.toString('hex'),
38
+ };
39
+ const filePath = keystorePath();
40
+ await mkdir(path.dirname(filePath), { recursive: true });
41
+ await writeFile(filePath, JSON.stringify(data, null, 2), { mode: 0o600 });
42
+ }
43
+ export async function decryptKeystore(password) {
44
+ let raw;
45
+ try {
46
+ raw = await readFile(keystorePath(), 'utf8');
47
+ }
48
+ catch {
49
+ throw new Error('No keystore found. Run "agentlink keystore set" first.');
50
+ }
51
+ let data;
52
+ try {
53
+ data = JSON.parse(raw);
54
+ }
55
+ catch {
56
+ throw new Error('Keystore file is corrupted.');
57
+ }
58
+ try {
59
+ const salt = Buffer.from(data.salt, 'hex');
60
+ const iv = Buffer.from(data.iv, 'hex');
61
+ const tag = Buffer.from(data.tag, 'hex');
62
+ const ciphertext = Buffer.from(data.ciphertext, 'hex');
63
+ const key = deriveKey(password, salt);
64
+ const decipher = createDecipheriv('aes-256-gcm', key, iv);
65
+ decipher.setAuthTag(tag);
66
+ return Buffer.concat([decipher.update(ciphertext), decipher.final()]).toString('utf8');
67
+ }
68
+ catch {
69
+ throw new Error('Wrong password or corrupted keystore.');
70
+ }
71
+ }
72
+ export async function deleteKeystore() {
73
+ try {
74
+ await unlink(keystorePath());
75
+ }
76
+ catch (error) {
77
+ if (error.code !== 'ENOENT') {
78
+ throw error;
79
+ }
80
+ }
81
+ }
@@ -55,7 +55,7 @@ async function promptForConsent(anonymousId, filePath = TELEMETRY_CONFIG_PATH) {
55
55
  const prompter = createConsolePrompter();
56
56
  try {
57
57
  const accepted = await prompter.confirm({
58
- message: 'Share anonymous CLI usage telemetry to improve Biomapper Link? Override anytime with AGENTLINK_TELEMETRY=on|off',
58
+ message: 'Share anonymous CLI usage telemetry to improve Humanode Agentlink? Override anytime with AGENTLINK_TELEMETRY=on|off',
59
59
  defaultValue: false,
60
60
  });
61
61
  const record = {
@@ -0,0 +1 @@
1
+ export declare const VERSION: string;
@@ -0,0 +1,4 @@
1
+ import { createRequire } from 'module';
2
+ const require = createRequire(import.meta.url);
3
+ const pkg = require('../package.json');
4
+ export const VERSION = pkg.version;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@techdigger/humanode-agentlink-cli",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "Scaffold, link, and inspect Biomapper-ready agent projects.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -23,7 +23,7 @@
23
23
  "cli": "tsx src/index.ts"
24
24
  },
25
25
  "dependencies": {
26
- "@techdigger/humanode-agentlink": "^0.2.0",
26
+ "@techdigger/humanode-agentlink": "^0.1.0",
27
27
  "posthog-node": "^5.21.2",
28
28
  "viem": "^2.46.2"
29
29
  },