Skip to content

Commit 7d70b4f

Browse files
authored
Merge branch 'develop' into issue-16
2 parents 1a600b8 + 26f71dd commit 7d70b4f

33 files changed

+3646
-2071
lines changed

.env.example

+1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ SUPABASE_ANON_KEY=
1919
REMOTE_CHARACTER_URLS=
2020

2121
# Logging
22+
DEFAULT_LOG_LEVEL=info
2223
LOG_JSON_FORMAT= # Print everything in logger as json; false by default
2324

2425
###############################

.github/workflows/smoke-tests.yml

+5-12
Original file line numberDiff line numberDiff line change
@@ -10,24 +10,17 @@ on:
1010
jobs:
1111
smoke-tests:
1212
runs-on: ubuntu-latest
13-
container:
14-
image: node:23-bullseye
1513
steps:
1614
- uses: actions/checkout@v4
1715

18-
- name: Cache pnpm
19-
uses: actions/cache@v4
16+
- uses: pnpm/action-setup@v3
2017
with:
21-
path: |
22-
~/.pnpm-store
23-
**/node_modules
24-
key: ${{ runner.os }}-pnpm-${{ hashFiles('**/pnpm-lock.yaml') }}
25-
restore-keys: ${{ runner.os }}-pnpm-
18+
version: 9.15.0
2619

27-
- name: Setup pnpm
28-
uses: pnpm/action-setup@v3
20+
- uses: actions/setup-node@v4
2921
with:
30-
version: 9.15.0
22+
node-version: "23.3.0"
23+
cache: "pnpm"
3124

3225
- name: Run smoke tests
3326
run: pnpm run smokeTests

agent/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@
6363
"@elizaos/plugin-node": "workspace:*",
6464
"@elizaos/plugin-solana": "workspace:*",
6565
"@elizaos/plugin-injective": "workspace:*",
66-
"@elizaos/plugin-solana-agentkit": "workspace:*",
66+
"@elizaos/plugin-solana-agent-kit": "workspace:*",
6767
"@elizaos/plugin-squid-router": "workspace:*",
6868
"@elizaos/plugin-autonome": "workspace:*",
6969
"@elizaos/plugin-starknet": "workspace:*",

agent/src/index.ts

