arga-sdk 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.
package/README.md ADDED
@@ -0,0 +1,96 @@
1
+ # Arga TypeScript SDK
2
+
3
+ TypeScript client for the [Arga](https://argalabs.com) API. Zero runtime dependencies -- uses native `fetch` (Node 18+).
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install arga-sdk
9
+ ```
10
+
11
+ ## Quick Start
12
+
13
+ ```typescript
14
+ import { Arga } from 'arga-sdk';
15
+
16
+ const client = new Arga({ apiKey: 'arga_...' });
17
+
18
+ // Create a URL run
19
+ const run = await client.runs.createUrlRun({
20
+ url: 'https://staging.myapp.com',
21
+ twins: ['stripe', 'slack'],
22
+ });
23
+ console.log(run.runId, run.status);
24
+
25
+ // Poll until the run completes
26
+ const detail = await client.runs.wait(run.runId);
27
+ console.log(detail.status, detail.resultsJson);
28
+
29
+ // Stream results via SSE
30
+ for await (const event of client.runs.streamResults(run.runId)) {
31
+ console.log(event);
32
+ }
33
+ ```
34
+
35
+ ## Configuration
36
+
37
+ ```typescript
38
+ const client = new Arga({
39
+ apiKey: 'arga_...', // required
40
+ baseUrl: 'https://app.argalabs.com', // optional, this is the default
41
+ });
42
+ ```
43
+
44
+ ## Available Methods
45
+
46
+ ### Runs
47
+
48
+ | Method | Description |
49
+ |--------|-------------|
50
+ | `client.runs.createUrlRun(params)` | Create a URL validation run |
51
+ | `client.runs.createPrRun(params)` | Create a PR validation run |
52
+ | `client.runs.createAgentRun(params)` | Create an autonomous agent run |
53
+ | `client.runs.get(runId)` | Get full run details |
54
+ | `client.runs.streamResults(runId)` | Stream run results as SSE events |
55
+ | `client.runs.cancel(runId)` | Cancel a running run |
56
+ | `client.runs.wait(runId, opts?)` | Poll until terminal status |
57
+
58
+ ### Twins
59
+
60
+ | Method | Description |
61
+ |--------|-------------|
62
+ | `client.twins.list()` | List all available twins |
63
+ | `client.twins.provision(params)` | Provision twins for a run |
64
+ | `client.twins.getStatus(runId)` | Get twin provisioning status |
65
+ | `client.twins.extend(runId, params?)` | Extend twin TTL |
66
+
67
+ ### Scenarios
68
+
69
+ | Method | Description |
70
+ |--------|-------------|
71
+ | `client.scenarios.create(params)` | Create a new scenario |
72
+ | `client.scenarios.list(params?)` | List scenarios (filter by twin or tag) |
73
+ | `client.scenarios.get(scenarioId)` | Get a scenario by ID |
74
+
75
+ ## Error Handling
76
+
77
+ ```typescript
78
+ import { ArgaAPIError } from 'arga-sdk';
79
+
80
+ try {
81
+ await client.runs.get('nonexistent');
82
+ } catch (err) {
83
+ if (err instanceof ArgaAPIError) {
84
+ console.error(err.statusCode); // e.g. 404
85
+ console.error(err.message); // error detail from API
86
+ }
87
+ }
88
+ ```
89
+
90
+ ## Documentation
91
+
92
+ Full API documentation is available at [docs.argalabs.com](https://docs.argalabs.com).
93
+
94
+ ## License
95
+
96
+ MIT
package/dist/index.cjs ADDED
@@ -0,0 +1,369 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ Arga: () => Arga,
24
+ ArgaAPIError: () => ArgaAPIError,
25
+ ArgaError: () => ArgaError
26
+ });
27
+ module.exports = __toCommonJS(index_exports);
28
+
29
+ // src/errors.ts
30
+ var ArgaError = class extends Error {
31
+ constructor(message) {
32
+ super(message);
33
+ this.name = "ArgaError";
34
+ }
35
+ };
36
+ var ArgaAPIError = class extends ArgaError {
37
+ /** HTTP status code returned by the API. */
38
+ statusCode;
39
+ /** Raw Response object from fetch. */
40
+ response;
41
+ /** Parsed response body, if available. */
42
+ body;
43
+ constructor(message, statusCode, response, body) {
44
+ super(message);
45
+ this.name = "ArgaAPIError";
46
+ this.statusCode = statusCode;
47
+ this.response = response;
48
+ this.body = body;
49
+ }
50
+ };
51
+
52
+ // src/http.ts
53
+ function toSnakeCase(str) {
54
+ return str.replace(/[A-Z]/g, (ch) => `_${ch.toLowerCase()}`);
55
+ }
56
+ function toCamelCase(str) {
57
+ return str.replace(/_([a-z])/g, (_, ch) => ch.toUpperCase());
58
+ }
59
+ function convertKeys(obj, mapper) {
60
+ if (Array.isArray(obj)) {
61
+ return obj.map((item) => convertKeys(item, mapper));
62
+ }
63
+ if (obj !== null && typeof obj === "object" && !(obj instanceof Date)) {
64
+ const result = {};
65
+ for (const [key, value] of Object.entries(obj)) {
66
+ result[mapper(key)] = convertKeys(value, mapper);
67
+ }
68
+ return result;
69
+ }
70
+ return obj;
71
+ }
72
+ function toSnakeCaseKeys(obj) {
73
+ return convertKeys(obj, toSnakeCase);
74
+ }
75
+ function toCamelCaseKeys(obj) {
76
+ return convertKeys(obj, toCamelCase);
77
+ }
78
+ var HttpClient = class {
79
+ baseUrl;
80
+ apiKey;
81
+ _fetch;
82
+ constructor(opts) {
83
+ this.baseUrl = opts.baseUrl.replace(/\/+$/, "");
84
+ this.apiKey = opts.apiKey;
85
+ this._fetch = opts.fetch;
86
+ }
87
+ // ---- public helpers -----------------------------------------------------
88
+ async get(path, query) {
89
+ const url = this.buildUrl(path, query);
90
+ const res = await this._fetch(url, {
91
+ method: "GET",
92
+ headers: this.headers()
93
+ });
94
+ return this.handleResponse(res);
95
+ }
96
+ async post(path, body) {
97
+ const url = this.buildUrl(path);
98
+ const res = await this._fetch(url, {
99
+ method: "POST",
100
+ headers: this.headers(),
101
+ body: body !== void 0 ? JSON.stringify(toSnakeCaseKeys(body)) : void 0
102
+ });
103
+ return this.handleResponse(res);
104
+ }
105
+ /**
106
+ * Perform a GET request and return the raw Response, for SSE streaming.
107
+ */
108
+ async getRaw(path) {
109
+ const url = this.buildUrl(path);
110
+ const res = await this._fetch(url, {
111
+ method: "GET",
112
+ headers: {
113
+ ...this.headers(),
114
+ Accept: "text/event-stream"
115
+ }
116
+ });
117
+ if (!res.ok) {
118
+ await this.throwApiError(res);
119
+ }
120
+ return res;
121
+ }
122
+ // ---- internals ----------------------------------------------------------
123
+ buildUrl(path, query) {
124
+ const url = new URL(`${this.baseUrl}${path}`);
125
+ if (query) {
126
+ for (const [k, v] of Object.entries(query)) {
127
+ if (v !== void 0 && v !== "") {
128
+ url.searchParams.set(k, v);
129
+ }
130
+ }
131
+ }
132
+ return url.toString();
133
+ }
134
+ headers() {
135
+ return {
136
+ Authorization: `Bearer ${this.apiKey}`,
137
+ "Content-Type": "application/json"
138
+ };
139
+ }
140
+ async handleResponse(res) {
141
+ if (!res.ok) {
142
+ await this.throwApiError(res);
143
+ }
144
+ const json = await res.json();
145
+ return toCamelCaseKeys(json);
146
+ }
147
+ async throwApiError(res) {
148
+ let body;
149
+ try {
150
+ body = await res.json();
151
+ } catch {
152
+ body = await res.text().catch(() => null);
153
+ }
154
+ const detail = body && typeof body === "object" && "detail" in body ? String(body.detail) : `HTTP ${res.status}`;
155
+ throw new ArgaAPIError(detail, res.status, res, body);
156
+ }
157
+ };
158
+ async function* parseSSE(response) {
159
+ const body = response.body;
160
+ if (!body) {
161
+ throw new ArgaError("Response body is null \u2014 cannot stream SSE");
162
+ }
163
+ const reader = body.getReader();
164
+ const decoder = new TextDecoder();
165
+ let buffer = "";
166
+ let currentEvent;
167
+ let currentData = [];
168
+ try {
169
+ while (true) {
170
+ const { done, value } = await reader.read();
171
+ if (done) break;
172
+ buffer += decoder.decode(value, { stream: true });
173
+ const lines = buffer.split("\n");
174
+ buffer = lines.pop() ?? "";
175
+ for (const line of lines) {
176
+ if (line === "") {
177
+ if (currentData.length > 0) {
178
+ yield {
179
+ event: currentEvent,
180
+ data: currentData.join("\n")
181
+ };
182
+ }
183
+ currentEvent = void 0;
184
+ currentData = [];
185
+ } else if (line.startsWith("event:")) {
186
+ currentEvent = line.slice(6).trim();
187
+ } else if (line.startsWith("data:")) {
188
+ currentData.push(line.slice(5).trim());
189
+ }
190
+ }
191
+ }
192
+ if (currentData.length > 0) {
193
+ yield {
194
+ event: currentEvent,
195
+ data: currentData.join("\n")
196
+ };
197
+ }
198
+ } finally {
199
+ reader.releaseLock();
200
+ }
201
+ }
202
+
203
+ // src/resources/runs.ts
204
+ var TERMINAL_STATUSES = /* @__PURE__ */ new Set([
205
+ "completed",
206
+ "failed",
207
+ "cancelled",
208
+ "canceled",
209
+ "error",
210
+ "timed_out"
211
+ ]);
212
+ var RunsResource = class {
213
+ constructor(http) {
214
+ this.http = http;
215
+ }
216
+ http;
217
+ /** Create a URL run. */
218
+ async createUrlRun(params) {
219
+ return this.http.post("/validate/url-run", params);
220
+ }
221
+ /** Create a PR run. */
222
+ async createPrRun(params) {
223
+ return this.http.post("/validate/pr-run", params);
224
+ }
225
+ /** Create an agent (sandbox) run. */
226
+ async createAgentRun(params) {
227
+ return this.http.post("/validate/agent-run", params);
228
+ }
229
+ /** Get full details of a run by ID. */
230
+ async get(runId) {
231
+ return this.http.get(`/runs/${encodeURIComponent(runId)}`);
232
+ }
233
+ /**
234
+ * Stream run results as Server-Sent Events.
235
+ *
236
+ * Returns an `AsyncIterable<RunEvent>`. Each yielded event contains an
237
+ * optional `event` field and a parsed `data` payload.
238
+ */
239
+ async *streamResults(runId) {
240
+ const response = await this.http.getRaw(
241
+ `/validate/${encodeURIComponent(runId)}/results`
242
+ );
243
+ for await (const sse of parseSSE(response)) {
244
+ let data;
245
+ try {
246
+ data = toCamelCaseKeys(JSON.parse(sse.data));
247
+ } catch {
248
+ data = sse.data;
249
+ }
250
+ yield { event: sse.event, data };
251
+ }
252
+ }
253
+ /** Cancel a run. */
254
+ async cancel(runId) {
255
+ return this.http.post(
256
+ `/validate/${encodeURIComponent(runId)}/cancel`
257
+ );
258
+ }
259
+ /**
260
+ * Poll a run until it reaches a terminal status.
261
+ *
262
+ * @param runId - The run ID to poll.
263
+ * @param opts - Optional polling configuration.
264
+ * @returns The final `RunDetail` once the run has completed (or failed/cancelled).
265
+ * @throws ArgaError if the timeout is exceeded.
266
+ */
267
+ async wait(runId, opts) {
268
+ const pollInterval = opts?.pollInterval ?? 2500;
269
+ const timeout = opts?.timeout ?? 6e5;
270
+ const deadline = Date.now() + timeout;
271
+ while (true) {
272
+ const detail = await this.get(runId);
273
+ if (TERMINAL_STATUSES.has(detail.status)) {
274
+ return detail;
275
+ }
276
+ if (Date.now() + pollInterval > deadline) {
277
+ throw new ArgaError(
278
+ `Timed out waiting for run ${runId} after ${timeout}ms (last status: ${detail.status})`
279
+ );
280
+ }
281
+ await new Promise((resolve) => setTimeout(resolve, pollInterval));
282
+ }
283
+ }
284
+ };
285
+
286
+ // src/resources/twins.ts
287
+ var TwinsResource = class {
288
+ constructor(http) {
289
+ this.http = http;
290
+ }
291
+ http;
292
+ /** List all available twins. */
293
+ async list() {
294
+ return this.http.get("/validate/twins");
295
+ }
296
+ /** Provision a set of twins. */
297
+ async provision(params) {
298
+ return this.http.post(
299
+ "/validate/twins/provision",
300
+ params
301
+ );
302
+ }
303
+ /** Get the provisioning status of twins for a given run. */
304
+ async getStatus(runId) {
305
+ return this.http.get(
306
+ `/validate/twins/provision/${encodeURIComponent(runId)}/status`
307
+ );
308
+ }
309
+ /** Extend the TTL of provisioned twins. */
310
+ async extend(runId, params) {
311
+ return this.http.post(
312
+ `/validate/twins/provision/${encodeURIComponent(runId)}/extend`,
313
+ params
314
+ );
315
+ }
316
+ };
317
+
318
+ // src/resources/scenarios.ts
319
+ var ScenariosResource = class {
320
+ constructor(http) {
321
+ this.http = http;
322
+ }
323
+ http;
324
+ /** Create a new scenario. */
325
+ async create(params) {
326
+ return this.http.post("/scenarios", params);
327
+ }
328
+ /** List scenarios, optionally filtered by twin or tag. */
329
+ async list(params) {
330
+ const query = {};
331
+ if (params?.twin) query.twin = params.twin;
332
+ if (params?.tag) query.tag = params.tag;
333
+ return this.http.get("/scenarios", query);
334
+ }
335
+ /** Get a scenario by ID. */
336
+ async get(scenarioId) {
337
+ return this.http.get(
338
+ `/scenarios/${encodeURIComponent(scenarioId)}`
339
+ );
340
+ }
341
+ };
342
+
343
+ // src/client.ts
344
+ var DEFAULT_BASE_URL = "https://app.argalabs.com";
345
+ var Arga = class {
346
+ /** Run management: create, get, stream, cancel, and wait for runs. */
347
+ runs;
348
+ /** Twin management: list, provision, check status, and extend twins. */
349
+ twins;
350
+ /** Scenario management: create, list, and get scenarios. */
351
+ scenarios;
352
+ constructor(options) {
353
+ const http = new HttpClient({
354
+ baseUrl: options.baseUrl ?? DEFAULT_BASE_URL,
355
+ apiKey: options.apiKey,
356
+ fetch: options.fetch ?? globalThis.fetch.bind(globalThis)
357
+ });
358
+ this.runs = new RunsResource(http);
359
+ this.twins = new TwinsResource(http);
360
+ this.scenarios = new ScenariosResource(http);
361
+ }
362
+ };
363
+ // Annotate the CommonJS export names for ESM import in node:
364
+ 0 && (module.exports = {
365
+ Arga,
366
+ ArgaAPIError,
367
+ ArgaError
368
+ });
369
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/errors.ts","../src/http.ts","../src/resources/runs.ts","../src/resources/twins.ts","../src/resources/scenarios.ts","../src/client.ts"],"sourcesContent":["export { Arga } from \"./client.js\";\n\nexport { ArgaError, ArgaAPIError } from \"./errors.js\";\n\nexport type {\n ArgaClientOptions,\n CreateUrlRunParams,\n CreatePrRunParams,\n CreateAgentRunParams,\n Run,\n RunDetail,\n RunEvent,\n StepSummary,\n CancelRunResponse,\n WaitOptions,\n Twin,\n ProvisionTwinsParams,\n ProvisionTwinsResponse,\n TwinInstance,\n TwinProvisionStatus,\n ExtendTwinsParams,\n ExtendTwinsResponse,\n CreateScenarioParams,\n ListScenariosParams,\n Scenario,\n} from \"./types.js\";\n","/**\n * Base error class for all Arga SDK errors.\n */\nexport class ArgaError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"ArgaError\";\n }\n}\n\n/**\n * Thrown when the Arga API returns a non-2xx response.\n */\nexport class ArgaAPIError extends ArgaError {\n /** HTTP status code returned by the API. */\n readonly statusCode: number;\n /** Raw Response object from fetch. */\n readonly response: Response;\n /** Parsed response body, if available. */\n readonly body: unknown;\n\n constructor(\n message: string,\n statusCode: number,\n response: Response,\n body?: unknown,\n ) {\n super(message);\n this.name = \"ArgaAPIError\";\n this.statusCode = statusCode;\n this.response = response;\n this.body = body;\n }\n}\n","import { ArgaAPIError, ArgaError } from \"./errors.js\";\n\n// ---------------------------------------------------------------------------\n// Case conversion helpers\n// ---------------------------------------------------------------------------\n\n/** Convert a camelCase string to snake_case. */\nfunction toSnakeCase(str: string): string {\n return str.replace(/[A-Z]/g, (ch) => `_${ch.toLowerCase()}`);\n}\n\n/** Convert a snake_case string to camelCase. */\nfunction toCamelCase(str: string): string {\n return str.replace(/_([a-z])/g, (_, ch: string) => ch.toUpperCase());\n}\n\n/** Recursively convert all object keys using the supplied key mapper. */\nfunction convertKeys(\n obj: unknown,\n mapper: (key: string) => string,\n): unknown {\n if (Array.isArray(obj)) {\n return obj.map((item) => convertKeys(item, mapper));\n }\n if (obj !== null && typeof obj === \"object\" && !(obj instanceof Date)) {\n const result: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(obj as Record<string, unknown>)) {\n result[mapper(key)] = convertKeys(value, mapper);\n }\n return result;\n }\n return obj;\n}\n\n/** Convert all keys in an object tree from camelCase to snake_case. */\nexport function toSnakeCaseKeys(obj: unknown): unknown {\n return convertKeys(obj, toSnakeCase);\n}\n\n/** Convert all keys in an object tree from snake_case to camelCase. */\nexport function toCamelCaseKeys(obj: unknown): unknown {\n return convertKeys(obj, toCamelCase);\n}\n\n// ---------------------------------------------------------------------------\n// HTTP client\n// ---------------------------------------------------------------------------\n\nexport interface HttpClientOptions {\n baseUrl: string;\n apiKey: string;\n fetch: typeof globalThis.fetch;\n}\n\nexport class HttpClient {\n private readonly baseUrl: string;\n private readonly apiKey: string;\n private readonly _fetch: typeof globalThis.fetch;\n\n constructor(opts: HttpClientOptions) {\n // Strip trailing slash from base URL\n this.baseUrl = opts.baseUrl.replace(/\\/+$/, \"\");\n this.apiKey = opts.apiKey;\n this._fetch = opts.fetch;\n }\n\n // ---- public helpers -----------------------------------------------------\n\n async get<T>(path: string, query?: Record<string, string>): Promise<T> {\n const url = this.buildUrl(path, query);\n const res = await this._fetch(url, {\n method: \"GET\",\n headers: this.headers(),\n });\n return this.handleResponse<T>(res);\n }\n\n async post<T>(path: string, body?: unknown): Promise<T> {\n const url = this.buildUrl(path);\n const res = await this._fetch(url, {\n method: \"POST\",\n headers: this.headers(),\n body: body !== undefined ? JSON.stringify(toSnakeCaseKeys(body)) : undefined,\n });\n return this.handleResponse<T>(res);\n }\n\n /**\n * Perform a GET request and return the raw Response, for SSE streaming.\n */\n async getRaw(path: string): Promise<Response> {\n const url = this.buildUrl(path);\n const res = await this._fetch(url, {\n method: \"GET\",\n headers: {\n ...this.headers(),\n Accept: \"text/event-stream\",\n },\n });\n if (!res.ok) {\n await this.throwApiError(res);\n }\n return res;\n }\n\n // ---- internals ----------------------------------------------------------\n\n private buildUrl(path: string, query?: Record<string, string>): string {\n const url = new URL(`${this.baseUrl}${path}`);\n if (query) {\n for (const [k, v] of Object.entries(query)) {\n if (v !== undefined && v !== \"\") {\n url.searchParams.set(k, v);\n }\n }\n }\n return url.toString();\n }\n\n private headers(): Record<string, string> {\n return {\n Authorization: `Bearer ${this.apiKey}`,\n \"Content-Type\": \"application/json\",\n };\n }\n\n private async handleResponse<T>(res: Response): Promise<T> {\n if (!res.ok) {\n await this.throwApiError(res);\n }\n const json = await res.json();\n return toCamelCaseKeys(json) as T;\n }\n\n private async throwApiError(res: Response): Promise<never> {\n let body: unknown;\n try {\n body = await res.json();\n } catch {\n body = await res.text().catch(() => null);\n }\n\n const detail =\n body && typeof body === \"object\" && \"detail\" in (body as Record<string, unknown>)\n ? String((body as Record<string, unknown>).detail)\n : `HTTP ${res.status}`;\n\n throw new ArgaAPIError(detail, res.status, res, body);\n }\n}\n\n// ---------------------------------------------------------------------------\n// SSE parser\n// ---------------------------------------------------------------------------\n\nexport interface SSEEvent {\n event?: string;\n data: string;\n}\n\n/**\n * Parse a fetch Response body as a Server-Sent Events stream.\n * Yields individual SSE events as they arrive.\n */\nexport async function* parseSSE(response: Response): AsyncIterable<SSEEvent> {\n const body = response.body;\n if (!body) {\n throw new ArgaError(\"Response body is null — cannot stream SSE\");\n }\n\n const reader = body.getReader();\n const decoder = new TextDecoder();\n let buffer = \"\";\n let currentEvent: string | undefined;\n let currentData: string[] = [];\n\n try {\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n const lines = buffer.split(\"\\n\");\n // Keep the last (possibly incomplete) line in the buffer\n buffer = lines.pop() ?? \"\";\n\n for (const line of lines) {\n if (line === \"\") {\n // Empty line = end of event\n if (currentData.length > 0) {\n yield {\n event: currentEvent,\n data: currentData.join(\"\\n\"),\n };\n }\n currentEvent = undefined;\n currentData = [];\n } else if (line.startsWith(\"event:\")) {\n currentEvent = line.slice(6).trim();\n } else if (line.startsWith(\"data:\")) {\n currentData.push(line.slice(5).trim());\n }\n // Ignore comments (lines starting with ':') and unknown fields\n }\n }\n\n // Flush any remaining event\n if (currentData.length > 0) {\n yield {\n event: currentEvent,\n data: currentData.join(\"\\n\"),\n };\n }\n } finally {\n reader.releaseLock();\n }\n}\n","import type { HttpClient } from \"../http.js\";\nimport { parseSSE, toCamelCaseKeys } from \"../http.js\";\nimport { ArgaError } from \"../errors.js\";\nimport type {\n CancelRunResponse,\n CreateAgentRunParams,\n CreatePrRunParams,\n CreateUrlRunParams,\n Run,\n RunDetail,\n RunEvent,\n WaitOptions,\n} from \"../types.js\";\n\nconst TERMINAL_STATUSES = new Set([\n \"completed\",\n \"failed\",\n \"cancelled\",\n \"canceled\",\n \"error\",\n \"timed_out\",\n]);\n\nexport class RunsResource {\n constructor(private readonly http: HttpClient) {}\n\n /** Create a URL run. */\n async createUrlRun(params: CreateUrlRunParams): Promise<Run> {\n return this.http.post<Run>(\"/validate/url-run\", params);\n }\n\n /** Create a PR run. */\n async createPrRun(params: CreatePrRunParams): Promise<Run> {\n return this.http.post<Run>(\"/validate/pr-run\", params);\n }\n\n /** Create an agent (sandbox) run. */\n async createAgentRun(params: CreateAgentRunParams): Promise<Run> {\n return this.http.post<Run>(\"/validate/agent-run\", params);\n }\n\n /** Get full details of a run by ID. */\n async get(runId: string): Promise<RunDetail> {\n return this.http.get<RunDetail>(`/runs/${encodeURIComponent(runId)}`);\n }\n\n /**\n * Stream run results as Server-Sent Events.\n *\n * Returns an `AsyncIterable<RunEvent>`. Each yielded event contains an\n * optional `event` field and a parsed `data` payload.\n */\n async *streamResults(runId: string): AsyncIterable<RunEvent> {\n const response = await this.http.getRaw(\n `/validate/${encodeURIComponent(runId)}/results`,\n );\n\n for await (const sse of parseSSE(response)) {\n let data: unknown;\n try {\n data = toCamelCaseKeys(JSON.parse(sse.data));\n } catch {\n data = sse.data;\n }\n yield { event: sse.event, data };\n }\n }\n\n /** Cancel a run. */\n async cancel(runId: string): Promise<CancelRunResponse> {\n return this.http.post<CancelRunResponse>(\n `/validate/${encodeURIComponent(runId)}/cancel`,\n );\n }\n\n /**\n * Poll a run until it reaches a terminal status.\n *\n * @param runId - The run ID to poll.\n * @param opts - Optional polling configuration.\n * @returns The final `RunDetail` once the run has completed (or failed/cancelled).\n * @throws ArgaError if the timeout is exceeded.\n */\n async wait(runId: string, opts?: WaitOptions): Promise<RunDetail> {\n const pollInterval = opts?.pollInterval ?? 2500;\n const timeout = opts?.timeout ?? 600_000;\n const deadline = Date.now() + timeout;\n\n while (true) {\n const detail = await this.get(runId);\n if (TERMINAL_STATUSES.has(detail.status)) {\n return detail;\n }\n if (Date.now() + pollInterval > deadline) {\n throw new ArgaError(\n `Timed out waiting for run ${runId} after ${timeout}ms (last status: ${detail.status})`,\n );\n }\n await new Promise((resolve) => setTimeout(resolve, pollInterval));\n }\n }\n}\n","import type { HttpClient } from \"../http.js\";\nimport type {\n ExtendTwinsParams,\n ExtendTwinsResponse,\n ProvisionTwinsParams,\n ProvisionTwinsResponse,\n Twin,\n TwinProvisionStatus,\n} from \"../types.js\";\n\nexport class TwinsResource {\n constructor(private readonly http: HttpClient) {}\n\n /** List all available twins. */\n async list(): Promise<Twin[]> {\n return this.http.get<Twin[]>(\"/validate/twins\");\n }\n\n /** Provision a set of twins. */\n async provision(params: ProvisionTwinsParams): Promise<ProvisionTwinsResponse> {\n return this.http.post<ProvisionTwinsResponse>(\n \"/validate/twins/provision\",\n params,\n );\n }\n\n /** Get the provisioning status of twins for a given run. */\n async getStatus(runId: string): Promise<TwinProvisionStatus> {\n return this.http.get<TwinProvisionStatus>(\n `/validate/twins/provision/${encodeURIComponent(runId)}/status`,\n );\n }\n\n /** Extend the TTL of provisioned twins. */\n async extend(\n runId: string,\n params?: ExtendTwinsParams,\n ): Promise<ExtendTwinsResponse> {\n return this.http.post<ExtendTwinsResponse>(\n `/validate/twins/provision/${encodeURIComponent(runId)}/extend`,\n params,\n );\n }\n}\n","import type { HttpClient } from \"../http.js\";\nimport type {\n CreateScenarioParams,\n ListScenariosParams,\n Scenario,\n} from \"../types.js\";\n\nexport class ScenariosResource {\n constructor(private readonly http: HttpClient) {}\n\n /** Create a new scenario. */\n async create(params: CreateScenarioParams): Promise<Scenario> {\n return this.http.post<Scenario>(\"/scenarios\", params);\n }\n\n /** List scenarios, optionally filtered by twin or tag. */\n async list(params?: ListScenariosParams): Promise<Scenario[]> {\n const query: Record<string, string> = {};\n if (params?.twin) query.twin = params.twin;\n if (params?.tag) query.tag = params.tag;\n return this.http.get<Scenario[]>(\"/scenarios\", query);\n }\n\n /** Get a scenario by ID. */\n async get(scenarioId: string): Promise<Scenario> {\n return this.http.get<Scenario>(\n `/scenarios/${encodeURIComponent(scenarioId)}`,\n );\n }\n}\n","import { HttpClient } from \"./http.js\";\nimport { RunsResource } from \"./resources/runs.js\";\nimport { TwinsResource } from \"./resources/twins.js\";\nimport { ScenariosResource } from \"./resources/scenarios.js\";\nimport type { ArgaClientOptions } from \"./types.js\";\n\nconst DEFAULT_BASE_URL = \"https://app.argalabs.com\";\n\n/**\n * Main client for the Arga API.\n *\n * ```ts\n * import { Arga } from 'arga-sdk';\n *\n * const client = new Arga({ apiKey: 'arga_...' });\n * const run = await client.runs.createUrlRun({ url: 'https://staging.myapp.com' });\n * ```\n */\nexport class Arga {\n /** Run management: create, get, stream, cancel, and wait for runs. */\n readonly runs: RunsResource;\n /** Twin management: list, provision, check status, and extend twins. */\n readonly twins: TwinsResource;\n /** Scenario management: create, list, and get scenarios. */\n readonly scenarios: ScenariosResource;\n\n constructor(options: ArgaClientOptions) {\n const http = new HttpClient({\n baseUrl: options.baseUrl ?? DEFAULT_BASE_URL,\n apiKey: options.apiKey,\n fetch: options.fetch ?? globalThis.fetch.bind(globalThis),\n });\n\n this.runs = new RunsResource(http);\n this.twins = new TwinsResource(http);\n this.scenarios = new ScenariosResource(http);\n }\n}\n"],"mappings":"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","names":[]}