@aiaiaichain/agent 0.1.2 → 0.1.3

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/dist/cli.js CHANGED
@@ -22,6 +22,9 @@ import { makeTheme } from "./tui/theme.js";
22
22
  import { AgentDir } from "./core/AgentDir.js";
23
23
  import { priceFeed } from "./tools/PriceFeed.js";
24
24
  import { agentWallet, DEPOSIT_WALLET, ACTION_WALLET, SIGNER } from "./wallet/AgentWallet.js";
25
+ import { PROVIDERS, getConfigured, getUnconfigured } from "./providers/ProviderRegistry.js";
26
+ import { env } from "./core/EnvLoader.js";
27
+ import { gmgnHelp, gmgnStatus, saveGmgnApiKey } from "./tools/GmgnIntegration.js";
25
28
  const AIAIAI_HOME = process.env.AIAIAI_HOME ?? join(homedir(), ".aiaiai");
26
29
  const envPath = join(AIAIAI_HOME, ".env");
27
30
  if (existsSync(envPath))
@@ -191,6 +194,227 @@ if (subcmd === "actions" || subcmd === "activity") {
191
194
  })();
192
195
  await new Promise(() => { });
193
196
  }
197
+ if (subcmd === "fees") {
198
+ (async () => {
199
+ const { actionFeed } = await import("./wallet/ActionFeed.js");
200
+ const result = await actionFeed.getFeesTool();
201
+ console.log(result.content[0].text);
202
+ process.exit(0);
203
+ })();
204
+ await new Promise(() => { });
205
+ }
206
+ if (subcmd === "keys" || subcmd === "providers") {
207
+ (async () => {
208
+ const readline = await import("node:readline");
209
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
210
+ const ask = (prompt) => new Promise((res) => rl.question(prompt, res));
211
+ const allEnv = env.getAll();
212
+ const configured = getConfigured(allEnv);
213
+ const unconfigured = getUnconfigured(allEnv);
214
+ console.log(T.accent("\n 🔑 AI Model Providers\n"));
215
+ console.log(T.muted(` Configure AI model providers. Add at least one API key to get started.`));
216
+ console.log(T.muted(` ${configured.length}/${PROVIDERS.length} configured\n`));
217
+ // Show configured
218
+ if (configured.length > 0) {
219
+ console.log(T.success(" Configured (" + configured.length + ")"));
220
+ for (const p of configured) {
221
+ const key = allEnv[p.envVar] || "";
222
+ const masked = key.slice(0, 6) + "••••" + key.slice(-4);
223
+ console.log(T.success(` ✓ ${p.name.padEnd(28)} ${masked}`));
224
+ }
225
+ }
226
+ // Show unconfigured
227
+ console.log("\n" + T.warn(" Unconfigured (" + unconfigured.length + ")"));
228
+ for (let i = 0; i < unconfigured.length; i++) {
229
+ const p = unconfigured[i];
230
+ console.log(T.muted(` ${String(i + 1).padStart(2)}. ${p.name}`));
231
+ }
232
+ // Add key flow
233
+ console.log("\n" + T.muted(" Enter number to add a key, 'r' to refresh models, or 'q' to quit:"));
234
+ const input = (await ask(" > ")).trim();
235
+ if (input === "q") {
236
+ rl.close();
237
+ process.exit(0);
238
+ }
239
+ if (input === "r") {
240
+ console.log(T.muted("\n Refreshing models from configured providers..."));
241
+ await modelRegistry.initialise();
242
+ console.log(T.success(` ✓ ${modelRegistry.modelCount} models available from ${modelRegistry.getProviderCount()} providers`));
243
+ rl.close();
244
+ process.exit(0);
245
+ }
246
+ const idx = parseInt(input) - 1;
247
+ if (isNaN(idx) || idx < 0 || idx >= unconfigured.length) {
248
+ console.log(T.error(" Invalid selection."));
249
+ rl.close();
250
+ process.exit(1);
251
+ }
252
+ const provider = unconfigured[idx];
253
+ console.log(T.accent(`\n ${provider.name}`));
254
+ console.log(T.muted(` ${provider.docsUrl}`));
255
+ console.log(T.muted(` Env var: ${provider.envVar}\n`));
256
+ const key = (await ask(` Enter ${provider.name} API key: `)).trim();
257
+ if (!key) {
258
+ console.log(T.error(" Key is required."));
259
+ rl.close();
260
+ process.exit(1);
261
+ }
262
+ // Save to .env
263
+ env.set(provider.envVar, key);
264
+ console.log(T.success(`\n ✓ ${provider.name} key saved to ~/.aiaiai/.env`));
265
+ // Fetch models from this provider
266
+ console.log(T.muted(" Fetching models from " + provider.name + "..."));
267
+ try {
268
+ await modelRegistry.initialise();
269
+ const providerModels = modelRegistry.getProviderModels(provider.id);
270
+ console.log(T.success(` ✓ ${providerModels.length} models available from ${provider.name}`));
271
+ console.log(T.success(` ✓ ${modelRegistry.modelCount} total models from ${modelRegistry.getProviderCount()} providers`));
272
+ }
273
+ catch (e) {
274
+ console.log(T.warn(` ⚠ Could not fetch models: ${e instanceof Error ? e.message : String(e)}`));
275
+ }
276
+ rl.close();
277
+ process.exit(0);
278
+ })().catch(e => { console.error(T.error(`Error: ${e.message}`)); process.exit(1); });
279
+ await new Promise(() => { });
280
+ }
281
+ if (subcmd === "gmgn" || subcmd === "gmgnhelp") {
282
+ const gmgnArgs = args.slice(1);
283
+ if (gmgnArgs.length === 0 || gmgnArgs[0] === "help" || gmgnArgs[0] === "--help") {
284
+ console.log(gmgnHelp());
285
+ process.exit(0);
286
+ }
287
+ if (gmgnArgs[0] === "setup" || gmgnArgs[0] === "config" || gmgnArgs[0] === "key") {
288
+ (async () => {
289
+ const readline = await import("node:readline");
290
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
291
+ const ask = (prompt) => new Promise((res) => rl.question(prompt, res));
292
+ console.log(T.accent("\n 🔍 GMGN API Key Setup\n"));
293
+ console.log(T.muted(" Get your API key at https://gmgn.ai/ai\n"));
294
+ const key = (await ask(" Enter GMGN_API_KEY: ")).trim();
295
+ if (!key) {
296
+ console.log(T.error(" Key is required."));
297
+ rl.close();
298
+ process.exit(1);
299
+ }
300
+ saveGmgnApiKey(key);
301
+ console.log(T.success("\n ✓ GMGN_API_KEY saved to ~/.config/gmgn/.env"));
302
+ console.log(T.muted(" All GMGN commands are now available.\n"));
303
+ rl.close();
304
+ process.exit(0);
305
+ })().catch(e => { console.error(T.error(`Error: ${e.message}`)); process.exit(1); });
306
+ await new Promise(() => { });
307
+ }
308
+ if (gmgnArgs[0] === "status") {
309
+ console.log(gmgnStatus());
310
+ process.exit(0);
311
+ }
312
+ // Forward to gmgn-cli
313
+ (async () => {
314
+ const { execSync } = await import("node:child_process");
315
+ const fullArgs = ["gmgn-cli", ...gmgnArgs];
316
+ try {
317
+ const output = execSync(fullArgs.join(" "), {
318
+ env: { ...process.env, ...(() => { const e = {}; try {
319
+ const f = require("fs").readFileSync(process.env.HOME + "/.config/gmgn/.env", "utf-8");
320
+ for (const l of f.split("\n")) {
321
+ const m = l.trim().match(/^([A-Z_]+)=(.*)$/);
322
+ if (m)
323
+ e[m[1]] = m[2];
324
+ }
325
+ }
326
+ catch { } return e; })() },
327
+ encoding: "utf-8",
328
+ timeout: 30_000,
329
+ });
330
+ process.stdout.write(output);
331
+ }
332
+ catch (e) {
333
+ const msg = e.stderr?.toString() || e.message || "";
334
+ process.stderr.write(`gmgn-cli: ${msg.slice(0, 500)}\n`);
335
+ process.exit(e.status ?? 1);
336
+ }
337
+ process.exit(0);
338
+ })();
339
+ await new Promise(() => { });
340
+ }
341
+ if (subcmd === "update" || subcmd === "upgrade") {
342
+ (async () => {
343
+ const { execSync } = await import("node:child_process");
344
+ const pkgName = "@aiaiaichain/agent";
345
+ console.log(T.accent("\n 🤖 AIAIAI Update Check\n"));
346
+ // Get current version
347
+ const __filename = fileURLToPath(import.meta.url);
348
+ const __dir = dirname(__filename);
349
+ const currentPkg = JSON.parse(readFileSync(resolve(__dir, "..", "package.json"), "utf-8"));
350
+ const current = currentPkg.version;
351
+ console.log(T.muted(` Current version: v${current}`));
352
+ // Check npm for latest
353
+ let latest = null;
354
+ let changelog = "";
355
+ try {
356
+ const viewOutput = execSync(`npm view ${pkgName} version`, {
357
+ encoding: "utf-8",
358
+ timeout: 15_000,
359
+ }).trim();
360
+ latest = viewOutput.split("\n").pop() || null;
361
+ // Try to get changelog from npm
362
+ try {
363
+ const changelogOutput = execSync(`npm view ${pkgName} --json`, {
364
+ encoding: "utf-8",
365
+ timeout: 15_000,
366
+ });
367
+ const pkgData = JSON.parse(changelogOutput);
368
+ if (pkgData.gitHead) {
369
+ changelog = `Git: ${pkgData.gitHead.slice(0, 8)}`;
370
+ }
371
+ }
372
+ catch { /* no changelog available */ }
373
+ }
374
+ catch {
375
+ console.log(T.error(" Could not reach npm registry. Check your connection."));
376
+ process.exit(1);
377
+ }
378
+ if (!latest) {
379
+ console.log(T.error(" Could not determine latest version."));
380
+ process.exit(1);
381
+ }
382
+ console.log(T.muted(` Latest version: v${latest}`));
383
+ if (changelog)
384
+ console.log(T.muted(` ${changelog}`));
385
+ if (latest === current) {
386
+ console.log(T.success("\n ✅ You're on the latest version!\n"));
387
+ process.exit(0);
388
+ }
389
+ console.log(T.accent(`\n ⚡ Update available: v${current} → v${latest}\n`));
390
+ // Ask to update
391
+ const readline = await import("node:readline");
392
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
393
+ const ask = (prompt) => new Promise((res) => rl.question(prompt, res));
394
+ const confirm = (await ask(" Install update now? (y/n): ")).trim().toLowerCase();
395
+ if (confirm !== "y" && confirm !== "yes") {
396
+ console.log(T.muted("\n Skipped. Run `aiaiai update` anytime to update.\n"));
397
+ rl.close();
398
+ process.exit(0);
399
+ }
400
+ console.log(T.muted("\n Installing update..."));
401
+ try {
402
+ execSync(`npm install -g ${pkgName}`, {
403
+ stdio: "inherit",
404
+ timeout: 120_000,
405
+ });
406
+ console.log(T.success(`\n ✅ Updated to v${latest}!`));
407
+ console.log(T.muted(" Run `aiaiai` to restart with new features.\n"));
408
+ }
409
+ catch (e) {
410
+ console.log(T.error(`\n ❌ Update failed: ${e.message}`));
411
+ console.log(T.muted(" Try manually: npm install -g @aiaiaichain/agent\n"));
412
+ }
413
+ rl.close();
414
+ process.exit(0);
415
+ })().catch(e => { console.error(T.error(`Error: ${e.message}`)); process.exit(1); });
416
+ await new Promise(() => { });
417
+ }
194
418
  // ── Resolve extension + prompt ────────────────────────────────────────────────
