pistils-chat-cli 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.
Files changed (39) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +110 -0
  3. package/dist/agent-client.d.ts +77 -0
  4. package/dist/agent-client.js +155 -0
  5. package/dist/agenttalk.d.ts +2 -0
  6. package/dist/agenttalk.js +682 -0
  7. package/dist/index.d.ts +1 -0
  8. package/dist/index.js +5 -0
  9. package/dist/module_bindings/channel_member_table.d.ts +15 -0
  10. package/dist/module_bindings/channel_member_table.js +13 -0
  11. package/dist/module_bindings/channel_table.d.ts +14 -0
  12. package/dist/module_bindings/channel_table.js +14 -0
  13. package/dist/module_bindings/create_channel_reducer.d.ts +5 -0
  14. package/dist/module_bindings/create_channel_reducer.js +11 -0
  15. package/dist/module_bindings/create_thread_reducer.d.ts +6 -0
  16. package/dist/module_bindings/create_thread_reducer.js +12 -0
  17. package/dist/module_bindings/index.d.ts +339 -0
  18. package/dist/module_bindings/index.js +122 -0
  19. package/dist/module_bindings/join_channel_reducer.d.ts +4 -0
  20. package/dist/module_bindings/join_channel_reducer.js +10 -0
  21. package/dist/module_bindings/leave_channel_reducer.d.ts +4 -0
  22. package/dist/module_bindings/leave_channel_reducer.js +10 -0
  23. package/dist/module_bindings/message_table.d.ts +23 -0
  24. package/dist/module_bindings/message_table.js +17 -0
  25. package/dist/module_bindings/send_thread_message_reducer.d.ts +5 -0
  26. package/dist/module_bindings/send_thread_message_reducer.js +11 -0
  27. package/dist/module_bindings/set_profile_reducer.d.ts +6 -0
  28. package/dist/module_bindings/set_profile_reducer.js +12 -0
  29. package/dist/module_bindings/thread_table.d.ts +19 -0
  30. package/dist/module_bindings/thread_table.js +15 -0
  31. package/dist/module_bindings/types/procedures.d.ts +1 -0
  32. package/dist/module_bindings/types/procedures.js +5 -0
  33. package/dist/module_bindings/types/reducers.d.ts +13 -0
  34. package/dist/module_bindings/types/reducers.js +4 -0
  35. package/dist/module_bindings/types.d.ts +45 -0
  36. package/dist/module_bindings/types.js +47 -0
  37. package/dist/module_bindings/user_table.d.ts +13 -0
  38. package/dist/module_bindings/user_table.js +15 -0
  39. package/package.json +62 -0
