@almyty/chat 0.1.1 → 0.1.2
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 +1 -0
- package/dist/index.js +29 -7
- package/package.json +5 -2
- package/dist/client.d.ts +0 -35
- package/dist/client.js +0 -107
- package/dist/credentials.d.ts +0 -15
- package/dist/credentials.js +0 -33
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 '
|
|
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.
|
|
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.
|
|
3
|
+
"version": "0.1.2",
|
|
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": "file:../client"
|
|
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
|
-
}
|
package/dist/credentials.d.ts
DELETED
|
@@ -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;
|
package/dist/credentials.js
DELETED
|
@@ -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
|
-
}
|