@stacklink/sdk 0.2.2 → 0.3.8
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 +50 -1
- package/compatibility.json +3 -1
- package/dist/src/domain/bot-contract.d.ts +4336 -0
- package/dist/src/domain/bot-contract.d.ts.map +1 -0
- package/dist/src/domain/bot-contract.js +382 -0
- package/dist/src/domain/bot-contract.js.map +1 -0
- package/dist/src/domain/memory-contract.d.ts +452 -452
- package/dist/src/domain/memory-contract.js +1 -1
- package/dist/src/domain/memory-contract.js.map +1 -1
- package/dist/src/domain/platform-approval-contract.d.ts +508 -0
- package/dist/src/domain/platform-approval-contract.d.ts.map +1 -0
- package/dist/src/domain/platform-approval-contract.js +77 -0
- package/dist/src/domain/platform-approval-contract.js.map +1 -0
- package/dist/src/domain/provider-branding.d.ts.map +1 -1
- package/dist/src/domain/provider-branding.js +17 -0
- package/dist/src/domain/provider-branding.js.map +1 -1
- package/dist/src/domain/stacklink-meta-tools.d.ts +2 -2
- package/dist/src/domain/stacklink-meta-tools.d.ts.map +1 -1
- package/dist/src/domain/stacklink-meta-tools.js +15 -4
- package/dist/src/domain/stacklink-meta-tools.js.map +1 -1
- package/dist/src/sdk/framework-tools.d.ts +21 -0
- package/dist/src/sdk/framework-tools.d.ts.map +1 -1
- package/dist/src/sdk/framework-tools.js +49 -0
- package/dist/src/sdk/framework-tools.js.map +1 -1
- package/dist/src/sdk/index.d.ts +225 -6
- package/dist/src/sdk/index.d.ts.map +1 -1
- package/dist/src/sdk/index.js +215 -21
- package/dist/src/sdk/index.js.map +1 -1
- package/dist/src/sdk/vercel-ai.d.ts +4 -0
- package/dist/src/sdk/vercel-ai.d.ts.map +1 -1
- package/dist/src/sdk/vercel-ai.js +13 -4
- package/dist/src/sdk/vercel-ai.js.map +1 -1
- package/dist/src/sdk/x-chat.d.ts +143 -0
- package/dist/src/sdk/x-chat.d.ts.map +1 -0
- package/dist/src/sdk/x-chat.js +160 -0
- package/dist/src/sdk/x-chat.js.map +1 -0
- package/package.json +15 -3
package/dist/src/sdk/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
import { botApiSignaturePayload } from '../domain/bot-contract.js';
|
|
1
2
|
import { materializeStacklinkWorkbenchExecutionResponse, } from '../domain/stacklink-meta-tools.js';
|
|
2
3
|
export { StacklinkMemoryLifecycle, StacklinkMemoryLifecycleError, createStacklinkMemoryLifecycle, serializeCompletedConversationTurn, } from './memory-lifecycle.js';
|
|
3
|
-
export const stacklinkTypeScriptSdkVersion = '0.
|
|
4
|
+
export const stacklinkTypeScriptSdkVersion = '0.3.8';
|
|
4
5
|
const workbenchReusableRunComparisonWaitMs = 120_000;
|
|
5
6
|
const workbenchReusableRunComparisonPollMs = 1_000;
|
|
6
7
|
export class StacklinkApiError extends Error {
|
|
@@ -140,6 +141,39 @@ export async function verifyWebhookSignature(input) {
|
|
|
140
141
|
}
|
|
141
142
|
return false;
|
|
142
143
|
}
|
|
144
|
+
/** Sign the exact JSON bytes sent to a saved inbound Bot webhook. Reuse requestId when retrying. */
|
|
145
|
+
export async function signBotTriggerRequest(input) {
|
|
146
|
+
const cryptoImpl = globalThis.crypto ?? (await import('node:crypto')).webcrypto;
|
|
147
|
+
const timestamp = String(Math.floor((input.now ?? new Date()).getTime() / 1000));
|
|
148
|
+
const requestId = input.requestId ?? cryptoImpl.randomUUID();
|
|
149
|
+
const digest = await cryptoImpl.subtle.digest('SHA-256', utf8(input.body));
|
|
150
|
+
const digestHex = Array.from(new Uint8Array(digest), value => value.toString(16).padStart(2, '0')).join('');
|
|
151
|
+
const key = await cryptoImpl.subtle.importKey('raw', utf8(input.secret), { name: 'HMAC', hash: 'SHA-256' }, false, ['sign']);
|
|
152
|
+
const signature = await cryptoImpl.subtle.sign('HMAC', key, utf8(JSON.stringify(['stacklink-trigger-v1', input.triggerId, timestamp, requestId, digestHex])));
|
|
153
|
+
return { 'content-type': 'application/json', 'stacklink-timestamp': timestamp, 'stacklink-request-id': requestId, 'stacklink-signature': Array.from(new Uint8Array(signature), value => value.toString(16).padStart(2, '0')).join('') };
|
|
154
|
+
}
|
|
155
|
+
/** Verify the original request bytes before using identity. consumeRequestId must atomically reject replays. */
|
|
156
|
+
export async function verifyBotApiRequest(input) {
|
|
157
|
+
const headers = new Headers(input.headers);
|
|
158
|
+
const get = (name) => headers.get(`x-stacklink-${name}`) ?? '';
|
|
159
|
+
const keyId = get('key-id'), timestamp = get('timestamp'), requestId = get('request-id');
|
|
160
|
+
const key = input.keys[keyId], projectId = get('project'), tenantId = get('tenant'), userId = get('customer');
|
|
161
|
+
const secret = typeof key === 'string' ? key : key?.secret;
|
|
162
|
+
const seconds = Number(timestamp), now = Math.floor((input.now ?? new Date()).getTime() / 1000), tolerance = input.toleranceSeconds ?? 300;
|
|
163
|
+
if (key && typeof key !== 'string' && key.validUntil !== null && (!Number.isFinite(Date.parse(key.validUntil)) || Date.parse(key.validUntil) <= now * 1000))
|
|
164
|
+
return null;
|
|
165
|
+
if (typeof secret !== 'string' || !secret || !tenantId || !requestId || projectId !== input.projectId || !Number.isInteger(seconds) || Math.abs(now - seconds) > tolerance)
|
|
166
|
+
return null;
|
|
167
|
+
const cryptoImpl = globalThis.crypto ?? (await import('node:crypto')).webcrypto;
|
|
168
|
+
const digest = await cryptoImpl.subtle.digest('SHA-256', utf8(input.body));
|
|
169
|
+
const bodySha256 = Array.from(new Uint8Array(digest), byte => byte.toString(16).padStart(2, '0')).join('');
|
|
170
|
+
const expected = await hmacSha256Base64(secret, botApiSignaturePayload({ method: input.method, url: input.url, requestId, timestamp, keyId, projectId, tenantId, userId, bodySha256 }));
|
|
171
|
+
if (!constantTimeStringEqual(get('signature'), `v1,${expected}`))
|
|
172
|
+
return null;
|
|
173
|
+
if (!await input.consumeRequestId(requestId, new Date((seconds + tolerance) * 1000)))
|
|
174
|
+
return null;
|
|
175
|
+
return { tenantId, userId: userId || null, requestId };
|
|
176
|
+
}
|
|
143
177
|
function signatureCandidates(header) {
|
|
144
178
|
const candidates = [];
|
|
145
179
|
for (const entry of header.split(' ')) {
|
|
@@ -194,6 +228,8 @@ export class Stacklink {
|
|
|
194
228
|
discoveryRequests = new Map();
|
|
195
229
|
timeoutMs;
|
|
196
230
|
maxAttempts;
|
|
231
|
+
bots;
|
|
232
|
+
approvals;
|
|
197
233
|
webhooks;
|
|
198
234
|
triggers;
|
|
199
235
|
sessions;
|
|
@@ -220,6 +256,8 @@ export class Stacklink {
|
|
|
220
256
|
searchTtlMs: options.cache?.searchTtlMs ?? 120_000,
|
|
221
257
|
maxEntries: options.cache?.maxEntries ?? 500,
|
|
222
258
|
};
|
|
259
|
+
this.bots = new StacklinkBotsClient(this);
|
|
260
|
+
this.approvals = new StacklinkApprovalsClient(this);
|
|
223
261
|
this.webhooks = new StacklinkWebhooksClient(this);
|
|
224
262
|
this.triggers = new StacklinkTriggersClient(this);
|
|
225
263
|
this.sessions = new StacklinkSessionsClient(this);
|
|
@@ -697,7 +735,7 @@ export class Stacklink {
|
|
|
697
735
|
const executionKind = accepted.executionKind === 'workbench_code' || accepted.executionKind === 'workbench_bash'
|
|
698
736
|
? accepted.executionKind
|
|
699
737
|
: undefined;
|
|
700
|
-
if (!sessionId || !executionId || !executionKind)
|
|
738
|
+
if (!sessionId || !executionId || !executionKind || accepted.status === 'waiting_for_approval')
|
|
701
739
|
return accepted;
|
|
702
740
|
while (true) {
|
|
703
741
|
const waited = await this.waitForExecution({
|
|
@@ -707,7 +745,7 @@ export class Stacklink {
|
|
|
707
745
|
waitTimeoutMs: 10_000,
|
|
708
746
|
}, options);
|
|
709
747
|
const execution = isSdkRecord(waited.execution) ? waited.execution : undefined;
|
|
710
|
-
if (execution && isTerminalSdkExecution(execution)) {
|
|
748
|
+
if (execution && (isTerminalSdkExecution(execution) || execution.status === 'waiting_for_approval')) {
|
|
711
749
|
return materializeStacklinkWorkbenchExecutionResponse(accepted, execution);
|
|
712
750
|
}
|
|
713
751
|
}
|
|
@@ -745,6 +783,56 @@ export class Stacklink {
|
|
|
745
783
|
signal: options.signal,
|
|
746
784
|
});
|
|
747
785
|
}
|
|
786
|
+
async *streamBotEvents(botId, runId, options = {}) {
|
|
787
|
+
let cursor = options.after ?? '0';
|
|
788
|
+
const deadline = Date.now() + positiveSdkInteger(options.totalTimeoutMs ?? 300_000, 'totalTimeoutMs');
|
|
789
|
+
const terminal = (event) => ['run.completed', 'run.failed', 'run.stopped'].includes(String(event.type));
|
|
790
|
+
while (!options.signal?.aborted) {
|
|
791
|
+
const remaining = deadline - Date.now();
|
|
792
|
+
if (remaining <= 0)
|
|
793
|
+
throw new StacklinkApiError('Bot observation timed out. The same run may still be active; resume with the last sequence.', 408, 'sdk_stream_timeout', { runId, after: cursor });
|
|
794
|
+
try {
|
|
795
|
+
const response = await this.requestRaw(`/v1/bots/${encodeURIComponent(botId)}/runs/${encodeURIComponent(runId)}/events/stream`, { headers: { accept: 'text/event-stream', 'Last-Event-ID': cursor }, signal: options.signal, timeoutMs: Math.min(options.timeoutMs ?? 30_000, remaining) });
|
|
796
|
+
for await (const event of this.readJsonEventStream(response)) {
|
|
797
|
+
if (!isSdkRecord(event) || typeof event.sequence !== 'string')
|
|
798
|
+
continue;
|
|
799
|
+
if (event.runId !== runId)
|
|
800
|
+
throw new StacklinkApiError('Bot event belongs to a different run.', 502, 'bot_event_invalid');
|
|
801
|
+
if (BigInt(event.sequence) <= BigInt(cursor))
|
|
802
|
+
continue;
|
|
803
|
+
cursor = event.sequence;
|
|
804
|
+
yield event;
|
|
805
|
+
if (terminal(event))
|
|
806
|
+
return;
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
catch (error) {
|
|
810
|
+
if (options.signal?.aborted)
|
|
811
|
+
throw options.signal.reason ?? error;
|
|
812
|
+
const retryable = error instanceof TypeError || (error instanceof Error && ['AbortError', 'TimeoutError'].includes(error.name))
|
|
813
|
+
|| (error instanceof StacklinkApiError && [408, 429, 502, 503, 504].includes(error.statusCode) && error.code !== 'bot_event_invalid');
|
|
814
|
+
if (!retryable)
|
|
815
|
+
throw error;
|
|
816
|
+
}
|
|
817
|
+
// Fetch the durable tail before considering completion. A status read alone
|
|
818
|
+
// could miss the final text and terminal event that arrived during a disconnect.
|
|
819
|
+
const page = await this.bots.events(botId, runId, cursor);
|
|
820
|
+
for (const event of page.events) {
|
|
821
|
+
if (BigInt(event.sequence) <= BigInt(cursor))
|
|
822
|
+
continue;
|
|
823
|
+
cursor = event.sequence;
|
|
824
|
+
yield event;
|
|
825
|
+
if (terminal(event))
|
|
826
|
+
return;
|
|
827
|
+
}
|
|
828
|
+
if (page.terminal && page.events.length === 0)
|
|
829
|
+
return;
|
|
830
|
+
if (page.events.length < 200)
|
|
831
|
+
await sdkDelay(Math.min(1000, Math.max(1, deadline - Date.now())), options.signal ?? AbortSignal.timeout(Math.max(1, deadline - Date.now())));
|
|
832
|
+
}
|
|
833
|
+
if (options.signal?.aborted)
|
|
834
|
+
throw options.signal.reason;
|
|
835
|
+
}
|
|
748
836
|
async *streamExecutionEvents(input, options = {}) {
|
|
749
837
|
let afterSequence = input.afterSequence === undefined ? undefined : String(input.afterSequence);
|
|
750
838
|
while (!options.signal?.aborted) {
|
|
@@ -849,6 +937,12 @@ export class Stacklink {
|
|
|
849
937
|
async getConnectedAccount(accountId) {
|
|
850
938
|
return this.request(`/v1/connections/${accountId}`);
|
|
851
939
|
}
|
|
940
|
+
async setConnectedAccountCredentialProfile(accountId, profileKey, credentials) {
|
|
941
|
+
return this.request(`/v1/connections/${encodeURIComponent(accountId)}/credential-profiles/${encodeURIComponent(profileKey)}`, { method: 'PUT', body: { credentials } });
|
|
942
|
+
}
|
|
943
|
+
async removeConnectedAccountCredentialProfile(accountId, profileKey) {
|
|
944
|
+
return this.request(`/v1/connections/${encodeURIComponent(accountId)}/credential-profiles/${encodeURIComponent(profileKey)}`, { method: 'DELETE' });
|
|
945
|
+
}
|
|
852
946
|
async listConnectedAccountTools(accountId, input) {
|
|
853
947
|
return this.request(`/v1/connections/${accountId}/tools`, {
|
|
854
948
|
query: {
|
|
@@ -1609,29 +1703,35 @@ export class Stacklink {
|
|
|
1609
1703
|
}
|
|
1610
1704
|
return undefined;
|
|
1611
1705
|
};
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1706
|
+
try {
|
|
1707
|
+
while (true) {
|
|
1708
|
+
const read = await reader.read();
|
|
1709
|
+
if (read.done)
|
|
1710
|
+
break;
|
|
1711
|
+
buffer += decoder.decode(read.value, { stream: true });
|
|
1712
|
+
let index = buffer.indexOf('\n');
|
|
1713
|
+
while (index >= 0) {
|
|
1714
|
+
const payload = payloadForLine(buffer.slice(0, index));
|
|
1715
|
+
if (payload !== undefined)
|
|
1716
|
+
yield payload;
|
|
1717
|
+
buffer = buffer.slice(index + 1);
|
|
1718
|
+
index = buffer.indexOf('\n');
|
|
1719
|
+
}
|
|
1720
|
+
}
|
|
1721
|
+
buffer += decoder.decode();
|
|
1722
|
+
if (buffer.length > 0) {
|
|
1723
|
+
const payload = payloadForLine(buffer);
|
|
1620
1724
|
if (payload !== undefined)
|
|
1621
1725
|
yield payload;
|
|
1622
|
-
buffer = buffer.slice(index + 1);
|
|
1623
|
-
index = buffer.indexOf('\n');
|
|
1624
1726
|
}
|
|
1727
|
+
const finalPayload = payloadForLine('');
|
|
1728
|
+
if (finalPayload !== undefined)
|
|
1729
|
+
yield finalPayload;
|
|
1625
1730
|
}
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
if (payload !== undefined)
|
|
1630
|
-
yield payload;
|
|
1731
|
+
finally {
|
|
1732
|
+
await reader.cancel().catch(() => undefined);
|
|
1733
|
+
reader.releaseLock();
|
|
1631
1734
|
}
|
|
1632
|
-
const finalPayload = payloadForLine('');
|
|
1633
|
-
if (finalPayload !== undefined)
|
|
1634
|
-
yield finalPayload;
|
|
1635
1735
|
}
|
|
1636
1736
|
requestHeaders(registration, extraHeaders = {}) {
|
|
1637
1737
|
if (registration) {
|
|
@@ -1931,6 +2031,55 @@ export class StacklinkMemoryClient {
|
|
|
1931
2031
|
return this.client.request(`/v1/memory/legal-holds/${holdId}/release`, { method: 'POST' });
|
|
1932
2032
|
}
|
|
1933
2033
|
}
|
|
2034
|
+
export class StacklinkApprovalsClient {
|
|
2035
|
+
client;
|
|
2036
|
+
constructor(client) {
|
|
2037
|
+
this.client = client;
|
|
2038
|
+
}
|
|
2039
|
+
/** Server-only access. Identity comes from the authenticated application session. */
|
|
2040
|
+
getCustomerGrant(sessionId) {
|
|
2041
|
+
return this.client.request('/v1/approvals/customer-grant', { query: { sessionId } });
|
|
2042
|
+
}
|
|
2043
|
+
saveCustomerGrant(sessionId, grant) {
|
|
2044
|
+
return this.client.request('/v1/approvals/customer-grant', { method: 'PUT', query: { sessionId }, body: grant });
|
|
2045
|
+
}
|
|
2046
|
+
removeCustomerGrant(sessionId, revision) {
|
|
2047
|
+
return this.client.request('/v1/approvals/customer-grant', { method: 'DELETE', query: { sessionId }, body: { revision } });
|
|
2048
|
+
}
|
|
2049
|
+
list(input) {
|
|
2050
|
+
return this.client.request('/v1/approvals', { query: { ...input } });
|
|
2051
|
+
}
|
|
2052
|
+
get(id, access) {
|
|
2053
|
+
return this.client.request(`/v1/approvals/${encodeURIComponent(id)}`, { query: { ...access } });
|
|
2054
|
+
}
|
|
2055
|
+
/** Decide only after displaying the exact proposal and checking permissions. Never resubmit the original action. */
|
|
2056
|
+
decide(id, access, decision) {
|
|
2057
|
+
return this.client.request(`/v1/approvals/${encodeURIComponent(id)}/decide`, { method: 'POST', query: { ...access }, body: decision });
|
|
2058
|
+
}
|
|
2059
|
+
change(id, access, change) {
|
|
2060
|
+
return this.client.request(`/v1/approvals/${encodeURIComponent(id)}/change`, { method: 'POST', query: { ...access }, body: change });
|
|
2061
|
+
}
|
|
2062
|
+
events(access, after = '0', options = {}) {
|
|
2063
|
+
return this.client.request('/v1/approvals/events', { ...options, query: { ...access, after } });
|
|
2064
|
+
}
|
|
2065
|
+
/** Durable cursor observation. Save each sequence and pass it as `after` when reconnecting. */
|
|
2066
|
+
async *observe(access, options = {}) {
|
|
2067
|
+
let cursor = options.after ?? '0';
|
|
2068
|
+
const deadline = Date.now() + (options.timeoutMs ?? 300_000);
|
|
2069
|
+
const signal = options.signal ?? AbortSignal.timeout(Math.max(1, deadline - Date.now()));
|
|
2070
|
+
while (!signal.aborted && Date.now() < deadline) {
|
|
2071
|
+
const page = await this.events(access, cursor, { signal, timeoutMs: Math.min(15_000, Math.max(1, deadline - Date.now())) });
|
|
2072
|
+
for (const event of page.events) {
|
|
2073
|
+
if (BigInt(event.sequence) <= BigInt(cursor))
|
|
2074
|
+
continue;
|
|
2075
|
+
cursor = event.sequence;
|
|
2076
|
+
yield event;
|
|
2077
|
+
}
|
|
2078
|
+
if (page.events.length < 100)
|
|
2079
|
+
await sdkDelay(Math.min(1000, Math.max(1, deadline - Date.now())), signal);
|
|
2080
|
+
}
|
|
2081
|
+
}
|
|
2082
|
+
}
|
|
1934
2083
|
export class StacklinkSessionsClient {
|
|
1935
2084
|
client;
|
|
1936
2085
|
constructor(client) {
|
|
@@ -2988,4 +3137,49 @@ function validateSdkMcpResponse(value, requestId) {
|
|
|
2988
3137
|
throw new StacklinkApiError('Stacklink returned an invalid or unrelated MCP response.', 502, 'mcp_response_invalid');
|
|
2989
3138
|
}
|
|
2990
3139
|
}
|
|
3140
|
+
/** Hosted bots share durable runs across the API, MCP and dashboard. */
|
|
3141
|
+
export class StacklinkBotsClient {
|
|
3142
|
+
client;
|
|
3143
|
+
constructor(client) {
|
|
3144
|
+
this.client = client;
|
|
3145
|
+
}
|
|
3146
|
+
apis() { return this.client.request('/v1/bots/apis'); }
|
|
3147
|
+
updateApi(apiId, revision, api) { return this.client.request(`/v1/bots/apis/${encodeURIComponent(apiId)}`, { method: 'PATCH', body: { revision, api } }); }
|
|
3148
|
+
deleteApi(apiId) { return this.client.request(`/v1/bots/apis/${encodeURIComponent(apiId)}`, { method: 'DELETE' }); }
|
|
3149
|
+
webhookSecret(botId) { return this.client.request(`/v1/bots/${encodeURIComponent(botId)}/webhook-secret`, { method: 'POST', body: {} }); }
|
|
3150
|
+
triggerWebhook(botId, triggerId) { return this.client.request(`/v1/bots/${encodeURIComponent(botId)}/triggers/${encodeURIComponent(triggerId)}/webhook`, { method: 'POST', body: {} }); }
|
|
3151
|
+
triggerAdmission(botId, triggerId, admissionId) { return this.client.request(`/v1/bots/${encodeURIComponent(botId)}/triggers/${encodeURIComponent(triggerId)}/admissions/${encodeURIComponent(admissionId)}`); }
|
|
3152
|
+
apiSigningKey(botId, apiId, request = { action: 'read' }) { return this.client.request(`/v1/bots/${encodeURIComponent(botId)}/apis/${encodeURIComponent(apiId)}/signing-key`, { method: 'POST', body: request }); }
|
|
3153
|
+
models() { return this.client.request('/v1/bots/models'); }
|
|
3154
|
+
setCredential(botId, secret) { return this.client.request(`/v1/bots/${encodeURIComponent(botId)}/credentials`, { method: 'POST', body: { secret } }); }
|
|
3155
|
+
deliveries(botId, runId) { return this.client.request(`/v1/bots/${encodeURIComponent(botId)}/runs/${encodeURIComponent(runId)}/deliveries`); }
|
|
3156
|
+
retryDelivery(botId, runId, deliveryId) { return this.client.request(`/v1/bots/${encodeURIComponent(botId)}/runs/${encodeURIComponent(runId)}/deliveries/retry`, { method: 'POST', body: { deliveryId } }); }
|
|
3157
|
+
setCustomerCredential(botId, input) { return this.client.request(`/v1/bots/${encodeURIComponent(botId)}/credentials`, { method: 'POST', body: input }); }
|
|
3158
|
+
delete(botId) { return this.client.request(`/v1/bots/${encodeURIComponent(botId)}`, { method: 'DELETE' }); }
|
|
3159
|
+
deleteCustomerData(botId, sessionId) { return this.client.request(`/v1/bots/${encodeURIComponent(botId)}/customer-data/delete`, { method: 'POST', body: { sessionId } }); }
|
|
3160
|
+
setState(botId, input) { return this.client.request(`/v1/bots/${encodeURIComponent(botId)}/state`, { method: 'POST', body: input }); }
|
|
3161
|
+
list() { return this.client.request('/v1/bots'); }
|
|
3162
|
+
create(name) { return this.client.request('/v1/bots', { method: 'POST', body: { name } }); }
|
|
3163
|
+
get(botId) { return this.client.request(`/v1/bots/${encodeURIComponent(botId)}`); }
|
|
3164
|
+
update(botId, revision, patch) { return this.client.request(`/v1/bots/${encodeURIComponent(botId)}`, { method: 'PATCH', body: { revision, patch } }); }
|
|
3165
|
+
publish(botId, revision, channels) { return this.client.request(`/v1/bots/${encodeURIComponent(botId)}/publish`, { method: 'POST', body: { revision, channels } }); }
|
|
3166
|
+
run(botId, input) { return this.client.request(`/v1/bots/${encodeURIComponent(botId)}/runs`, { method: 'POST', body: { ...input, runId: input.runId ?? globalThis.crypto.randomUUID() } }); }
|
|
3167
|
+
getRun(botId, runId) { return this.client.request(`/v1/bots/${encodeURIComponent(botId)}/runs/${encodeURIComponent(runId)}`); }
|
|
3168
|
+
/** Observe the same durable run. Returns when finished or when a person must act. */
|
|
3169
|
+
async wait(botId, runId, options = {}) {
|
|
3170
|
+
const deadline = Date.now() + positiveSdkInteger(options.totalTimeoutMs ?? 300_000, 'totalTimeoutMs');
|
|
3171
|
+
while (Date.now() < deadline) {
|
|
3172
|
+
if (options.signal?.aborted)
|
|
3173
|
+
throw options.signal.reason;
|
|
3174
|
+
const run = await this.client.request(`/v1/bots/${encodeURIComponent(botId)}/runs/${encodeURIComponent(runId)}`, { signal: options.signal, timeoutMs: Math.min(30_000, deadline - Date.now()) });
|
|
3175
|
+
if (['completed', 'failed', 'stopped', 'cancelled', 'waiting_for_approval', 'waiting_for_connection'].includes(run.status))
|
|
3176
|
+
return run;
|
|
3177
|
+
await sdkDelay(Math.min(1000, Math.max(1, deadline - Date.now())), options.signal ?? AbortSignal.timeout(Math.max(1, deadline - Date.now())));
|
|
3178
|
+
}
|
|
3179
|
+
throw new StacklinkApiError('Bot observation timed out. Continue observing the same run.', 408, 'sdk_run_timeout', { botId, runId });
|
|
3180
|
+
}
|
|
3181
|
+
events(botId, runId, after = '0') { return this.client.request(`/v1/bots/${encodeURIComponent(botId)}/runs/${encodeURIComponent(runId)}/events`, { query: { after } }); }
|
|
3182
|
+
stream(botId, runId, options = {}) { return this.client.streamBotEvents(botId, runId, options); }
|
|
3183
|
+
cancel(botId, runId) { return this.client.request(`/v1/bots/${encodeURIComponent(botId)}/runs/${encodeURIComponent(runId)}/stop`, { method: 'POST', body: {} }); }
|
|
3184
|
+
}
|
|
2991
3185
|
//# sourceMappingURL=index.js.map
|