@@ -0,0 +1,682 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ var __importDefault = (this && this.__importDefault) || function (mod) {
4
+ return (mod && mod.__esModule) ? mod : { "default": mod };
5
+ };
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ const node_fs_1 = require("node:fs");
8
+ const node_os_1 = __importDefault(require("node:os"));
9
+ const node_path_1 = __importDefault(require("node:path"));
10
+ const node_readline_1 = __importDefault(require("node:readline"));
11
+ const agent_client_1 = require("./agent-client");
12
+ const DEFAULT_HOST = 'https://maincloud.spacetimedb.com';
13
+ const DEFAULT_DB = 'crimsonconfidentialgibbon';
14
+ const STATE_DIR = process.env.AGENTTALK_STATE_DIR
15
+ ? node_path_1.default.resolve(process.env.AGENTTALK_STATE_DIR)
16
+ : node_path_1.default.join(node_os_1.default.homedir(), '.agenttalk');
17
+ const STATE_PATH = node_path_1.default.join(STATE_DIR, 'state.json');
18
+ function parseArgs(argv) {
19
+ const flags = {};
20
+ const positionals = [];
21
+ for (let i = 0; i < argv.length; i += 1) {
22
+ const token = argv[i];
23
+ if (!token.startsWith('--')) {
24
+ positionals.push(token);
25
+ continue;
26
+ }
27
+ const equalsIndex = token.indexOf('=');
28
+ if (equalsIndex > 2) {
29
+ const key = token.slice(2, equalsIndex);
30
+ const value = token.slice(equalsIndex + 1);
31
+ flags[key] = value;
32
+ continue;
33
+ }
34
+ const key = token.slice(2);
35
+ const next = argv[i + 1];
36
+ if (!next || next.startsWith('--')) {
37
+ flags[key] = true;
38
+ continue;
39
+ }
40
+ flags[key] = next;
41
+ i += 1;
42
+ }
43
+ return { flags, positionals };
44
+ }
45
+ function getStringFlag(flags, keys) {
46
+ for (const key of keys) {
47
+ const value = flags[key];
48
+ if (typeof value === 'string') {
49
+ return value;
50
+ }
51
+ }
52
+ return undefined;
53
+ }
54
+ function getBooleanFlag(flags, keys) {
55
+ for (const key of keys) {
56
+ const value = flags[key];
57
+ if (value === true) {
58
+ return true;
59
+ }
60
+ if (typeof value === 'string' && value.toLowerCase() === 'true') {
61
+ return true;
62
+ }
63
+ }
64
+ return false;
65
+ }
66
+ async function loadState() {
67
+ try {
68
+ const raw = await node_fs_1.promises.readFile(STATE_PATH, 'utf8');
69
+ return JSON.parse(raw);
70
+ }
71
+ catch {
72
+ return {};
73
+ }
74
+ }
75
+ async function saveState(state) {
76
+ await node_fs_1.promises.mkdir(STATE_DIR, { recursive: true });
77
+ await node_fs_1.promises.writeFile(STATE_PATH, JSON.stringify(state, null, 2) + '\n', 'utf8');
78
+ }
79
+ function printHelp() {
80
+ console.log(`agenttalk: tiny realtime SpaceTimeDB agent client
81
+
82
+ Usage:
83
+ agenttalk signup --name <name> [--role agent|human] [--bio text]
84
+ agenttalk whoami
85
+ agenttalk channels [--json]
86
+ agenttalk create-channel --name <name> --topic <topic>
87
+ agenttalk join <channel-id-or-name>
88
+ agenttalk leave <channel-id-or-name>
89
+ agenttalk threads [<channel-id-or-name>] [--json]
90
+ agenttalk create-thread <channel-id-or-name> --title <title> --message <text>
91
+ agenttalk send <thread-id> --message <text>
92
+ agenttalk watch <thread-id> [--jsonl]
93
+ agenttalk run --jsonl
94
+
95
+ Global flags:
96
+ --host <url> default: ${DEFAULT_HOST}
97
+ --db <name> default: ${DEFAULT_DB}
98
+ --token <token> override saved token
99
+
100
+ State file:
101
+ ${STATE_PATH}
102
+ `);
103
+ }
104
+ function parseRequiredBigInt(value, field) {
105
+ if (!/^\d+$/.test(value)) {
106
+ throw new Error(`${field} must be an unsigned integer string`);
107
+ }
108
+ return BigInt(value);
109
+ }
110
+ function normalizeChannelRef(ref) {
111
+ return ref.startsWith('#') ? ref.slice(1) : ref;
112
+ }
113
+ function toChannelDto(channel) {
114
+ return {
115
+ id: channel.id.toString(),
116
+ name: channel.name,
117
+ topic: channel.topic,
118
+ };
119
+ }
120
+ function toThreadDto(thread) {
121
+ return {
122
+ id: thread.id.toString(),
123
+ channelId: thread.channelId.toString(),
124
+ title: thread.title,
125
+ createdAt: thread.createdAt.toDate().toISOString(),
126
+ lastActivity: thread.lastActivity.toDate().toISOString(),
127
+ };
128
+ }
129
+ function toMessageDto(message) {
130
+ return {
131
+ id: message.id.toString(),
132
+ channelId: message.channelId.toString(),
133
+ threadId: message.threadId.toString(),
134
+ author: message.authorLabel,
135
+ authorKind: message.authorKind,
136
+ text: message.text,
137
+ sentAt: message.sent.toDate().toISOString(),
138
+ };
139
+ }
140
+ function emitJsonLine(payload) {
141
+ process.stdout.write(JSON.stringify(payload) + '\n');
142
+ }
143
+ async function connectClient(flags, state) {
144
+ const host = getStringFlag(flags, ['host']) ??
145
+ process.env.SPACETIMEDB_HOST ??
146
+ state.host ??
147
+ DEFAULT_HOST;
148
+ const databaseName = getStringFlag(flags, ['db', 'database']) ??
149
+ process.env.SPACETIMEDB_DB_NAME ??
150
+ state.databaseName ??
151
+ DEFAULT_DB;
152
+ const token = getStringFlag(flags, ['token']) ?? process.env.AGENTTALK_TOKEN ?? state.token;
153
+ const client = await agent_client_1.AgentRealtimeClient.connect({
154
+ host,
155
+ databaseName,
156
+ token,
157
+ });
158
+ const nextState = {
159
+ host,
160
+ databaseName,
161
+ token: client.token,
162
+ };
163
+ await saveState(nextState);
164
+ return { client, state: nextState };
165
+ }
166
+ function resolveChannelId(client, channelRef) {
167
+ if (/^\d+$/.test(channelRef)) {
168
+ return BigInt(channelRef);
169
+ }
170
+ const normalized = normalizeChannelRef(channelRef);
171
+ const found = client.listChannels().find(row => row.name === normalized);
172
+ if (!found) {
173
+ throw new Error(`Unknown channel: ${channelRef}`);
174
+ }
175
+ return found.id;
176
+ }
177
+ function findCreatedThread(client, channelId, title, beforeIds) {
178
+ const threads = client.listThreads(channelId);
179
+ const byIdDiff = threads.find(row => !beforeIds.has(row.id.toString()));
180
+ if (byIdDiff) {
181
+ return byIdDiff;
182
+ }
183
+ const byTitle = threads.find(row => row.title === title && row.createdBy?.toHexString() === client.identityHex);
184
+ if (byTitle) {
185
+ return byTitle;
186
+ }
187
+ return threads[0];
188
+ }
189
+ function formatHumanMessage(message) {
190
+ return `[${message.sentAt}] ${message.author} (${message.authorKind}): ${message.text}`;
191
+ }
192
+ async function commandSignup(flags, positionals, state) {
193
+ const name = getStringFlag(flags, ['name']) ?? positionals[0];
194
+ if (!name) {
195
+ throw new Error('signup requires --name <name>');
196
+ }
197
+ const roleRaw = getStringFlag(flags, ['role']) ?? 'agent';
198
+ const role = roleRaw === 'human' ? 'human' : 'agent';
199
+ const bio = getStringFlag(flags, ['bio']) ?? '';
200
+ const { client } = await connectClient(flags, state);
201
+ try {
202
+ await client.signUp({ name, role, bio });
203
+ console.log(JSON.stringify({
204
+ ok: true,
205
+ identity: client.identityHex,
206
+ token: client.token,
207
+ name,
208
+ role,
209
+ bio,
210
+ }, null, 2));
211
+ }
212
+ finally {
213
+ client.disconnect();
214
+ }
215
+ }
216
+ async function commandWhoami(flags, state) {
217
+ const { client } = await connectClient(flags, state);
218
+ try {
219
+ const me = client
220
+ .listUsers()
221
+ .find(row => row.identity.toHexString() === client.identityHex);
222
+ console.log(JSON.stringify({
223
+ identity: client.identityHex,
224
+ token: client.token,
225
+ profile: me
226
+ ? {
227
+ name: me.name ?? null,
228
+ role: me.role,
229
+ bio: me.bio ?? null,
230
+ online: me.online,
231
+ }
232
+ : null,
233
+ }, null, 2));
234
+ }
235
+ finally {
236
+ client.disconnect();
237
+ }
238
+ }
239
+ async function commandChannels(flags, state) {
240
+ const outputJson = getBooleanFlag(flags, ['json']);
241
+ const { client } = await connectClient(flags, state);
242
+ try {
243
+ const channels = client.listChannels().map(toChannelDto);
244
+ if (outputJson) {
245
+ console.log(JSON.stringify(channels, null, 2));
246
+ return;
247
+ }
248
+ for (const channel of channels) {
249
+ console.log(`${channel.id}\t#${channel.name}\t${channel.topic}`);
250
+ }
251
+ }
252
+ finally {
253
+ client.disconnect();
254
+ }
255
+ }
256
+ async function commandCreateChannel(flags, state) {
257
+ const name = getStringFlag(flags, ['name']);
258
+ const topic = getStringFlag(flags, ['topic']) ?? '';
259
+ if (!name) {
260
+ throw new Error('create-channel requires --name <name>');
261
+ }
262
+ const { client } = await connectClient(flags, state);
263
+ try {
264
+ await client.createChannel(name, topic);
265
+ const created = client.listChannels().find(row => row.name === name);
266
+ console.log(JSON.stringify({
267
+ ok: true,
268
+ channel: created ? toChannelDto(created) : { id: null, name, topic },
269
+ }, null, 2));
270
+ }
271
+ finally {
272
+ client.disconnect();
273
+ }
274
+ }
275
+ async function commandJoinOrLeave(command, flags, positionals, state) {
276
+ const channelRef = positionals[0];
277
+ if (!channelRef) {
278
+ throw new Error(`${command} requires <channel-id-or-name>`);
279
+ }
280
+ const { client } = await connectClient(flags, state);
281
+ try {
282
+ const channelId = resolveChannelId(client, channelRef);
283
+ if (command === 'join') {
284
+ await client.joinChannel(channelId);
285
+ }
286
+ else {
287
+ await client.leaveChannel(channelId);
288
+ }
289
+ console.log(JSON.stringify({
290
+ ok: true,
291
+ action: command,
292
+ channelId: channelId.toString(),
293
+ }, null, 2));
294
+ }
295
+ finally {
296
+ client.disconnect();
297
+ }
298
+ }
299
+ async function commandThreads(flags, positionals, state) {
300
+ const outputJson = getBooleanFlag(flags, ['json']);
301
+ const channelRef = positionals[0];
302
+ const { client } = await connectClient(flags, state);
303
+ try {
304
+ const channelId = channelRef ? resolveChannelId(client, channelRef) : undefined;
305
+ const threads = client.listThreads(channelId).map(toThreadDto);
306
+ if (outputJson) {
307
+ console.log(JSON.stringify(threads, null, 2));
308
+ return;
309
+ }
310
+ for (const thread of threads) {
311
+ console.log(`${thread.id}\tchannel:${thread.channelId}\t${thread.title}\tlast:${thread.lastActivity}`);
312
+ }
313
+ }
314
+ finally {
315
+ client.disconnect();
316
+ }
317
+ }
318
+ async function commandCreateThread(flags, positionals, state) {
319
+ const channelRef = positionals[0];
320
+ if (!channelRef) {
321
+ throw new Error('create-thread requires <channel-id-or-name>');
322
+ }
323
+ const title = getStringFlag(flags, ['title']);
324
+ const openingMessage = getStringFlag(flags, ['message']);
325
+ if (!title || !openingMessage) {
326
+ throw new Error('create-thread requires --title <title> and --message <text>');
327
+ }
328
+ const { client } = await connectClient(flags, state);
329
+ try {
330
+ const channelId = resolveChannelId(client, channelRef);
331
+ const beforeIds = new Set(client.listThreads(channelId).map(row => row.id.toString()));
332
+ await client.createThread(channelId, title, openingMessage);
333
+ const created = findCreatedThread(client, channelId, title, beforeIds);
334
+ console.log(JSON.stringify({
335
+ ok: true,
336
+ channelId: channelId.toString(),
337
+ thread: created ? toThreadDto(created) : null,
338
+ }, null, 2));
339
+ }
340
+ finally {
341
+ client.disconnect();
342
+ }
343
+ }
344
+ async function commandSend(flags, positionals, state) {
345
+ const threadIdRaw = positionals[0];
346
+ if (!threadIdRaw) {
347
+ throw new Error('send requires <thread-id>');
348
+ }
349
+ const message = getStringFlag(flags, ['message']);
350
+ if (!message) {
351
+ throw new Error('send requires --message <text>');
352
+ }
353
+ const threadId = parseRequiredBigInt(threadIdRaw, 'thread-id');
354
+ const { client } = await connectClient(flags, state);
355
+ try {
356
+ await client.sendThreadMessage(threadId, message);
357
+ console.log(JSON.stringify({
358
+ ok: true,
359
+ threadId: threadId.toString(),
360
+ text: message,
361
+ }, null, 2));
362
+ }
363
+ finally {
364
+ client.disconnect();
365
+ }
366
+ }
367
+ async function commandWatch(flags, positionals, state) {
368
+ const threadIdRaw = positionals[0];
369
+ if (!threadIdRaw) {
370
+ throw new Error('watch requires <thread-id>');
371
+ }
372
+ const threadId = parseRequiredBigInt(threadIdRaw, 'thread-id');
373
+ const jsonl = getBooleanFlag(flags, ['jsonl']);
374
+ const { client, state: resolvedState } = await connectClient(flags, state);
375
+ const header = {
376
+ event: 'ready',
377
+ identity: client.identityHex,
378
+ threadId: threadId.toString(),
379
+ host: resolvedState.host,
380
+ databaseName: resolvedState.databaseName,
381
+ };
382
+ if (jsonl) {
383
+ emitJsonLine(header);
384
+ }
385
+ else {
386
+ console.log(`watching thread ${threadId.toString()} as ${client.identityHex.slice(0, 12)}...`);
387
+ }
388
+ const snapshot = client.listMessages(threadId).map(toMessageDto);
389
+ for (const row of snapshot) {
390
+ if (jsonl) {
391
+ emitJsonLine({ event: 'snapshot', message: row });
392
+ }
393
+ else {
394
+ console.log(formatHumanMessage(row));
395
+ }
396
+ }
397
+ const detach = client.onMessageInsert(row => {
398
+ if (row.threadId !== threadId) {
399
+ return;
400
+ }
401
+ const message = toMessageDto(row);
402
+ if (jsonl) {
403
+ emitJsonLine({ event: 'message', message });
404
+ }
405
+ else {
406
+ console.log(formatHumanMessage(message));
407
+ }
408
+ });
409
+ const cleanup = () => {
410
+ detach();
411
+ client.disconnect();
412
+ process.exit(0);
413
+ };
414
+ process.on('SIGINT', cleanup);
415
+ process.on('SIGTERM', cleanup);
416
+ await new Promise(() => undefined);
417
+ }
418
+ function resolveCommandChannelId(client, payload) {
419
+ const byId = payload.channel_id;
420
+ if (byId !== undefined) {
421
+ return parseRequiredBigInt(String(byId), 'channel_id');
422
+ }
423
+ const byName = payload.channel;
424
+ if (typeof byName === 'string') {
425
+ return resolveChannelId(client, byName);
426
+ }
427
+ throw new Error('command requires channel_id or channel');
428
+ }
429
+ async function commandRunJsonl(flags, state) {
430
+ const { client, state: resolvedState } = await connectClient(flags, state);
431
+ const subscribedThreads = new Set();
432
+ const send = (event, data, id) => {
433
+ emitJsonLine({
434
+ ...(id !== undefined ? { id } : {}),
435
+ event,
436
+ ...data,
437
+ });
438
+ };
439
+ send('ready', {
440
+ identity: client.identityHex,
441
+ host: resolvedState.host,
442
+ databaseName: resolvedState.databaseName,
443
+ statePath: STATE_PATH,
444
+ commands: [
445
+ 'ping',
446
+ 'list_channels',
447
+ 'create_channel',
448
+ 'join_channel',
449
+ 'leave_channel',
450
+ 'list_threads',
451
+ 'create_thread',
452
+ 'list_messages',
453
+ 'send',
454
+ 'subscribe_thread',
455
+ 'unsubscribe_thread',
456
+ ],
457
+ });
458
+ const detach = client.onMessageInsert(row => {
459
+ const threadId = row.threadId.toString();
460
+ if (!subscribedThreads.has(threadId)) {
461
+ return;
462
+ }
463
+ send('message', {
464
+ message: toMessageDto(row),
465
+ });
466
+ });
467
+ const rl = node_readline_1.default.createInterface({
468
+ input: process.stdin,
469
+ crlfDelay: Infinity,
470
+ });
471
+ const cleanup = () => {
472
+ detach();
473
+ rl.close();
474
+ client.disconnect();
475
+ process.exit(0);
476
+ };
477
+ process.on('SIGINT', cleanup);
478
+ process.on('SIGTERM', cleanup);
479
+ rl.on('line', (line) => {
480
+ void (async () => {
481
+ if (!line.trim()) {
482
+ return;
483
+ }
484
+ let payload;
485
+ try {
486
+ payload = JSON.parse(line);
487
+ }
488
+ catch {
489
+ send('error', { error: 'invalid_json' });
490
+ return;
491
+ }
492
+ const id = typeof payload.id === 'string' || typeof payload.id === 'number'
493
+ ? payload.id
494
+ : undefined;
495
+ const command = payload.cmd;
496
+ if (typeof command !== 'string') {
497
+ send('error', { error: 'missing_cmd' }, id);
498
+ return;
499
+ }
500
+ try {
501
+ if (command === 'ping') {
502
+ send('ok', { pong: true }, id);
503
+ return;
504
+ }
505
+ if (command === 'list_channels') {
506
+ send('ok', {
507
+ channels: client.listChannels().map(toChannelDto),
508
+ }, id);
509
+ return;
510
+ }
511
+ if (command === 'create_channel') {
512
+ const name = String(payload.name ?? '').trim();
513
+ const topic = String(payload.topic ?? '').trim();
514
+ if (!name) {
515
+ throw new Error('create_channel requires name');
516
+ }
517
+ await client.createChannel(name, topic);
518
+ const created = client.listChannels().find(row => row.name === name);
519
+ send('ok', {
520
+ channel: created ? toChannelDto(created) : { id: null, name, topic },
521
+ }, id);
522
+ return;
523
+ }
524
+ if (command === 'join_channel') {
525
+ const channelId = resolveCommandChannelId(client, payload);
526
+ await client.joinChannel(channelId);
527
+ send('ok', { channelId: channelId.toString() }, id);
528
+ return;
529
+ }
530
+ if (command === 'leave_channel') {
531
+ const channelId = resolveCommandChannelId(client, payload);
532
+ await client.leaveChannel(channelId);
533
+ send('ok', { channelId: channelId.toString() }, id);
534
+ return;
535
+ }
536
+ if (command === 'list_threads') {
537
+ const channelId = payload.channel_id !== undefined || payload.channel !== undefined
538
+ ? resolveCommandChannelId(client, payload)
539
+ : undefined;
540
+ send('ok', {
541
+ threads: client.listThreads(channelId).map(toThreadDto),
542
+ }, id);
543
+ return;
544
+ }
545
+ if (command === 'create_thread') {
546
+ const channelId = resolveCommandChannelId(client, payload);
547
+ const title = String(payload.title ?? '');
548
+ const openingMessage = String(payload.opening_message ?? '');
549
+ if (!title.trim() || !openingMessage.trim()) {
550
+ throw new Error('create_thread requires title and opening_message');
551
+ }
552
+ const beforeIds = new Set(client.listThreads(channelId).map(row => row.id.toString()));
553
+ await client.createThread(channelId, title, openingMessage);
554
+ const created = findCreatedThread(client, channelId, title, beforeIds);
555
+ send('ok', {
556
+ thread: created ? toThreadDto(created) : null,
557
+ }, id);
558
+ return;
559
+ }
560
+ if (command === 'list_messages') {
561
+ const threadIdRaw = payload.thread_id;
562
+ if (threadIdRaw === undefined) {
563
+ throw new Error('list_messages requires thread_id');
564
+ }
565
+ const threadId = parseRequiredBigInt(String(threadIdRaw), 'thread_id');
566
+ send('ok', {
567
+ messages: client.listMessages(threadId).map(toMessageDto),
568
+ }, id);
569
+ return;
570
+ }
571
+ if (command === 'send') {
572
+ const threadIdRaw = payload.thread_id;
573
+ const text = String(payload.text ?? '');
574
+ if (threadIdRaw === undefined || !text.trim()) {
575
+ throw new Error('send requires thread_id and text');
576
+ }
577
+ const threadId = parseRequiredBigInt(String(threadIdRaw), 'thread_id');
578
+ await client.sendThreadMessage(threadId, text);
579
+ send('ok', {
580
+ threadId: threadId.toString(),
581
+ text,
582
+ }, id);
583
+ return;
584
+ }
585
+ if (command === 'subscribe_thread') {
586
+ const threadIdRaw = payload.thread_id;
587
+ if (threadIdRaw === undefined) {
588
+ throw new Error('subscribe_thread requires thread_id');
589
+ }
590
+ const threadId = parseRequiredBigInt(String(threadIdRaw), 'thread_id');
591
+ const threadIdText = threadId.toString();
592
+ subscribedThreads.add(threadIdText);
593
+ const snapshot = client.listMessages(threadId).map(toMessageDto);
594
+ for (const message of snapshot) {
595
+ send('snapshot', { message });
596
+ }
597
+ send('ok', { threadId: threadIdText, subscribed: true }, id);
598
+ return;
599
+ }
600
+ if (command === 'unsubscribe_thread') {
601
+ const threadIdRaw = payload.thread_id;
602
+ if (threadIdRaw === undefined) {
603
+ throw new Error('unsubscribe_thread requires thread_id');
604
+ }
605
+ const threadId = parseRequiredBigInt(String(threadIdRaw), 'thread_id');
606
+ const threadIdText = threadId.toString();
607
+ subscribedThreads.delete(threadIdText);
608
+ send('ok', { threadId: threadIdText, subscribed: false }, id);
609
+ return;
610
+ }
611
+ send('error', { error: `unknown_cmd:${command}` }, id);
612
+ }
613
+ catch (error) {
614
+ send('error', {
615
+ error: error instanceof Error ? error.message : String(error),
616
+ }, id);
617
+ }
618
+ })();
619
+ });
620
+ await new Promise(() => undefined);
621
+ }
622
+ async function main() {
623
+ const [command = 'help', ...rest] = process.argv.slice(2);
624
+ const { flags, positionals } = parseArgs(rest);
625
+ const state = await loadState();
626
+ if (command === 'help' || command === '--help') {
627
+ printHelp();
628
+ return;
629
+ }
630
+ if (command === 'signup') {
631
+ await commandSignup(flags, positionals, state);
632
+ return;
633
+ }
634
+ if (command === 'whoami') {
635
+ await commandWhoami(flags, state);
636
+ return;
637
+ }
638
+ if (command === 'channels') {
639
+ await commandChannels(flags, state);
640
+ return;
641
+ }
642
+ if (command === 'create-channel') {
643
+ await commandCreateChannel(flags, state);
644
+ return;
645
+ }
646
+ if (command === 'join') {
647
+ await commandJoinOrLeave('join', flags, positionals, state);
648
+ return;
649
+ }
650
+ if (command === 'leave') {
651
+ await commandJoinOrLeave('leave', flags, positionals, state);
652
+ return;
653
+ }
654
+ if (command === 'threads') {
655
+ await commandThreads(flags, positionals, state);
656
+ return;
657
+ }
658
+ if (command === 'create-thread') {
659
+ await commandCreateThread(flags, positionals, state);
660
+ return;
661
+ }
662
+ if (command === 'send') {
663
+ await commandSend(flags, positionals, state);
664
+ return;
665
+ }
666
+ if (command === 'watch') {
667
+ await commandWatch(flags, positionals, state);
668
+ return;
669
+ }
670
+ if (command === 'run') {
671
+ if (!getBooleanFlag(flags, ['jsonl'])) {
672
+ throw new Error('run requires --jsonl');
673
+ }
674
+ await commandRunJsonl(flags, state);
675
+ return;
676
+ }
677
+ throw new Error(`Unknown command: ${command}`);
678
+ }
679
+ main().catch(error => {
680
+ console.error(error instanceof Error ? error.message : String(error));
681
+ process.exit(1);
682
+ });
@@ -0,0 +1 @@
1
+ export { AgentRealtimeClient, type AgentClientOptions, type AgentRole, type SignUpInput, } from './agent-client';
package/dist/index.js ADDED
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AgentRealtimeClient = void 0;
4
+ var agent_client_1 = require("./agent-client");
5
+ Object.defineProperty(exports, "AgentRealtimeClient", { enumerable: true, get: function () { return agent_client_1.AgentRealtimeClient; } });
@@ -0,0 +1,15 @@
1
+ declare const _default: import("spacetimedb").RowBuilder<{
2
+ id: import("spacetimedb").U64ColumnBuilder<{
3
+ isPrimaryKey: true;
4
+ }>;
5
+ channelId: import("spacetimedb").U64ColumnBuilder<{
6
+ name: "channel_id";
7
+ }>;
8
+ memberIdentity: import("spacetimedb").IdentityColumnBuilder<{
9
+ name: "member_identity";
10
+ }>;
11
+ joinedAt: import("spacetimedb").TimestampColumnBuilder<{
12
+ name: "joined_at";
13
+ }>;
14
+ }>;
15
+ export default _default;