@almyty/chat 0.1.1 → 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/index.d.ts CHANGED
@@ -4,6 +4,7 @@
4
4
  *
5
5
  * npx @almyty/chat # pick from a menu
6
6
  * npx @almyty/chat <agent> # start chatting with that agent
7
+ * npx @almyty/chat <agent> --resume <id> # resume a conversation
7
8
  *
8
9
  * Reads credentials from ~/.almyty/credentials.json (created by
9
10
  * `npx @almyty/auth login`).
package/dist/index.js CHANGED
@@ -4,6 +4,7 @@
4
4
  *
5
5
  * npx @almyty/chat # pick from a menu
6
6
  * npx @almyty/chat <agent> # start chatting with that agent
7
+ * npx @almyty/chat <agent> --resume <id> # resume a conversation
7
8
  *
8
9
  * Reads credentials from ~/.almyty/credentials.json (created by
9
10
  * `npx @almyty/auth login`).
@@ -17,8 +18,7 @@
17
18
  * /quit, /exit leave the REPL
18
19
  */
19
20
  import { createInterface } from 'readline';
20
- import { AlmytyClient } from './client.js';
21
- import { resolveCredentialsOrExit } from './credentials.js';
21
+ import { AlmytyClient, resolveCredentialsOrExit } from '@almyty/client';
22
22
  const VERSION = '0.1.0';
