@almyty/client 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.
@@ -0,0 +1,71 @@
1
+ /**
2
+ * Shared HTTP client for the almyty REST API.
3
+ *
4
+ * Used by @almyty/agents, @almyty/chat, @almyty/acp-server,
5
+ * and @almyty/mcp-server. Covers agent discovery, invocation,
6
+ * autonomous run management, and polling.
7
+ */
8
+ export interface AgentInfo {
9
+ id: string;
10
+ name: string;
11
+ slug?: string;
12
+ description?: string;
13
+ mode?: 'workflow' | 'autonomous';
14
+ status?: string;
15
+ pipeline?: {
16
+ nodes?: PipelineNode[];
17
+ };
18
+ modelConfig?: Record<string, unknown>;
19
+ }
20
+ export interface PipelineNode {
21
+ id: string;
22
+ type: string;
23
+ label?: string;
24
+ config?: Record<string, unknown>;
25
+ }
26
+ export interface AgentRun {
27
+ id: string;
28
+ agentId?: string;
29
+ status: string;
30
+ conversationId?: string;
31
+ output?: unknown;
32
+ error?: string;
33
+ steps?: unknown[];
34
+ totalCost?: number;
35
+ totalTokens?: number;
36
+ }
37
+ export interface RunLimits {
38
+ maxSteps?: number;
39
+ maxCostCents?: number;
40
+ maxDurationMs?: number;
41
+ }
42
+ export declare class AlmytyClient {
43
+ private readonly baseUrl;
44
+ private readonly token;
45
+ constructor(baseUrl: string, token: string);
46
+ private headers;
47
+ request(path: string, init?: RequestInit): Promise<any>;
48
+ private unwrap;
49
+ listAgents(): Promise<AgentInfo[]>;
50
+ getAgent(id: string): Promise<AgentInfo>;
51
+ findAgentByNameOrId(nameOrId: string): Promise<AgentInfo | null>;
52
+ invokeAgent(agentId: string, input: Record<string, any>): Promise<any>;
53
+ startRun(agentId: string, input: any, options?: RunLimits & {
54
+ conversationId?: string;
55
+ }): Promise<AgentRun>;
56
+ getRun(agentId: string, runId: string): Promise<AgentRun>;
57
+ listRuns(agentId: string, page?: number, limit?: number): Promise<{
58
+ data: AgentRun[];
59
+ total: number;
60
+ }>;
61
+ sendRunInput(agentId: string, runId: string, input: string): Promise<void>;
62
+ cancelRun(agentId: string, runId: string): Promise<void>;
63
+ /**
64
+ * Poll a run until it reaches a terminal status (or waiting_input).
65
+ */
66
+ pollRun(agentId: string, runId: string, options?: {
67
+ intervalMs?: number;
68
+ timeoutMs?: number;
69
+ onStep?: (run: AgentRun) => void;
70
+ }): Promise<AgentRun>;
71
+ }
package/dist/client.js ADDED
@@ -0,0 +1,158 @@
1
+ /**
2
+ * Shared HTTP client for the almyty REST API.
3
+ *
4
+ * Used by @almyty/agents, @almyty/chat, @almyty/acp-server,
5
+ * and @almyty/mcp-server. Covers agent discovery, invocation,
6
+ * autonomous run management, and polling.
7
+ */
8
+ const TERMINAL_STATUSES = new Set(['completed', 'failed', 'cancelled', 'timeout']);
9
+ export class AlmytyClient {
10
+ baseUrl;
11
+ token;
12
+ constructor(baseUrl, token) {
13
+ this.baseUrl = baseUrl.replace(/\/$/, '');
14
+ this.token = token;
15
+ }
16
+ // ── HTTP helpers ────────────────────────────────────────────────
17
+ headers() {
18
+ return {
19
+ Authorization: `Bearer ${this.token}`,
20
+ 'Content-Type': 'application/json',
21
+ };
22
+ }
23
+ async request(path, init = {}) {
24
+ const url = `${this.baseUrl}${path}`;
25
+ const headers = {
26
+ ...this.headers(),
27
+ ...(init.headers || {}),
28
+ };
29
+ const res = await fetch(url, { ...init, headers });
30
+ if (!res.ok) {
31
+ if (res.status === 401) {
32
+ throw new Error('Authentication failed. Run: npx @almyty/auth login');
33
+ }
34
+ const text = await res.text().catch(() => '');
35
+ throw new Error(`API error ${res.status}: ${text}`);
36
+ }
37
+ if (res.status === 204)
38
+ return null;
39
+ return res.json();
40
+ }
41
+ unwrap(data) {
42
+ return data?.data ?? data;
43
+ }
44
+ // ── Agent discovery ─────────────────────────────────────────────
45
+ async listAgents() {
46
+ const data = await this.request('/agents');
47
+ const list = data?.data?.data || data?.data || data || [];
48
+ return list.map((a) => ({
49
+ id: a.id,
50
+ name: a.name,
51
+ slug: a.slug,
52
+ description: a.description,
53
+ mode: a.mode,
54
+ status: a.status,
55
+ pipeline: a.pipeline,
56
+ modelConfig: a.modelConfig,
57
+ }));
58
+ }
59
+ async getAgent(id) {
60
+ const data = await this.request(`/agents/${encodeURIComponent(id)}`);
61
+ return this.unwrap(data);
62
+ }
63
+ async findAgentByNameOrId(nameOrId) {
64
+ const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
65
+ if (uuidRegex.test(nameOrId)) {
66
+ try {
67
+ const agent = await this.getAgent(nameOrId);
68
+ if (agent?.id)
69
+ return agent;
70
+ }
71
+ catch {
72
+ // fall through to name search
73
+ }
74
+ }
75
+ const all = await this.listAgents();
76
+ const lower = nameOrId.toLowerCase();
77
+ return (all.find((a) => a.name === nameOrId) ||
78
+ all.find((a) => a.name.toLowerCase() === lower) ||
79
+ all.find((a) => a.name.toLowerCase().replace(/\s+/g, '-') === lower) ||
80
+ all.find((a) => a.slug === nameOrId) ||
81
+ all.find((a) => a.slug?.toLowerCase() === lower) ||
82
+ null);
83
+ }
84
+ // ── Workflow invocation ───────────────────────���─────────────────
85
+ async invokeAgent(agentId, input) {
86
+ const data = await this.request(`/agents/${encodeURIComponent(agentId)}/invoke`, {
87
+ method: 'POST',
88
+ body: JSON.stringify({ input }),
89
+ });
90
+ return this.unwrap(data);
91
+ }
92
+ // ── Autonomous run management ───────────────────────────────────
93
+ async startRun(agentId, input, options) {
94
+ const body = { input };
95
+ if (options?.maxSteps)
96
+ body.maxSteps = options.maxSteps;
97
+ if (options?.maxCostCents)
98
+ body.maxCostCents = options.maxCostCents;
99
+ if (options?.maxDurationMs)
100
+ body.maxDurationMs = options.maxDurationMs;
101
+ if (options?.conversationId)
102
+ body.conversationId = options.conversationId;
103
+ const data = await this.request(`/agents/${encodeURIComponent(agentId)}/runs`, {
104
+ method: 'POST',
105
+ body: JSON.stringify(body),
106
+ });
107
+ const run = this.unwrap(data);
108
+ return {
109
+ id: run.id,
110
+ agentId: run.agentId,
111
+ status: run.status,
112
+ conversationId: run.conversationId,
113
+ output: run.output,
114
+ error: run.error,
115
+ steps: run.steps,
116
+ totalCost: run.totalCost,
117
+ totalTokens: run.totalTokens,
118
+ };
119
+ }
120
+ async getRun(agentId, runId) {
121
+ const data = await this.request(`/agents/${encodeURIComponent(agentId)}/runs/${encodeURIComponent(runId)}`);
122
+ return this.unwrap(data);
123
+ }
124
+ async listRuns(agentId, page = 1, limit = 20) {
125
+ const data = await this.request(`/agents/${encodeURIComponent(agentId)}/runs?page=${page}&limit=${limit}`);
126
+ return {
127
+ data: data?.data ?? [],
128
+ total: data?.pagination?.total ?? 0,
129
+ };
130
+ }
131
+ async sendRunInput(agentId, runId, input) {
132
+ await this.request(`/agents/${encodeURIComponent(agentId)}/runs/${encodeURIComponent(runId)}/input`, { method: 'POST', body: JSON.stringify({ input }) });
133
+ }
134
+ async cancelRun(agentId, runId) {
135
+ await this.request(`/agents/${encodeURIComponent(agentId)}/runs/${encodeURIComponent(runId)}/cancel`, { method: 'POST' });
136
+ }
137
+ /**
138
+ * Poll a run until it reaches a terminal status (or waiting_input).
139
+ */
140
+ async pollRun(agentId, runId, options = {}) {
141
+ const intervalMs = options.intervalMs ?? 1500;
142
+ const timeoutMs = options.timeoutMs ?? 5 * 60_000;
143
+ const deadline = Date.now() + timeoutMs;
144
+ let lastStepCount = -1;
145
+ while (Date.now() < deadline) {
146
+ const run = await this.getRun(agentId, runId);
147
+ if (Array.isArray(run.steps) && run.steps.length !== lastStepCount) {
148
+ lastStepCount = run.steps.length;
149
+ options.onStep?.(run);
150
+ }
151
+ if (run.status && (TERMINAL_STATUSES.has(run.status) || run.status === 'waiting_input')) {
152
+ return run;
153
+ }
154
+ await new Promise((r) => setTimeout(r, intervalMs));
155
+ }
156
+ throw new Error(`Run ${runId} did not finish within ${Math.round(timeoutMs / 1000)}s`);
157
+ }
158
+ }
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Shared credential resolver for all almyty CLI packages.
3
+ *
4
+ * Reads from ALMYTY_TOKEN env var first, then falls back to
5
+ * ~/.almyty/credentials.json written by `npx @almyty/auth login`.
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
+ /**
16
+ * Resolve credentials from env or file. Returns null if nothing found.
17
+ */
18
+ export declare function resolveCredentials(): StoredCredentials | null;
19
+ /**
20
+ * Resolve credentials or exit with an error message.
21
+ */
22
+ export declare function resolveCredentialsOrExit(): StoredCredentials;
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Shared credential resolver for all almyty CLI packages.
3
+ *
4
+ * Reads from ALMYTY_TOKEN env var first, then falls back to
5
+ * ~/.almyty/credentials.json written by `npx @almyty/auth login`.
6
+ */
7
+ import { readFileSync, existsSync } from 'node:fs';
8
+ import { homedir } from 'node:os';
9
+ import { join } from 'node: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
+ /**
22
+ * Resolve credentials from env or file. Returns null if nothing found.
23
+ */
24
+ export function resolveCredentials() {
25
+ const envToken = process.env.ALMYTY_TOKEN;
26
+ const envUrl = process.env.ALMYTY_URL || 'https://api.almyty.com';
27
+ if (envToken)
28
+ return { url: envUrl, token: envToken };
29
+ const stored = loadCredentials();
30
+ if (stored?.token)
31
+ return stored;
32
+ return null;
33
+ }
34
+ /**
35
+ * Resolve credentials or exit with an error message.
36
+ */
37
+ export function resolveCredentialsOrExit() {
38
+ const creds = resolveCredentials();
39
+ if (creds)
40
+ return creds;
41
+ console.error('Not authenticated. Run one of:');
42
+ console.error(' npx @almyty/auth login');
43
+ console.error(' export ALMYTY_TOKEN=<your-token>');
44
+ process.exit(1);
45
+ }
@@ -0,0 +1,4 @@
1
+ export { AlmytyClient } from './client.js';
2
+ export type { AgentInfo, AgentRun, PipelineNode, RunLimits } from './client.js';
3
+ export { loadCredentials, resolveCredentials, resolveCredentialsOrExit, CREDENTIALS_FILE, } from './credentials.js';
4
+ export type { StoredCredentials } from './credentials.js';
package/dist/index.js ADDED
@@ -0,0 +1,2 @@
1
+ export { AlmytyClient } from './client.js';
2
+ export { loadCredentials, resolveCredentials, resolveCredentialsOrExit, CREDENTIALS_FILE, } from './credentials.js';
package/package.json ADDED
@@ -0,0 +1,29 @@
1
+ {
2
+ "name": "@almyty/client",
3
+ "version": "0.1.0",
4
+ "description": "Shared HTTP client and credential resolver for almyty CLI packages",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "files": [
9
+ "dist"
10
+ ],
11
+ "scripts": {
12
+ "build": "tsc",
13
+ "test": "vitest run",
14
+ "test:watch": "vitest",
15
+ "prepublishOnly": "npm run build"
16
+ },
17
+ "keywords": [
18
+ "almyty",
19
+ "client",
20
+ "sdk"
21
+ ],
22
+ "author": "almyty",
23
+ "license": "BSL-1.1",
24
+ "devDependencies": {
25
+ "@types/node": "^25.4.0",
26
+ "typescript": "^5.3.0",
27
+ "vitest": "^4.1.0"
28
+ }
29
+ }