@almyty/client 0.1.0 → 1.2.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 +36 -0
- package/dist/client.d.ts +112 -0
- package/dist/client.js +218 -1
- package/dist/credentials.d.ts +5 -0
- package/dist/credentials.js +27 -0
- package/dist/index.d.ts +3 -3
- package/dist/index.js +2 -2
- package/package.json +15 -3
package/README.md
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# @almyty/client
|
|
2
|
+
|
|
3
|
+
Shared HTTP client and credential resolver used by all almyty CLI packages.
|
|
4
|
+
|
|
5
|
+
## Usage
|
|
6
|
+
|
|
7
|
+
```typescript
|
|
8
|
+
import { AlmytyClient, resolveCredentialsOrExit } from '@almyty/client';
|
|
9
|
+
|
|
10
|
+
const creds = resolveCredentialsOrExit();
|
|
11
|
+
const client = new AlmytyClient(creds.url, creds.token);
|
|
12
|
+
|
|
13
|
+
const agents = await client.listAgents();
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Exports
|
|
17
|
+
|
|
18
|
+
- `AlmytyClient` -- API client (agents, runs, gateways)
|
|
19
|
+
- `GatewayClient` -- gateway-scoped client (invoke, stream, conversations)
|
|
20
|
+
- `resolveCredentials()` -- read `~/.almyty/credentials.json` (returns null if missing)
|
|
21
|
+
- `resolveCredentialsOrExit()` -- same, but exits with an error message if missing
|
|
22
|
+
- `getOrgSlugFromToken(token)` -- extract org slug from JWT
|
|
23
|
+
- `loadCredentials()` -- raw file read
|
|
24
|
+
- `CREDENTIALS_FILE` -- path to `~/.almyty/credentials.json`
|
|
25
|
+
|
|
26
|
+
## About almyty
|
|
27
|
+
|
|
28
|
+
almyty is the full-stack platform for AI agents, agnostic by design: any LLM, any
|
|
29
|
+
API turned into tools, served over MCP, A2A, UTCP, and Agent Skills. Open source,
|
|
30
|
+
no lock-in.
|
|
31
|
+
|
|
32
|
+
- Website — https://almyty.com
|
|
33
|
+
- Docs — https://docs.almyty.com
|
|
34
|
+
- Source — https://github.com/almyty-inc/almyty
|
|
35
|
+
|
|
36
|
+
Apache-2.0 © Almyty Inc.
|
package/dist/client.d.ts
CHANGED
|
@@ -5,6 +5,11 @@
|
|
|
5
5
|
* and @almyty/mcp-server. Covers agent discovery, invocation,
|
|
6
6
|
* autonomous run management, and polling.
|
|
7
7
|
*/
|
|
8
|
+
export interface AgentTool {
|
|
9
|
+
id: string;
|
|
10
|
+
name: string;
|
|
11
|
+
description?: string;
|
|
12
|
+
}
|
|
8
13
|
export interface AgentInfo {
|
|
9
14
|
id: string;
|
|
10
15
|
name: string;
|
|
@@ -16,6 +21,7 @@ export interface AgentInfo {
|
|
|
16
21
|
nodes?: PipelineNode[];
|
|
17
22
|
};
|
|
18
23
|
modelConfig?: Record<string, unknown>;
|
|
24
|
+
tools?: AgentTool[];
|
|
19
25
|
}
|
|
20
26
|
export interface PipelineNode {
|
|
21
27
|
id: string;
|
|
@@ -39,16 +45,61 @@ export interface RunLimits {
|
|
|
39
45
|
maxCostCents?: number;
|
|
40
46
|
maxDurationMs?: number;
|
|
41
47
|
}
|
|
48
|
+
/** SSE event from the agent run stream. */
|
|
49
|
+
export interface StreamEvent {
|
|
50
|
+
type: string;
|
|
51
|
+
data: Record<string, unknown>;
|
|
52
|
+
}
|
|
53
|
+
/** A coding CLI detected on a runner machine. */
|
|
54
|
+
export interface RunnerCodingAgent {
|
|
55
|
+
id: string;
|
|
56
|
+
displayName: string;
|
|
57
|
+
binary: string;
|
|
58
|
+
version?: string;
|
|
59
|
+
providerFamily?: string;
|
|
60
|
+
}
|
|
61
|
+
/** A registered runner (one of the user's machines). */
|
|
62
|
+
export interface RunnerSummary {
|
|
63
|
+
id: string;
|
|
64
|
+
name: string;
|
|
65
|
+
state?: string;
|
|
66
|
+
labels?: Record<string, string>;
|
|
67
|
+
/** Coding CLIs the runner reported at registration. */
|
|
68
|
+
codingAgents: RunnerCodingAgent[];
|
|
69
|
+
}
|
|
70
|
+
/** A coding session running on a runner. */
|
|
71
|
+
export interface CodingSession {
|
|
72
|
+
sessionId: string;
|
|
73
|
+
agent: string;
|
|
74
|
+
binary?: string;
|
|
75
|
+
processId?: string;
|
|
76
|
+
cwd?: string;
|
|
77
|
+
task?: string;
|
|
78
|
+
status?: string;
|
|
79
|
+
exitCode?: number | null;
|
|
80
|
+
}
|
|
81
|
+
/** Callback for stream events. */
|
|
82
|
+
export type StreamEventHandler = (event: StreamEvent) => void;
|
|
42
83
|
export declare class AlmytyClient {
|
|
43
84
|
private readonly baseUrl;
|
|
44
85
|
private readonly token;
|
|
45
86
|
constructor(baseUrl: string, token: string);
|
|
46
87
|
private headers;
|
|
47
88
|
request(path: string, init?: RequestInit): Promise<any>;
|
|
89
|
+
/**
|
|
90
|
+
* Connect to an SSE endpoint and call handler for each event.
|
|
91
|
+
* Returns when the stream ends or a terminal event is received.
|
|
92
|
+
*/
|
|
93
|
+
streamSSE(path: string, handler: StreamEventHandler, signal?: AbortSignal): Promise<void>;
|
|
48
94
|
private unwrap;
|
|
49
95
|
listAgents(): Promise<AgentInfo[]>;
|
|
50
96
|
getAgent(id: string): Promise<AgentInfo>;
|
|
51
97
|
findAgentByNameOrId(nameOrId: string): Promise<AgentInfo | null>;
|
|
98
|
+
/**
|
|
99
|
+
* Return a gateway-scoped client that routes all calls through
|
|
100
|
+
* /:orgSlug/:agentSlug instead of /agents/:id.
|
|
101
|
+
*/
|
|
102
|
+
gateway(orgSlug: string, agentSlug: string): GatewayClient;
|
|
52
103
|
invokeAgent(agentId: string, input: Record<string, any>): Promise<any>;
|
|
53
104
|
startRun(agentId: string, input: any, options?: RunLimits & {
|
|
54
105
|
conversationId?: string;
|
|
@@ -68,4 +119,65 @@ export declare class AlmytyClient {
|
|
|
68
119
|
timeoutMs?: number;
|
|
69
120
|
onStep?: (run: AgentRun) => void;
|
|
70
121
|
}): Promise<AgentRun>;
|
|
122
|
+
/** The caller's registered runners, with their detected coding CLIs. */
|
|
123
|
+
listRunners(): Promise<RunnerSummary[]>;
|
|
124
|
+
/** Fresh probe of coding CLIs installed on the runner machine. */
|
|
125
|
+
listRunnerCodingAgents(runnerId: string): Promise<RunnerCodingAgent[]>;
|
|
126
|
+
/** Start a coding session (spawns the CLI with the task prompt). */
|
|
127
|
+
startCodingSession(runnerId: string, options: {
|
|
128
|
+
agent: string;
|
|
129
|
+
task: string;
|
|
130
|
+
cwd?: string;
|
|
131
|
+
model?: string;
|
|
132
|
+
}): Promise<CodingSession>;
|
|
133
|
+
getCodingSession(runnerId: string, sessionId: string): Promise<CodingSession>;
|
|
134
|
+
/** Route a line of user input to the session's stdin. */
|
|
135
|
+
sendCodingInput(runnerId: string, sessionId: string, data: string): Promise<void>;
|
|
136
|
+
stopCodingSession(runnerId: string, sessionId: string, force?: boolean): Promise<void>;
|
|
137
|
+
/**
|
|
138
|
+
* Stream a coding session's output via SSE. Calls handler for each
|
|
139
|
+
* coding.output / coding.exit event; returns when the session exits or
|
|
140
|
+
* the stream ends.
|
|
141
|
+
*/
|
|
142
|
+
streamCodingEvents(runnerId: string, sessionId: string, handler: StreamEventHandler, signal?: AbortSignal): Promise<void>;
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Routes all agent calls through the gateway unified endpoint
|
|
146
|
+
* (/:orgSlug/:agentSlug/...) instead of /agents/:id/...
|
|
147
|
+
*
|
|
148
|
+
* Authenticates via API key (same Bearer token).
|
|
149
|
+
*/
|
|
150
|
+
export declare class GatewayClient {
|
|
151
|
+
private readonly client;
|
|
152
|
+
private readonly prefix;
|
|
153
|
+
readonly orgSlug: string;
|
|
154
|
+
readonly agentSlug: string;
|
|
155
|
+
constructor(client: AlmytyClient, orgSlug: string, agentSlug: string);
|
|
156
|
+
getInfo(): Promise<AgentInfo>;
|
|
157
|
+
invoke(input: Record<string, any>): Promise<any>;
|
|
158
|
+
startRun(input: any, options?: RunLimits & {
|
|
159
|
+
conversationId?: string;
|
|
160
|
+
}): Promise<AgentRun>;
|
|
161
|
+
getRun(runId: string): Promise<AgentRun>;
|
|
162
|
+
/**
|
|
163
|
+
* Stream run events via SSE. Calls handler for each event
|
|
164
|
+
* (llm.started, llm.chunk, llm.response, tool.started, tool.result,
|
|
165
|
+
* step.completed, run.completed, run.failed).
|
|
166
|
+
* Returns when the run completes or fails.
|
|
167
|
+
* Falls back to polling if SSE fails.
|
|
168
|
+
*/
|
|
169
|
+
streamRun(runId: string, handler: StreamEventHandler, signal?: AbortSignal): Promise<AgentRun>;
|
|
170
|
+
getConversationMessages(conversationId: string): Promise<Array<{
|
|
171
|
+
id: string;
|
|
172
|
+
role: string;
|
|
173
|
+
content: string;
|
|
174
|
+
createdAt: string;
|
|
175
|
+
}>>;
|
|
176
|
+
sendRunInput(runId: string, input: string): Promise<void>;
|
|
177
|
+
cancelRun(runId: string): Promise<void>;
|
|
178
|
+
pollRun(runId: string, options?: {
|
|
179
|
+
intervalMs?: number;
|
|
180
|
+
timeoutMs?: number;
|
|
181
|
+
onStep?: (run: AgentRun) => void;
|
|
182
|
+
}): Promise<AgentRun>;
|
|
71
183
|
}
|
package/dist/client.js
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
* autonomous run management, and polling.
|
|
7
7
|
*/
|
|
8
8
|
const TERMINAL_STATUSES = new Set(['completed', 'failed', 'cancelled', 'timeout']);
|
|
9
|
+
const TERMINAL_EVENT_TYPES = new Set(['run.completed', 'run.failed', 'run.cancelled', 'coding.exit']);
|
|
9
10
|
export class AlmytyClient {
|
|
10
11
|
baseUrl;
|
|
11
12
|
token;
|
|
@@ -38,6 +39,67 @@ export class AlmytyClient {
|
|
|
38
39
|
return null;
|
|
39
40
|
return res.json();
|
|
40
41
|
}
|
|
42
|
+
/**
|
|
43
|
+
* Connect to an SSE endpoint and call handler for each event.
|
|
44
|
+
* Returns when the stream ends or a terminal event is received.
|
|
45
|
+
*/
|
|
46
|
+
async streamSSE(path, handler, signal) {
|
|
47
|
+
const url = `${this.baseUrl}${path}`;
|
|
48
|
+
const res = await fetch(url, {
|
|
49
|
+
headers: { ...this.headers(), Accept: 'text/event-stream' },
|
|
50
|
+
signal,
|
|
51
|
+
});
|
|
52
|
+
if (!res.ok) {
|
|
53
|
+
const text = await res.text().catch(() => '');
|
|
54
|
+
throw new Error(`SSE ${res.status}: ${text}`);
|
|
55
|
+
}
|
|
56
|
+
const body = res.body;
|
|
57
|
+
if (!body)
|
|
58
|
+
return;
|
|
59
|
+
const reader = body.getReader();
|
|
60
|
+
const decoder = new TextDecoder();
|
|
61
|
+
let buffer = '';
|
|
62
|
+
try {
|
|
63
|
+
while (true) {
|
|
64
|
+
const { value, done } = await reader.read();
|
|
65
|
+
if (done)
|
|
66
|
+
break;
|
|
67
|
+
buffer += decoder.decode(value, { stream: true });
|
|
68
|
+
// Parse SSE frames
|
|
69
|
+
const lines = buffer.split('\n');
|
|
70
|
+
buffer = lines.pop(); // keep incomplete line
|
|
71
|
+
let eventType = 'message';
|
|
72
|
+
let dataLines = [];
|
|
73
|
+
for (const line of lines) {
|
|
74
|
+
if (line.startsWith('event: ')) {
|
|
75
|
+
eventType = line.slice(7).trim();
|
|
76
|
+
}
|
|
77
|
+
else if (line.startsWith('data: ')) {
|
|
78
|
+
dataLines.push(line.slice(6));
|
|
79
|
+
}
|
|
80
|
+
else if (line === '') {
|
|
81
|
+
// End of frame
|
|
82
|
+
if (dataLines.length) {
|
|
83
|
+
const raw = dataLines.join('\n');
|
|
84
|
+
try {
|
|
85
|
+
const data = JSON.parse(raw);
|
|
86
|
+
const event = { type: data.type || eventType, data };
|
|
87
|
+
handler(event);
|
|
88
|
+
if (TERMINAL_EVENT_TYPES.has(event.type))
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
catch { /* skip malformed */ }
|
|
92
|
+
dataLines = [];
|
|
93
|
+
eventType = 'message';
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
finally {
|
|
100
|
+
reader.releaseLock();
|
|
101
|
+
}
|
|
102
|
+
}
|
|
41
103
|
unwrap(data) {
|
|
42
104
|
return data?.data ?? data;
|
|
43
105
|
}
|
|
@@ -81,7 +143,15 @@ export class AlmytyClient {
|
|
|
81
143
|
all.find((a) => a.slug?.toLowerCase() === lower) ||
|
|
82
144
|
null);
|
|
83
145
|
}
|
|
84
|
-
// ──
|
|
146
|
+
// ── Gateway-scoped client ───────────────────────────────────────
|
|
147
|
+
/**
|
|
148
|
+
* Return a gateway-scoped client that routes all calls through
|
|
149
|
+
* /:orgSlug/:agentSlug instead of /agents/:id.
|
|
150
|
+
*/
|
|
151
|
+
gateway(orgSlug, agentSlug) {
|
|
152
|
+
return new GatewayClient(this, orgSlug, agentSlug);
|
|
153
|
+
}
|
|
154
|
+
// ── Workflow invocation ────────────────────────────────────────
|
|
85
155
|
async invokeAgent(agentId, input) {
|
|
86
156
|
const data = await this.request(`/agents/${encodeURIComponent(agentId)}/invoke`, {
|
|
87
157
|
method: 'POST',
|
|
@@ -155,4 +225,151 @@ export class AlmytyClient {
|
|
|
155
225
|
}
|
|
156
226
|
throw new Error(`Run ${runId} did not finish within ${Math.round(timeoutMs / 1000)}s`);
|
|
157
227
|
}
|
|
228
|
+
// ── Runners & coding sessions ───────────────────────────────────
|
|
229
|
+
/** The caller's registered runners, with their detected coding CLIs. */
|
|
230
|
+
async listRunners() {
|
|
231
|
+
const data = await this.request('/runners');
|
|
232
|
+
const list = data?.data ?? data ?? [];
|
|
233
|
+
return list.map((r) => ({
|
|
234
|
+
id: r.id,
|
|
235
|
+
name: r.name,
|
|
236
|
+
state: r.state,
|
|
237
|
+
labels: r.labels,
|
|
238
|
+
codingAgents: r.runtimeInfo?.codingAgents ?? [],
|
|
239
|
+
}));
|
|
240
|
+
}
|
|
241
|
+
/** Fresh probe of coding CLIs installed on the runner machine. */
|
|
242
|
+
async listRunnerCodingAgents(runnerId) {
|
|
243
|
+
const data = await this.request(`/runners/${encodeURIComponent(runnerId)}/coding/agents`);
|
|
244
|
+
return this.unwrap(data)?.agents ?? [];
|
|
245
|
+
}
|
|
246
|
+
/** Start a coding session (spawns the CLI with the task prompt). */
|
|
247
|
+
async startCodingSession(runnerId, options) {
|
|
248
|
+
const data = await this.request(`/runners/${encodeURIComponent(runnerId)}/coding/sessions`, { method: 'POST', body: JSON.stringify(options) });
|
|
249
|
+
return this.unwrap(data);
|
|
250
|
+
}
|
|
251
|
+
async getCodingSession(runnerId, sessionId) {
|
|
252
|
+
const data = await this.request(`/runners/${encodeURIComponent(runnerId)}/coding/sessions/${encodeURIComponent(sessionId)}`);
|
|
253
|
+
return this.unwrap(data);
|
|
254
|
+
}
|
|
255
|
+
/** Route a line of user input to the session's stdin. */
|
|
256
|
+
async sendCodingInput(runnerId, sessionId, data) {
|
|
257
|
+
await this.request(`/runners/${encodeURIComponent(runnerId)}/coding/sessions/${encodeURIComponent(sessionId)}/input`, { method: 'POST', body: JSON.stringify({ data }) });
|
|
258
|
+
}
|
|
259
|
+
async stopCodingSession(runnerId, sessionId, force = false) {
|
|
260
|
+
await this.request(`/runners/${encodeURIComponent(runnerId)}/coding/sessions/${encodeURIComponent(sessionId)}/stop`, { method: 'POST', body: JSON.stringify(force ? { force } : {}) });
|
|
261
|
+
}
|
|
262
|
+
/**
|
|
263
|
+
* Stream a coding session's output via SSE. Calls handler for each
|
|
264
|
+
* coding.output / coding.exit event; returns when the session exits or
|
|
265
|
+
* the stream ends.
|
|
266
|
+
*/
|
|
267
|
+
async streamCodingEvents(runnerId, sessionId, handler, signal) {
|
|
268
|
+
await this.streamSSE(`/runners/${encodeURIComponent(runnerId)}/coding/sessions/${encodeURIComponent(sessionId)}/events`, handler, signal);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
// ── Gateway-scoped client ───────────────────────────────────────
|
|
272
|
+
/**
|
|
273
|
+
* Routes all agent calls through the gateway unified endpoint
|
|
274
|
+
* (/:orgSlug/:agentSlug/...) instead of /agents/:id/...
|
|
275
|
+
*
|
|
276
|
+
* Authenticates via API key (same Bearer token).
|
|
277
|
+
*/
|
|
278
|
+
export class GatewayClient {
|
|
279
|
+
client;
|
|
280
|
+
prefix;
|
|
281
|
+
orgSlug;
|
|
282
|
+
agentSlug;
|
|
283
|
+
constructor(client, orgSlug, agentSlug) {
|
|
284
|
+
this.client = client;
|
|
285
|
+
this.orgSlug = orgSlug;
|
|
286
|
+
this.agentSlug = agentSlug;
|
|
287
|
+
this.prefix = `/${encodeURIComponent(orgSlug)}/${encodeURIComponent(agentSlug)}`;
|
|
288
|
+
}
|
|
289
|
+
async getInfo() {
|
|
290
|
+
const data = await this.client.request(this.prefix);
|
|
291
|
+
return data?.data ?? data;
|
|
292
|
+
}
|
|
293
|
+
async invoke(input) {
|
|
294
|
+
const data = await this.client.request(`${this.prefix}/invoke`, {
|
|
295
|
+
method: 'POST',
|
|
296
|
+
body: JSON.stringify({ input }),
|
|
297
|
+
});
|
|
298
|
+
return data?.data ?? data;
|
|
299
|
+
}
|
|
300
|
+
async startRun(input, options) {
|
|
301
|
+
const body = { input };
|
|
302
|
+
if (options?.maxSteps)
|
|
303
|
+
body.maxSteps = options.maxSteps;
|
|
304
|
+
if (options?.maxCostCents)
|
|
305
|
+
body.maxCostCents = options.maxCostCents;
|
|
306
|
+
if (options?.maxDurationMs)
|
|
307
|
+
body.maxDurationMs = options.maxDurationMs;
|
|
308
|
+
if (options?.conversationId)
|
|
309
|
+
body.conversationId = options.conversationId;
|
|
310
|
+
const data = await this.client.request(`${this.prefix}/runs`, {
|
|
311
|
+
method: 'POST',
|
|
312
|
+
body: JSON.stringify(body),
|
|
313
|
+
});
|
|
314
|
+
const run = data?.data ?? data;
|
|
315
|
+
return {
|
|
316
|
+
id: run.id,
|
|
317
|
+
agentId: run.agentId,
|
|
318
|
+
status: run.status,
|
|
319
|
+
conversationId: run.conversationId,
|
|
320
|
+
output: run.output,
|
|
321
|
+
error: run.error,
|
|
322
|
+
steps: run.steps,
|
|
323
|
+
};
|
|
324
|
+
}
|
|
325
|
+
async getRun(runId) {
|
|
326
|
+
const data = await this.client.request(`${this.prefix}/runs/${encodeURIComponent(runId)}`);
|
|
327
|
+
return (data?.data ?? data);
|
|
328
|
+
}
|
|
329
|
+
/**
|
|
330
|
+
* Stream run events via SSE. Calls handler for each event
|
|
331
|
+
* (llm.started, llm.chunk, llm.response, tool.started, tool.result,
|
|
332
|
+
* step.completed, run.completed, run.failed).
|
|
333
|
+
* Returns when the run completes or fails.
|
|
334
|
+
* Falls back to polling if SSE fails.
|
|
335
|
+
*/
|
|
336
|
+
async streamRun(runId, handler, signal) {
|
|
337
|
+
try {
|
|
338
|
+
await this.client.streamSSE(`${this.prefix}/runs/${encodeURIComponent(runId)}/stream`, handler, signal);
|
|
339
|
+
// Stream ended — get final state
|
|
340
|
+
return this.getRun(runId);
|
|
341
|
+
}
|
|
342
|
+
catch {
|
|
343
|
+
// SSE failed — fall back to polling until completion
|
|
344
|
+
return this.pollRun(runId);
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
async getConversationMessages(conversationId) {
|
|
348
|
+
const data = await this.client.request(`${this.prefix}/conversations/${encodeURIComponent(conversationId)}/messages`);
|
|
349
|
+
return data?.data ?? [];
|
|
350
|
+
}
|
|
351
|
+
async sendRunInput(runId, input) {
|
|
352
|
+
await this.client.request(`${this.prefix}/runs/${encodeURIComponent(runId)}/input`, { method: 'POST', body: JSON.stringify({ input }) });
|
|
353
|
+
}
|
|
354
|
+
async cancelRun(runId) {
|
|
355
|
+
await this.client.request(`${this.prefix}/runs/${encodeURIComponent(runId)}/cancel`, { method: 'POST' });
|
|
356
|
+
}
|
|
357
|
+
async pollRun(runId, options = {}) {
|
|
358
|
+
const intervalMs = options.intervalMs ?? 1500;
|
|
359
|
+
const timeoutMs = options.timeoutMs ?? 5 * 60_000;
|
|
360
|
+
const deadline = Date.now() + timeoutMs;
|
|
361
|
+
let lastStepCount = -1;
|
|
362
|
+
while (Date.now() < deadline) {
|
|
363
|
+
const run = await this.getRun(runId);
|
|
364
|
+
if (Array.isArray(run.steps) && run.steps.length !== lastStepCount) {
|
|
365
|
+
lastStepCount = run.steps.length;
|
|
366
|
+
options.onStep?.(run);
|
|
367
|
+
}
|
|
368
|
+
if (run.status && (TERMINAL_STATUSES.has(run.status) || run.status === 'waiting_input')) {
|
|
369
|
+
return run;
|
|
370
|
+
}
|
|
371
|
+
await new Promise((r) => setTimeout(r, intervalMs));
|
|
372
|
+
}
|
|
373
|
+
throw new Error(`Run ${runId} did not finish within ${Math.round(timeoutMs / 1000)}s`);
|
|
374
|
+
}
|
|
158
375
|
}
|
package/dist/credentials.d.ts
CHANGED
|
@@ -20,3 +20,8 @@ export declare function resolveCredentials(): StoredCredentials | null;
|
|
|
20
20
|
* Resolve credentials or exit with an error message.
|
|
21
21
|
*/
|
|
22
22
|
export declare function resolveCredentialsOrExit(): StoredCredentials;
|
|
23
|
+
/**
|
|
24
|
+
* Extract the default org slug from a JWT token.
|
|
25
|
+
* Returns null if the token isn't a JWT or has no orgs.
|
|
26
|
+
*/
|
|
27
|
+
export declare function getOrgSlugFromToken(token: string): string | null;
|
package/dist/credentials.js
CHANGED
|
@@ -43,3 +43,30 @@ export function resolveCredentialsOrExit() {
|
|
|
43
43
|
console.error(' export ALMYTY_TOKEN=<your-token>');
|
|
44
44
|
process.exit(1);
|
|
45
45
|
}
|
|
46
|
+
/**
|
|
47
|
+
* Extract the default org slug from a JWT token.
|
|
48
|
+
* Returns null if the token isn't a JWT or has no orgs.
|
|
49
|
+
*/
|
|
50
|
+
export function getOrgSlugFromToken(token) {
|
|
51
|
+
try {
|
|
52
|
+
const parts = token.split('.');
|
|
53
|
+
if (parts.length !== 3)
|
|
54
|
+
return null;
|
|
55
|
+
let payload = parts[1];
|
|
56
|
+
payload += '='.repeat((4 - payload.length % 4) % 4);
|
|
57
|
+
const decoded = JSON.parse(Buffer.from(payload, 'base64url').toString());
|
|
58
|
+
const orgs = decoded.organizations;
|
|
59
|
+
if (!Array.isArray(orgs) || !orgs.length)
|
|
60
|
+
return null;
|
|
61
|
+
// Use slug if available, otherwise derive from name
|
|
62
|
+
const org = orgs[0];
|
|
63
|
+
if (org.slug)
|
|
64
|
+
return org.slug;
|
|
65
|
+
if (org.name)
|
|
66
|
+
return org.name.toLowerCase().replace(/\s+/g, '-');
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +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';
|
|
1
|
+
export { AlmytyClient, GatewayClient } from './client.js';
|
|
2
|
+
export type { AgentInfo, AgentTool, AgentRun, PipelineNode, RunLimits, StreamEvent, StreamEventHandler, RunnerSummary, RunnerCodingAgent, CodingSession, } from './client.js';
|
|
3
|
+
export { loadCredentials, resolveCredentials, resolveCredentialsOrExit, getOrgSlugFromToken, CREDENTIALS_FILE, } from './credentials.js';
|
|
4
4
|
export type { StoredCredentials } from './credentials.js';
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { AlmytyClient } from './client.js';
|
|
2
|
-
export { loadCredentials, resolveCredentials, resolveCredentialsOrExit, CREDENTIALS_FILE, } from './credentials.js';
|
|
1
|
+
export { AlmytyClient, GatewayClient } from './client.js';
|
|
2
|
+
export { loadCredentials, resolveCredentials, resolveCredentialsOrExit, getOrgSlugFromToken, CREDENTIALS_FILE, } from './credentials.js';
|
package/package.json
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@almyty/client",
|
|
3
|
-
"version": "
|
|
4
|
-
"
|
|
3
|
+
"version": "1.2.0",
|
|
4
|
+
"publishConfig": {
|
|
5
|
+
"access": "public"
|
|
6
|
+
},
|
|
7
|
+
"description": "Shared HTTP client and credential resolver used by the almyty CLIs. Reads ~/.almyty/credentials.json; not usually installed on its own.",
|
|
5
8
|
"type": "module",
|
|
6
9
|
"main": "dist/index.js",
|
|
7
10
|
"types": "dist/index.d.ts",
|
|
@@ -20,10 +23,19 @@
|
|
|
20
23
|
"sdk"
|
|
21
24
|
],
|
|
22
25
|
"author": "almyty",
|
|
23
|
-
"license": "
|
|
26
|
+
"license": "Apache-2.0",
|
|
24
27
|
"devDependencies": {
|
|
25
28
|
"@types/node": "^25.4.0",
|
|
26
29
|
"typescript": "^5.3.0",
|
|
27
30
|
"vitest": "^4.1.0"
|
|
31
|
+
},
|
|
32
|
+
"homepage": "https://almyty.com",
|
|
33
|
+
"repository": {
|
|
34
|
+
"type": "git",
|
|
35
|
+
"url": "git+https://github.com/almyty-inc/almyty.git",
|
|
36
|
+
"directory": "packages/client"
|
|
37
|
+
},
|
|
38
|
+
"bugs": {
|
|
39
|
+
"url": "https://github.com/almyty-inc/almyty/issues"
|
|
28
40
|
}
|
|
29
41
|
}
|