@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.
@@ -0,0 +1,435 @@
1
+ import path from 'node:path';
2
+ import { fileURLToPath } from 'node:url';
3
+ import { spawn } from 'node:child_process';
4
+ import { stdin, stdout } from 'node:process';
5
+ import { privateKeyToAccount } from 'viem/accounts';
6
+ import { isAddress } from 'viem';
7
+ import { createInterface } from 'node:readline/promises';
8
+ import { access, mkdir, readFile, readdir, writeFile } from 'node:fs/promises';
9
+ import { BIOMAPPER_APP_URLS } from '@techdigger/humanode-agentlink';
10
+ export const NETWORKS = {
11
+ base: {
12
+ label: 'Base mainnet',
13
+ biomapperAppUrl: BIOMAPPER_APP_URLS.base,
14
+ },
15
+ 'base-sepolia': {
16
+ label: 'Base Sepolia',
17
+ biomapperAppUrl: BIOMAPPER_APP_URLS['base-sepolia'],
18
+ },
19
+ };
20
+ export const TEMPLATE_NAMES = ['langchain', 'vercel-ai-sdk', 'mcp'];
21
+ export const PACKAGE_MANAGERS = ['npm', 'pnpm', 'yarn', 'bun'];
22
+ export const PACKAGE_SOURCE_NAMES = ['npm', 'local'];
23
+ export class CliError extends Error {
24
+ constructor(message) {
25
+ super(message);
26
+ this.name = 'CliError';
27
+ }
28
+ }
29
+ function parseEnvValue(value) {
30
+ const trimmed = value.trim();
31
+ if ((trimmed.startsWith('"') && trimmed.endsWith('"')) || (trimmed.startsWith("'") && trimmed.endsWith("'"))) {
32
+ return trimmed.slice(1, -1);
33
+ }
34
+ return trimmed;
35
+ }
36
+ function parseEnvFile(content) {
37
+ const env = {};
38
+ for (const rawLine of content.split(/\r?\n/u)) {
39
+ const line = rawLine.trim();
40
+ if (!line || line.startsWith('#'))
41
+ continue;
42
+ const normalizedLine = line.startsWith('export ') ? line.slice(7) : line;
43
+ const separator = normalizedLine.indexOf('=');
44
+ if (separator <= 0)
45
+ continue;
46
+ const key = normalizedLine.slice(0, separator).trim();
47
+ const value = normalizedLine.slice(separator + 1);
48
+ if (!key)
49
+ continue;
50
+ env[key] = parseEnvValue(value);
51
+ }
52
+ return env;
53
+ }
54
+ export async function loadEnvFiles(cwd, baseEnv = process.env) {
55
+ const envFromFiles = {};
56
+ for (const name of ['.env', '.env.local']) {
57
+ const filePath = path.join(cwd, name);
58
+ try {
59
+ Object.assign(envFromFiles, parseEnvFile(await readFile(filePath, 'utf8')));
60
+ }
61
+ catch (error) {
62
+ if (error.code !== 'ENOENT') {
63
+ throw error;
64
+ }
65
+ }
66
+ }
67
+ return {
68
+ ...envFromFiles,
69
+ ...baseEnv,
70
+ };
71
+ }
72
+ export function parseFlags(argv) {
73
+ const args = [...argv];
74
+ const positionals = [];
75
+ const values = {};
76
+ const flags = new Set();
77
+ while (args.length > 0) {
78
+ const token = args.shift();
79
+ if (token === '--help' || token === '-h') {
80
+ flags.add('help');
81
+ continue;
82
+ }
83
+ if (!token.startsWith('--')) {
84
+ positionals.push(token);
85
+ continue;
86
+ }
87
+ const name = token.slice(2);
88
+ if (!name) {
89
+ throw new CliError('Unexpected empty flag name.');
90
+ }
91
+ const next = args[0];
92
+ if (!next || next.startsWith('--')) {
93
+ flags.add(name);
94
+ continue;
95
+ }
96
+ values[name] = args.shift();
97
+ }
98
+ return { positionals, values, flags };
99
+ }
100
+ export function assertNoExtraPositionals(command, positionals) {
101
+ if (positionals.length > 0) {
102
+ throw new CliError(`Unexpected argument${positionals.length > 1 ? 's' : ''} for ${command}: ${positionals.join(', ')}`);
103
+ }
104
+ }
105
+ export function resolveNetwork(value, env) {
106
+ const candidate = value ?? env.BIOMAPPER_NETWORK ?? 'base-sepolia';
107
+ if (!(candidate in NETWORKS)) {
108
+ throw new CliError(`Invalid --network. Expected one of: ${Object.keys(NETWORKS).join(', ')}`);
109
+ }
110
+ return candidate;
111
+ }
112
+ export function parseRequiredAddress(name, value) {
113
+ if (!value) {
114
+ throw new CliError(`Missing --${name}.`);
115
+ }
116
+ if (!isAddress(value)) {
117
+ throw new CliError(`Invalid --${name}. Expected a checksummed or lowercase EVM address.`);
118
+ }
119
+ return value;
120
+ }
121
+ export function parseAddressOrPlaceholder(name, value, fallback) {
122
+ const candidate = value?.trim() || fallback;
123
+ if (candidate === fallback)
124
+ return candidate;
125
+ if (!isAddress(candidate)) {
126
+ throw new CliError(`Invalid ${name}. Expected a checksummed or lowercase EVM address.`);
127
+ }
128
+ return candidate;
129
+ }
130
+ export function resolveRegistryAddress(value, env) {
131
+ return parseRequiredAddress('registry', value ?? env.BIOMAPPER_REGISTRY);
132
+ }
133
+ export function resolveRpcUrl(value, env) {
134
+ return value ?? env.BIOMAPPER_RPC_URL;
135
+ }
136
+ export function resolveLinkerUrl(value, env) {
137
+ return value ?? env.BIOMAPPER_LINKER_URL ?? 'http://localhost:5173/';
138
+ }
139
+ export function normalizePrivateKey(value) {
140
+ if (!value) {
141
+ throw new CliError('Missing agent private key. Pass --private-key, pipe it with --private-key-stdin, or set AGENT_PRIVATE_KEY.');
142
+ }
143
+ const normalized = value.startsWith('0x') ? value : `0x${value}`;
144
+ if (!/^0x[0-9a-fA-F]{64}$/u.test(normalized)) {
145
+ throw new CliError('Invalid private key. Expected a 32-byte hex string.');
146
+ }
147
+ return normalized;
148
+ }
149
+ export function resolvePrivateKey(value, env, override) {
150
+ return normalizePrivateKey(override ?? value ?? env.AGENT_PRIVATE_KEY);
151
+ }
152
+ export function deriveAgentAddress(privateKey) {
153
+ return privateKeyToAccount(privateKey).address;
154
+ }
155
+ export function resolveAgentAddress(agent, privateKey, env) {
156
+ if (agent) {
157
+ return parseRequiredAddress('agent', agent);
158
+ }
159
+ return deriveAgentAddress(resolvePrivateKey(privateKey, env));
160
+ }
161
+ export function parseFutureDeadline(value, now) {
162
+ if (!value) {
163
+ throw new CliError('Missing --deadline.');
164
+ }
165
+ if (!/^\d+$/u.test(value)) {
166
+ throw new CliError('Invalid --deadline. Expected unix seconds.');
167
+ }
168
+ const deadline = BigInt(value);
169
+ const nowSeconds = BigInt(Math.floor(now.getTime() / 1000));
170
+ if (deadline <= nowSeconds) {
171
+ throw new CliError('Invalid --deadline. The timestamp must be in the future.');
172
+ }
173
+ return deadline;
174
+ }
175
+ export function defaultDeadline(now, hours = 24) {
176
+ return BigInt(Math.floor(now.getTime() / 1000) + hours * 60 * 60);
177
+ }
178
+ export function formatJson(value) {
179
+ return JSON.stringify(value, null, 2);
180
+ }
181
+ const PM_COMMANDS = {
182
+ npm: { install: 'npm install', run: 'npm run' },
183
+ pnpm: { install: 'pnpm install', run: 'pnpm run' },
184
+ yarn: { install: 'yarn install', run: 'yarn run' },
185
+ bun: { install: 'bun install', run: 'bun run' },
186
+ };
187
+ export function getPackageManagerInstallCommand(packageManager) {
188
+ return PM_COMMANDS[packageManager].install;
189
+ }
190
+ export function getPackageManagerRunCommand(packageManager, script) {
191
+ return `${PM_COMMANDS[packageManager].run} ${script}`;
192
+ }
193
+ const HUMANODE_PACKAGE_VERSION = '^0.2.0';
194
+ const HUMANODE_PACKAGE_META = {
195
+ agentlink: { dirName: 'agentlink', packageName: '@techdigger/humanode-agentlink', version: HUMANODE_PACKAGE_VERSION },
196
+ langchain: {
197
+ dirName: 'langchain',
198
+ packageName: '@techdigger/humanode-agentlink-langchain',
199
+ version: HUMANODE_PACKAGE_VERSION,
200
+ },
201
+ aiSdk: { dirName: 'ai-sdk', packageName: '@techdigger/humanode-agentlink-ai-sdk', version: HUMANODE_PACKAGE_VERSION },
202
+ mcp: { dirName: 'mcp', packageName: '@techdigger/humanode-agentlink-mcp', version: HUMANODE_PACKAGE_VERSION },
203
+ cli: { dirName: 'cli', packageName: '@techdigger/humanode-agentlink-cli', version: HUMANODE_PACKAGE_VERSION },
204
+ };
205
+ async function pathExists(targetPath) {
206
+ try {
207
+ await access(targetPath);
208
+ return true;
209
+ }
210
+ catch (error) {
211
+ if (error.code === 'ENOENT') {
212
+ return false;
213
+ }
214
+ throw error;
215
+ }
216
+ }
217
+ async function directoryMatchesPackageName(targetPath, packageName) {
218
+ try {
219
+ const packageJson = JSON.parse(await readFile(path.join(targetPath, 'package.json'), 'utf8'));
220
+ return packageJson.name === packageName;
221
+ }
222
+ catch (error) {
223
+ if (error.code === 'ENOENT') {
224
+ return false;
225
+ }
226
+ throw error;
227
+ }
228
+ }
229
+ function toFileDependency(fromDir, targetPath) {
230
+ const relativePath = path.relative(fromDir, targetPath).split(path.sep).join('/');
231
+ return `file:${relativePath || '.'}`;
232
+ }
233
+ export function resolvePackageSource(value) {
234
+ if (value === undefined)
235
+ return undefined;
236
+ if (!PACKAGE_SOURCE_NAMES.includes(value)) {
237
+ throw new CliError(`Invalid --package-source. Expected one of: ${PACKAGE_SOURCE_NAMES.join(', ')}`);
238
+ }
239
+ return value;
240
+ }
241
+ export async function resolveHumanodePackageSpecs(targetDir, requestedSource) {
242
+ const cliPackageRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../..');
243
+ const monorepoRoot = path.resolve(cliPackageRoot, '..');
244
+ const localPackageDirs = Object.fromEntries(Object.entries(HUMANODE_PACKAGE_META).map(([key, meta]) => [key, path.join(monorepoRoot, meta.dirName)]));
245
+ const localPackageChecks = await Promise.all(Object.keys(HUMANODE_PACKAGE_META).map(async (key) => {
246
+ const meta = HUMANODE_PACKAGE_META[key];
247
+ const directory = localPackageDirs[key];
248
+ return (await pathExists(directory)) && (await directoryMatchesPackageName(directory, meta.packageName));
249
+ }));
250
+ const localPackagesAvailable = localPackageChecks.every(Boolean);
251
+ const packageSource = requestedSource ?? (localPackagesAvailable ? 'local' : 'npm');
252
+ if (packageSource === 'local' && !localPackagesAvailable) {
253
+ throw new CliError('Local package sources are not available from this CLI install. Re-run without --package-source local.');
254
+ }
255
+ if (packageSource === 'local') {
256
+ return {
257
+ packageSource,
258
+ packages: {
259
+ agentlink: toFileDependency(targetDir, localPackageDirs.agentlink),
260
+ langchain: toFileDependency(targetDir, localPackageDirs.langchain),
261
+ aiSdk: toFileDependency(targetDir, localPackageDirs.aiSdk),
262
+ mcp: toFileDependency(targetDir, localPackageDirs.mcp),
263
+ cli: toFileDependency(targetDir, localPackageDirs.cli),
264
+ },
265
+ };
266
+ }
267
+ return {
268
+ packageSource,
269
+ packages: {
270
+ agentlink: HUMANODE_PACKAGE_META.agentlink.version,
271
+ langchain: HUMANODE_PACKAGE_META.langchain.version,
272
+ aiSdk: HUMANODE_PACKAGE_META.aiSdk.version,
273
+ mcp: HUMANODE_PACKAGE_META.mcp.version,
274
+ cli: HUMANODE_PACKAGE_META.cli.version,
275
+ },
276
+ };
277
+ }
278
+ export function toPackageName(value) {
279
+ const normalized = value
280
+ .trim()
281
+ .toLowerCase()
282
+ .replace(/[^a-z0-9._/-]+/gu, '-')
283
+ .replace(/\/+/gu, '/')
284
+ .replace(/^-+|-+$/gu, '')
285
+ .replace(/\/-+/gu, '/');
286
+ if (!normalized) {
287
+ throw new CliError('Project name cannot be empty.');
288
+ }
289
+ return normalized;
290
+ }
291
+ export async function ensureTargetDirectoryIsWritable(targetDir) {
292
+ try {
293
+ await access(targetDir);
294
+ const entries = await readdir(targetDir);
295
+ if (entries.length > 0) {
296
+ throw new CliError(`Refusing to overwrite a non-empty directory: ${targetDir}`);
297
+ }
298
+ }
299
+ catch (error) {
300
+ if (error.code === 'ENOENT') {
301
+ return;
302
+ }
303
+ throw error;
304
+ }
305
+ }
306
+ export async function writeGeneratedFiles(targetDir, files) {
307
+ await mkdir(targetDir, { recursive: true });
308
+ for (const file of files) {
309
+ const destination = path.join(targetDir, file.path);
310
+ await mkdir(path.dirname(destination), { recursive: true });
311
+ await writeFile(destination, file.content, 'utf8');
312
+ }
313
+ }
314
+ export function createConsolePrompter() {
315
+ if (!stdin.isTTY || !stdout.isTTY) {
316
+ throw new CliError('Interactive prompts require a TTY. Re-run with --yes and explicit flags in non-interactive environments.');
317
+ }
318
+ const rl = createInterface({ input: stdin, output: stdout });
319
+ return {
320
+ async text({ message, defaultValue, allowEmpty = false }) {
321
+ while (true) {
322
+ const suffix = defaultValue ? ` [${defaultValue}]` : '';
323
+ const answer = (await rl.question(`${message}${suffix}: `)).trim();
324
+ if (!answer && defaultValue !== undefined)
325
+ return defaultValue;
326
+ if (answer || allowEmpty)
327
+ return answer;
328
+ }
329
+ },
330
+ async select({ message, options, defaultValue }) {
331
+ const defaultIndex = options.findIndex(option => option.value === defaultValue);
332
+ stdout.write(`${message}\n`);
333
+ for (const [index, option] of options.entries()) {
334
+ const marker = option.value === defaultValue ? ' (default)' : '';
335
+ stdout.write(` ${index + 1}. ${option.label}${marker}\n`);
336
+ }
337
+ while (true) {
338
+ const answer = (await rl.question(`Choose [${defaultIndex + 1}]: `)).trim();
339
+ if (!answer)
340
+ return defaultValue;
341
+ const numeric = Number(answer);
342
+ if (Number.isInteger(numeric) && numeric >= 1 && numeric <= options.length) {
343
+ return options[numeric - 1].value;
344
+ }
345
+ }
346
+ },
347
+ async confirm({ message, defaultValue = true }) {
348
+ const suffix = defaultValue ? ' [Y/n]' : ' [y/N]';
349
+ while (true) {
350
+ const answer = (await rl.question(`${message}${suffix}: `)).trim().toLowerCase();
351
+ if (!answer)
352
+ return defaultValue;
353
+ if (['y', 'yes'].includes(answer))
354
+ return true;
355
+ if (['n', 'no'].includes(answer))
356
+ return false;
357
+ }
358
+ },
359
+ close() {
360
+ rl.close();
361
+ },
362
+ };
363
+ }
364
+ function spawnCommand(command, args, options) {
365
+ return new Promise((resolve, reject) => {
366
+ const child = spawn(command, args, {
367
+ cwd: options.cwd,
368
+ detached: options.detached,
369
+ stdio: options.stdio,
370
+ });
371
+ child.once('error', reject);
372
+ child.once('spawn', () => {
373
+ if (options.detached) {
374
+ child.unref();
375
+ }
376
+ resolve();
377
+ });
378
+ });
379
+ }
380
+ export async function openUrlInBrowser(url) {
381
+ if (process.platform === 'darwin') {
382
+ await spawnCommand('open', [url], { detached: true, stdio: 'ignore' });
383
+ return;
384
+ }
385
+ if (process.platform === 'win32') {
386
+ await spawnCommand('cmd', ['/c', 'start', '', url], { detached: true, stdio: 'ignore' });
387
+ return;
388
+ }
389
+ await spawnCommand('xdg-open', [url], { detached: true, stdio: 'ignore' });
390
+ }
391
+ export async function readAllFromStdin() {
392
+ return new Promise((resolve, reject) => {
393
+ const chunks = [];
394
+ stdin.on('data', chunk => {
395
+ chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
396
+ });
397
+ stdin.once('error', reject);
398
+ stdin.once('end', () => {
399
+ resolve(Buffer.concat(chunks).toString('utf8'));
400
+ });
401
+ });
402
+ }
403
+ export async function installProjectDependencies(packageManager, cwd) {
404
+ const [command, ...args] = getPackageManagerInstallCommand(packageManager).split(' ');
405
+ await new Promise((resolve, reject) => {
406
+ const child = spawn(command, args, {
407
+ cwd,
408
+ stdio: 'inherit',
409
+ });
410
+ child.once('error', reject);
411
+ child.once('exit', code => {
412
+ if (code === 0) {
413
+ resolve();
414
+ return;
415
+ }
416
+ reject(new CliError(`${command} exited with code ${code ?? 1}.`));
417
+ });
418
+ });
419
+ }
420
+ export function resolvePackageManager(value) {
421
+ if (value === undefined)
422
+ return undefined;
423
+ if (!PACKAGE_MANAGERS.includes(value)) {
424
+ throw new CliError(`Invalid --package-manager. Expected one of: ${PACKAGE_MANAGERS.join(', ')}`);
425
+ }
426
+ return value;
427
+ }
428
+ export function resolveTemplateName(value) {
429
+ if (value === undefined)
430
+ return undefined;
431
+ if (!TEMPLATE_NAMES.includes(value)) {
432
+ throw new CliError(`Invalid --template. Expected one of: ${TEMPLATE_NAMES.join(', ')}`);
433
+ }
434
+ return value;
435
+ }
@@ -0,0 +1,13 @@
1
+ import { type EnvSource, type NetworkName } from './core.js';
2
+ export interface CliTelemetryInput {
3
+ eventName: string;
4
+ stage: string;
5
+ network?: NetworkName;
6
+ audience?: 'agent-developers' | 'platform-operators' | 'end-users' | 'community';
7
+ source?: string;
8
+ campaign?: string;
9
+ contentId?: string;
10
+ packageName?: string;
11
+ properties?: Record<string, unknown>;
12
+ }
13
+ export declare function captureCliTelemetry(input: CliTelemetryInput, env: EnvSource): Promise<void>;
@@ -0,0 +1,123 @@
1
+ import path from 'node:path';
2
+ import { homedir } from 'node:os';
3
+ import { PostHog } from 'posthog-node';
4
+ import { randomUUID } from 'node:crypto';
5
+ import { mkdir, readFile, writeFile } from 'node:fs/promises';
6
+ import { createConsolePrompter } from './core.js';
7
+ const DEFAULT_POSTHOG_HOST = 'https://us.i.posthog.com';
8
+ const TELEMETRY_CONFIG_PATH = path.join(homedir(), '.config', 'agentlink', 'telemetry.json');
9
+ function resolvePreferenceOverride(env) {
10
+ const candidate = env.AGENTLINK_TELEMETRY?.trim().toLowerCase();
11
+ if (candidate === 'on' || candidate === 'off') {
12
+ return candidate;
13
+ }
14
+ return null;
15
+ }
16
+ function resolveTelemetryConfig(env) {
17
+ const apiKey = env.AGENTLINK_POSTHOG_KEY?.trim();
18
+ if (!apiKey) {
19
+ return null;
20
+ }
21
+ return {
22
+ apiKey,
23
+ host: env.AGENTLINK_POSTHOG_HOST?.trim() || DEFAULT_POSTHOG_HOST,
24
+ };
25
+ }
26
+ async function readStoredPreference(filePath = TELEMETRY_CONFIG_PATH) {
27
+ try {
28
+ const parsed = JSON.parse(await readFile(filePath, 'utf8'));
29
+ if (typeof parsed.anonymousId === 'string' &&
30
+ parsed.anonymousId.length > 0 &&
31
+ (parsed.preference === 'on' || parsed.preference === 'off')) {
32
+ return {
33
+ anonymousId: parsed.anonymousId,
34
+ preference: parsed.preference,
35
+ updatedAt: typeof parsed.updatedAt === 'string' ? parsed.updatedAt : new Date(0).toISOString(),
36
+ };
37
+ }
38
+ return null;
39
+ }
40
+ catch (error) {
41
+ if (error.code === 'ENOENT') {
42
+ return null;
43
+ }
44
+ return null;
45
+ }
46
+ }
47
+ async function writeStoredPreference(record, filePath = TELEMETRY_CONFIG_PATH) {
48
+ await mkdir(path.dirname(filePath), { recursive: true });
49
+ await writeFile(filePath, `${JSON.stringify(record, null, 2)}\n`, 'utf8');
50
+ }
51
+ async function promptForConsent(anonymousId, filePath = TELEMETRY_CONFIG_PATH) {
52
+ if (!process.stdin.isTTY || !process.stdout.isTTY) {
53
+ return null;
54
+ }
55
+ const prompter = createConsolePrompter();
56
+ try {
57
+ const accepted = await prompter.confirm({
58
+ message: 'Share anonymous CLI usage telemetry to improve Biomapper Link? Override anytime with AGENTLINK_TELEMETRY=on|off',
59
+ defaultValue: false,
60
+ });
61
+ const record = {
62
+ anonymousId,
63
+ preference: accepted ? 'on' : 'off',
64
+ updatedAt: new Date().toISOString(),
65
+ };
66
+ await writeStoredPreference(record, filePath);
67
+ return accepted ? record : null;
68
+ }
69
+ finally {
70
+ prompter.close();
71
+ }
72
+ }
73
+ export async function captureCliTelemetry(input, env) {
74
+ const config = resolveTelemetryConfig(env);
75
+ if (!config) {
76
+ return;
77
+ }
78
+ const storedPreference = await readStoredPreference();
79
+ const override = resolvePreferenceOverride(env);
80
+ let anonymousId = storedPreference?.anonymousId ?? randomUUID();
81
+ let preference = override ?? storedPreference?.preference ?? null;
82
+ if (preference == null) {
83
+ const prompted = await promptForConsent(anonymousId);
84
+ if (!prompted) {
85
+ return;
86
+ }
87
+ preference = prompted.preference;
88
+ anonymousId = prompted.anonymousId;
89
+ }
90
+ if (preference !== 'on') {
91
+ return;
92
+ }
93
+ const timestamp = new Date().toISOString();
94
+ const event = {
95
+ event_name: input.eventName,
96
+ anonymous_id: anonymousId,
97
+ audience: input.audience ?? 'agent-developers',
98
+ source: input.source ?? env.AGENTLINK_SOURCE ?? 'cli',
99
+ campaign: input.campaign ?? env.AGENTLINK_CAMPAIGN ?? 'activation',
100
+ content_id: input.contentId ?? env.AGENTLINK_CONTENT_ID ?? `cli:${input.stage}`,
101
+ package: input.packageName ?? '@techdigger/humanode-agentlink-cli',
102
+ network: input.network ?? null,
103
+ stage: input.stage,
104
+ timestamp,
105
+ ...input.properties,
106
+ };
107
+ try {
108
+ const client = new PostHog(config.apiKey, {
109
+ host: config.host,
110
+ flushAt: 1,
111
+ flushInterval: 0,
112
+ });
113
+ client.capture({
114
+ distinctId: anonymousId,
115
+ event: input.eventName,
116
+ properties: event,
117
+ });
118
+ await client.shutdown();
119
+ }
120
+ catch {
121
+ // Telemetry failures should never block CLI flows.
122
+ }
123
+ }
@@ -0,0 +1,12 @@
1
+ import type { GeneratedFile, HumanodePackageSpecs, NetworkName, PackageManager, PackageSourceName, TemplateName } from './lib/core.js';
2
+ export interface TemplateInput {
3
+ projectName: string;
4
+ packageName: string;
5
+ template: TemplateName;
6
+ network: NetworkName;
7
+ registry: string;
8
+ packageManager: PackageManager;
9
+ packageSource: PackageSourceName;
10
+ humanodePackages: HumanodePackageSpecs;
11
+ }
12
+ export declare function buildTemplateFiles(input: TemplateInput): GeneratedFile[];