@controlflow-ai/daemon 0.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 +360 -0
- package/bin/console.js +2 -0
- package/bin/daemon.js +2 -0
- package/bin/pal.js +2 -0
- package/bin/server.js +2 -0
- package/package.json +31 -0
- package/src/agent-runtime.ts +285 -0
- package/src/app.ts +745 -0
- package/src/args.ts +54 -0
- package/src/artifacts.ts +85 -0
- package/src/cli.ts +284 -0
- package/src/client.ts +310 -0
- package/src/coco.ts +52 -0
- package/src/codex.ts +41 -0
- package/src/coding-agent-runtime.ts +20 -0
- package/src/config.ts +106 -0
- package/src/console.ts +349 -0
- package/src/daemon-client.ts +91 -0
- package/src/daemon.ts +580 -0
- package/src/db.ts +2830 -0
- package/src/failure-message.ts +17 -0
- package/src/format.ts +13 -0
- package/src/http.ts +55 -0
- package/src/lark/agent-runtime.ts +142 -0
- package/src/lark/cli.ts +549 -0
- package/src/lark/credentials.ts +105 -0
- package/src/lark/daemon-integration.ts +108 -0
- package/src/lark/dispatcher.ts +374 -0
- package/src/lark/event-router.ts +329 -0
- package/src/lark/inbound-events.ts +131 -0
- package/src/lark/server-integration.ts +445 -0
- package/src/lark/setup.ts +326 -0
- package/src/lark/ws-daemon.ts +224 -0
- package/src/lark-fixture-diagnostics.ts +56 -0
- package/src/lark-fixture.ts +277 -0
- package/src/local-api.ts +155 -0
- package/src/local-auth.ts +45 -0
- package/src/migrations/001_initial.ts +61 -0
- package/src/migrations/002_daemon_deliveries.ts +52 -0
- package/src/migrations/003_sessions_runs.ts +49 -0
- package/src/migrations/004_message_idempotency.ts +21 -0
- package/src/migrations/005_artifacts.ts +24 -0
- package/src/migrations/006_lark_channel_foundation.ts +119 -0
- package/src/migrations/007_agents_a0.ts +17 -0
- package/src/migrations/008_b0_chat_history.ts +31 -0
- package/src/migrations/009_b0_transcript_ingest_seq.ts +35 -0
- package/src/migrations/010_b0_transcript_shadow_external_ids.ts +32 -0
- package/src/migrations/011_b0_channel_conversation_audit_only.ts +27 -0
- package/src/migrations/012_b0_cross_conversation_invariant.ts +45 -0
- package/src/migrations/013_b1_0_eng_inbound_raw_events.ts +56 -0
- package/src/migrations/014_agents_runtime.ts +10 -0
- package/src/migrations/015_agent_runtime_sessions.ts +15 -0
- package/src/migrations/016_room_participants.ts +27 -0
- package/src/migrations/017_unified_room_delivery.ts +203 -0
- package/src/migrations/018_room_display_names.ts +36 -0
- package/src/migrations/019_computer_connections.ts +63 -0
- package/src/migrations/020_computer_agent_assignments.ts +20 -0
- package/src/migrations/021_provider_identity_bindings.ts +32 -0
- package/src/migrations.ts +85 -0
- package/src/neeko.ts +23 -0
- package/src/provider-identity.ts +40 -0
- package/src/runtime-registry.ts +41 -0
- package/src/server-auth.ts +13 -0
- package/src/server.ts +63 -0
- package/src/token-file.ts +57 -0
- package/src/types.ts +408 -0
- package/src/web.ts +565 -0
package/src/console.ts
ADDED
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
import { createInterface } from 'node:readline';
|
|
3
|
+
import { boolFlag, parseArgs, flag, numberFlag } from './args.js';
|
|
4
|
+
import { LockClient } from './client.js';
|
|
5
|
+
import { defaultServerUrl } from './config.js';
|
|
6
|
+
import { formatMessages } from './format.js';
|
|
7
|
+
import type { Computer } from './types.js';
|
|
8
|
+
|
|
9
|
+
interface Prompt {
|
|
10
|
+
askLine(label: string): Promise<string>;
|
|
11
|
+
close(): void;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function usage(): string {
|
|
15
|
+
return `pal console (admin CLI)
|
|
16
|
+
|
|
17
|
+
Usage:
|
|
18
|
+
bun run src/console.ts health
|
|
19
|
+
bun run src/console.ts chats [--json]
|
|
20
|
+
bun run src/console.ts messages [--chat general] [--parent 1] [--after 0] [--limit 50] [--q text] [--json]
|
|
21
|
+
bun run src/console.ts inbox --agent neeko [--after 0] [--limit 50] [--json]
|
|
22
|
+
bun run src/console.ts runs [--json]
|
|
23
|
+
bun run src/console.ts run-action <run-id> kill|restart
|
|
24
|
+
bun run src/console.ts agents list [--json]
|
|
25
|
+
bun run src/console.ts agents onboard [--interactive] [--key <agent-key>] [--name <display-name>] [--runtime codex] [--computer-id <machine>]
|
|
26
|
+
bun run src/console.ts agents create --key <agent-key> --name <display-name> [--runtime neeko|coco|coco-stream-json|codex] [--desc <description>]
|
|
27
|
+
bun run src/console.ts agents update --key <agent-key> --runtime neeko|coco|coco-stream-json|codex
|
|
28
|
+
bun run src/console.ts lark <setup|list|daemon|events|send> [flags]
|
|
29
|
+
|
|
30
|
+
Environment:
|
|
31
|
+
PAL_SERVER=${defaultServerUrl()}
|
|
32
|
+
`;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function printJson(value: unknown): void {
|
|
36
|
+
console.log(JSON.stringify(value, null, 2));
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
async function createPrompt(): Promise<Prompt> {
|
|
40
|
+
if (process.stdin.isTTY !== true) {
|
|
41
|
+
const lines = (await Bun.stdin.text()).split(/\r?\n/);
|
|
42
|
+
return {
|
|
43
|
+
askLine(label: string) {
|
|
44
|
+
process.stdout.write(label);
|
|
45
|
+
return Promise.resolve(lines.shift() ?? '');
|
|
46
|
+
},
|
|
47
|
+
close() {},
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
52
|
+
return {
|
|
53
|
+
askLine(label: string) {
|
|
54
|
+
return new Promise<string>((resolve) => rl.question(label, resolve));
|
|
55
|
+
},
|
|
56
|
+
close() {
|
|
57
|
+
rl.close();
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
async function ask(prompt: Prompt, label: string, defaultValue = ''): Promise<string> {
|
|
63
|
+
const suffix = defaultValue ? ` [${defaultValue}]` : '';
|
|
64
|
+
const answer = await prompt.askLine(`${label}${suffix}: `);
|
|
65
|
+
return answer.trim() || defaultValue;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
async function askRequired(prompt: Prompt, label: string, defaultValue = ''): Promise<string> {
|
|
69
|
+
while (true) {
|
|
70
|
+
const answer = await ask(prompt, label, defaultValue);
|
|
71
|
+
if (answer) return answer;
|
|
72
|
+
console.log(`${label} is required.`);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
async function askRuntime(prompt: Prompt, defaultValue: string): Promise<string> {
|
|
77
|
+
const runtimes = ['codex', 'neeko', 'coco', 'coco-stream-json'];
|
|
78
|
+
const fallback = runtimes.includes(defaultValue) ? defaultValue : 'codex';
|
|
79
|
+
console.log('Runtime:');
|
|
80
|
+
runtimes.forEach((runtime, index) => {
|
|
81
|
+
const marker = runtime === fallback ? ' (default)' : '';
|
|
82
|
+
console.log(` ${index + 1}. ${runtime}${marker}`);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
while (true) {
|
|
86
|
+
const answer = await ask(prompt, 'Choose runtime', fallback);
|
|
87
|
+
const selected = runtimes[Number(answer) - 1] ?? answer;
|
|
88
|
+
if (runtimes.includes(selected)) return selected;
|
|
89
|
+
console.log(`Runtime must be one of: ${runtimes.join(', ')}`);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
async function askYesNo(prompt: Prompt, label: string, defaultValue = false): Promise<boolean> {
|
|
94
|
+
const fallback = defaultValue ? 'Y/n' : 'y/N';
|
|
95
|
+
while (true) {
|
|
96
|
+
const answer = (await ask(prompt, label, fallback)).toLowerCase();
|
|
97
|
+
if (answer === fallback.toLowerCase()) return defaultValue;
|
|
98
|
+
if (answer === 'y' || answer === 'yes') return true;
|
|
99
|
+
if (answer === 'n' || answer === 'no') return false;
|
|
100
|
+
console.log('Answer yes or no.');
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
async function askComputerId(prompt: Prompt, computers: Computer[]): Promise<string> {
|
|
105
|
+
if (computers.length === 0) {
|
|
106
|
+
throw new Error('no computers are configured. Set up a computer first in the web UI Provision Computer panel or via POST /api/computers/provision');
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
console.log('Computers:');
|
|
110
|
+
computers.forEach((computer, index) => {
|
|
111
|
+
const seen = computer.last_seen_at ? ` last_seen=${computer.last_seen_at}` : '';
|
|
112
|
+
console.log(` ${index + 1}. ${computer.name} (${computer.id}) status=${computer.status}${seen}`);
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
while (true) {
|
|
116
|
+
const answer = await ask(prompt, 'Choose computer', computers.length === 1 ? '1' : '');
|
|
117
|
+
const selected = computers[Number(answer) - 1] ?? computers.find((computer) => computer.id === answer);
|
|
118
|
+
if (selected) return selected.id;
|
|
119
|
+
console.log('Choose a listed number or computer id.');
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
async function collectOnboardInput(serverClient: LockClient, flags: Record<string, string | boolean>): Promise<{ agentKey: string; displayName: string; runtime: string; desc: string | null; computerId: string | undefined }> {
|
|
124
|
+
const hasRequiredFlags = Boolean(flag(flags, 'key') && flag(flags, 'name'));
|
|
125
|
+
const interactive = boolFlag(flags, 'interactive') || (!hasRequiredFlags && process.stdin.isTTY === true);
|
|
126
|
+
if (!interactive) {
|
|
127
|
+
if (!hasRequiredFlags) throw new Error('agents onboard requires --key and --name when not running interactively');
|
|
128
|
+
return {
|
|
129
|
+
agentKey: flag(flags, 'key')!,
|
|
130
|
+
displayName: flag(flags, 'name')!,
|
|
131
|
+
runtime: flag(flags, 'runtime') ?? 'codex',
|
|
132
|
+
desc: flag(flags, 'desc') ?? null,
|
|
133
|
+
computerId: flag(flags, 'computer-id'),
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const prompt = await createPrompt();
|
|
138
|
+
try {
|
|
139
|
+
console.log('Agent onboarding');
|
|
140
|
+
console.log('Create or update a logical agent, optionally assigning it to a computer.');
|
|
141
|
+
const agentKey = await askRequired(prompt, 'Agent key', flag(flags, 'key') ?? 'codex');
|
|
142
|
+
const displayName = await askRequired(prompt, 'Display name', flag(flags, 'name') ?? agentKey);
|
|
143
|
+
const runtime = await askRuntime(prompt, flag(flags, 'runtime') ?? 'codex');
|
|
144
|
+
const desc = await ask(prompt, 'Description (optional)', flag(flags, 'desc') ?? '');
|
|
145
|
+
const shouldAssign = Boolean(flag(flags, 'computer-id')) || await askYesNo(prompt, 'Assign this agent to a computer?', false);
|
|
146
|
+
const computerId = shouldAssign
|
|
147
|
+
? flag(flags, 'computer-id') ?? await askComputerId(prompt, await serverClient.listComputers())
|
|
148
|
+
: '';
|
|
149
|
+
console.log('');
|
|
150
|
+
console.log('Summary:');
|
|
151
|
+
console.log(` agent_key: ${agentKey}`);
|
|
152
|
+
console.log(` display_name: ${displayName}`);
|
|
153
|
+
console.log(` runtime: ${runtime}`);
|
|
154
|
+
console.log(` description: ${desc || '-'}`);
|
|
155
|
+
console.log(` computer_id: ${computerId || '-'}`);
|
|
156
|
+
if (!await askYesNo(prompt, 'Create/update this agent?', true)) throw new Error('onboarding cancelled');
|
|
157
|
+
return {
|
|
158
|
+
agentKey,
|
|
159
|
+
displayName,
|
|
160
|
+
runtime,
|
|
161
|
+
desc: desc || null,
|
|
162
|
+
computerId: computerId || undefined,
|
|
163
|
+
};
|
|
164
|
+
} finally {
|
|
165
|
+
prompt.close();
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
async function main(): Promise<void> {
|
|
170
|
+
const args = parseArgs();
|
|
171
|
+
const serverClient = new LockClient(flag(args.flags, 'server'));
|
|
172
|
+
|
|
173
|
+
if (args.command === 'help' || args.flags.help) {
|
|
174
|
+
console.log(usage());
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
if (args.command === 'health') {
|
|
179
|
+
printJson(await serverClient.health());
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
if (args.command === 'chats') {
|
|
184
|
+
const chats = await serverClient.listChats();
|
|
185
|
+
if (args.flags.json) {
|
|
186
|
+
printJson(chats);
|
|
187
|
+
} else {
|
|
188
|
+
for (const chat of chats) {
|
|
189
|
+
console.log(`#${chat.name} messages=${chat.message_count} last=${chat.last_message_at ?? '-'}`);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
if (args.command === 'runs') {
|
|
196
|
+
const runs = await serverClient.listRuns();
|
|
197
|
+
if (args.flags.json) {
|
|
198
|
+
printJson(runs);
|
|
199
|
+
} else {
|
|
200
|
+
for (const run of runs) {
|
|
201
|
+
console.log(`${run.id} status=${run.status} agent=${run.agent} message=${run.message_id} attempt=${run.attempt} pid=${run.pid ?? '-'}`);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
if (args.command === 'run-action') {
|
|
208
|
+
const [runId, action] = args.values;
|
|
209
|
+
if (!runId) throw new Error('run id is required');
|
|
210
|
+
if (action !== 'kill' && action !== 'restart') throw new Error('action must be kill or restart');
|
|
211
|
+
const run = await serverClient.requestRunAction(runId, action);
|
|
212
|
+
printJson(run);
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
if (args.command === 'messages') {
|
|
217
|
+
const params = new URLSearchParams();
|
|
218
|
+
const chat = flag(args.flags, 'chat');
|
|
219
|
+
const q = flag(args.flags, 'q');
|
|
220
|
+
if (chat) params.set('chat', chat);
|
|
221
|
+
if (q) params.set('q', q);
|
|
222
|
+
const parent = numberFlag(args.flags, 'parent');
|
|
223
|
+
const after = numberFlag(args.flags, 'after');
|
|
224
|
+
const limit = numberFlag(args.flags, 'limit', 50);
|
|
225
|
+
if (parent !== undefined) params.set('parent_id', String(parent));
|
|
226
|
+
if (after !== undefined) params.set('after', String(after));
|
|
227
|
+
if (limit !== undefined) params.set('limit', String(limit));
|
|
228
|
+
|
|
229
|
+
const messages = await serverClient.getMessages(params);
|
|
230
|
+
if (args.flags.json) {
|
|
231
|
+
printJson(messages);
|
|
232
|
+
} else {
|
|
233
|
+
console.log(formatMessages(messages));
|
|
234
|
+
}
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
if (args.command === 'inbox') {
|
|
239
|
+
const agent = flag(args.flags, 'agent');
|
|
240
|
+
if (!agent) throw new Error('--agent is required');
|
|
241
|
+
const messages = await serverClient.getInbox(agent, numberFlag(args.flags, 'after', 0), numberFlag(args.flags, 'limit', 50));
|
|
242
|
+
if (args.flags.json) {
|
|
243
|
+
printJson(messages);
|
|
244
|
+
} else {
|
|
245
|
+
console.log(formatMessages(messages));
|
|
246
|
+
}
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
if (args.command === 'agents') {
|
|
251
|
+
const sub = args.values[0];
|
|
252
|
+
|
|
253
|
+
if (sub === 'list') {
|
|
254
|
+
const response = await fetch(`${serverClient.baseUrl}/api/agents`);
|
|
255
|
+
const payload = await response.json() as { data?: { agents: Array<Record<string, unknown>> }; message?: string };
|
|
256
|
+
if (!response.ok) throw new Error(payload.message ?? `list failed: ${response.status}`);
|
|
257
|
+
const agents = payload.data?.agents ?? [];
|
|
258
|
+
if (args.flags.json) {
|
|
259
|
+
printJson(agents);
|
|
260
|
+
} else {
|
|
261
|
+
for (const agent of agents) {
|
|
262
|
+
console.log(`${agent.agent_key} name="${agent.display_name}" runtime=${agent.runtime ?? '-'} desc=${agent.description ?? '-'}`);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
if (sub === 'create') {
|
|
269
|
+
const agentKey = flag(args.flags, 'key');
|
|
270
|
+
const displayName = flag(args.flags, 'name');
|
|
271
|
+
const runtime = flag(args.flags, 'runtime');
|
|
272
|
+
const desc = flag(args.flags, 'desc');
|
|
273
|
+
if (!agentKey) throw new Error('--key is required');
|
|
274
|
+
if (!displayName) throw new Error('--name is required');
|
|
275
|
+
|
|
276
|
+
const response = await fetch(`${serverClient.baseUrl}/api/agents`, {
|
|
277
|
+
method: 'POST',
|
|
278
|
+
headers: { 'content-type': 'application/json' },
|
|
279
|
+
body: JSON.stringify({
|
|
280
|
+
agent_key: agentKey,
|
|
281
|
+
display_name: displayName,
|
|
282
|
+
runtime: runtime ?? null,
|
|
283
|
+
description: desc ?? null,
|
|
284
|
+
}),
|
|
285
|
+
});
|
|
286
|
+
const payload = await response.json() as { data?: { agent: Record<string, unknown> }; message?: string };
|
|
287
|
+
if (!response.ok) throw new Error(payload.message ?? `create failed: ${response.status}`);
|
|
288
|
+
printJson(payload.data?.agent);
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
if (sub === 'onboard') {
|
|
293
|
+
const { agentKey, displayName, runtime, desc, computerId } = await collectOnboardInput(serverClient, args.flags);
|
|
294
|
+
if (!agentKey) throw new Error('agent key is required');
|
|
295
|
+
if (!displayName) throw new Error('display name is required');
|
|
296
|
+
|
|
297
|
+
const response = await fetch(`${serverClient.baseUrl}/api/agents/onboard`, {
|
|
298
|
+
method: 'POST',
|
|
299
|
+
headers: { 'content-type': 'application/json' },
|
|
300
|
+
body: JSON.stringify({
|
|
301
|
+
agent_key: agentKey,
|
|
302
|
+
display_name: displayName,
|
|
303
|
+
runtime,
|
|
304
|
+
description: desc,
|
|
305
|
+
computer_id: computerId,
|
|
306
|
+
}),
|
|
307
|
+
});
|
|
308
|
+
const payload = await response.json() as { data?: Record<string, unknown>; message?: string };
|
|
309
|
+
if (!response.ok) throw new Error(payload.message ?? `onboard failed: ${response.status}`);
|
|
310
|
+
printJson(payload.data);
|
|
311
|
+
return;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
if (sub === 'update') {
|
|
315
|
+
const agentKey = flag(args.flags, 'key');
|
|
316
|
+
const runtime = flag(args.flags, 'runtime');
|
|
317
|
+
if (!agentKey) throw new Error('--key is required');
|
|
318
|
+
if (!runtime) throw new Error('--runtime is required');
|
|
319
|
+
|
|
320
|
+
const response = await fetch(`${serverClient.baseUrl}/api/agents/${encodeURIComponent(agentKey)}`, {
|
|
321
|
+
method: 'PATCH',
|
|
322
|
+
headers: { 'content-type': 'application/json' },
|
|
323
|
+
body: JSON.stringify({ runtime }),
|
|
324
|
+
});
|
|
325
|
+
const payload = await response.json() as { data?: { agent: Record<string, unknown> }; message?: string };
|
|
326
|
+
if (!response.ok) throw new Error(payload.message ?? `update failed: ${response.status}`);
|
|
327
|
+
printJson(payload.data?.agent);
|
|
328
|
+
return;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
throw new Error(`unknown agents subcommand: ${sub ?? '(none)'}\n\n${usage()}`);
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
if (args.command === 'lark') {
|
|
335
|
+
const { runLarkCli } = await import('./lark/cli.js');
|
|
336
|
+
const code = await runLarkCli({
|
|
337
|
+
argv: { command: args.command, values: args.values, flags: args.flags },
|
|
338
|
+
});
|
|
339
|
+
if (code !== 0) process.exit(code);
|
|
340
|
+
return;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
throw new Error(`unknown command: ${args.command}\n\n${usage()}`);
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
main().catch((error) => {
|
|
347
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
348
|
+
process.exit(1);
|
|
349
|
+
});
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { defaultDaemonToken, defaultDaemonUrl } from './config.js';
|
|
2
|
+
import { localHeaders } from './local-auth.js';
|
|
3
|
+
import type { AgentRoomSubscriptionMode, AgentRun, ApiResponse, Chat, Message, RoomChannel, RoomParticipant, RunAction } from './types.js';
|
|
4
|
+
import type { CreateArtifactRequest, SendRequest } from './client.js';
|
|
5
|
+
|
|
6
|
+
export class DaemonClient {
|
|
7
|
+
readonly baseUrl: string;
|
|
8
|
+
readonly token: string | undefined;
|
|
9
|
+
|
|
10
|
+
constructor(baseUrl = defaultDaemonUrl(), token = defaultDaemonToken()) {
|
|
11
|
+
this.baseUrl = baseUrl.replace(/\/$/, '');
|
|
12
|
+
this.token = token;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
async health(): Promise<{ status: string; mode: string }> {
|
|
16
|
+
return this.get('/local/health');
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
async sendMessage(input: SendRequest): Promise<Message> {
|
|
20
|
+
const data = await this.post<{ message: Message }>('/local/messages', input);
|
|
21
|
+
return data.message;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
async getMessages(params: URLSearchParams): Promise<Message[]> {
|
|
25
|
+
const data = await this.get<{ messages: Message[] }>(`/local/messages?${params}`);
|
|
26
|
+
return data.messages;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
async listChats(): Promise<Chat[]> {
|
|
30
|
+
const data = await this.get<{ chats: Chat[] }>('/local/chats');
|
|
31
|
+
return data.chats;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
async listRooms(): Promise<Chat[]> {
|
|
35
|
+
const data = await this.get<{ rooms: Chat[] }>('/local/rooms');
|
|
36
|
+
return data.rooms;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
async listRoomMembers(room: string): Promise<{ room: Chat; participants: RoomParticipant[]; completeness: string }> {
|
|
40
|
+
return this.get<{ room: Chat; participants: RoomParticipant[]; completeness: string }>(`/local/rooms/${encodeURIComponent(room)}/members`);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
async inviteAgentToRoom(room: string, input: { agent: string; mode?: AgentRoomSubscriptionMode }): Promise<unknown> {
|
|
44
|
+
return this.post<unknown>(`/local/rooms/${encodeURIComponent(room)}/agents`, input);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
async createTopic(room: string, input: { name: string; created_by?: string | null }): Promise<RoomChannel> {
|
|
48
|
+
const data = await this.post<{ channel: RoomChannel }>(`/local/rooms/${encodeURIComponent(room)}/topics`, input);
|
|
49
|
+
return data.channel;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
async getMessage(id: number): Promise<Message> {
|
|
53
|
+
const data = await this.get<{ message: Message }>(`/local/messages/${id}`);
|
|
54
|
+
return data.message;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
async listRuns(): Promise<AgentRun[]> {
|
|
58
|
+
const data = await this.get<{ runs: AgentRun[] }>('/local/runs');
|
|
59
|
+
return data.runs;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
async createArtifact(input: CreateArtifactRequest): Promise<{ artifact: unknown; token: string; url: string }> {
|
|
63
|
+
return this.post<{ artifact: unknown; token: string; url: string }>('/local/artifacts', input);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
async requestRunAction(runId: string, action: RunAction): Promise<AgentRun> {
|
|
67
|
+
const data = await this.post<{ run: AgentRun }>(`/local/runs/${runId}/${action}`, {});
|
|
68
|
+
return data.run;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
private async get<T>(path: string): Promise<T> {
|
|
72
|
+
return this.request<T>(path, { method: 'GET', headers: localHeaders(this.token) });
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
private async post<T>(path: string, body: unknown): Promise<T> {
|
|
76
|
+
return this.request<T>(path, {
|
|
77
|
+
method: 'POST',
|
|
78
|
+
headers: { 'content-type': 'application/json', ...localHeaders(this.token) },
|
|
79
|
+
body: JSON.stringify(body),
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
private async request<T>(path: string, init: RequestInit): Promise<T> {
|
|
84
|
+
const response = await fetch(`${this.baseUrl}${path}`, init);
|
|
85
|
+
const payload = await response.json() as ApiResponse<T>;
|
|
86
|
+
if (!response.ok || !payload.ok) {
|
|
87
|
+
throw new Error(payload.message ?? `request failed: ${response.status}`);
|
|
88
|
+
}
|
|
89
|
+
return payload.data as T;
|
|
90
|
+
}
|
|
91
|
+
}
|