23
23
  function printHelp() {
24
24
  console.log(`
@@ -27,6 +27,8 @@ function printHelp() {
27
27
  Usage:
28
28
  npx @almyty/chat Pick an agent from a menu
29
29
  npx @almyty/chat <name|id> Start chatting with that agent
30
+ npx @almyty/chat <name|id> --resume <conversation-id>
31
+ Resume an existing conversation
30
32
 
31
33
  Inside the REPL:
32
34
  /switch <agent> switch to a different agent
@@ -143,6 +145,7 @@ async function handleSlashCommand(command, state) {
143
145
  }
144
146
  state.agent = next;
145
147
  state.pendingRunId = null;
148
+ state.conversationId = null;
146
149
  printAgentBanner(state.agent);
147
150
  return {};
148
151
  }
@@ -159,8 +162,13 @@ async function handleAutonomousTurn(state, message) {
159
162
  state.pendingRunId = null;
160
163
  }
161
164
  else {
162
- const run = await state.client.startAgentRun(state.agent.id, message);
165
+ const run = await state.client.startRun(state.agent.id, message, {
166
+ conversationId: state.conversationId ?? undefined,
167
+ });
163
168
  runId = run.id;
169
+ if (run.conversationId) {
170
+ state.conversationId = run.conversationId;
171
+ }
164
172
  }
165
173
  let printed = 0;
166
174
  const final = await state.client.pollRun(state.agent.id, runId, {
@@ -212,8 +220,11 @@ async function handleUserTurn(state, message) {
212
220
  }
213
221
  return handleWorkflowTurn(state, message);
214
222
  }
215
- async function startChat(client, agent) {
216
- const state = { client, agent, pendingRunId: null };
223
+ async function startChat(client, agent, resumeConversationId) {
224
+ const state = { client, agent, pendingRunId: null, conversationId: resumeConversationId ?? null };
225
+ if (resumeConversationId) {
226
+ console.log(` Resuming conversation ${resumeConversationId}`);
227
+ }
217
228
  printAgentBanner(state.agent);
218
229
  const rl = createInterface({
219
230
  input: process.stdin,
@@ -268,8 +279,19 @@ async function main() {
268
279
  }
269
280
  const creds = resolveCredentialsOrExit();
270
281
  const client = new AlmytyClient(creds.url, creds.token);
282
+ // Parse --resume <conversation-id>
283
+ let resumeConversationId;
284
+ const resumeIdx = argv.indexOf('--resume');
285
+ if (resumeIdx !== -1) {
286
+ const nextArg = argv[resumeIdx + 1];
287
+ if (!nextArg || nextArg.startsWith('-')) {
288
+ console.error('Usage: --resume <conversation-id>');
289
+ process.exit(1);
290
+ }
291
+ resumeConversationId = nextArg;
292
+ }
271
293
  let agent = null;
272
- const ref = argv.find((a) => !a.startsWith('-'));
294
+ const ref = argv.find((a) => !a.startsWith('-') && a !== resumeConversationId);
273
295
  if (ref) {
274
296
  agent = await client.findAgentByNameOrId(ref);
275
297
  if (!agent) {
@@ -282,7 +304,7 @@ async function main() {
282
304
  if (!agent)
283
305
  process.exit(1);
284
306
  }
285
- await startChat(client, agent);
307
+ await startChat(client, agent, resumeConversationId);
286
308
  }
287
309
  main().catch((err) => {
288
310
  console.error(`Error: ${err.message}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@almyty/chat",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "description": "Interactive chat REPL with almyty agents",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -11,7 +11,7 @@
11
11
  "dist"
12
12
  ],
13
13
  "scripts": {
14
- "build": "tsc",
14
+ "build": "tsc && chmod +x dist/index.js",
15
15
  "dev": "tsx src/index.ts",
16
16
  "test": "vitest run",
17
17
  "test:watch": "vitest",
@@ -26,6 +26,9 @@
26
26
  ],
27
27
  "author": "almyty",
28
28
  "license": "BSL-1.1",
29
+ "dependencies": {
30
+ "@almyty/client": "^0.1.0"
31
+ },
29
32
  "devDependencies": {
30
33
  "@types/node": "^25.4.0",
31
34
  "tsx": "^4.7.0",
package/dist/client.d.ts DELETED
@@ -1,35 +0,0 @@
1
- /**
2
- * Tiny HTTP client used by @almyty/chat. Subset of @almyty/agents'
3
- * client — kept inline so this package has zero workspace deps.
4
- */
5
- export interface AgentInfo {
6
- id: string;
7
- name: string;
8
- description?: string;
9
- mode?: 'workflow' | 'autonomous';
10
- status?: string;
11
- }
12
- export interface AgentRun {
13
- id: string;
14
- status: string;
15
- output?: any;
16
- error?: string;
17
- steps?: any[];
18
- }
19
- export declare class AlmytyClient {
20
- private readonly baseUrl;
21
- private readonly token;
22
- constructor(baseUrl: string, token: string);
23
- private request;
24
- listAgents(): Promise<AgentInfo[]>;
25
- findAgentByNameOrId(nameOrId: string): Promise<AgentInfo | null>;
26
- invokeAgent(agentId: string, input: Record<string, any>): Promise<any>;
27
- startAgentRun(agentId: string, input: any): Promise<AgentRun>;
28
- getRun(agentId: string, runId: string): Promise<AgentRun>;
29
- sendRunInput(agentId: string, runId: string, input: string): Promise<void>;
30
- pollRun(agentId: string, runId: string, options?: {
31
- intervalMs?: number;
32
- timeoutMs?: number;
33
- onStep?: (run: AgentRun) => void;
34
- }): Promise<AgentRun>;
35
- }
package/dist/client.js DELETED
@@ -1,107 +0,0 @@
1
- /**
2
- * Tiny HTTP client used by @almyty/chat. Subset of @almyty/agents'
3
- * client — kept inline so this package has zero workspace deps.
4
- */
5
- const TERMINAL_STATUSES = new Set(['completed', 'failed', 'cancelled', 'timeout']);
6
- export class AlmytyClient {
7
- baseUrl;
8
- token;
9
- constructor(baseUrl, token) {
10
- this.baseUrl = baseUrl.replace(/\/$/, '');
11
- this.token = token;
12
- }
13
- async request(path, init = {}) {
14
- const url = `${this.baseUrl}${path}`;
15
- const headers = {
16
- Authorization: `Bearer ${this.token}`,
17
- 'Content-Type': 'application/json',
18
- ...(init.headers || {}),
19
- };
20
- const res = await fetch(url, { ...init, headers });
21
- if (!res.ok) {
22
- if (res.status === 401) {
23
- throw new Error('Authentication failed. Run: npx @almyty/auth login');
24
- }
25
- const text = await res.text().catch(() => '');
26
- throw new Error(`API error ${res.status}: ${text}`);
27
- }
28
- if (res.status === 204)
29
- return null;
30
- return res.json();
31
- }
32
- async listAgents() {
33
- const data = await this.request('/agents');
34
- const agents = data?.data?.data || data?.data || data || [];
35
- return agents.map((a) => ({
36
- id: a.id,
37
- name: a.name,
38
- description: a.description,
39
- mode: a.mode,
40
- status: a.status,
41
- }));
42
- }
43
- async findAgentByNameOrId(nameOrId) {
44
- const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
45
- if (uuidRegex.test(nameOrId)) {
46
- try {
47
- const data = await this.request(`/agents/${nameOrId}`);
48
- const a = data?.data ?? data;
49
- if (a?.id) {
50
- return { id: a.id, name: a.name, description: a.description, mode: a.mode, status: a.status };
51
- }
52
- }
53
- catch {
54
- // fall through
55
- }
56
- }
57
- const all = await this.listAgents();
58
- const lower = nameOrId.toLowerCase();
59
- return (all.find((a) => a.name === nameOrId) ||
60
- all.find((a) => a.name.toLowerCase() === lower) ||
61
- all.find((a) => a.name.toLowerCase().replace(/\s+/g, '-') === lower) ||
62
- null);
63
- }
64
- async invokeAgent(agentId, input) {
65
- const data = await this.request(`/agents/${agentId}/invoke`, {
66
- method: 'POST',
67
- body: JSON.stringify({ input }),
68
- });
69
- return data?.data ?? data;
70
- }
71
- async startAgentRun(agentId, input) {
72
- const data = await this.request(`/agents/${agentId}/runs`, {
73
- method: 'POST',
74
- body: JSON.stringify({ input }),
75
- });
76
- const run = data?.data ?? data;
77
- return { id: run.id, status: run.status };
78
- }
79
- async getRun(agentId, runId) {
80
- const data = await this.request(`/agents/${agentId}/runs/${runId}`);
81
- return (data?.data ?? data);
82
- }
83
- async sendRunInput(agentId, runId, input) {
84
- await this.request(`/agents/${agentId}/runs/${runId}/input`, {
85
- method: 'POST',
86
- body: JSON.stringify({ input }),
87
- });
88
- }
89
- async pollRun(agentId, runId, options = {}) {
90
- const intervalMs = options.intervalMs ?? 1500;
91
- const timeoutMs = options.timeoutMs ?? 5 * 60_000;
92
- const deadline = Date.now() + timeoutMs;
93
- let lastStepCount = -1;
94
- while (Date.now() < deadline) {
95
- const run = await this.getRun(agentId, runId);
96
- if (Array.isArray(run.steps) && run.steps.length !== lastStepCount) {
97
- lastStepCount = run.steps.length;
98
- options.onStep?.(run);
99
- }
100
- if (run.status && (TERMINAL_STATUSES.has(run.status) || run.status === 'waiting_input')) {
101
- return run;
102
- }
103
- await new Promise((r) => setTimeout(r, intervalMs));
104
- }
105
- throw new Error(`Run ${runId} did not finish within ${Math.round(timeoutMs / 1000)}s`);
106
- }
107
- }
@@ -1,15 +0,0 @@
1
- /**
2
- * Read shared almyty credentials from ~/.almyty/credentials.json.
3
- *
4
- * Identical to the helper in @almyty/auth and @almyty/agents — kept
5
- * inline to keep each CLI package self-contained.
6
- */
7
- export declare const CREDENTIALS_FILE: string;
8
- export interface StoredCredentials {
9
- url: string;
10
- token: string;
11
- email?: string;
12
- frontendUrl?: string;
13
- }
14
- export declare function loadCredentials(): StoredCredentials | null;
15
- export declare function resolveCredentialsOrExit(): StoredCredentials;
@@ -1,33 +0,0 @@
1
- /**
2
- * Read shared almyty credentials from ~/.almyty/credentials.json.
3
- *
4
- * Identical to the helper in @almyty/auth and @almyty/agents — kept
5
- * inline to keep each CLI package self-contained.
6
- */
7
- import { readFileSync, existsSync } from 'fs';
8
- import { homedir } from 'os';
9
- import { join } from 'path';
10
- export const CREDENTIALS_FILE = join(homedir(), '.almyty', 'credentials.json');
11
- export function loadCredentials() {
12
- try {
13
- if (!existsSync(CREDENTIALS_FILE))
14
- return null;
15
- return JSON.parse(readFileSync(CREDENTIALS_FILE, 'utf-8'));
16
- }
17
- catch {
18
- return null;
19
- }
20
- }
21
- export function resolveCredentialsOrExit() {
22
- const envToken = process.env.ALMYTY_TOKEN;
23
- const envUrl = process.env.ALMYTY_URL || 'https://api.almyty.com';
24
- if (envToken)
25
- return { url: envUrl, token: envToken };
26
- const stored = loadCredentials();
27
- if (stored?.token)
28
- return stored;
29
- console.error('Not authenticated. Run one of:');
30
- console.error(' npx @almyty/auth login');
31
- console.error(' export ALMYTY_TOKEN=<your-token>');
32
- process.exit(1);
33
- }