daishi-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.
@@ -0,0 +1,367 @@
1
+ // Generated from the Daishi Studio API's OpenAPI document, https://daishi.ai/api/v1/openapi.json. Do not edit.
2
+ /* eslint-disable */
3
+ /** The API version these types describe. */
4
+ export const API_VERSION = '1.0.0';
5
+ /** One method per API operation. The transport is the subclass's. */
6
+ export class Operations {
7
+ /**
8
+ * API index.
9
+ *
10
+ * Discover the API without a token: endpoints, docs, the MCP path.
11
+ *
12
+ * @see https://daishi.ai/docs/developer/rest#ep-index
13
+ */
14
+ index() {
15
+ return this.request('GET', '/');
16
+ }
17
+ /**
18
+ * OpenAPI document.
19
+ *
20
+ * The API as an OpenAPI 3.1 document, for generators and API clients.
21
+ *
22
+ * @see https://daishi.ai/docs/developer/rest#ep-openapi
23
+ */
24
+ openapi() {
25
+ return this.request('GET', '/openapi.json');
26
+ }
27
+ /**
28
+ * Who am I.
29
+ *
30
+ * The account, plan, this token’s scopes and the limits that bound scenarios and runs.
31
+ *
32
+ * @see https://daishi.ai/docs/developer/rest#ep-me
33
+ */
34
+ me() {
35
+ return this.request('GET', '/me');
36
+ }
37
+ /**
38
+ * Scenario schema.
39
+ *
40
+ * JSON Schema (draft 2020-12) for a scenario body, with the live resource types and anchor ids.
41
+ *
42
+ * @see https://daishi.ai/docs/developer/rest#ep-schema
43
+ */
44
+ schema() {
45
+ return this.request('GET', '/scenarios/schema');
46
+ }
47
+ /**
48
+ * Library scenarios.
49
+ *
50
+ * The scenarios the platform ships. Any of their ids can be launched directly.
51
+ *
52
+ * @see https://daishi.ai/docs/developer/rest#ep-library-scenarios
53
+ */
54
+ libraryScenarios() {
55
+ return this.request('GET', '/library/scenarios');
56
+ }
57
+ /**
58
+ * Anchor bots.
59
+ *
60
+ * Baseline bot policies and populations a scenario may field as `env.anchors`.
61
+ *
62
+ * @see https://daishi.ai/docs/developer/rest#ep-library-anchors
63
+ */
64
+ libraryAnchors() {
65
+ return this.request('GET', '/library/anchors');
66
+ }
67
+ /**
68
+ * Validate a scenario.
69
+ *
70
+ * Dry run: the normalized definition, its content hash and the plan receipt. Nothing is saved.
71
+ *
72
+ * @see https://daishi.ai/docs/developer/rest#ep-validate
73
+ */
74
+ validate(body) {
75
+ return this.request('POST', '/scenarios/validate', { body });
76
+ }
77
+ /**
78
+ * List your scenarios.
79
+ *
80
+ * Every scenario saved on the account, newest first.
81
+ *
82
+ * @see https://daishi.ai/docs/developer/rest#ep-list-scenarios
83
+ */
84
+ listScenarios() {
85
+ return this.request('GET', '/scenarios');
86
+ }
87
+ /**
88
+ * Create a scenario.
89
+ *
90
+ * Save a scenario. Idempotent on name plus content hash.
91
+ *
92
+ * @see https://daishi.ai/docs/developer/rest#ep-create-scenario
93
+ */
94
+ createScenario(body) {
95
+ return this.request('POST', '/scenarios', { body });
96
+ }
97
+ /**
98
+ * Get a scenario.
99
+ *
100
+ * One saved scenario by id.
101
+ *
102
+ * @see https://daishi.ai/docs/developer/rest#ep-get-scenario
103
+ */
104
+ getScenario(id) {
105
+ return this.request('GET', `/scenarios/${encodeURIComponent(id)}`);
106
+ }
107
+ /**
108
+ * Update a scenario.
109
+ *
110
+ * Edit a saved scenario in place. Omitted fields keep their value.
111
+ *
112
+ * @see https://daishi.ai/docs/developer/rest#ep-update-scenario
113
+ */
114
+ updateScenario(id, body) {
115
+ return this.request('PUT', `/scenarios/${encodeURIComponent(id)}`, { body });
116
+ }
117
+ /**
118
+ * Delete a scenario.
119
+ *
120
+ * Remove a saved scenario. Played runs keep their snapshot.
121
+ *
122
+ * @see https://daishi.ai/docs/developer/rest#ep-delete-scenario
123
+ */
124
+ deleteScenario(id) {
125
+ return this.request('DELETE', `/scenarios/${encodeURIComponent(id)}`);
126
+ }
127
+ /**
128
+ * List skills.
129
+ *
130
+ * The built-in skills, your own, and the authoring bounds.
131
+ *
132
+ * @see https://daishi.ai/docs/developer/rest#ep-list-skills
133
+ */
134
+ listSkills() {
135
+ return this.request('GET', '/skills');
136
+ }
137
+ /**
138
+ * Create a skill.
139
+ *
140
+ * Save a prompt module on the account.
141
+ *
142
+ * @see https://daishi.ai/docs/developer/rest#ep-create-skill
143
+ */
144
+ createSkill(body) {
145
+ return this.request('POST', '/skills', { body });
146
+ }
147
+ /**
148
+ * Get a skill.
149
+ *
150
+ * One skill by id: a built-in or one of yours.
151
+ *
152
+ * @see https://daishi.ai/docs/developer/rest#ep-get-skill
153
+ */
154
+ getSkill(id) {
155
+ return this.request('GET', `/skills/${encodeURIComponent(id)}`);
156
+ }
157
+ /**
158
+ * Update a skill.
159
+ *
160
+ * Edit in place. Omitted fields keep their value.
161
+ *
162
+ * @see https://daishi.ai/docs/developer/rest#ep-update-skill
163
+ */
164
+ updateSkill(id, body) {
165
+ return this.request('PUT', `/skills/${encodeURIComponent(id)}`, { body });
166
+ }
167
+ /**
168
+ * Delete a skill.
169
+ *
170
+ * Remove one of your skills. Played runs keep their snapshot.
171
+ *
172
+ * @see https://daishi.ai/docs/developer/rest#ep-delete-skill
173
+ */
174
+ deleteSkill(id) {
175
+ return this.request('DELETE', `/skills/${encodeURIComponent(id)}`);
176
+ }
177
+ /**
178
+ * Estimate a run.
179
+ *
180
+ * What a launch of this body would cost and commit, or the refusal it would get. Nothing is queued.
181
+ *
182
+ * @see https://daishi.ai/docs/developer/rest#ep-estimate
183
+ */
184
+ estimate(body) {
185
+ return this.request('POST', '/runs/estimate', { body });
186
+ }
187
+ /**
188
+ * Launch a run.
189
+ *
190
+ * Queue a run, or a series batch when `trials` is above 1.
191
+ *
192
+ * @see https://daishi.ai/docs/developer/rest#ep-launch
193
+ */
194
+ launch(body) {
195
+ return this.request('POST', '/runs', { body });
196
+ }
197
+ /**
198
+ * List your runs.
199
+ *
200
+ * Your runs, newest first.
201
+ *
202
+ * @see https://daishi.ai/docs/developer/rest#ep-list-runs
203
+ */
204
+ listRuns(query = {}) {
205
+ return this.request('GET', '/runs', { query });
206
+ }
207
+ /**
208
+ * Get a run.
209
+ *
210
+ * One run: status, the as-played configuration and, once finished, the scored results per seat.
211
+ *
212
+ * @see https://daishi.ai/docs/developer/rest#ep-get-run
213
+ */
214
+ getRun(id) {
215
+ return this.request('GET', `/runs/${encodeURIComponent(id)}`);
216
+ }
217
+ /**
218
+ * Usage and allowances.
219
+ *
220
+ * The meter’s record of your runs and where the account stands against the plan this month.
221
+ *
222
+ * @see https://daishi.ai/docs/developer/rest#ep-usage
223
+ */
224
+ usage() {
225
+ return this.request('GET', '/usage');
226
+ }
227
+ /**
228
+ * Live worlds.
229
+ *
230
+ * Every world your account has playing right now, as small boards, and every run waiting for one.
231
+ *
232
+ * @see https://daishi.ai/docs/developer/rest#ep-live
233
+ */
234
+ live() {
235
+ return this.request('GET', '/live');
236
+ }
237
+ /**
238
+ * Get a batch report.
239
+ *
240
+ * A series batch as one report: per-model intervals, survival, cost, every trial, and a verdict.
241
+ *
242
+ * @see https://daishi.ai/docs/developer/rest#ep-get-batch
243
+ */
244
+ getBatch(id) {
245
+ return this.request('GET', `/batches/${encodeURIComponent(id)}`);
246
+ }
247
+ /**
248
+ * Estimate an experiment.
249
+ *
250
+ * What creating this experiment would cost and check against, or the refusal it would get. Nothing is queued.
251
+ *
252
+ * @see https://daishi.ai/docs/developer/rest#ep-estimate-experiment
253
+ */
254
+ estimateExperiment(body) {
255
+ return this.request('POST', '/experiments/estimate', { body });
256
+ }
257
+ /**
258
+ * Create an experiment.
259
+ *
260
+ * Queue a paired experiment: two to four arms of one scenario on shared seeds, under a stopping rule.
261
+ *
262
+ * @see https://daishi.ai/docs/developer/rest#ep-create-experiment
263
+ */
264
+ createExperiment(body) {
265
+ return this.request('POST', '/experiments', { body });
266
+ }
267
+ /**
268
+ * List your experiments.
269
+ *
270
+ * Your experiments, newest first, with the verdict on record.
271
+ *
272
+ * @see https://daishi.ai/docs/developer/rest#ep-list-experiments
273
+ */
274
+ listExperiments() {
275
+ return this.request('GET', '/experiments');
276
+ }
277
+ /**
278
+ * Get an experiment report.
279
+ *
280
+ * The paired report as scored right now: per arm, per pair, each comparison against the control, the decision and a verdict.
281
+ *
282
+ * @see https://daishi.ai/docs/developer/rest#ep-get-experiment
283
+ */
284
+ getExperiment(id) {
285
+ return this.request('GET', `/experiments/${encodeURIComponent(id)}`);
286
+ }
287
+ /**
288
+ * Cancel an experiment.
289
+ *
290
+ * Stop a queued or running experiment: every unfinished trial of every arm, then the record.
291
+ *
292
+ * @see https://daishi.ai/docs/developer/rest#ep-cancel-experiment
293
+ */
294
+ cancelExperiment(id) {
295
+ return this.request('POST', `/experiments/${encodeURIComponent(id)}/cancel`);
296
+ }
297
+ /**
298
+ * Cancel a run.
299
+ *
300
+ * Stop a queued or running run.
301
+ *
302
+ * @see https://daishi.ai/docs/developer/rest#ep-cancel
303
+ */
304
+ cancel(id) {
305
+ return this.request('POST', `/runs/${encodeURIComponent(id)}/cancel`);
306
+ }
307
+ /**
308
+ * List a run’s logs.
309
+ *
310
+ * The harness log files kept for one of your runs, with their sizes and how long they are kept.
311
+ *
312
+ * @see https://daishi.ai/docs/developer/rest#ep-run-logs
313
+ */
314
+ runLogs(id) {
315
+ return this.request('GET', `/runs/${encodeURIComponent(id)}/logs`);
316
+ }
317
+ /**
318
+ * Read a run log.
319
+ *
320
+ * One log file, as the JSONL the harness wrote.
321
+ *
322
+ * @see https://daishi.ai/docs/developer/rest#ep-run-log
323
+ */
324
+ runLog(id, file) {
325
+ return this.request('GET', `/runs/${encodeURIComponent(id)}/logs/${encodeURIComponent(file)}`, { text: true });
326
+ }
327
+ /**
328
+ * List a run’s guest seats.
329
+ *
330
+ * Every guest seat of one of your world runs: its link, its state and who claimed it.
331
+ *
332
+ * @see https://daishi.ai/docs/developer/rest#ep-list-invites
333
+ */
334
+ listInvites(id) {
335
+ return this.request('GET', `/runs/${encodeURIComponent(id)}/invites`);
336
+ }
337
+ /**
338
+ * Extend the signup window.
339
+ *
340
+ * Move every open seat’s claim-by deadline to `minutes` from now.
341
+ *
342
+ * @see https://daishi.ai/docs/developer/rest#ep-extend-invites
343
+ */
344
+ extendInvites(id, body) {
345
+ return this.request('POST', `/runs/${encodeURIComponent(id)}/invites/extend`, { body });
346
+ }
347
+ /**
348
+ * Regenerate a seat’s link.
349
+ *
350
+ * A fresh link for one seat; the old token stops resolving at once.
351
+ *
352
+ * @see https://daishi.ai/docs/developer/rest#ep-regenerate-invite
353
+ */
354
+ regenerateInvite(id, inviteId) {
355
+ return this.request('POST', `/runs/${encodeURIComponent(id)}/invites/${encodeURIComponent(inviteId)}/regenerate`);
356
+ }
357
+ /**
358
+ * Withdraw a seat.
359
+ *
360
+ * Withdraw one guest seat: its link stops resolving and the seat leaves the roster at launch.
361
+ *
362
+ * @see https://daishi.ai/docs/developer/rest#ep-revoke-invite
363
+ */
364
+ revokeInvite(id, inviteId) {
365
+ return this.request('POST', `/runs/${encodeURIComponent(id)}/invites/${encodeURIComponent(inviteId)}/revoke`);
366
+ }
367
+ }
@@ -0,0 +1,78 @@
1
+ import { Operations, type ErrorCode, type GetBatchResponse, type GetExperimentResponse, type GetRunResponse, type Method, type RequestParts } from './generated.js';
2
+ export * from './generated.js';
3
+ export declare const VERSION = "0.1.0";
4
+ export declare const DEFAULT_BASE_URL = "https://daishi.ai/api/v1";
5
+ export interface DaishiOptions {
6
+ /** A Studio access token (`dsk_...`). Default: the `DAISHI_TOKEN` environment variable. */
7
+ token?: string;
8
+ /** Default: `https://daishi.ai/api/v1`. */
9
+ baseUrl?: string;
10
+ /** Retries after a refusal that is safe to repeat. Default 3; 0 turns retrying off. */
11
+ maxRetries?: number;
12
+ /** Per-attempt timeout in milliseconds. Default 60000. */
13
+ timeoutMs?: number;
14
+ /** A fetch implementation. Default: the global one (Node 18+, browsers, Deno, Bun). */
15
+ fetch?: typeof fetch;
16
+ }
17
+ export interface WaitOptions<T> {
18
+ /** Milliseconds between polls. Default 30000. */
19
+ intervalMs?: number;
20
+ /** Stops the wait; `AbortSignal.timeout(ms)` gives it a deadline. */
21
+ signal?: AbortSignal;
22
+ /** Called with every poll's answer, the last one included. */
23
+ onPoll?: (answer: T) => void;
24
+ }
25
+ /** A refusal from the API, or a transport failure after the retries ran out. */
26
+ export declare class DaishiError extends Error {
27
+ /** HTTP status; 0 when no response arrived. */
28
+ readonly status: number;
29
+ /** The API's error code (`unknown_run`, `rate_limited`, ...), or `http_<status>` when the body carried none. */
30
+ readonly code: ErrorCode | (string & {});
31
+ /** Seconds the `Retry-After` header asked for, when it sent one. */
32
+ readonly retryAfterSeconds: number | null;
33
+ /** The parsed error body, when there was one. */
34
+ readonly body: unknown;
35
+ readonly name = "DaishiError";
36
+ constructor(
37
+ /** HTTP status; 0 when no response arrived. */
38
+ status: number,
39
+ /** The API's error code (`unknown_run`, `rate_limited`, ...), or `http_<status>` when the body carried none. */
40
+ code: ErrorCode | (string & {}), message: string,
41
+ /** Seconds the `Retry-After` header asked for, when it sent one. */
42
+ retryAfterSeconds?: number | null,
43
+ /** The parsed error body, when there was one. */
44
+ body?: unknown);
45
+ /** Where the code is explained. */
46
+ get docs(): string;
47
+ }
48
+ /**
49
+ * The Daishi Studio API. Every operation is a method (`getRun`, `launch`,
50
+ * `createScenario`, ...) typed from the API's OpenAPI document; the
51
+ * `waitFor*` helpers poll until a run, batch or experiment ends.
52
+ *
53
+ * Retries: a 429 is retried after the seconds its `Retry-After` names, and
54
+ * a `run_launching` refusal after a short pause, on any method, because the
55
+ * server refused before doing anything. A gateway error (502, 503, 504) or
56
+ * a dropped connection is retried on GET only: a write that may have landed
57
+ * is never sent twice.
58
+ */
59
+ export declare class Daishi extends Operations {
60
+ readonly baseUrl: string;
61
+ private readonly token;
62
+ private readonly maxRetries;
63
+ private readonly timeoutMs;
64
+ private readonly fetchImpl;
65
+ constructor(options?: DaishiOptions);
66
+ protected request<T>(method: Method, path: string, parts?: RequestParts): Promise<T>;
67
+ /**
68
+ * Polls a run until it ends (finished, failed or cancelled) and answers
69
+ * with the last read. A finished run is read until its `results` arrive,
70
+ * which is when the match archives.
71
+ */
72
+ waitForRun(id: string, options?: WaitOptions<GetRunResponse>): Promise<GetRunResponse>;
73
+ /** Polls a series batch until no trial is still to play. */
74
+ waitForBatch(id: string, options?: WaitOptions<GetBatchResponse>): Promise<GetBatchResponse>;
75
+ /** Polls a paired experiment until it finishes or is cancelled. */
76
+ waitForExperiment(id: string, options?: WaitOptions<GetExperimentResponse>): Promise<GetExperimentResponse>;
77
+ private poll;
78
+ }
package/dist/index.js ADDED
@@ -0,0 +1,165 @@
1
+ import { Operations } from './generated.js';
2
+ export * from './generated.js';
3
+ export const VERSION = '0.1.0';
4
+ export const DEFAULT_BASE_URL = 'https://daishi.ai/api/v1';
5
+ /** A refusal from the API, or a transport failure after the retries ran out. */
6
+ export class DaishiError extends Error {
7
+ status;
8
+ code;
9
+ retryAfterSeconds;
10
+ body;
11
+ name = 'DaishiError';
12
+ constructor(
13
+ /** HTTP status; 0 when no response arrived. */
14
+ status,
15
+ /** The API's error code (`unknown_run`, `rate_limited`, ...), or `http_<status>` when the body carried none. */
16
+ code, message,
17
+ /** Seconds the `Retry-After` header asked for, when it sent one. */
18
+ retryAfterSeconds = null,
19
+ /** The parsed error body, when there was one. */
20
+ body = null) {
21
+ super(message);
22
+ this.status = status;
23
+ this.code = code;
24
+ this.retryAfterSeconds = retryAfterSeconds;
25
+ this.body = body;
26
+ }
27
+ /** Where the code is explained. */
28
+ get docs() {
29
+ return `https://daishi.ai/docs/developer/errors#${this.code}`;
30
+ }
31
+ }
32
+ const RUN_DONE = new Set(['finished', 'failed', 'cancelled']);
33
+ const EXPERIMENT_DONE = new Set(['finished', 'cancelled']);
34
+ const GATEWAY = new Set([502, 503, 504]);
35
+ const sleep = (ms, signal) => new Promise((resolve, reject) => {
36
+ if (signal?.aborted)
37
+ return reject(signal.reason);
38
+ const t = setTimeout(() => {
39
+ signal?.removeEventListener('abort', onAbort);
40
+ resolve();
41
+ }, ms);
42
+ const onAbort = () => {
43
+ clearTimeout(t);
44
+ reject(signal.reason);
45
+ };
46
+ signal?.addEventListener('abort', onAbort, { once: true });
47
+ });
48
+ function envToken() {
49
+ const env = globalThis.process?.env;
50
+ return env?.DAISHI_TOKEN || undefined;
51
+ }
52
+ /**
53
+ * The Daishi Studio API. Every operation is a method (`getRun`, `launch`,
54
+ * `createScenario`, ...) typed from the API's OpenAPI document; the
55
+ * `waitFor*` helpers poll until a run, batch or experiment ends.
56
+ *
57
+ * Retries: a 429 is retried after the seconds its `Retry-After` names, and
58
+ * a `run_launching` refusal after a short pause, on any method, because the
59
+ * server refused before doing anything. A gateway error (502, 503, 504) or
60
+ * a dropped connection is retried on GET only: a write that may have landed
61
+ * is never sent twice.
62
+ */
63
+ export class Daishi extends Operations {
64
+ baseUrl;
65
+ token;
66
+ maxRetries;
67
+ timeoutMs;
68
+ fetchImpl;
69
+ constructor(options = {}) {
70
+ super();
71
+ this.token = options.token ?? envToken();
72
+ this.baseUrl = (options.baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, '');
73
+ this.maxRetries = options.maxRetries ?? 3;
74
+ this.timeoutMs = options.timeoutMs ?? 60_000;
75
+ const f = options.fetch ?? globalThis.fetch;
76
+ if (!f)
77
+ throw new Error('No fetch implementation: use Node 18 or later, or pass options.fetch.');
78
+ this.fetchImpl = f.bind(globalThis);
79
+ }
80
+ async request(method, path, parts = {}) {
81
+ const url = new URL(this.baseUrl + (path === '/' ? '' : path));
82
+ for (const [k, v] of Object.entries(parts.query ?? {}))
83
+ if (v !== undefined && v !== null)
84
+ url.searchParams.set(k, String(v));
85
+ const headers = { accept: parts.text ? 'application/x-ndjson, application/json' : 'application/json' };
86
+ if (this.token)
87
+ headers.authorization = `Bearer ${this.token}`;
88
+ if (parts.body !== undefined)
89
+ headers['content-type'] = 'application/json';
90
+ const body = parts.body !== undefined ? JSON.stringify(parts.body) : undefined;
91
+ for (let attempt = 0;; attempt++) {
92
+ const canRetry = attempt < this.maxRetries;
93
+ let res;
94
+ try {
95
+ res = await this.fetchImpl(url, { method, headers, body, signal: AbortSignal.timeout(this.timeoutMs) });
96
+ }
97
+ catch (err) {
98
+ if (method === 'GET' && canRetry) {
99
+ await sleep(backoffMs(attempt));
100
+ continue;
101
+ }
102
+ throw new DaishiError(0, 'network_error', `${method} ${url.pathname} failed: ${err.message ?? String(err)}`, null, null);
103
+ }
104
+ if (res.ok)
105
+ return (parts.text ? await res.text() : await res.json());
106
+ const text = await res.text();
107
+ let parsed = null;
108
+ try {
109
+ parsed = JSON.parse(text);
110
+ }
111
+ catch {
112
+ parsed = null;
113
+ }
114
+ const code = parsed?.error ?? `http_${res.status}`;
115
+ const retryAfter = parseRetryAfter(res.headers.get('retry-after'));
116
+ const retryable = res.status === 429 || code === 'run_launching' || (method === 'GET' && GATEWAY.has(res.status));
117
+ if (retryable && canRetry) {
118
+ await sleep(retryAfter !== null ? retryAfter * 1000 : backoffMs(attempt));
119
+ continue;
120
+ }
121
+ throw new DaishiError(res.status, code, parsed?.message ?? (text.slice(0, 200) || res.statusText), retryAfter, parsed);
122
+ }
123
+ }
124
+ /**
125
+ * Polls a run until it ends (finished, failed or cancelled) and answers
126
+ * with the last read. A finished run is read until its `results` arrive,
127
+ * which is when the match archives.
128
+ */
129
+ waitForRun(id, options = {}) {
130
+ return this.poll(() => this.getRun(id), (r) => RUN_DONE.has(r.run.status) && (r.run.status !== 'finished' || r.results !== null), options);
131
+ }
132
+ /** Polls a series batch until no trial is still to play. */
133
+ waitForBatch(id, options = {}) {
134
+ return this.poll(() => this.getBatch(id), (r) => r.batch.pending === 0, options);
135
+ }
136
+ /** Polls a paired experiment until it finishes or is cancelled. */
137
+ waitForExperiment(id, options = {}) {
138
+ return this.poll(() => this.getExperiment(id), (r) => EXPERIMENT_DONE.has(r.experiment.status), options);
139
+ }
140
+ async poll(read, done, options) {
141
+ const interval = options.intervalMs ?? 30_000;
142
+ for (;;) {
143
+ options.signal?.throwIfAborted();
144
+ const answer = await read();
145
+ options.onPoll?.(answer);
146
+ if (done(answer))
147
+ return answer;
148
+ await sleep(interval, options.signal);
149
+ }
150
+ }
151
+ }
152
+ function parseRetryAfter(header) {
153
+ if (!header)
154
+ return null;
155
+ const seconds = Number(header);
156
+ if (Number.isFinite(seconds) && seconds >= 0)
157
+ return seconds;
158
+ const at = Date.parse(header);
159
+ return Number.isNaN(at) ? null : Math.max(0, Math.ceil((at - Date.now()) / 1000));
160
+ }
161
+ /** 1 s, 2 s, 4 s ... with up to a quarter of jitter, capped at 30 s. */
162
+ function backoffMs(attempt) {
163
+ const base = Math.min(30_000, 1000 * 2 ** attempt);
164
+ return base + Math.floor(Math.random() * base * 0.25);
165
+ }
package/package.json ADDED
@@ -0,0 +1,33 @@
1
+ {
2
+ "name": "daishi-sdk",
3
+ "version": "0.1.0",
4
+ "description": "Typed client for the Daishi Studio API: scenarios, runs, series batches and paired experiments, with retries and waiting helpers.",
5
+ "keywords": [
6
+ "daishi",
7
+ "ai",
8
+ "agents",
9
+ "evaluation",
10
+ "benchmark",
11
+ "llm",
12
+ "multi-agent"
13
+ ],
14
+ "homepage": "https://daishi.ai/docs/developer",
15
+ "license": "MIT",
16
+ "author": "Arkeous LLC",
17
+ "type": "module",
18
+ "main": "./dist/index.js",
19
+ "types": "./dist/index.d.ts",
20
+ "exports": {
21
+ ".": {
22
+ "types": "./dist/index.d.ts",
23
+ "default": "./dist/index.js"
24
+ }
25
+ },
26
+ "files": [
27
+ "dist"
28
+ ],
29
+ "sideEffects": false,
30
+ "engines": {
31
+ "node": ">=18"
32
+ }
33
+ }