@tillstack/cli 0.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/dist/api.js ADDED
@@ -0,0 +1,759 @@
1
+ import { readFileSync } from 'node:fs';
2
+ import { basename } from 'node:path';
3
+ export class ApiError extends Error {
4
+ status;
5
+ constructor(status, message) {
6
+ super(message);
7
+ this.status = status;
8
+ }
9
+ }
10
+ function authHeaders(opts) {
11
+ return opts.token ? { Authorization: `Bearer ${opts.token}` } : {};
12
+ }
13
+ async function parse(res) {
14
+ const text = await res.text().catch(() => '');
15
+ if (!res.ok) {
16
+ // Surface the server's error message when it sent JSON `{ error }`.
17
+ let msg = `${res.status}`;
18
+ try {
19
+ const j = JSON.parse(text);
20
+ if (typeof j.error === 'string')
21
+ msg = `${res.status}: ${j.error}`;
22
+ else
23
+ msg = `${res.status}: ${text.slice(0, 300)}`;
24
+ }
25
+ catch {
26
+ msg = `${res.status}: ${text.slice(0, 300)}`;
27
+ }
28
+ throw new ApiError(res.status, msg);
29
+ }
30
+ if (!text)
31
+ return undefined;
32
+ return JSON.parse(text);
33
+ }
34
+ /** Generic JSON request against the dashboard API. */
35
+ export async function request(opts, method, path, body) {
36
+ const headers = { ...authHeaders(opts) };
37
+ let payload;
38
+ if (body !== undefined) {
39
+ headers['Content-Type'] = 'application/json';
40
+ payload = JSON.stringify(body);
41
+ }
42
+ const res = await fetch(`${opts.apiUrl}${path}`, {
43
+ method,
44
+ headers,
45
+ ...(payload !== undefined ? { body: payload } : {}),
46
+ });
47
+ return parse(res);
48
+ }
49
+ export function listStacks(opts) {
50
+ return request(opts, 'GET', '/api/stacks');
51
+ }
52
+ export function createStack(opts, input) {
53
+ return request(opts, 'POST', '/api/stacks', input);
54
+ }
55
+ export function getStack(opts, slug) {
56
+ return request(opts, 'GET', `/api/stacks/${encodeURIComponent(slug)}`);
57
+ }
58
+ export function updateStack(opts, slug, patch) {
59
+ return request(opts, 'PATCH', `/api/stacks/${encodeURIComponent(slug)}`, patch);
60
+ }
61
+ export function deleteStack(opts, slug) {
62
+ return request(opts, 'DELETE', `/api/stacks/${encodeURIComponent(slug)}`);
63
+ }
64
+ export function setStackModule(opts, slug, module, status) {
65
+ return request(opts, 'PATCH', `/api/stacks/${encodeURIComponent(slug)}/modules`, { module, status });
66
+ }
67
+ export function setStackResource(opts, slug, module, resourceId, action) {
68
+ return request(opts, 'PATCH', `/api/stacks/${encodeURIComponent(slug)}/resources`, {
69
+ module,
70
+ resource_id: resourceId,
71
+ action,
72
+ });
73
+ }
74
+ // ── Auth ────────────────────────────────────────────────────────────────────
75
+ export async function login(opts, email, password, totp) {
76
+ const res = await fetch(`${opts.apiUrl}/api/auth/login`, {
77
+ method: 'POST',
78
+ headers: { 'Content-Type': 'application/json', 'X-Token-In-Body': '1' },
79
+ body: JSON.stringify({ email, password, ...(totp ? { totp_token: totp } : {}) }),
80
+ });
81
+ const data = (await parse(res)) ?? {};
82
+ if (data.totp_required)
83
+ return { token: '', org: data.org ?? '', totpRequired: true };
84
+ if (!data.access_token)
85
+ throw new ApiError(500, 'login: server did not return access_token');
86
+ return {
87
+ token: data.access_token,
88
+ ...(data.refresh_token ? { refresh: data.refresh_token } : {}),
89
+ org: data.org ?? '',
90
+ };
91
+ }
92
+ export function getMe(opts) {
93
+ return request(opts, 'GET', '/api/me');
94
+ }
95
+ export function listProjects(opts) {
96
+ return request(opts, 'GET', '/api/projects');
97
+ }
98
+ export function createProject(opts, name, platform) {
99
+ return request(opts, 'POST', '/api/projects', { name, platform });
100
+ }
101
+ export function getProject(opts, projectId) {
102
+ return request(opts, 'GET', `/api/projects/${encodeURIComponent(projectId)}`);
103
+ }
104
+ export function rotateDsn(opts, projectId) {
105
+ return request(opts, 'POST', `/api/projects/${encodeURIComponent(projectId)}/rotate-dsn`, {});
106
+ }
107
+ export function listIssues(opts, params = {}) {
108
+ const q = new URLSearchParams();
109
+ // snake_case is the canonical query-param spelling across the API.
110
+ if (params.projectId)
111
+ q.set('project_id', params.projectId);
112
+ if (params.limit)
113
+ q.set('limit', String(params.limit));
114
+ const qs = q.toString();
115
+ return request(opts, 'GET', `/api/issues${qs ? `?${qs}` : ''}`);
116
+ }
117
+ export function getIssue(opts, issueId) {
118
+ return request(opts, 'GET', `/api/issues/${encodeURIComponent(issueId)}`);
119
+ }
120
+ // ── TillPulse: metrics / performance / alerts ────────────────────────────────
121
+ export function getMetrics(opts, projectId) {
122
+ const qs = projectId ? `?project_id=${encodeURIComponent(projectId)}` : '';
123
+ return request(opts, 'GET', `/api/metrics${qs}`);
124
+ }
125
+ export function listAlerts(opts, limit) {
126
+ const qs = limit ? `?limit=${limit}` : '';
127
+ return request(opts, 'GET', `/api/alerts/recent${qs}`);
128
+ }
129
+ export function listAlertRules(opts, projectId) {
130
+ const qs = projectId ? `?project_id=${encodeURIComponent(projectId)}` : '';
131
+ return request(opts, 'GET', `/api/alerts${qs}`);
132
+ }
133
+ // ── TillPulse: releases ──────────────────────────────────────────────────────
134
+ export function createRelease(opts, projectId, body) {
135
+ return request(opts, 'POST', `/api/projects/${encodeURIComponent(projectId)}/releases`, body);
136
+ }
137
+ // ── TillPulse: symbol uploads ────────────────────────────────────────────────
138
+ async function uploadForm(opts, path, fields, filePath) {
139
+ const buf = readFileSync(filePath);
140
+ const fd = new FormData();
141
+ for (const [k, v] of Object.entries(fields))
142
+ fd.append(k, v);
143
+ fd.append('file', new Blob([buf]), basename(filePath));
144
+ const res = await fetch(`${opts.apiUrl}${path}`, { method: 'POST', headers: authHeaders(opts), body: fd });
145
+ return parse(res);
146
+ }
147
+ export function uploadSourceMap(opts, projectId, p) {
148
+ return uploadForm(opts, `/api/projects/${encodeURIComponent(projectId)}/source-maps`, { release: p.release, symbolication_type: 'js_source_map' }, p.filePath);
149
+ }
150
+ export function uploadProguardMapping(opts, projectId, p) {
151
+ return uploadForm(opts, `/api/projects/${encodeURIComponent(projectId)}/source-maps`, { release: p.release, symbolication_type: 'proguard' }, p.filePath);
152
+ }
153
+ export function uploadDsym(opts, projectId, p) {
154
+ return uploadForm(opts, `/api/projects/${encodeURIComponent(projectId)}/dsyms`, { release: p.release }, p.filePath);
155
+ }
156
+ export function listApiKeys(opts) {
157
+ return request(opts, 'GET', '/api/api-keys');
158
+ }
159
+ export function createApiKey(opts, name, scopes) {
160
+ return request(opts, 'POST', '/api/api-keys', { name, scopes });
161
+ }
162
+ export function listAuthApps(opts) {
163
+ return request(opts, 'GET', '/api/auth/admin/apps');
164
+ }
165
+ export function createAuthApp(opts, name) {
166
+ return request(opts, 'POST', '/api/auth/admin/apps', { name });
167
+ }
168
+ export function getAuthApp(opts, appId) {
169
+ return request(opts, 'GET', `/api/auth/admin/apps/${encodeURIComponent(appId)}`);
170
+ }
171
+ export function listTeam(opts) {
172
+ return request(opts, 'GET', '/api/team');
173
+ }
174
+ export function listEdgeRules(opts) {
175
+ return request(opts, 'GET', '/api/shield/edge-rules');
176
+ }
177
+ export function createEdgeRule(opts, body) {
178
+ return request(opts, 'POST', '/api/shield/edge-rules', body);
179
+ }
180
+ export function updateEdgeRule(opts, id, body) {
181
+ return request(opts, 'PATCH', `/api/shield/edge-rules/${encodeURIComponent(id)}`, body);
182
+ }
183
+ export function deleteEdgeRule(opts, id) {
184
+ return request(opts, 'DELETE', `/api/shield/edge-rules/${encodeURIComponent(id)}`);
185
+ }
186
+ export function listEdgeKeys(opts) {
187
+ return request(opts, 'GET', '/api/shield/edge-keys');
188
+ }
189
+ export function createEdgeKey(opts, projectId, name) {
190
+ return request(opts, 'POST', '/api/shield/edge-keys', { project_id: projectId, name });
191
+ }
192
+ export function revokeEdgeKey(opts, id) {
193
+ return request(opts, 'DELETE', `/api/shield/edge-keys/${encodeURIComponent(id)}`);
194
+ }
195
+ export function getShieldSettings(opts) {
196
+ return request(opts, 'GET', '/api/shield/settings');
197
+ }
198
+ export function listIncidents(opts, status) {
199
+ const qs = status ? `?status=${encodeURIComponent(status)}` : '';
200
+ return request(opts, 'GET', `/api/shield/incidents${qs}`);
201
+ }
202
+ export function listGateSites(opts) {
203
+ return request(opts, 'GET', '/api/tillgate/sites');
204
+ }
205
+ export function createGateSite(opts, body) {
206
+ return request(opts, 'POST', '/api/tillgate/sites', body);
207
+ }
208
+ export function updateGateSite(opts, id, body) {
209
+ return request(opts, 'PATCH', `/api/tillgate/sites/${encodeURIComponent(id)}`, body);
210
+ }
211
+ export function deleteGateSite(opts, id) {
212
+ return request(opts, 'DELETE', `/api/tillgate/sites/${encodeURIComponent(id)}`);
213
+ }
214
+ export function listTracker(opts, projectId) {
215
+ return request(opts, 'GET', `/api/projects/${encodeURIComponent(projectId)}/integrations/tracker`);
216
+ }
217
+ export function connectTracker(opts, projectId, body) {
218
+ return request(opts, 'PUT', `/api/projects/${encodeURIComponent(projectId)}/integrations/tracker`, body);
219
+ }
220
+ export function disconnectTracker(opts, projectId, provider) {
221
+ return request(opts, 'DELETE', `/api/projects/${encodeURIComponent(projectId)}/integrations/tracker?provider=${provider}`);
222
+ }
223
+ export function getTrackerWebhook(opts, projectId, provider) {
224
+ return request(opts, 'GET', `/api/projects/${encodeURIComponent(projectId)}/integrations/tracker/webhook-secret?provider=${provider}`);
225
+ }
226
+ export function setTrackerWebhook(opts, projectId, body) {
227
+ return request(opts, 'POST', `/api/projects/${encodeURIComponent(projectId)}/integrations/tracker/webhook-secret`, body);
228
+ }
229
+ // ── Ask (NLQ assistant over your telemetry) ──────────────────────────────────
230
+ // The /api/ask route proxies an LLM and streams the answer back. We read the
231
+ // stream and print assistant text as it arrives, tolerating either a raw text
232
+ // stream or SSE `data:` lines carrying OpenAI-style chat deltas.
233
+ export async function askStream(opts, orgSlug, question, onChunk) {
234
+ const res = await fetch(`${opts.apiUrl}/api/ask`, {
235
+ method: 'POST',
236
+ headers: { 'Content-Type': 'application/json', ...authHeaders(opts) },
237
+ body: JSON.stringify({ orgSlug, messages: [{ role: 'user', content: question }] }),
238
+ });
239
+ if (!res.ok || !res.body) {
240
+ const text = await res.text().catch(() => '');
241
+ throw new ApiError(res.status, text.slice(0, 300) || `ask failed: ${res.status}`);
242
+ }
243
+ const reader = res.body.getReader();
244
+ const decoder = new TextDecoder();
245
+ let buffer = '';
246
+ for (;;) {
247
+ const { done, value } = await reader.read();
248
+ if (done)
249
+ break;
250
+ buffer += decoder.decode(value, { stream: true });
251
+ const lines = buffer.split('\n');
252
+ buffer = lines.pop() ?? '';
253
+ for (const line of lines) {
254
+ const trimmed = line.trim();
255
+ if (!trimmed)
256
+ continue;
257
+ if (trimmed.startsWith('data:')) {
258
+ const data = trimmed.slice(5).trim();
259
+ if (data === '[DONE]')
260
+ return;
261
+ try {
262
+ const j = JSON.parse(data);
263
+ const piece = j.choices?.[0]?.delta?.content;
264
+ if (piece)
265
+ onChunk(piece);
266
+ }
267
+ catch {
268
+ onChunk(data);
269
+ }
270
+ }
271
+ else {
272
+ onChunk(line + '\n');
273
+ }
274
+ }
275
+ }
276
+ if (buffer.trim())
277
+ onChunk(buffer);
278
+ }
279
+ export function listNamespaces(opts) {
280
+ return request(opts, 'GET', '/api/cache/namespaces');
281
+ }
282
+ export function createNamespace(opts, input) {
283
+ return request(opts, 'POST', '/api/cache/namespaces', input);
284
+ }
285
+ export function getNamespace(opts, id) {
286
+ return request(opts, 'GET', `/api/cache/namespaces/${encodeURIComponent(id)}`);
287
+ }
288
+ export function updateNamespace(opts, id, patch) {
289
+ return request(opts, 'PATCH', `/api/cache/namespaces/${encodeURIComponent(id)}`, patch);
290
+ }
291
+ export function deleteNamespace(opts, id) {
292
+ return request(opts, 'DELETE', `/api/cache/namespaces/${encodeURIComponent(id)}`);
293
+ }
294
+ export function listCacheTokens(opts, nsId) {
295
+ return request(opts, 'GET', `/api/cache/namespaces/${encodeURIComponent(nsId)}/tokens`);
296
+ }
297
+ export function createCacheToken(opts, nsId, name, scope) {
298
+ return request(opts, 'POST', `/api/cache/namespaces/${encodeURIComponent(nsId)}/tokens`, { name, scope });
299
+ }
300
+ export function revokeCacheToken(opts, nsId, tokenId) {
301
+ return request(opts, 'DELETE', `/api/cache/namespaces/${encodeURIComponent(nsId)}/tokens/${encodeURIComponent(tokenId)}`);
302
+ }
303
+ export function listCacheQueues(opts, nsId) {
304
+ return request(opts, 'GET', `/api/cache/namespaces/${encodeURIComponent(nsId)}/queues`);
305
+ }
306
+ export function listCacheKeys(opts, nsId) {
307
+ return request(opts, 'GET', `/api/cache/namespaces/${encodeURIComponent(nsId)}/keys`);
308
+ }
309
+ export function getCacheUsage(opts, nsId) {
310
+ return request(opts, 'GET', `/api/cache/namespaces/${encodeURIComponent(nsId)}/usage`);
311
+ }
312
+ export function listSecretsProjects(opts) {
313
+ return request(opts, 'GET', '/api/secrets/projects');
314
+ }
315
+ export function createSecretsProject(opts, name, slug, description = '') {
316
+ return request(opts, 'POST', '/api/secrets/projects', { name, slug, description });
317
+ }
318
+ export function listSecretsEnvironments(opts, projectId) {
319
+ return request(opts, 'GET', `/api/secrets/projects/${encodeURIComponent(projectId)}/environments`);
320
+ }
321
+ export function createSecretsEnvironment(opts, projectId, name, slug) {
322
+ return request(opts, 'POST', `/api/secrets/projects/${encodeURIComponent(projectId)}/environments`, { name, slug });
323
+ }
324
+ export function listSecrets(opts, envId) {
325
+ return request(opts, 'GET', `/api/secrets/environments/${encodeURIComponent(envId)}/secrets`);
326
+ }
327
+ export function setSecret(opts, envId, key, value, comment = '') {
328
+ return request(opts, 'POST', `/api/secrets/environments/${encodeURIComponent(envId)}/secrets`, { key, value, comment });
329
+ }
330
+ export function revealSecret(opts, secretId, version) {
331
+ const qs = version ? `?version=${version}` : '';
332
+ return request(opts, 'POST', `/api/secrets/secrets/${encodeURIComponent(secretId)}/reveal${qs}`);
333
+ }
334
+ export function listSecretVersions(opts, secretId) {
335
+ return request(opts, 'GET', `/api/secrets/secrets/${encodeURIComponent(secretId)}/versions`);
336
+ }
337
+ export function rollbackSecret(opts, secretId, version) {
338
+ return request(opts, 'POST', `/api/secrets/secrets/${encodeURIComponent(secretId)}/rollback`, { version });
339
+ }
340
+ export function deleteSecret(opts, secretId) {
341
+ return request(opts, 'DELETE', `/api/secrets/secrets/${encodeURIComponent(secretId)}`);
342
+ }
343
+ export function listSecretsTokens(opts, projectId) {
344
+ return request(opts, 'GET', `/api/secrets/projects/${encodeURIComponent(projectId)}/tokens`);
345
+ }
346
+ export function createSecretsToken(opts, projectId, body) {
347
+ return request(opts, 'POST', `/api/secrets/projects/${encodeURIComponent(projectId)}/tokens`, body);
348
+ }
349
+ export function revokeSecretsToken(opts, projectId, tokenId) {
350
+ return request(opts, 'DELETE', `/api/secrets/projects/${encodeURIComponent(projectId)}/tokens/${encodeURIComponent(tokenId)}`);
351
+ }
352
+ export function listSyncTargets(opts, projectId) {
353
+ return request(opts, 'GET', `/api/secrets/projects/${encodeURIComponent(projectId)}/sync-targets`);
354
+ }
355
+ export function createSyncTarget(opts, projectId, input) {
356
+ return request(opts, 'POST', `/api/secrets/projects/${encodeURIComponent(projectId)}/sync-targets`, input);
357
+ }
358
+ export function setSyncTargetStatus(opts, projectId, targetId, status) {
359
+ return request(opts, 'PATCH', `/api/secrets/projects/${encodeURIComponent(projectId)}/sync-targets/${encodeURIComponent(targetId)}`, { status });
360
+ }
361
+ export function deleteSyncTarget(opts, projectId, targetId) {
362
+ return request(opts, 'DELETE', `/api/secrets/projects/${encodeURIComponent(projectId)}/sync-targets/${encodeURIComponent(targetId)}`);
363
+ }
364
+ export function pushSyncTarget(opts, projectId, targetId) {
365
+ return request(opts, 'POST', `/api/secrets/projects/${encodeURIComponent(projectId)}/sync-targets/${encodeURIComponent(targetId)}/push`);
366
+ }
367
+ export function pullSecrets(opts, environmentId) {
368
+ return request(opts, 'POST', '/api/secrets/pull', environmentId ? { environment_id: environmentId } : {});
369
+ }
370
+ export function leaseSecret(opts, body) {
371
+ // `opts.apiUrl` here is the lease service base (e.g. https://secrets.tilldev.dev).
372
+ return request(opts, 'POST', '/v1/lease', body);
373
+ }
374
+ export function revokeLease(opts, leaseId) {
375
+ return request(opts, 'POST', `/v1/lease/${encodeURIComponent(leaseId)}/revoke`);
376
+ }
377
+ export function listArkSources(opts) {
378
+ return request(opts, 'GET', '/api/ark/sources');
379
+ }
380
+ export function createArkSource(opts, body) {
381
+ return request(opts, 'POST', '/api/ark/sources', body);
382
+ }
383
+ export function getArkSource(opts, id) {
384
+ return request(opts, 'GET', `/api/ark/sources/${encodeURIComponent(id)}`);
385
+ }
386
+ export function deleteArkSource(opts, id) {
387
+ return request(opts, 'DELETE', `/api/ark/sources/${encodeURIComponent(id)}`);
388
+ }
389
+ export function listArkTargets(opts) {
390
+ return request(opts, 'GET', '/api/ark/targets');
391
+ }
392
+ export function createArkTarget(opts, body) {
393
+ return request(opts, 'POST', '/api/ark/targets', body);
394
+ }
395
+ export function getArkTarget(opts, id) {
396
+ return request(opts, 'GET', `/api/ark/targets/${encodeURIComponent(id)}`);
397
+ }
398
+ export function deleteArkTarget(opts, id) {
399
+ return request(opts, 'DELETE', `/api/ark/targets/${encodeURIComponent(id)}`);
400
+ }
401
+ export function listArkPolicies(opts, sourceId) {
402
+ const qs = sourceId ? `?source_id=${encodeURIComponent(sourceId)}` : '';
403
+ return request(opts, 'GET', `/api/ark/policies${qs}`);
404
+ }
405
+ export function createArkPolicy(opts, body) {
406
+ return request(opts, 'POST', '/api/ark/policies', body);
407
+ }
408
+ export function listArkBackups(opts, sourceId) {
409
+ const qs = sourceId ? `?source_id=${encodeURIComponent(sourceId)}` : '';
410
+ return request(opts, 'GET', `/api/ark/backups${qs}`);
411
+ }
412
+ export function runArkBackup(opts, sourceId, policyId) {
413
+ return request(opts, 'POST', '/api/ark/backups/run', { source_id: sourceId, ...(policyId ? { policy_id: policyId } : {}) });
414
+ }
415
+ export function listArkRestores(opts, backupId) {
416
+ const qs = backupId ? `?backup_id=${encodeURIComponent(backupId)}` : '';
417
+ return request(opts, 'GET', `/api/ark/restores${qs}`);
418
+ }
419
+ export function startArkRestore(opts, body) {
420
+ return request(opts, 'POST', '/api/ark/restores', body);
421
+ }
422
+ export function getArkFailover(opts, sourceId) {
423
+ return request(opts, 'GET', `/api/ark/failover?source_id=${encodeURIComponent(sourceId)}`);
424
+ }
425
+ export function promoteArkFailover(opts, body) {
426
+ return request(opts, 'POST', '/api/ark/failover/promote', body);
427
+ }
428
+ export function failbackArkFailover(opts, body) {
429
+ return request(opts, 'POST', '/api/ark/failover/failback', body);
430
+ }
431
+ export function listArkReplicas(opts) {
432
+ return request(opts, 'GET', '/api/ark/replicas');
433
+ }
434
+ export function listArkTokens(opts) {
435
+ return request(opts, 'GET', '/api/ark/tokens');
436
+ }
437
+ export function createArkToken(opts, body) {
438
+ return request(opts, 'POST', '/api/ark/tokens', body);
439
+ }
440
+ export function revokeArkToken(opts, id) {
441
+ return request(opts, 'DELETE', `/api/ark/tokens/${encodeURIComponent(id)}`);
442
+ }
443
+ export function listArkAudit(opts, limit) {
444
+ const qs = limit ? `?limit=${limit}` : '';
445
+ return request(opts, 'GET', `/api/ark/audit${qs}`);
446
+ }
447
+ export function listArkCanary(opts) {
448
+ return request(opts, 'GET', '/api/ark/canary');
449
+ }
450
+ export function ackArkCanary(opts, id, status) {
451
+ return request(opts, 'POST', `/api/ark/canary/${encodeURIComponent(id)}/ack`, { status });
452
+ }
453
+ export function listArkClones(opts) {
454
+ return request(opts, 'GET', '/api/ark/clones');
455
+ }
456
+ export function createArkClone(opts, body) {
457
+ return request(opts, 'POST', '/api/ark/clones', body);
458
+ }
459
+ export function expireArkClone(opts, id) {
460
+ return request(opts, 'POST', `/api/ark/clones/${encodeURIComponent(id)}/expire`);
461
+ }
462
+ const enc = encodeURIComponent;
463
+ export function forgeDeviceCode(opts, scope) {
464
+ return request(opts, 'POST', '/api/forge/device/code', { scope });
465
+ }
466
+ /** One poll of the device-token endpoint. `authorization_pending` (and a 429
467
+ * slow_down) are normal mid-flow answers, not errors — so this speaks fetch
468
+ * directly instead of request(), which throws on any non-2xx. */
469
+ export async function forgeDeviceToken(opts, deviceCode) {
470
+ const res = await fetch(`${opts.apiUrl}/api/forge/device/token`, {
471
+ method: 'POST',
472
+ headers: { 'Content-Type': 'application/json' },
473
+ body: JSON.stringify({ device_code: deviceCode }),
474
+ });
475
+ const body = (await res.json().catch(() => null));
476
+ if (res.ok && typeof body?.access_token === 'string') {
477
+ return { status: 'ok', token: body.access_token, scope: body.scope ?? 'read' };
478
+ }
479
+ if (res.status === 429)
480
+ return { status: 'slow_down' };
481
+ if (body?.error === 'authorization_pending')
482
+ return { status: 'pending' };
483
+ return { status: 'expired' };
484
+ }
485
+ export function listForgeRepos(opts) {
486
+ return request(opts, 'GET', '/api/forge/repos');
487
+ }
488
+ export function createForgeRepo(opts, body) {
489
+ return request(opts, 'POST', '/api/forge/repos', body);
490
+ }
491
+ export function getForgeRepo(opts, repo) {
492
+ return request(opts, 'GET', `/api/forge/repos/${enc(repo)}`);
493
+ }
494
+ export function deleteForgeRepo(opts, repo) {
495
+ return request(opts, 'DELETE', `/api/forge/repos/${enc(repo)}`);
496
+ }
497
+ export function forgeTree(opts, repo, rev, path) {
498
+ const q = new URLSearchParams();
499
+ if (rev)
500
+ q.set('rev', rev);
501
+ if (path)
502
+ q.set('path', path);
503
+ const qs = q.toString() ? `?${q}` : '';
504
+ return request(opts, 'GET', `/api/forge/repos/${enc(repo)}/tree${qs}`);
505
+ }
506
+ export function forgeBlob(opts, repo, path, rev) {
507
+ const q = new URLSearchParams({ path });
508
+ if (rev)
509
+ q.set('rev', rev);
510
+ return request(opts, 'GET', `/api/forge/repos/${enc(repo)}/blob?${q}`);
511
+ }
512
+ export function forgeCommits(opts, repo, rev, limit) {
513
+ const q = new URLSearchParams();
514
+ if (rev)
515
+ q.set('rev', rev);
516
+ if (limit)
517
+ q.set('limit', String(limit));
518
+ const qs = q.toString() ? `?${q}` : '';
519
+ return request(opts, 'GET', `/api/forge/repos/${enc(repo)}/commits${qs}`);
520
+ }
521
+ export function forgeRefs(opts, repo) {
522
+ return request(opts, 'GET', `/api/forge/repos/${enc(repo)}/refs`);
523
+ }
524
+ export function forgeBlame(opts, repo, path, rev) {
525
+ const q = new URLSearchParams({ path });
526
+ if (rev)
527
+ q.set('rev', rev);
528
+ return request(opts, 'GET', `/api/forge/repos/${enc(repo)}/blame?${q}`);
529
+ }
530
+ export function listForgePolicies(opts, repo) {
531
+ return request(opts, 'GET', `/api/forge/repos/${enc(repo)}/policies`);
532
+ }
533
+ export function putForgePolicy(opts, repo, body) {
534
+ return request(opts, 'PUT', `/api/forge/repos/${enc(repo)}/policies`, body);
535
+ }
536
+ export function listForgeReflog(opts, repo, limit) {
537
+ const qs = limit ? `?limit=${limit}` : '';
538
+ return request(opts, 'GET', `/api/forge/repos/${enc(repo)}/reflog${qs}`);
539
+ }
540
+ export function recoverForgeRef(opts, repo, id) {
541
+ return request(opts, 'POST', `/api/forge/repos/${enc(repo)}/reflog/${enc(id)}/recover`, {});
542
+ }
543
+ export function listForgeFindings(opts, repo, limit) {
544
+ const qs = limit ? `?limit=${limit}` : '';
545
+ return request(opts, 'GET', `/api/forge/repos/${enc(repo)}/findings${qs}`);
546
+ }
547
+ export function allowlistForgeFinding(opts, repo, id, reason) {
548
+ return request(opts, 'POST', `/api/forge/repos/${enc(repo)}/findings/${enc(id)}/allowlist`, { reason });
549
+ }
550
+ export function listForgePulls(opts, repo, state) {
551
+ const qs = state ? `?state=${enc(state)}` : '';
552
+ return request(opts, 'GET', `/api/forge/repos/${enc(repo)}/pulls${qs}`);
553
+ }
554
+ export function createForgePull(opts, repo, body) {
555
+ return request(opts, 'POST', `/api/forge/repos/${enc(repo)}/pulls`, body);
556
+ }
557
+ export function getForgePull(opts, repo, number) {
558
+ return request(opts, 'GET', `/api/forge/repos/${enc(repo)}/pulls/${number}`);
559
+ }
560
+ export function mergeForgePull(opts, repo, number, body) {
561
+ return request(opts, 'POST', `/api/forge/repos/${enc(repo)}/pulls/${number}/merge`, body);
562
+ }
563
+ export function reviewForgePull(opts, repo, number, body) {
564
+ return request(opts, 'POST', `/api/forge/repos/${enc(repo)}/pulls/${number}/reviews`, body);
565
+ }
566
+ export function listForgeChecks(opts, repo, sha) {
567
+ const qs = sha ? `?sha=${enc(sha)}` : '';
568
+ return request(opts, 'GET', `/api/forge/repos/${enc(repo)}/checks${qs}`);
569
+ }
570
+ export function reportForgeCheck(opts, repo, body) {
571
+ return request(opts, 'POST', `/api/forge/repos/${enc(repo)}/checks`, body);
572
+ }
573
+ export function listForgeCiJobs(opts, repo, status, workflow, limit) {
574
+ const q = new URLSearchParams();
575
+ if (status)
576
+ q.set('status', status);
577
+ if (workflow)
578
+ q.set('workflow', workflow);
579
+ if (limit)
580
+ q.set('limit', String(limit));
581
+ const qs = q.toString() ? `?${q}` : '';
582
+ return request(opts, 'GET', `/api/forge/repos/${enc(repo)}/ci/jobs${qs}`);
583
+ }
584
+ export function getForgeCiJob(opts, repo, id) {
585
+ return request(opts, 'GET', `/api/forge/repos/${enc(repo)}/ci/jobs/${enc(id)}`);
586
+ }
587
+ export function runForgeCi(opts, repo, sha, ref) {
588
+ return request(opts, 'POST', `/api/forge/repos/${enc(repo)}/ci/run`, {
589
+ ...(sha ? { sha } : {}),
590
+ ...(ref ? { ref } : {}),
591
+ });
592
+ }
593
+ export function forgeCorrelation(opts, repo, rev) {
594
+ const qs = rev ? `?rev=${enc(rev)}` : '';
595
+ return request(opts, 'GET', `/api/forge/repos/${enc(repo)}/correlation${qs}`);
596
+ }
597
+ export function listForgeTokens(opts) {
598
+ return request(opts, 'GET', '/api/forge/tokens');
599
+ }
600
+ export function createForgeToken(opts, body) {
601
+ return request(opts, 'POST', '/api/forge/tokens', body);
602
+ }
603
+ export function revokeForgeToken(opts, id) {
604
+ return request(opts, 'DELETE', `/api/forge/tokens/${enc(id)}`);
605
+ }
606
+ export function listForgeAudit(opts, limit) {
607
+ const qs = limit ? `?limit=${limit}` : '';
608
+ return request(opts, 'GET', `/api/forge/audit${qs}`);
609
+ }
610
+ export function getForgeAiPolicy(opts, repo) {
611
+ return request(opts, 'GET', `/api/forge/repos/${enc(repo)}/ai-policy`);
612
+ }
613
+ export function putForgeAiPolicy(opts, repo, body) {
614
+ return request(opts, 'PUT', `/api/forge/repos/${enc(repo)}/ai-policy`, body);
615
+ }
616
+ export function listForgeEgress(opts, limit) {
617
+ const qs = limit ? `?limit=${limit}` : '';
618
+ return request(opts, 'GET', `/api/forge/ai-egress${qs}`);
619
+ }
620
+ export function setForgeMirror(opts, repo, body) {
621
+ return request(opts, 'PUT', `/api/forge/repos/${enc(repo)}/mirror`, body);
622
+ }
623
+ export function removeForgeMirror(opts, repo) {
624
+ return request(opts, 'DELETE', `/api/forge/repos/${enc(repo)}/mirror`);
625
+ }
626
+ export function listForgeKeys(opts, repo) {
627
+ return request(opts, 'GET', `/api/forge/repos/${enc(repo)}/keys`);
628
+ }
629
+ export function addForgeKey(opts, repo, body) {
630
+ return request(opts, 'POST', `/api/forge/repos/${enc(repo)}/keys`, body);
631
+ }
632
+ export function revokeForgeKey(opts, repo, keyId) {
633
+ return request(opts, 'DELETE', `/api/forge/repos/${enc(repo)}/keys/${enc(keyId)}`);
634
+ }
635
+ export function getForgeCodeowners(opts, repo, rev) {
636
+ const qs = rev ? `?rev=${enc(rev)}` : '';
637
+ return request(opts, 'GET', `/api/forge/repos/${enc(repo)}/codeowners${qs}`);
638
+ }
639
+ export function listForgeIssues(opts, repo, params) {
640
+ const q = new URLSearchParams();
641
+ if (params?.state)
642
+ q.set('state', params.state);
643
+ if (params?.label)
644
+ q.set('label', params.label);
645
+ const qs = q.toString() ? `?${q.toString()}` : '';
646
+ return request(opts, 'GET', `/api/forge/repos/${enc(repo)}/issues${qs}`);
647
+ }
648
+ export function createForgeIssue(opts, repo, body) {
649
+ return request(opts, 'POST', `/api/forge/repos/${enc(repo)}/issues`, body);
650
+ }
651
+ export function getForgeIssue(opts, repo, number) {
652
+ return request(opts, 'GET', `/api/forge/repos/${enc(repo)}/issues/${number}`);
653
+ }
654
+ export function updateForgeIssue(opts, repo, number, body) {
655
+ return request(opts, 'PATCH', `/api/forge/repos/${enc(repo)}/issues/${number}`, body);
656
+ }
657
+ export function commentForgeIssue(opts, repo, number, body) {
658
+ return request(opts, 'POST', `/api/forge/repos/${enc(repo)}/issues/${number}/comments`, { body });
659
+ }
660
+ export function listForgeLabels(opts, repo) {
661
+ return request(opts, 'GET', `/api/forge/repos/${enc(repo)}/labels`);
662
+ }
663
+ export function createForgeLabel(opts, repo, body) {
664
+ return request(opts, 'POST', `/api/forge/repos/${enc(repo)}/labels`, body);
665
+ }
666
+ export function listForgeCollaborators(opts, repo) {
667
+ return request(opts, 'GET', `/api/forge/repos/${enc(repo)}/collaborators`);
668
+ }
669
+ export function addForgeCollaborator(opts, repo, body) {
670
+ return request(opts, 'POST', `/api/forge/repos/${enc(repo)}/collaborators`, body);
671
+ }
672
+ export function removeForgeCollaborator(opts, repo, id) {
673
+ return request(opts, 'DELETE', `/api/forge/repos/${enc(repo)}/collaborators/${enc(id)}`);
674
+ }
675
+ export function updateForgePull(opts, repo, number, body) {
676
+ return request(opts, 'PATCH', `/api/forge/repos/${enc(repo)}/pulls/${number}`, body);
677
+ }
678
+ export function listForgePullComments(opts, repo, number) {
679
+ return request(opts, 'GET', `/api/forge/repos/${enc(repo)}/pulls/${number}/comments`);
680
+ }
681
+ export function commentForgePull(opts, repo, number, body) {
682
+ return request(opts, 'POST', `/api/forge/repos/${enc(repo)}/pulls/${number}/comments`, body);
683
+ }
684
+ export function forgePullDiff(opts, repo, number) {
685
+ return request(opts, 'GET', `/api/forge/repos/${enc(repo)}/pulls/${number}/diff`);
686
+ }
687
+ export function forgeCompare(opts, repo, base, head) {
688
+ return request(opts, 'GET', `/api/forge/repos/${enc(repo)}/compare?base=${enc(base)}&head=${enc(head)}`);
689
+ }
690
+ export function forgeSearch(opts, repo, q, rev, limit) {
691
+ const qp = new URLSearchParams({ q });
692
+ if (rev)
693
+ qp.set('rev', rev);
694
+ if (limit)
695
+ qp.set('limit', String(limit));
696
+ return request(opts, 'GET', `/api/forge/repos/${enc(repo)}/search?${qp}`);
697
+ }
698
+ export function forgeEnvironments(opts, repo) {
699
+ return request(opts, 'GET', `/api/forge/repos/${enc(repo)}/environments`);
700
+ }
701
+ export function forgeSetEnvironment(opts, repo, body) {
702
+ return request(opts, 'POST', `/api/forge/repos/${enc(repo)}/environments`, body);
703
+ }
704
+ export function forgeDeployments(opts, repo, environment) {
705
+ const q = new URLSearchParams();
706
+ if (environment)
707
+ q.set('environment', environment);
708
+ const qs = q.toString() ? `?${q}` : '';
709
+ return request(opts, 'GET', `/api/forge/repos/${enc(repo)}/deployments${qs}`);
710
+ }
711
+ export function forgeCreateDeployment(opts, repo, body) {
712
+ return request(opts, 'POST', `/api/forge/repos/${enc(repo)}/deployments`, body);
713
+ }
714
+ export function forgeUpdateDeployment(opts, repo, id, body) {
715
+ return request(opts, 'PATCH', `/api/forge/repos/${enc(repo)}/deployments/${enc(id)}`, body);
716
+ }
717
+ export function forgeReleases(opts, repo) {
718
+ return request(opts, 'GET', `/api/forge/repos/${enc(repo)}/releases`);
719
+ }
720
+ export function forgeGetRelease(opts, repo, tag) {
721
+ return request(opts, 'GET', `/api/forge/repos/${enc(repo)}/releases/${enc(tag)}`);
722
+ }
723
+ export function forgeCreateRelease(opts, repo, body) {
724
+ return request(opts, 'POST', `/api/forge/repos/${enc(repo)}/releases`, body);
725
+ }
726
+ export function forgeDeleteRelease(opts, repo, tag) {
727
+ return request(opts, 'DELETE', `/api/forge/repos/${enc(repo)}/releases/${enc(tag)}`);
728
+ }
729
+ export function forgeAddAsset(opts, repo, tag, body) {
730
+ return request(opts, 'POST', `/api/forge/repos/${enc(repo)}/releases/${enc(tag)}/assets`, body);
731
+ }
732
+ export function forgeRemoveAsset(opts, repo, tag, id) {
733
+ return request(opts, 'DELETE', `/api/forge/repos/${enc(repo)}/releases/${enc(tag)}/assets/${enc(id)}`);
734
+ }
735
+ export function forgeWebhooks(opts) {
736
+ return request(opts, 'GET', '/api/forge/webhooks');
737
+ }
738
+ export function forgeCreateWebhook(opts, body) {
739
+ return request(opts, 'POST', '/api/forge/webhooks', body);
740
+ }
741
+ export function forgeDeleteWebhook(opts, id) {
742
+ return request(opts, 'DELETE', `/api/forge/webhooks/${enc(id)}`);
743
+ }
744
+ export function forgePingWebhook(opts, id) {
745
+ return request(opts, 'POST', `/api/forge/webhooks/${enc(id)}/ping`, {});
746
+ }
747
+ export function forgeWebhookDeliveries(opts, id) {
748
+ return request(opts, 'GET', `/api/forge/webhooks/${enc(id)}/deliveries`);
749
+ }
750
+ export function listForgeAccountKeys(opts) {
751
+ return request(opts, 'GET', '/api/forge/keys');
752
+ }
753
+ export function addForgeAccountKey(opts, body) {
754
+ return request(opts, 'POST', '/api/forge/keys', body);
755
+ }
756
+ export function revokeForgeAccountKey(opts, id) {
757
+ return request(opts, 'DELETE', `/api/forge/keys/${enc(id)}`);
758
+ }
759
+ //# sourceMappingURL=api.js.map