195
419
  let extensionPath = null;
196
420
  let promptPath = null;
@@ -1,5 +1,5 @@
1
1
  /**
2
- * EnvLoader — loads environment variables from ~/.aiaiai/.env and process.env
2
+ * EnvLoader — loads/writes environment variables from ~/.aiaiai/.env
3
3
  */
4
4
  export declare class EnvLoader {
5
5
  private cache;
@@ -9,6 +9,9 @@ export declare class EnvLoader {
9
9
  get(key: string, defaultValue?: string): string | undefined;
10
10
  getRequired(key: string): string;
11
11
  has(key: string): boolean;
12
+ /** Write a key=value to the .env file and update cache */
13
+ set(key: string, value: string): void;
14
+ getAll(): Record<string, string>;
12
15
  reload(): void;
13
16
  }
14
17
  export declare const env: EnvLoader;
@@ -1,40 +1,37 @@
1
1
  /**
2
- * EnvLoader — loads environment variables from ~/.aiaiai/.env and process.env
2
+ * EnvLoader — loads/writes environment variables from ~/.aiaiai/.env
3
3
  */
4
- import { existsSync, readFileSync } from 'node:fs';
5
- import { resolve } from 'node:path';
6
- import { homedir } from 'node:os';
4
+ import { existsSync, readFileSync, writeFileSync, chmodSync } from "node:fs";
5
+ import { resolve } from "node:path";
6
+ import { homedir } from "node:os";
7
7
  export class EnvLoader {
8
8
  cache = {};
9
9
  envPath;
10
10
  constructor() {
11
11
  const home = process.env.AIAIAI_HOME ?? resolve(homedir(), '.aiaiai');
12
- this.envPath = resolve(home, '.env');
12
+ this.envPath = resolve(home, ".env");
13
13
  this.load();
14
14
  }
15
15
  load() {
16
- // Load from .env file
17
16
  if (existsSync(this.envPath)) {
18
- const content = readFileSync(this.envPath, 'utf-8');
19
- for (const line of content.split('\n')) {
17
+ const content = readFileSync(this.envPath, "utf-8");
18
+ for (const line of content.split("\n")) {
20
19
  const trimmed = line.trim();
21
- if (!trimmed || trimmed.startsWith('#'))
20
+ if (!trimmed || trimmed.startsWith("#"))
22
21
  continue;
23
- const eqIdx = trimmed.indexOf('=');
22
+ const eqIdx = trimmed.indexOf("=");
24
23
  if (eqIdx === -1)
25
24
  continue;
26
25
  const key = trimmed.slice(0, eqIdx).trim();
27
- const value = trimmed.slice(eqIdx + 1).trim().replace(/^["']|["']$/g, '');
26
+ const value = trimmed.slice(eqIdx + 1).trim().replace(/^["']|["']$/g, "");
28
27
  if (key)
29
28
  this.cache[key] = value;
30
29
  }
31
30
  }
32
- // Process.env overrides file
33
31
  for (const key of Object.keys(process.env)) {
34
32
  const value = process.env[key];
35
- if (value !== undefined) {
33
+ if (value !== undefined)
36
34
  this.cache[key] = value;
37
- }
38
35
  }
39
36
  }
40
37
  get(key, defaultValue) {
@@ -47,7 +44,42 @@ export class EnvLoader {
47
44
  return value;
48
45
  }
49
46
  has(key) {
50
- return this.cache[key] !== undefined;
47
+ return this.cache[key] !== undefined && this.cache[key].trim() !== "";
48
+ }
49
+ /** Write a key=value to the .env file and update cache */
50
+ set(key, value) {
51
+ const content = existsSync(this.envPath) ? readFileSync(this.envPath, "utf-8") : "";
52
+ const lines = content.split("\n");
53
+ let found = false;
54
+ for (let i = 0; i < lines.length; i++) {
55
+ const line = lines[i].trim();
56
+ if (line.startsWith(`${key}=`)) {
57
+ lines[i] = `${key}=${value}`;
58
+ found = true;
59
+ break;
60
+ }
61
+ }
62
+ if (!found) {
63
+ if (content && !content.endsWith("\n"))
64
+ lines.push("");
65
+ lines.push(`${key}=${value}`);
66
+ }
67
+ const newContent = lines.join("\n") + "\n";
68
+ writeFileSync(this.envPath, newContent, "utf-8");
69
+ try {
70
+ chmodSync(this.envPath, 0o600);
71
+ }
72
+ catch { /* non-fatal */ }
73
+ this.cache[key] = value;
74
+ process.env[key] = value;
75
+ }
76
+ getAll() {
77
+ const result = {};
78
+ for (const [key, value] of Object.entries(this.cache)) {
79
+ if (value !== undefined)
80
+ result[key] = value;
81
+ }
82
+ return result;
51
83
  }
52
84
  reload() {
53
85
  this.cache = {};
package/dist/index.d.ts CHANGED
@@ -17,7 +17,7 @@ export { loadExtension } from "./loader.js";
17
17
  export { makeTheme, T, AIAIAI_COLORS } from "./tui/theme.js";
18
18
  export { ModelRegistry, modelRegistry, classifyModel } from "./models/ModelRegistry.js";
19
19
  export { CostTracker } from "./models/CostTracker.js";
20
- export type { OpenRouterModel, TieredModel, TieredPool, ModelTier } from "./models/ModelRegistry.js";
20
+ export type { ModelInfo, TieredModel, ModelTier } from "./models/ModelRegistry.js";
21
21
  export { fullAnalysis, rsi, macd, ema, sma, bollingerBands, atr, getCandlesTool, getCandlesParams } from "./tools/TechnicalAnalysis.js";
22
22
  export { PriceFeed, priceFeed } from "./tools/PriceFeed.js";
23
23
  export { NewsFeed, newsFeed, getNewsTool, scoreSentiment } from "./tools/NewsSentiment.js";
@@ -37,6 +37,7 @@ export { agentWallet, COLD_WALLET, ACTION_WALLET, DEPOSIT_WALLET, SIGNER, AIAIAI
37
37
  export { actionFeed } from "./wallet/ActionFeed.js";
38
38
  export type { WalletBalance, AgentWallets } from "./wallet/AgentWallet.js";
39
39
  export type { AgentAction, ActionType, FeeTracker } from "./wallet/ActionFeed.js";
40
+ export { gmgnTool, gmgnHelp, gmgnStatus, hasGmgnApiKey, saveGmgnApiKey, GMGN_SUBCOMMANDS } from "./tools/GmgnIntegration.js";
40
41
  export { MCPServer } from "./mcp/server.js";
41
42
  export { AgentDir } from "./core/AgentDir.js";
42
43
  export { EnvLoader, env } from "./core/EnvLoader.js";
package/dist/index.js CHANGED
@@ -36,6 +36,8 @@ export { AgentScheduler, agentScheduler } from "./scheduler/AgentScheduler.js";
36
36
  // Wallet
37
37
  export { agentWallet, COLD_WALLET, ACTION_WALLET, DEPOSIT_WALLET, SIGNER, AIAIAI_MINT } from "./wallet/AgentWallet.js";
38
38
  export { actionFeed } from "./wallet/ActionFeed.js";
39
+ // GMGN
40
+ export { gmgnTool, gmgnHelp, gmgnStatus, hasGmgnApiKey, saveGmgnApiKey, GMGN_SUBCOMMANDS } from "./tools/GmgnIntegration.js";
39
41
  // MCP
40
42
  export { MCPServer } from "./mcp/server.js";
41
43
  // Core
@@ -23,14 +23,8 @@ export class CostTracker {
23
23
  this.sessionEntry.totalCost += cost;
24
24
  this.sessionEntry.totalTokens += promptTokens + completionTokens;
25
25
  }
26
- estimateCost(model, prompt, completion) {
27
- const all = this.registry.getAllModels();
28
- const m = all.find(m => m.id === model);
29
- if (!m)
30
- return (prompt + completion) * 0.000001;
31
- const p = parseFloat(m.pricing.prompt) || DEFAULT_RATES.prompt;
32
- const c = parseFloat(m.pricing.completion) || DEFAULT_RATES.completion;
33
- return (prompt * p) + (completion * c);
26
+ estimateCost(_model, prompt, completion) {
27
+ return (prompt * DEFAULT_RATES.prompt) + (completion * DEFAULT_RATES.completion);
34
28
  }
35
29
  statusLine() {
36
30
  return `$${this.sessionEntry.totalCost.toFixed(4)}`;
@@ -1,70 +1,45 @@
1
1
  /**
2
- * ModelRegistry — discovers available models via OpenRouter API.
3
- * Caches results and provides tiered model pools.
2
+ * ModelRegistry — discovers available models from any configured provider.
3
+ * Supports all 29 providers via their API keys.
4
4
  */
5
5
  import type { ToolResult } from "../api/ExtensionAPI.js";
6
6
  export type ModelTier = "orchestrator" | "analyst" | "worker" | "free";
7
- export interface OpenRouterModel {
7
+ export interface ModelInfo {
8
8
  id: string;
9
9
  name: string;
10
- created: number;
11
- description: string;
10
+ provider: string;
12
11
  context_length: number;
13
- architecture: {
14
- modality: string;
15
- tokenizer: string;
16
- instruct_type: string | null;
17
- };
18
- pricing: {
19
- prompt: string;
20
- completion: string;
21
- image: string;
22
- request: string;
23
- };
24
- top_provider: {
25
- max_completion_tokens: number | null;
26
- is_moderated: boolean;
27
- };
28
- per_request_limits: {
29
- prompt_tokens: number | null;
30
- completion_tokens: number | null;
31
- };
12
+ max_tokens: number;
32
13
  }
33
14
  export interface TieredModel {
34
- model: OpenRouterModel;
15
+ model: ModelInfo;
35
16
  tier: ModelTier;
36
17
  available: boolean;
37
18
  failures: number;
38
19
  }
39
- export interface TieredPool {
40
- tier: ModelTier;
41
- models: TieredModel[];
42
- }
43
- export declare function classifyModel(model: OpenRouterModel): ModelTier;
20
+ export declare function classifyModel(model: ModelInfo): ModelTier;
44
21
  export declare class ModelRegistry {
45
22
  private models;
46
23
  private tiered;
24
+ private providerModels;
47
25
  private initialized;
48
26
  initialise(): Promise<void>;
27
+ private buildTiers;
49
28
  get modelCount(): number;
29
+ getProviderCount(): number;
30
+ getProviderModels(providerId: string): ModelInfo[];
31
+ getAllModels(): ModelInfo[];
50
32
  getPool(tier: ModelTier): TieredModel[];
51
- getAllModels(): OpenRouterModel[];
52
33
  getTier(modelId: string): ModelTier | undefined;
53
34
  resolvePrimary(): string;
54
35
  listModelsParams: import("@sinclair/typebox").TObject<{
55
36
  query: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
37
+ provider: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
56
38
  limit: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TNumber>;
57
39
  available_only: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TBoolean>;
58
40
  }>;
59
- pickModelParams: import("@sinclair/typebox").TObject<{
60
- tier: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
61
- min_context: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TNumber>;
62
- }>;
63
- summaryParams: import("@sinclair/typebox").TObject<{}>;
64
41
  listModelsTool(_id: string, params: Record<string, unknown>): Promise<ToolResult>;
65
- pickModelTool(_id: string, params: Record<string, unknown>): Promise<ToolResult>;
66
- summaryTool(): Promise<ToolResult>;
67
- private getFallbackModels;
42
+ modelSummaryTool(): Promise<ToolResult>;
68
43
  }
69
44
  export declare const modelRegistry: ModelRegistry;
70
45
  //# sourceMappingURL=ModelRegistry.d.ts.map