agentbnb 4.0.2 → 5.1.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 +31 -2
- package/dist/chunk-AUBHR7HH.js +25 -0
- package/dist/chunk-B5FTAGFN.js +393 -0
- package/dist/{chunk-GGYC5U2Z.js → chunk-BTTL24TZ.js} +29 -91
- package/dist/chunk-C6KPAFCC.js +387 -0
- package/dist/chunk-CRFCWD6V.js +366 -0
- package/dist/chunk-CSATDXZC.js +89 -0
- package/dist/{chunk-T7NS2J2B.js → chunk-DFBX3BBD.js} +84 -1
- package/dist/{chunk-DNWT5FZQ.js → chunk-EANI2N2V.js} +98 -1
- package/dist/{chunk-HH24WMFN.js → chunk-FLY3WIQR.js} +1 -1
- package/dist/chunk-HLUEOLSZ.js +62 -0
- package/dist/chunk-IVOYM3WG.js +25 -0
- package/dist/chunk-LCAIAAG2.js +916 -0
- package/dist/chunk-MLS6IGGG.js +294 -0
- package/dist/{chunk-4P3EMGL4.js → chunk-MNO4COST.js} +5 -3
- package/dist/chunk-NH2FIERR.js +138 -0
- package/dist/chunk-UKT6H7YT.js +29 -0
- package/dist/{chunk-BH6WGYFB.js → chunk-VE3E4AMH.js} +8 -8
- package/dist/{chunk-7NA43XCG.js → chunk-W5BZMKMF.js} +163 -29
- package/dist/{chunk-FF226TIV.js → chunk-ZX5623ER.js} +0 -57
- package/dist/cli/index.js +362 -4631
- package/dist/{conduct-N52JX7RT.js → conduct-KM6ZNJGE.js} +10 -8
- package/dist/{conduct-GZQNFTRP.js → conduct-WGTMQND5.js} +10 -8
- package/dist/{conductor-mode-XUWGR4ZE.js → conductor-mode-OL2FNOYY.js} +6 -4
- package/dist/conductor-mode-VRO7TYW2.js +592 -0
- package/dist/execute-CPFSOOO3.js +13 -0
- package/dist/execute-IP2QHALV.js +10 -0
- package/dist/index.d.ts +19 -8
- package/dist/index.js +190 -37
- package/dist/peers-CJ7T4RJO.js +13 -0
- package/dist/process-guard-CC7CNRQJ.js +176 -0
- package/dist/{request-4GQSSM4B.js → request-YOWPXVLQ.js} +13 -10
- package/dist/schema-7BSSLZ4S.js +8 -0
- package/dist/{serve-skill-TPHZH6BS.js → serve-skill-JHFNR7BW.js} +8 -7
- package/dist/{server-365V3GYD.js → server-HKJJWFRG.js} +10 -8
- package/dist/service-coordinator-5R4LQW6L.js +4917 -0
- package/dist/skills/agentbnb/bootstrap.js +6181 -0
- package/dist/websocket-client-WRN3HO73.js +6 -0
- package/openclaw.plugin.json +54 -0
- package/package.json +11 -2
- package/skills/agentbnb/SKILL.md +87 -70
- package/skills/agentbnb/bootstrap.test.ts +142 -242
- package/skills/agentbnb/bootstrap.ts +88 -95
- package/skills/agentbnb/install.sh +97 -27
- package/dist/execute-PNGQOMYO.js +0 -10
|
@@ -1,323 +1,223 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Unit tests for bootstrap.ts thin OpenClaw adapter.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
4
|
+
* bootstrap.ts is a thin adapter — it delegates all lifecycle logic to
|
|
5
|
+
* ServiceCoordinator via AgentBnBService. Tests verify the adapter's own
|
|
6
|
+
* responsibilities:
|
|
7
|
+
* - CONFIG_NOT_FOUND when no config exists
|
|
8
|
+
* - BootstrapContext shape (service, status, startDisposition)
|
|
9
|
+
* - Signal handler registration and removal
|
|
10
|
+
* - deactivate() only stops node when startDisposition === 'started'
|
|
11
|
+
* - deactivate() is idempotent
|
|
9
12
|
*/
|
|
10
13
|
|
|
11
|
-
import { describe, it, expect, afterEach } from 'vitest';
|
|
12
|
-
import {
|
|
13
|
-
import { tmpdir } from 'node:os';
|
|
14
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
15
|
+
import { homedir } from 'node:os';
|
|
14
16
|
import { join } from 'node:path';
|
|
15
17
|
|
|
16
|
-
import { activate, deactivate } from './bootstrap.js';
|
|
17
|
-
import type { BootstrapContext } from './bootstrap.js';
|
|
18
|
-
import { IdleMonitor } from '../../src/autonomy/idle-monitor.js';
|
|
19
|
-
import { loadIdentity } from '../../src/identity/identity.js';
|
|
20
|
-
|
|
21
18
|
// ---------------------------------------------------------------------------
|
|
22
|
-
//
|
|
19
|
+
// Mocks — must be hoisted before imports
|
|
23
20
|
// ---------------------------------------------------------------------------
|
|
24
|
-
const SOUL_MD_CONTENT = `# Test Agent
|
|
25
21
|
|
|
26
|
-
|
|
22
|
+
const mockEnsureRunning = vi.fn<() => Promise<'started' | 'already_running'>>();
|
|
23
|
+
const mockGetNodeStatus = vi.fn();
|
|
24
|
+
const mockStop = vi.fn<() => Promise<void>>();
|
|
25
|
+
|
|
26
|
+
vi.mock('../../src/app/agentbnb-service.js', () => ({
|
|
27
|
+
AgentBnBService: vi.fn().mockImplementation(() => ({
|
|
28
|
+
ensureRunning: mockEnsureRunning,
|
|
29
|
+
getNodeStatus: mockGetNodeStatus,
|
|
30
|
+
stop: mockStop,
|
|
31
|
+
})),
|
|
32
|
+
}));
|
|
33
|
+
|
|
34
|
+
vi.mock('../../src/runtime/service-coordinator.js', () => ({
|
|
35
|
+
ServiceCoordinator: vi.fn().mockImplementation(() => ({})),
|
|
36
|
+
}));
|
|
27
37
|
|
|
28
|
-
|
|
29
|
-
|
|
38
|
+
vi.mock('../../src/runtime/process-guard.js', () => ({
|
|
39
|
+
ProcessGuard: vi.fn().mockImplementation(() => ({})),
|
|
40
|
+
}));
|
|
30
41
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
42
|
+
vi.mock('../../src/cli/config.js', () => ({
|
|
43
|
+
loadConfig: vi.fn(),
|
|
44
|
+
getConfigDir: vi.fn(() => join(homedir(), '.agentbnb')),
|
|
45
|
+
}));
|
|
46
|
+
|
|
47
|
+
import { loadConfig } from '../../src/cli/config.js';
|
|
48
|
+
import { activate, deactivate } from './bootstrap.js';
|
|
49
|
+
import type { BootstrapContext } from './bootstrap.js';
|
|
50
|
+
|
|
51
|
+
const mockLoadConfig = vi.mocked(loadConfig);
|
|
52
|
+
|
|
53
|
+
const MINIMAL_CONFIG = {
|
|
54
|
+
owner: 'test-agent',
|
|
55
|
+
gateway_url: 'http://localhost:7700',
|
|
56
|
+
gateway_port: 7700,
|
|
57
|
+
db_path: ':memory:',
|
|
58
|
+
credit_db_path: ':memory:',
|
|
59
|
+
token: 'test-token',
|
|
60
|
+
api_key: 'test-api-key',
|
|
61
|
+
registry: 'https://agentbnb.fly.dev',
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
const MOCK_STATUS = {
|
|
65
|
+
state: 'running' as const,
|
|
66
|
+
pid: 1234,
|
|
67
|
+
port: 7700,
|
|
68
|
+
owner: 'test-agent',
|
|
69
|
+
relayConnected: false,
|
|
70
|
+
uptime_ms: 100,
|
|
71
|
+
};
|
|
34
72
|
|
|
35
73
|
// ---------------------------------------------------------------------------
|
|
36
74
|
// Test suite
|
|
37
75
|
// ---------------------------------------------------------------------------
|
|
38
76
|
|
|
39
77
|
describe('bootstrap activate/deactivate lifecycle', () => {
|
|
40
|
-
let tmpDir: string | undefined;
|
|
41
78
|
let ctx: BootstrapContext | undefined;
|
|
42
79
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
return path;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
// Ensure all resources are torn down after every test
|
|
80
|
+
beforeEach(() => {
|
|
81
|
+
vi.clearAllMocks();
|
|
82
|
+
mockEnsureRunning.mockResolvedValue('started');
|
|
83
|
+
mockGetNodeStatus.mockResolvedValue(MOCK_STATUS);
|
|
84
|
+
mockStop.mockResolvedValue(undefined);
|
|
85
|
+
mockLoadConfig.mockReturnValue(MINIMAL_CONFIG as ReturnType<typeof loadConfig>);
|
|
86
|
+
});
|
|
87
|
+
|
|
55
88
|
afterEach(async () => {
|
|
56
89
|
if (ctx) {
|
|
57
90
|
await deactivate(ctx).catch(() => undefined);
|
|
58
91
|
ctx = undefined;
|
|
59
92
|
}
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
tmpDir = undefined;
|
|
63
|
-
}
|
|
93
|
+
process.removeAllListeners('SIGTERM');
|
|
94
|
+
process.removeAllListeners('SIGINT');
|
|
64
95
|
});
|
|
65
96
|
|
|
66
97
|
// ---------------------------------------------------------------------------
|
|
67
|
-
// Test 1:
|
|
98
|
+
// Test 1: CONFIG_NOT_FOUND when no config exists
|
|
68
99
|
// ---------------------------------------------------------------------------
|
|
69
|
-
it('activate()
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
soulMdPath,
|
|
75
|
-
registryDbPath: ':memory:',
|
|
76
|
-
creditDbPath: ':memory:',
|
|
77
|
-
gatewayPort: 0,
|
|
78
|
-
silent: true,
|
|
100
|
+
it('activate() throws CONFIG_NOT_FOUND when config is missing', async () => {
|
|
101
|
+
mockLoadConfig.mockReturnValue(null);
|
|
102
|
+
|
|
103
|
+
await expect(activate()).rejects.toMatchObject({
|
|
104
|
+
code: 'CONFIG_NOT_FOUND',
|
|
79
105
|
});
|
|
106
|
+
});
|
|
80
107
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
108
|
+
// ---------------------------------------------------------------------------
|
|
109
|
+
// Test 2: BootstrapContext shape
|
|
110
|
+
// ---------------------------------------------------------------------------
|
|
111
|
+
it('activate() returns BootstrapContext with correct shape', async () => {
|
|
112
|
+
ctx = await activate();
|
|
85
113
|
|
|
86
|
-
|
|
87
|
-
expect(ctx
|
|
88
|
-
expect(
|
|
114
|
+
expect(ctx).toHaveProperty('service');
|
|
115
|
+
expect(ctx).toHaveProperty('status');
|
|
116
|
+
expect(ctx).toHaveProperty('startDisposition');
|
|
117
|
+
expect(ctx).toHaveProperty('_removeSignalHandlers');
|
|
118
|
+
expect(typeof ctx._removeSignalHandlers).toBe('function');
|
|
119
|
+
});
|
|
89
120
|
|
|
90
|
-
|
|
91
|
-
|
|
121
|
+
// ---------------------------------------------------------------------------
|
|
122
|
+
// Test 3: startDisposition reflects ensureRunning result
|
|
123
|
+
// ---------------------------------------------------------------------------
|
|
124
|
+
it('startDisposition is "started" when ensureRunning returns "started"', async () => {
|
|
125
|
+
mockEnsureRunning.mockResolvedValue('started');
|
|
126
|
+
ctx = await activate();
|
|
127
|
+
expect(ctx.startDisposition).toBe('started');
|
|
128
|
+
});
|
|
92
129
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
expect(ctx.
|
|
130
|
+
it('startDisposition is "already_running" when node was already up', async () => {
|
|
131
|
+
mockEnsureRunning.mockResolvedValue('already_running');
|
|
132
|
+
ctx = await activate();
|
|
133
|
+
expect(ctx.startDisposition).toBe('already_running');
|
|
97
134
|
});
|
|
98
135
|
|
|
99
136
|
// ---------------------------------------------------------------------------
|
|
100
|
-
// Test
|
|
137
|
+
// Test 4: status snapshot from getNodeStatus
|
|
101
138
|
// ---------------------------------------------------------------------------
|
|
102
|
-
it('activate()
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
ctx = await activate({
|
|
106
|
-
owner: 'test-agent',
|
|
107
|
-
soulMdPath,
|
|
108
|
-
registryDbPath: ':memory:',
|
|
109
|
-
creditDbPath: ':memory:',
|
|
110
|
-
gatewayPort: 0,
|
|
111
|
-
silent: true,
|
|
112
|
-
});
|
|
113
|
-
|
|
114
|
-
// Query the registry DB for capability_cards owned by this agent
|
|
115
|
-
const rows = ctx.runtime.registryDb
|
|
116
|
-
.prepare('SELECT id, owner, data FROM capability_cards WHERE owner = ?')
|
|
117
|
-
.all('test-agent') as Array<{ id: string; owner: string; data: string }>;
|
|
118
|
-
|
|
119
|
-
expect(rows.length).toBe(1);
|
|
120
|
-
expect(rows[0].owner).toBe('test-agent');
|
|
121
|
-
|
|
122
|
-
// Parse the card data and verify both skills are present
|
|
123
|
-
const cardData = JSON.parse(rows[0].data) as {
|
|
124
|
-
skills?: Array<{ name: string }>;
|
|
125
|
-
};
|
|
126
|
-
const skillNames = (cardData.skills ?? []).map((s) => s.name);
|
|
127
|
-
expect(skillNames).toContain('Code Review');
|
|
128
|
-
expect(skillNames).toContain('Translation');
|
|
139
|
+
it('activate() status reflects getNodeStatus() snapshot', async () => {
|
|
140
|
+
ctx = await activate();
|
|
141
|
+
expect(ctx.status).toEqual(MOCK_STATUS);
|
|
129
142
|
});
|
|
130
143
|
|
|
131
144
|
// ---------------------------------------------------------------------------
|
|
132
|
-
// Test
|
|
145
|
+
// Test 5: signal handlers registered
|
|
133
146
|
// ---------------------------------------------------------------------------
|
|
134
|
-
it('activate()
|
|
135
|
-
const
|
|
136
|
-
|
|
137
|
-
ctx = await activate({
|
|
138
|
-
owner: 'test-agent',
|
|
139
|
-
soulMdPath,
|
|
140
|
-
registryDbPath: ':memory:',
|
|
141
|
-
creditDbPath: ':memory:',
|
|
142
|
-
gatewayPort: 0,
|
|
143
|
-
silent: true,
|
|
144
|
-
});
|
|
147
|
+
it('activate() registers SIGTERM and SIGINT handlers', async () => {
|
|
148
|
+
const sigtermBefore = process.listenerCount('SIGTERM');
|
|
149
|
+
const sigintBefore = process.listenerCount('SIGINT');
|
|
145
150
|
|
|
146
|
-
|
|
147
|
-
const response = await ctx.gateway.inject({
|
|
148
|
-
method: 'GET',
|
|
149
|
-
url: '/health',
|
|
150
|
-
});
|
|
151
|
+
ctx = await activate();
|
|
151
152
|
|
|
152
|
-
expect(
|
|
153
|
-
|
|
154
|
-
expect(body.status).toBe('ok');
|
|
153
|
+
expect(process.listenerCount('SIGTERM')).toBe(sigtermBefore + 1);
|
|
154
|
+
expect(process.listenerCount('SIGINT')).toBe(sigintBefore + 1);
|
|
155
155
|
});
|
|
156
156
|
|
|
157
157
|
// ---------------------------------------------------------------------------
|
|
158
|
-
// Test
|
|
158
|
+
// Test 6: _removeSignalHandlers removes them
|
|
159
159
|
// ---------------------------------------------------------------------------
|
|
160
|
-
it('
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
silent: true,
|
|
170
|
-
});
|
|
160
|
+
it('_removeSignalHandlers() removes SIGTERM and SIGINT handlers', async () => {
|
|
161
|
+
ctx = await activate();
|
|
162
|
+
const sigtermAfterActivate = process.listenerCount('SIGTERM');
|
|
163
|
+
const sigintAfterActivate = process.listenerCount('SIGINT');
|
|
164
|
+
|
|
165
|
+
ctx._removeSignalHandlers();
|
|
166
|
+
|
|
167
|
+
expect(process.listenerCount('SIGTERM')).toBe(sigtermAfterActivate - 1);
|
|
168
|
+
expect(process.listenerCount('SIGINT')).toBe(sigintAfterActivate - 1);
|
|
171
169
|
|
|
172
|
-
|
|
173
|
-
expect(ctx.runtime.jobs.length).toBeGreaterThanOrEqual(1);
|
|
170
|
+
ctx = undefined;
|
|
174
171
|
});
|
|
175
172
|
|
|
176
173
|
// ---------------------------------------------------------------------------
|
|
177
|
-
// Test
|
|
174
|
+
// Test 7: deactivate() stops node when startDisposition === 'started'
|
|
178
175
|
// ---------------------------------------------------------------------------
|
|
179
|
-
it('deactivate()
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
ctx = await activate({
|
|
183
|
-
owner: 'test-agent',
|
|
184
|
-
soulMdPath,
|
|
185
|
-
registryDbPath: ':memory:',
|
|
186
|
-
creditDbPath: ':memory:',
|
|
187
|
-
gatewayPort: 0,
|
|
188
|
-
silent: true,
|
|
189
|
-
});
|
|
190
|
-
|
|
191
|
-
expect(ctx.runtime.isDraining).toBe(false);
|
|
176
|
+
it('deactivate() calls service.stop() when startDisposition is "started"', async () => {
|
|
177
|
+
mockEnsureRunning.mockResolvedValue('started');
|
|
178
|
+
ctx = await activate();
|
|
192
179
|
|
|
193
180
|
await deactivate(ctx);
|
|
194
|
-
// Clear so afterEach doesn't double-deactivate
|
|
195
|
-
const savedCtx = ctx;
|
|
196
181
|
ctx = undefined;
|
|
197
182
|
|
|
198
|
-
expect(
|
|
183
|
+
expect(mockStop).toHaveBeenCalledTimes(1);
|
|
199
184
|
});
|
|
200
185
|
|
|
201
186
|
// ---------------------------------------------------------------------------
|
|
202
|
-
// Test
|
|
187
|
+
// Test 8: deactivate() does NOT stop node when already_running
|
|
203
188
|
// ---------------------------------------------------------------------------
|
|
204
|
-
it('deactivate()
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
ctx = await activate({
|
|
208
|
-
owner: 'test-agent',
|
|
209
|
-
soulMdPath,
|
|
210
|
-
registryDbPath: ':memory:',
|
|
211
|
-
creditDbPath: ':memory:',
|
|
212
|
-
gatewayPort: 0,
|
|
213
|
-
silent: true,
|
|
214
|
-
});
|
|
189
|
+
it('deactivate() does NOT call service.stop() when startDisposition is "already_running"', async () => {
|
|
190
|
+
mockEnsureRunning.mockResolvedValue('already_running');
|
|
191
|
+
ctx = await activate();
|
|
215
192
|
|
|
216
|
-
// Capture reference before clearing ctx
|
|
217
|
-
const runtime = ctx.runtime;
|
|
218
193
|
await deactivate(ctx);
|
|
219
194
|
ctx = undefined;
|
|
220
195
|
|
|
221
|
-
|
|
222
|
-
expect(() => {
|
|
223
|
-
runtime.registryDb.prepare('SELECT 1').get();
|
|
224
|
-
}).toThrow();
|
|
196
|
+
expect(mockStop).not.toHaveBeenCalled();
|
|
225
197
|
});
|
|
226
198
|
|
|
227
199
|
// ---------------------------------------------------------------------------
|
|
228
|
-
// Test
|
|
200
|
+
// Test 9: deactivate() is idempotent
|
|
229
201
|
// ---------------------------------------------------------------------------
|
|
230
|
-
it('deactivate() is idempotent', async () => {
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
ctx = await activate({
|
|
234
|
-
owner: 'test-agent',
|
|
235
|
-
soulMdPath,
|
|
236
|
-
registryDbPath: ':memory:',
|
|
237
|
-
creditDbPath: ':memory:',
|
|
238
|
-
gatewayPort: 0,
|
|
239
|
-
silent: true,
|
|
240
|
-
});
|
|
202
|
+
it('deactivate() is idempotent — second call does not throw', async () => {
|
|
203
|
+
ctx = await activate();
|
|
241
204
|
|
|
242
205
|
await deactivate(ctx);
|
|
243
|
-
// Second call must not throw
|
|
244
206
|
await expect(deactivate(ctx)).resolves.not.toThrow();
|
|
245
207
|
|
|
246
208
|
ctx = undefined;
|
|
247
209
|
});
|
|
248
210
|
|
|
249
211
|
// ---------------------------------------------------------------------------
|
|
250
|
-
// Test
|
|
212
|
+
// Test 10: deactivate() removes signal handlers
|
|
251
213
|
// ---------------------------------------------------------------------------
|
|
252
|
-
it('
|
|
253
|
-
await
|
|
254
|
-
|
|
255
|
-
owner: 'test-agent',
|
|
256
|
-
soulMdPath: '/nonexistent/path/SOUL.md',
|
|
257
|
-
registryDbPath: ':memory:',
|
|
258
|
-
creditDbPath: ':memory:',
|
|
259
|
-
gatewayPort: 0,
|
|
260
|
-
silent: true,
|
|
261
|
-
}),
|
|
262
|
-
).rejects.toThrow();
|
|
263
|
-
});
|
|
264
|
-
|
|
265
|
-
// ---------------------------------------------------------------------------
|
|
266
|
-
// Test 9: activate() with identityRequired creates identity.json
|
|
267
|
-
// ---------------------------------------------------------------------------
|
|
268
|
-
it('activate() with identityRequired creates identity.json', async () => {
|
|
269
|
-
const soulMdPath = setupSoulMd();
|
|
270
|
-
const identityDir = mkdtempSync(join(tmpdir(), 'agentbnb-identity-'));
|
|
271
|
-
|
|
272
|
-
// Temporarily override HOME so identity writes to temp dir
|
|
273
|
-
const origHome = process.env['HOME'];
|
|
274
|
-
process.env['HOME'] = identityDir;
|
|
275
|
-
|
|
276
|
-
try {
|
|
277
|
-
ctx = await activate({
|
|
278
|
-
owner: 'identity-agent',
|
|
279
|
-
soulMdPath,
|
|
280
|
-
registryDbPath: ':memory:',
|
|
281
|
-
creditDbPath: ':memory:',
|
|
282
|
-
gatewayPort: 0,
|
|
283
|
-
silent: true,
|
|
284
|
-
identityRequired: true,
|
|
285
|
-
});
|
|
286
|
-
|
|
287
|
-
// identity should be populated
|
|
288
|
-
expect(ctx.identity).not.toBeNull();
|
|
289
|
-
expect(ctx.identity!.owner).toBe('identity-agent');
|
|
290
|
-
expect(ctx.identity!.agent_id).toBeTruthy();
|
|
291
|
-
|
|
292
|
-
// identity.json should exist on disk
|
|
293
|
-
const identityPath = join(identityDir, '.agentbnb', 'identity.json');
|
|
294
|
-
expect(existsSync(identityPath)).toBe(true);
|
|
295
|
-
|
|
296
|
-
const loaded = loadIdentity(join(identityDir, '.agentbnb'));
|
|
297
|
-
expect(loaded).not.toBeNull();
|
|
298
|
-
expect(loaded!.agent_id).toBe(ctx.identity!.agent_id);
|
|
299
|
-
} finally {
|
|
300
|
-
process.env['HOME'] = origHome;
|
|
301
|
-
rmSync(identityDir, { recursive: true, force: true });
|
|
302
|
-
}
|
|
303
|
-
});
|
|
214
|
+
it('deactivate() removes signal handlers', async () => {
|
|
215
|
+
ctx = await activate();
|
|
216
|
+
const sigtermAfterActivate = process.listenerCount('SIGTERM');
|
|
304
217
|
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
// ---------------------------------------------------------------------------
|
|
308
|
-
it('activate() without identityRequired skips identity creation', async () => {
|
|
309
|
-
const soulMdPath = setupSoulMd();
|
|
310
|
-
|
|
311
|
-
ctx = await activate({
|
|
312
|
-
owner: 'test-agent',
|
|
313
|
-
soulMdPath,
|
|
314
|
-
registryDbPath: ':memory:',
|
|
315
|
-
creditDbPath: ':memory:',
|
|
316
|
-
gatewayPort: 0,
|
|
317
|
-
silent: true,
|
|
318
|
-
identityRequired: false,
|
|
319
|
-
});
|
|
218
|
+
await deactivate(ctx);
|
|
219
|
+
ctx = undefined;
|
|
320
220
|
|
|
321
|
-
expect(
|
|
221
|
+
expect(process.listenerCount('SIGTERM')).toBeLessThan(sigtermAfterActivate);
|
|
322
222
|
});
|
|
323
223
|
});
|