+8-8
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ import { openWeatherPlugin } from "@elizaos/plugin-open-weather";
8585
import { quaiPlugin } from "@elizaos/plugin-quai";
8686
import { sgxPlugin } from "@elizaos/plugin-sgx";
8787
import { solanaPlugin } from "@elizaos/plugin-solana";
88-
import { solanaAgentkitPlugin } from "@elizaos/plugin-solana-agentkit";
88+
import { solanaAgentkitPlugin } from "@elizaos/plugin-solana-agent-kit";
8989
import { squidRouterPlugin } from "@elizaos/plugin-squid-router";
9090
import { stargazePlugin } from "@elizaos/plugin-stargaze";
9191
import { storyPlugin } from "@elizaos/plugin-story";
@@ -437,13 +437,6 @@ export function getTokenForProvider(
437437
character: Character
438438
): string | undefined {
439439
switch (provider) {
440-
// no key needed for llama_local or gaianet
441-
case ModelProviderName.LLAMALOCAL:
442-
return "";
443-
case ModelProviderName.OLLAMA:
444-
return "";
445-
case ModelProviderName.GAIANET:
446-
return "";
447440
case ModelProviderName.OPENAI:
448441
return (
449442
character.settings?.secrets?.OPENAI_API_KEY ||
@@ -576,6 +569,13 @@ export function getTokenForProvider(
576569
character.settings?.secrets?.LIVEPEER_GATEWAY_URL ||
577570
settings.LIVEPEER_GATEWAY_URL
578571
);
572+
// no key needed for llama_local or gaianet
573+
case ModelProviderName.LLAMALOCAL:
574+
return "";
575+
case ModelProviderName.OLLAMA:
576+
return "";
577+
case ModelProviderName.GAIANET:
578+
return "";
579579
default:
580580
const errorMessage = `Failed to get token - unsupported model provider: ${provider}`;
581581
elizaLogger.error(errorMessage);

client/version.cmd

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
@echo off
2+
setlocal enabledelayedexpansion
3+
4+
set "LERNA_FILE=..\lerna.json"
5+
6+
if not exist "%LERNA_FILE%" (
7+
echo Error: %LERNA_FILE% does not exist.
8+
exit /b 1
9+
)
10+
11+
for /f "tokens=2 delims=:, " %%a in ('findstr "version" "%LERNA_FILE%"') do (
12+
set "VERSION=%%~a"
13+
)
14+
15+
if "!VERSION!"=="" (
16+
echo Error: Unable to extract version from %LERNA_FILE%.
17+
exit /b 1
18+
)
19+
20+
if not exist "src\" mkdir src
21+
echo { "version": !VERSION! } > src\info.json
22+
if errorlevel 1 (
23+
echo Error: Failed to write src\info.json
24+
exit /b 1
25+
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
import { describe, it, expect, vi } from 'vitest';
2+
import { validateInstagramConfig, instagramEnvSchema } from '../src/environment';
3+
import { IAgentRuntime } from '@elizaos/core';
4+
5+
describe('Instagram Environment Configuration', () => {
6+
const mockRuntime: IAgentRuntime = {
7+
getSetting: vi.fn(),
8+
} as unknown as IAgentRuntime;
9+
10+
it('validates correct Instagram configuration', async () => {
11+
const validConfig = {
12+
INSTAGRAM_DRY_RUN: false,
13+
INSTAGRAM_USERNAME: 'test_user',
14+
INSTAGRAM_PASSWORD: 'test_password',
15+
INSTAGRAM_APP_ID: 'test_app_id',
16+
INSTAGRAM_APP_SECRET: 'test_app_secret',
17+
INSTAGRAM_POST_INTERVAL_MIN: 60,
18+
INSTAGRAM_POST_INTERVAL_MAX: 120,
19+
INSTAGRAM_ENABLE_ACTION_PROCESSING: false,
20+
INSTAGRAM_ACTION_INTERVAL: 5,
21+
INSTAGRAM_MAX_ACTIONS: 1,
22+
};
23+
24+
vi.mocked(mockRuntime.getSetting).mockImplementation((key: string) => {
25+
if (key === 'INSTAGRAM_DRY_RUN') return 'false';
26+
if (key === 'INSTAGRAM_ENABLE_ACTION_PROCESSING') return 'false';
27+
return validConfig[key as keyof typeof validConfig];
28+
});
29+
30+
const config = await validateInstagramConfig(mockRuntime);
31+
expect(config).toEqual(validConfig);
32+
});
33+
34+
it('validates configuration with optional business account', async () => {
35+
const validConfig = {
36+
INSTAGRAM_DRY_RUN: false,
37+
INSTAGRAM_USERNAME: 'test_user',
38+
INSTAGRAM_PASSWORD: 'test_password',
39+
INSTAGRAM_APP_ID: 'test_app_id',
40+
INSTAGRAM_APP_SECRET: 'test_app_secret',
41+
INSTAGRAM_BUSINESS_ACCOUNT_ID: 'business_123',
42+
INSTAGRAM_POST_INTERVAL_MIN: 60,
43+
INSTAGRAM_POST_INTERVAL_MAX: 120,
44+
INSTAGRAM_ENABLE_ACTION_PROCESSING: false,
45+
INSTAGRAM_ACTION_INTERVAL: 5,
46+
INSTAGRAM_MAX_ACTIONS: 1,
47+
};
48+
49+
vi.mocked(mockRuntime.getSetting).mockImplementation((key: string) => {
50+
if (key === 'INSTAGRAM_DRY_RUN') return 'false';
51+
if (key === 'INSTAGRAM_ENABLE_ACTION_PROCESSING') return 'false';
52+
return validConfig[key as keyof typeof validConfig];
53+
});
54+
55+
const config = await validateInstagramConfig(mockRuntime);
56+
expect(config).toEqual(validConfig);
57+
});
58+
59+
it('throws error for invalid username format', async () => {
60+
const invalidConfig = {
61+
INSTAGRAM_DRY_RUN: false,
62+
INSTAGRAM_USERNAME: 'invalid@username', // Invalid characters
63+
INSTAGRAM_PASSWORD: 'test_password',
64+
INSTAGRAM_APP_ID: 'test_app_id',
65+
INSTAGRAM_APP_SECRET: 'test_app_secret',
66+
};
67+
68+
vi.mocked(mockRuntime.getSetting).mockImplementation((key: string) => {
69+
if (key === 'INSTAGRAM_DRY_RUN') return 'false';
70+
return invalidConfig[key as keyof typeof invalidConfig];
71+
});
72+
73+
await expect(validateInstagramConfig(mockRuntime)).rejects.toThrow();
74+
});
75+
76+
it('throws error for missing required fields', async () => {
77+
const invalidConfig = {
78+
INSTAGRAM_DRY_RUN: false,
79+
INSTAGRAM_USERNAME: 'test_user',
80+
// Missing password and other required fields
81+
};
82+
83+
vi.mocked(mockRuntime.getSetting).mockImplementation((key: string) => {
84+
if (key === 'INSTAGRAM_DRY_RUN') return 'false';
85+
return invalidConfig[key as keyof typeof invalidConfig];
86+
});
87+
88+
await expect(validateInstagramConfig(mockRuntime)).rejects.toThrow();
89+
});
90+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
import { describe, it, expect, vi, beforeEach } from 'vitest';
2+
import { InstagramClientInterface } from '../src';
3+
import { IAgentRuntime, elizaLogger } from '@elizaos/core';
4+
import { InstagramInteractionService } from '../src/services/interaction';
5+
import { InstagramPostService } from '../src/services/post';
6+
7+
// Mock dependencies
8+
vi.mock('@elizaos/core', async (importOriginal) => {
9+
const actual = await importOriginal();
10+
return {
11+
...actual,
12+
elizaLogger: {
13+
log: vi.fn(),
14+
error: vi.fn(),
15+
},
16+
parseBooleanFromText: (value: string | undefined) => value === 'true',
17+
};
18+
});
19+
20+
// Mock service instances
21+
const mockPostService = {
22+
start: vi.fn().mockResolvedValue(undefined),
23+
};
24+
25+
const mockInteractionService = {
26+
start: vi.fn().mockResolvedValue(undefined),
27+
};
28+
29+
vi.mock('../src/lib/auth', () => ({
30+
initializeClient: vi.fn().mockResolvedValue({
31+
ig: {},
32+
config: {
33+
INSTAGRAM_DRY_RUN: false,
34+
INSTAGRAM_ENABLE_ACTION_PROCESSING: true,
35+
},
36+
}),
37+
}));
38+
39+
vi.mock('../src/services/post', () => ({
40+
InstagramPostService: vi.fn().mockImplementation(() => mockPostService),
41+
}));
42+
43+
vi.mock('../src/services/interaction', () => ({
44+
InstagramInteractionService: vi.fn().mockImplementation(() => mockInteractionService),
45+
}));
46+
47+
describe('InstagramClientInterface', () => {
48+
let mockRuntime: IAgentRuntime;
49+
const mockConfig = {
50+
INSTAGRAM_DRY_RUN: false,
51+
INSTAGRAM_USERNAME: 'test_user',
52+
INSTAGRAM_PASSWORD: 'test_password',
53+
INSTAGRAM_APP_ID: 'test_app_id',
54+
INSTAGRAM_APP_SECRET: 'test_app_secret',
55+
INSTAGRAM_POST_INTERVAL_MIN: 60,
56+
INSTAGRAM_POST_INTERVAL_MAX: 120,
57+
INSTAGRAM_ENABLE_ACTION_PROCESSING: true,
58+
INSTAGRAM_ACTION_INTERVAL: 5,
59+
INSTAGRAM_MAX_ACTIONS: 1,
60+
};
61+
62+
beforeEach(() => {
63+
vi.clearAllMocks();
64+
mockRuntime = {
65+
getSetting: vi.fn((key: string) => {
66+
if (key === 'INSTAGRAM_DRY_RUN' || key === 'INSTAGRAM_ENABLE_ACTION_PROCESSING') {
67+
return String(mockConfig[key as keyof typeof mockConfig]);
68+
}
69+
return mockConfig[key as keyof typeof mockConfig];
70+
}),
71+
} as unknown as IAgentRuntime;
72+
});
73+
74+
it('starts successfully with all services', async () => {
75+
const result = await InstagramClientInterface.start(mockRuntime);
76+
77+
expect(result).toBeDefined();
78+
expect(result.post).toBeDefined();
79+
expect(result.interaction).toBeDefined();
80+
expect(InstagramPostService).toHaveBeenCalled();
81+
expect(InstagramInteractionService).toHaveBeenCalled();
82+
expect(result.post.start).toHaveBeenCalled();
83+
expect(result.interaction.start).toHaveBeenCalled();
84+
expect(elizaLogger.log).toHaveBeenCalledWith('Instagram client configuration validated');
85+
expect(elizaLogger.log).toHaveBeenCalledWith('Instagram client initialized');
86+
expect(elizaLogger.log).toHaveBeenCalledWith('Instagram post service started');
87+
expect(elizaLogger.log).toHaveBeenCalledWith('Instagram interaction service started');
88+
});
89+
90+
it('starts in dry-run mode', async () => {
91+
const dryRunConfig = { ...mockConfig, INSTAGRAM_DRY_RUN: true };
92+
mockRuntime.getSetting = vi.fn((key: string) => {
93+
if (key === 'INSTAGRAM_DRY_RUN') return 'true';
94+
if (key === 'INSTAGRAM_ENABLE_ACTION_PROCESSING') return String(dryRunConfig.INSTAGRAM_ENABLE_ACTION_PROCESSING);
95+
return dryRunConfig[key as keyof typeof dryRunConfig];
96+
});
97+
98+
const result = await InstagramClientInterface.start(mockRuntime);
99+
100+
expect(result).toBeDefined();
101+
expect(elizaLogger.log).toHaveBeenCalledWith('Instagram client running in dry-run mode');
102+
expect(mockPostService.start).not.toHaveBeenCalled();
103+
expect(mockInteractionService.start).not.toHaveBeenCalled();
104+
});
105+
106+
it('handles errors during startup', async () => {
107+
const error = new Error('Startup failed');
108+
vi.mocked(mockRuntime.getSetting).mockImplementation(() => {
109+
throw error;
110+
});
111+
112+
await expect(InstagramClientInterface.start(mockRuntime)).rejects.toThrow('Startup failed');
113+
expect(elizaLogger.error).toHaveBeenCalledWith('Failed to start Instagram client:', error);
114+
});
115+
116+
it('stops gracefully', async () => {
117+
await InstagramClientInterface.stop(mockRuntime);
118+
expect(elizaLogger.log).toHaveBeenCalledWith('Stopping Instagram client services...');
119+
});
120+
});

packages/client-instagram/package.json

+5-2
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,14 @@
2727
},
2828
"devDependencies": {
2929
"tsup": "8.3.5",
30-
"@types/sharp": "^0.32.0"
30+
"@types/sharp": "^0.32.0",
31+
"vitest": "^1.2.1"
3132
},
3233
"scripts": {
3334
"build": "tsup --format esm --dts",
3435
"dev": "tsup --format esm --dts --watch",
35-
"lint": "eslint --fix --cache ."
36+
"lint": "eslint --fix --cache .",
37+
"test": "vitest run",
38+
"test:watch": "vitest"
3639
}
3740
}

packages/core/src/defaultCharacter.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ export const defaultCharacter: Character = {
55
username: "eliza",
66
plugins: [],
77
clients: [],
8-
modelProvider: ModelProviderName.LLAMALOCAL,
8+
modelProvider: ModelProviderName.OPENAI,
99
settings: {
1010
secrets: {},
1111
voice: {

packages/core/src/logger.ts

+3
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,10 @@ const createStream = () => {
2626
});
2727
};
2828

29+
const defaultLevel = process?.env?.DEFAULT_LOG_LEVEL || "info";
30+
2931
const options = {
32+
level: defaultLevel,
3033
customLevels,
3134
hooks: {
3235
logMethod(

packages/plugin-solana-agentkit/package.json packages/plugin-solana-agent-kit/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"name": "@elizaos/plugin-solana-agentkit",
2+
"name": "@elizaos/plugin-solana-agent-kit",
33
"version": "0.1.9-alpha.1",
44
"main": "dist/index.js",
55
"type": "module",

packages/plugin-solana-agentkit/src/actions/createToken.ts packages/plugin-solana-agent-kit/src/actions/createToken.ts

+2-11
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,7 @@ import {
1111
State,
1212
type Action,
1313
} from "@elizaos/core";
14-
15-
import { SolanaAgentKit } from "solana-agent-kit";
16-
14+
import { getSAK } from "../client";
1715
export interface CreateTokenContent extends Content {
1816
name: string;
1917
uri: string;
@@ -103,14 +101,7 @@ export default {
103101
}
104102

105103
elizaLogger.log("Init solana agent kit...");
106-
const solanaPrivatekey = runtime.getSetting("SOLANA_PRIVATE_KEY");
107-
const rpc = runtime.getSetting("SOLANA_RPC_URL");
108-
const openAIKey = runtime.getSetting("OPENAI_API_KEY");
109-
const solanaAgentKit = new SolanaAgentKit(
110-
solanaPrivatekey,
111-
rpc,
112-
openAIKey
113-
);
104+
const solanaAgentKit = await getSAK(runtime);
114105
try {
115106
const deployedAddress = await solanaAgentKit.deployToken(
116107
content.name,

0 commit comments

Comments
 (0)