@agentforge-io/sdk-console 0.0.0-stage → 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/README.md +172 -2
- package/dist/client.d.ts +43 -0
- package/dist/client.js +58 -0
- package/dist/errors.d.ts +25 -0
- package/dist/errors.js +93 -0
- package/dist/http.d.ts +76 -0
- package/dist/http.js +289 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.js +57 -0
- package/dist/oauth.d.ts +37 -0
- package/dist/oauth.js +97 -0
- package/dist/resources/agents.d.ts +40 -0
- package/dist/resources/agents.js +67 -0
- package/dist/resources/apps.d.ts +16 -0
- package/dist/resources/apps.js +26 -0
- package/dist/resources/auth.d.ts +50 -0
- package/dist/resources/auth.js +87 -0
- package/dist/resources/connectors.d.ts +25 -0
- package/dist/resources/connectors.js +47 -0
- package/dist/resources/marketplace.d.ts +10 -0
- package/dist/resources/marketplace.js +14 -0
- package/dist/resources/members.d.ts +62 -0
- package/dist/resources/members.js +85 -0
- package/dist/resources/playground.d.ts +22 -0
- package/dist/resources/playground.js +32 -0
- package/dist/resources/teams.d.ts +49 -0
- package/dist/resources/teams.js +78 -0
- package/dist/resources/templates.d.ts +115 -0
- package/dist/resources/templates.js +187 -0
- package/dist/resources/tenants.d.ts +12 -0
- package/dist/resources/tenants.js +25 -0
- package/dist/resources/tools.d.ts +13 -0
- package/dist/resources/tools.js +17 -0
- package/dist/resources/variables.d.ts +28 -0
- package/dist/resources/variables.js +43 -0
- package/dist/token-store.d.ts +23 -0
- package/dist/token-store.js +27 -0
- package/dist/types.d.ts +1009 -0
- package/dist/types.js +19 -0
- package/package.json +44 -4
package/dist/http.js
ADDED
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// HTTP core: injectable fetch, bearer auth from a TokenStore, single-flight
|
|
3
|
+
// refresh on 401 + retry once, typed errors. Runs unchanged in React Native,
|
|
4
|
+
// browsers and Node: only `fetch`, `AbortController` and `setTimeout` are used
|
|
5
|
+
// (no Buffer / crypto / fs, and no URLSearchParams: RN's polyfill lacks most methods).
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.HttpCore = void 0;
|
|
8
|
+
exports.buildQuery = buildQuery;
|
|
9
|
+
exports.seg = seg;
|
|
10
|
+
const errors_1 = require("./errors");
|
|
11
|
+
const DEFAULT_TIMEOUT_MS = 30000;
|
|
12
|
+
/** Join a query object: skips null/undefined, arrays become `a,b`. */
|
|
13
|
+
function buildQuery(query) {
|
|
14
|
+
if (!query)
|
|
15
|
+
return '';
|
|
16
|
+
// Hand-built (no URLSearchParams): React Native's polyfill lacks set/get/append.
|
|
17
|
+
const parts = [];
|
|
18
|
+
for (const [key, value] of Object.entries(query)) {
|
|
19
|
+
if (value === undefined || value === null)
|
|
20
|
+
continue;
|
|
21
|
+
let str;
|
|
22
|
+
if (Array.isArray(value)) {
|
|
23
|
+
if (!value.length)
|
|
24
|
+
continue;
|
|
25
|
+
str = value.join(',');
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
str = String(value);
|
|
29
|
+
}
|
|
30
|
+
parts.push(`${encodeURIComponent(key)}=${encodeURIComponent(str)}`);
|
|
31
|
+
}
|
|
32
|
+
return parts.length ? `?${parts.join('&')}` : '';
|
|
33
|
+
}
|
|
34
|
+
function seg(value) {
|
|
35
|
+
return encodeURIComponent(value);
|
|
36
|
+
}
|
|
37
|
+
class HttpCore {
|
|
38
|
+
constructor(opts) {
|
|
39
|
+
this.refreshing = null;
|
|
40
|
+
this.signingOut = null;
|
|
41
|
+
/** Bumped whenever the session is replaced or wiped; a refresh that started under an older epoch is stale. */
|
|
42
|
+
this.epoch = 0;
|
|
43
|
+
if (!opts.baseURL)
|
|
44
|
+
throw new Error('baseURL is required');
|
|
45
|
+
if (!opts.tokenStore)
|
|
46
|
+
throw new Error('tokenStore is required');
|
|
47
|
+
this.opts = opts;
|
|
48
|
+
this.baseURL = opts.baseURL.replace(/\/+$/, '');
|
|
49
|
+
this.store = opts.tokenStore;
|
|
50
|
+
this.crypto = opts.crypto;
|
|
51
|
+
}
|
|
52
|
+
/** Authenticated (or `auth: false`) JSON request. Resolves `undefined` on 204 / empty body. */
|
|
53
|
+
async request(method, path, options = {}) {
|
|
54
|
+
return this.send(method, path, options, false, 0);
|
|
55
|
+
}
|
|
56
|
+
/** Persist a session obtained from login / register / invite accept. */
|
|
57
|
+
async setSession(tokens) {
|
|
58
|
+
this.epoch++;
|
|
59
|
+
await this.store.setTokens({ accessToken: tokens.accessToken, refreshToken: tokens.refreshToken });
|
|
60
|
+
}
|
|
61
|
+
/** Invalidate any in-flight refresh without touching the store (call before
|
|
62
|
+
* an auth call that will replace or wipe the session). */
|
|
63
|
+
beginSessionChange() {
|
|
64
|
+
this.epoch++;
|
|
65
|
+
}
|
|
66
|
+
/** Wipe the session without firing `onSignedOut` (explicit logout). */
|
|
67
|
+
async clearSession() {
|
|
68
|
+
this.epoch++;
|
|
69
|
+
await this.store.clear();
|
|
70
|
+
}
|
|
71
|
+
/** Force a refresh now (shares the in-flight one). Throws if it does not succeed. */
|
|
72
|
+
async refreshNow() {
|
|
73
|
+
const result = await this.refreshSingleFlight();
|
|
74
|
+
if (result.outcome === 'ok')
|
|
75
|
+
return result.tokens;
|
|
76
|
+
if (result.outcome === 'rejected')
|
|
77
|
+
await this.signOut('refresh_rejected');
|
|
78
|
+
if (result.outcome === 'stale') {
|
|
79
|
+
// A newer session (or a sign-out) won: hand back the live pair if there is one.
|
|
80
|
+
const accessToken = await this.store.getAccessToken();
|
|
81
|
+
const refreshToken = await this.store.getRefreshToken();
|
|
82
|
+
if (accessToken && refreshToken)
|
|
83
|
+
return { accessToken, refreshToken };
|
|
84
|
+
}
|
|
85
|
+
throw result.error;
|
|
86
|
+
}
|
|
87
|
+
// ─── internals ──────────────────────────────────────────────────────────
|
|
88
|
+
async send(method, path, options, retriedAuth, rateLimitAttempt) {
|
|
89
|
+
const authed = options.auth !== false;
|
|
90
|
+
const tokenAtSend = authed ? await this.store.getAccessToken() : null;
|
|
91
|
+
const res = await this.rawFetch(method, path, options, tokenAtSend);
|
|
92
|
+
const maxRetries = this.opts.maxRateLimitRetries ?? 2;
|
|
93
|
+
if (res.status === 429 && method === 'GET' && rateLimitAttempt < maxRetries) {
|
|
94
|
+
const header = res.headers.get('retry-after');
|
|
95
|
+
const seconds = header ? parseInt(header, 10) : NaN;
|
|
96
|
+
// Clamp so a misconfigured upstream cannot park the UI for minutes.
|
|
97
|
+
const waitMs = Number.isFinite(seconds)
|
|
98
|
+
? Math.min(Math.max(seconds * 1000, 500), 10000)
|
|
99
|
+
: 800 * Math.pow(3, rateLimitAttempt);
|
|
100
|
+
await (this.opts.sleep ?? defaultSleep)(waitMs);
|
|
101
|
+
return this.send(method, path, options, retriedAuth, rateLimitAttempt + 1);
|
|
102
|
+
}
|
|
103
|
+
if (res.status === 401 && authed && !retriedAuth) {
|
|
104
|
+
if (await this.recoverFrom401(tokenAtSend)) {
|
|
105
|
+
return this.send(method, path, options, true, rateLimitAttempt);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
if (res.status === 204)
|
|
109
|
+
return undefined;
|
|
110
|
+
const text = res.text;
|
|
111
|
+
if (!res.ok)
|
|
112
|
+
throw (0, errors_1.errorFromResponse)(res.status, res.statusText, text);
|
|
113
|
+
if (!text)
|
|
114
|
+
return undefined;
|
|
115
|
+
try {
|
|
116
|
+
return JSON.parse(text);
|
|
117
|
+
}
|
|
118
|
+
catch {
|
|
119
|
+
throw new errors_1.ApiError({ status: res.status, message: 'Invalid JSON in response', details: text });
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
/** @returns true when the request should be replayed once. */
|
|
123
|
+
async recoverFrom401(tokenAtSend) {
|
|
124
|
+
// The session was replaced while this request was in flight (a new
|
|
125
|
+
// sign-in, or another call already refreshed): replay under the new one.
|
|
126
|
+
const current = await this.store.getAccessToken();
|
|
127
|
+
if (current && current !== tokenAtSend)
|
|
128
|
+
return true;
|
|
129
|
+
const refreshToken = await this.store.getRefreshToken();
|
|
130
|
+
// Never signed in: a plain 401, nothing to tear down.
|
|
131
|
+
if (!tokenAtSend && !refreshToken)
|
|
132
|
+
return false;
|
|
133
|
+
const result = await this.refreshSingleFlight();
|
|
134
|
+
if (result.outcome === 'ok')
|
|
135
|
+
return true;
|
|
136
|
+
if (result.outcome === 'unavailable')
|
|
137
|
+
return false; // keep the session; surface the 401
|
|
138
|
+
if (result.outcome === 'stale') {
|
|
139
|
+
// Signed out meanwhile: no replay. Replace by a new sign-in: replay under it.
|
|
140
|
+
const live = await this.store.getAccessToken();
|
|
141
|
+
return !!live && live !== tokenAtSend;
|
|
142
|
+
}
|
|
143
|
+
// Rejected: lose the race gracefully if a newer sign-in landed meanwhile.
|
|
144
|
+
const latest = await this.store.getAccessToken();
|
|
145
|
+
if (latest && latest !== tokenAtSend)
|
|
146
|
+
return true;
|
|
147
|
+
await this.signOut(refreshToken ? 'refresh_rejected' : 'no_refresh_token');
|
|
148
|
+
return false;
|
|
149
|
+
}
|
|
150
|
+
/** Idempotent: waiters of the same failed refresh all land here, but only
|
|
151
|
+
* one tears the session down and fires the callback. */
|
|
152
|
+
signOut(reason) {
|
|
153
|
+
if (this.signingOut)
|
|
154
|
+
return this.signingOut;
|
|
155
|
+
const run = (async () => {
|
|
156
|
+
const hadSession = !!((await this.store.getAccessToken()) || (await this.store.getRefreshToken()));
|
|
157
|
+
await this.store.clear();
|
|
158
|
+
if (hadSession)
|
|
159
|
+
this.opts.onSignedOut?.(reason);
|
|
160
|
+
})().finally(() => {
|
|
161
|
+
this.signingOut = null;
|
|
162
|
+
});
|
|
163
|
+
this.signingOut = run;
|
|
164
|
+
return run;
|
|
165
|
+
}
|
|
166
|
+
/** One /auth/refresh at a time: a burst of 401s awaits the same promise.
|
|
167
|
+
* The refresh token rotates on use, so a second concurrent call would be
|
|
168
|
+
* read as token reuse by the server and revoke the family. */
|
|
169
|
+
refreshSingleFlight() {
|
|
170
|
+
if (this.refreshing)
|
|
171
|
+
return this.refreshing;
|
|
172
|
+
const promise = this.doRefresh().finally(() => {
|
|
173
|
+
this.refreshing = null;
|
|
174
|
+
});
|
|
175
|
+
this.refreshing = promise;
|
|
176
|
+
return promise;
|
|
177
|
+
}
|
|
178
|
+
async doRefresh() {
|
|
179
|
+
const refreshToken = await this.store.getRefreshToken();
|
|
180
|
+
if (!refreshToken) {
|
|
181
|
+
return {
|
|
182
|
+
outcome: 'rejected',
|
|
183
|
+
error: new errors_1.ApiError({ status: 401, message: 'No refresh token', code: 'no_refresh_token' }),
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
const epoch = this.epoch;
|
|
187
|
+
const stale = () => ({
|
|
188
|
+
outcome: 'stale',
|
|
189
|
+
error: new errors_1.ApiError({ status: 401, message: 'Session changed during refresh', code: 'session_changed' }),
|
|
190
|
+
});
|
|
191
|
+
let res;
|
|
192
|
+
try {
|
|
193
|
+
res = await this.rawFetch('POST', '/auth/refresh', { body: { refreshToken } }, null);
|
|
194
|
+
}
|
|
195
|
+
catch (err) {
|
|
196
|
+
// Offline / timeout: the session may still be fine. Do not sign out.
|
|
197
|
+
return this.epoch !== epoch ? stale() : { outcome: 'unavailable', error: err };
|
|
198
|
+
}
|
|
199
|
+
if (this.epoch !== epoch)
|
|
200
|
+
return stale();
|
|
201
|
+
const text = res.text;
|
|
202
|
+
if (!res.ok) {
|
|
203
|
+
const error = (0, errors_1.errorFromResponse)(res.status, res.statusText, text);
|
|
204
|
+
// Only an explicit rejection of the token ends the session. 404/408/410/
|
|
205
|
+
// other 4xx (proxy, tunnel, deploy gaps), 429, 5xx: unavailable.
|
|
206
|
+
const rejected = res.status === 400 || res.status === 401 || res.status === 403;
|
|
207
|
+
return { outcome: rejected ? 'rejected' : 'unavailable', error };
|
|
208
|
+
}
|
|
209
|
+
let parsed;
|
|
210
|
+
try {
|
|
211
|
+
parsed = text ? JSON.parse(text) : undefined;
|
|
212
|
+
}
|
|
213
|
+
catch {
|
|
214
|
+
parsed = undefined;
|
|
215
|
+
}
|
|
216
|
+
if (!parsed?.accessToken || !parsed.refreshToken) {
|
|
217
|
+
return {
|
|
218
|
+
outcome: 'unavailable',
|
|
219
|
+
error: new errors_1.ApiError({ status: res.status, message: 'Malformed refresh response' }),
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
const tokens = { accessToken: parsed.accessToken, refreshToken: parsed.refreshToken };
|
|
223
|
+
if (this.epoch !== epoch)
|
|
224
|
+
return stale();
|
|
225
|
+
await this.store.setTokens(tokens);
|
|
226
|
+
return { outcome: 'ok', tokens };
|
|
227
|
+
}
|
|
228
|
+
async rawFetch(method, path, options, accessToken) {
|
|
229
|
+
const fetchImpl = this.opts.fetch ?? globalThis.fetch;
|
|
230
|
+
if (!fetchImpl)
|
|
231
|
+
throw new Error('No fetch available: pass `fetch` in the client options');
|
|
232
|
+
const headers = { Accept: 'application/json', ...this.opts.defaultHeaders };
|
|
233
|
+
if (options.body !== undefined)
|
|
234
|
+
headers['Content-Type'] = 'application/json';
|
|
235
|
+
if (accessToken)
|
|
236
|
+
headers.Authorization = `Bearer ${accessToken}`;
|
|
237
|
+
Object.assign(headers, options.headers);
|
|
238
|
+
const timeoutMs = this.opts.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
239
|
+
const controller = timeoutMs > 0 && typeof AbortController !== 'undefined' ? new AbortController() : undefined;
|
|
240
|
+
const timer = controller ? setTimeout(() => controller.abort(), timeoutMs) : undefined;
|
|
241
|
+
try {
|
|
242
|
+
const res = await fetchImpl(`${this.baseURL}${path}${buildQuery(options.query)}`, {
|
|
243
|
+
method,
|
|
244
|
+
headers,
|
|
245
|
+
body: options.body === undefined ? undefined : JSON.stringify(options.body),
|
|
246
|
+
signal: controller?.signal,
|
|
247
|
+
});
|
|
248
|
+
// The deadline also covers the body: a stalled stream must not hang forever.
|
|
249
|
+
const text = res.status === 204 ? '' : await readBody(res, controller?.signal);
|
|
250
|
+
return { status: res.status, statusText: res.statusText, ok: res.ok, headers: res.headers, text };
|
|
251
|
+
}
|
|
252
|
+
catch (err) {
|
|
253
|
+
const aborted = controller?.signal.aborted === true;
|
|
254
|
+
throw new errors_1.ApiError({
|
|
255
|
+
status: 0,
|
|
256
|
+
code: aborted ? 'timeout' : 'network_error',
|
|
257
|
+
message: aborted ? `Request timed out after ${timeoutMs}ms` : err instanceof Error ? err.message : 'Network request failed',
|
|
258
|
+
cause: err,
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
finally {
|
|
262
|
+
if (timer)
|
|
263
|
+
clearTimeout(timer);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
exports.HttpCore = HttpCore;
|
|
268
|
+
function defaultSleep(ms) {
|
|
269
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
270
|
+
}
|
|
271
|
+
/** Read the body, rejecting if `signal` aborts first (RN / mocks may not tie the stream to the signal). */
|
|
272
|
+
function readBody(res, signal) {
|
|
273
|
+
const read = res.text().catch(() => '');
|
|
274
|
+
if (!signal)
|
|
275
|
+
return read;
|
|
276
|
+
return new Promise((resolve, reject) => {
|
|
277
|
+
const onAbort = () => reject(new Error('aborted'));
|
|
278
|
+
if (signal.aborted)
|
|
279
|
+
return onAbort();
|
|
280
|
+
signal.addEventListener('abort', onAbort, { once: true });
|
|
281
|
+
read.then((t) => {
|
|
282
|
+
signal.removeEventListener('abort', onAbort);
|
|
283
|
+
resolve(t);
|
|
284
|
+
}, (e) => {
|
|
285
|
+
signal.removeEventListener('abort', onAbort);
|
|
286
|
+
reject(e);
|
|
287
|
+
});
|
|
288
|
+
});
|
|
289
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export { ConsoleClient, TenantScope } from './client';
|
|
2
|
+
export { ApiError, isApiError } from './errors';
|
|
3
|
+
export { MemoryTokenStore } from './token-store';
|
|
4
|
+
export type { AuthTokens, MaybePromise, TokenStore } from './token-store';
|
|
5
|
+
export { parseOAuthReturn } from './oauth';
|
|
6
|
+
export type { OAuthCrypto, OAuthReturn } from './oauth';
|
|
7
|
+
export type { ConsoleClientOptions, FetchLike, QueryValue, RequestOptions, SignedOutReason } from './http';
|
|
8
|
+
export * from './types';
|
|
9
|
+
export { AgentsResource, ChatTokensResource } from './resources/agents';
|
|
10
|
+
export { AppsResource } from './resources/apps';
|
|
11
|
+
export { AuthResource } from './resources/auth';
|
|
12
|
+
export { ConnectorsResource } from './resources/connectors';
|
|
13
|
+
export { InvitesResource, MembersResource, PublicInvitesResource } from './resources/members';
|
|
14
|
+
export { MarketplaceResource } from './resources/marketplace';
|
|
15
|
+
export { PlaygroundResource } from './resources/playground';
|
|
16
|
+
export { TeamHumanMembersResource, TeamsResource } from './resources/teams';
|
|
17
|
+
export { TemplatesResource, TenantTemplatesResource } from './resources/templates';
|
|
18
|
+
export { TenantsResource } from './resources/tenants';
|
|
19
|
+
export { ToolsResource } from './resources/tools';
|
|
20
|
+
export { VariablesResource } from './resources/variables';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
exports.VariablesResource = exports.ToolsResource = exports.TenantsResource = exports.TenantTemplatesResource = exports.TemplatesResource = exports.TeamsResource = exports.TeamHumanMembersResource = exports.PlaygroundResource = exports.MarketplaceResource = exports.PublicInvitesResource = exports.MembersResource = exports.InvitesResource = exports.ConnectorsResource = exports.AuthResource = exports.AppsResource = exports.ChatTokensResource = exports.AgentsResource = exports.parseOAuthReturn = exports.MemoryTokenStore = exports.isApiError = exports.ApiError = exports.TenantScope = exports.ConsoleClient = void 0;
|
|
18
|
+
var client_1 = require("./client");
|
|
19
|
+
Object.defineProperty(exports, "ConsoleClient", { enumerable: true, get: function () { return client_1.ConsoleClient; } });
|
|
20
|
+
Object.defineProperty(exports, "TenantScope", { enumerable: true, get: function () { return client_1.TenantScope; } });
|
|
21
|
+
var errors_1 = require("./errors");
|
|
22
|
+
Object.defineProperty(exports, "ApiError", { enumerable: true, get: function () { return errors_1.ApiError; } });
|
|
23
|
+
Object.defineProperty(exports, "isApiError", { enumerable: true, get: function () { return errors_1.isApiError; } });
|
|
24
|
+
var token_store_1 = require("./token-store");
|
|
25
|
+
Object.defineProperty(exports, "MemoryTokenStore", { enumerable: true, get: function () { return token_store_1.MemoryTokenStore; } });
|
|
26
|
+
var oauth_1 = require("./oauth");
|
|
27
|
+
Object.defineProperty(exports, "parseOAuthReturn", { enumerable: true, get: function () { return oauth_1.parseOAuthReturn; } });
|
|
28
|
+
__exportStar(require("./types"), exports);
|
|
29
|
+
var agents_1 = require("./resources/agents");
|
|
30
|
+
Object.defineProperty(exports, "AgentsResource", { enumerable: true, get: function () { return agents_1.AgentsResource; } });
|
|
31
|
+
Object.defineProperty(exports, "ChatTokensResource", { enumerable: true, get: function () { return agents_1.ChatTokensResource; } });
|
|
32
|
+
var apps_1 = require("./resources/apps");
|
|
33
|
+
Object.defineProperty(exports, "AppsResource", { enumerable: true, get: function () { return apps_1.AppsResource; } });
|
|
34
|
+
var auth_1 = require("./resources/auth");
|
|
35
|
+
Object.defineProperty(exports, "AuthResource", { enumerable: true, get: function () { return auth_1.AuthResource; } });
|
|
36
|
+
var connectors_1 = require("./resources/connectors");
|
|
37
|
+
Object.defineProperty(exports, "ConnectorsResource", { enumerable: true, get: function () { return connectors_1.ConnectorsResource; } });
|
|
38
|
+
var members_1 = require("./resources/members");
|
|
39
|
+
Object.defineProperty(exports, "InvitesResource", { enumerable: true, get: function () { return members_1.InvitesResource; } });
|
|
40
|
+
Object.defineProperty(exports, "MembersResource", { enumerable: true, get: function () { return members_1.MembersResource; } });
|
|
41
|
+
Object.defineProperty(exports, "PublicInvitesResource", { enumerable: true, get: function () { return members_1.PublicInvitesResource; } });
|
|
42
|
+
var marketplace_1 = require("./resources/marketplace");
|
|
43
|
+
Object.defineProperty(exports, "MarketplaceResource", { enumerable: true, get: function () { return marketplace_1.MarketplaceResource; } });
|
|
44
|
+
var playground_1 = require("./resources/playground");
|
|
45
|
+
Object.defineProperty(exports, "PlaygroundResource", { enumerable: true, get: function () { return playground_1.PlaygroundResource; } });
|
|
46
|
+
var teams_1 = require("./resources/teams");
|
|
47
|
+
Object.defineProperty(exports, "TeamHumanMembersResource", { enumerable: true, get: function () { return teams_1.TeamHumanMembersResource; } });
|
|
48
|
+
Object.defineProperty(exports, "TeamsResource", { enumerable: true, get: function () { return teams_1.TeamsResource; } });
|
|
49
|
+
var templates_1 = require("./resources/templates");
|
|
50
|
+
Object.defineProperty(exports, "TemplatesResource", { enumerable: true, get: function () { return templates_1.TemplatesResource; } });
|
|
51
|
+
Object.defineProperty(exports, "TenantTemplatesResource", { enumerable: true, get: function () { return templates_1.TenantTemplatesResource; } });
|
|
52
|
+
var tenants_1 = require("./resources/tenants");
|
|
53
|
+
Object.defineProperty(exports, "TenantsResource", { enumerable: true, get: function () { return tenants_1.TenantsResource; } });
|
|
54
|
+
var tools_1 = require("./resources/tools");
|
|
55
|
+
Object.defineProperty(exports, "ToolsResource", { enumerable: true, get: function () { return tools_1.ToolsResource; } });
|
|
56
|
+
var variables_1 = require("./resources/variables");
|
|
57
|
+
Object.defineProperty(exports, "VariablesResource", { enumerable: true, get: function () { return variables_1.VariablesResource; } });
|
package/dist/oauth.d.ts
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal crypto surface needed for PKCE. Pass it as the `crypto` client option
|
|
3
|
+
* on runtimes without a global WebCrypto (React Native). With `expo-crypto`:
|
|
4
|
+
*
|
|
5
|
+
* crypto: {
|
|
6
|
+
* getRandomValues: (bytes) => Crypto.getRandomValues(bytes),
|
|
7
|
+
* sha256: async (data) => new Uint8Array(await Crypto.digest(Crypto.CryptoDigestAlgorithm.SHA256, data)),
|
|
8
|
+
* }
|
|
9
|
+
*/
|
|
10
|
+
export interface OAuthCrypto {
|
|
11
|
+
/** Fill `bytes` with cryptographically secure random values. May fill in
|
|
12
|
+
* place (return nothing) or return the filled array. */
|
|
13
|
+
getRandomValues(bytes: Uint8Array): Uint8Array | void;
|
|
14
|
+
/** SHA-256 digest (32 bytes) of `data`. */
|
|
15
|
+
sha256(data: Uint8Array): Promise<Uint8Array>;
|
|
16
|
+
}
|
|
17
|
+
export declare const CODE_VERIFIER_LENGTH = 64;
|
|
18
|
+
/** Resolve the adapter, falling back to the global WebCrypto. Throws if neither exists. */
|
|
19
|
+
export declare function resolveCrypto(adapter?: OAuthCrypto): OAuthCrypto;
|
|
20
|
+
/** RFC 7636 verifier: 64 chars of the unreserved set. */
|
|
21
|
+
export declare function generateCodeVerifier(c: OAuthCrypto): string;
|
|
22
|
+
export declare function base64UrlEncode(bytes: Uint8Array): string;
|
|
23
|
+
/** S256 challenge: base64url(SHA-256(ASCII(verifier))), no padding. */
|
|
24
|
+
export declare function codeChallengeS256(c: OAuthCrypto, verifier: string): Promise<string>;
|
|
25
|
+
/** Result of `parseOAuthReturn`. */
|
|
26
|
+
export type OAuthReturn = {
|
|
27
|
+
code: string;
|
|
28
|
+
} | {
|
|
29
|
+
error: string;
|
|
30
|
+
};
|
|
31
|
+
/**
|
|
32
|
+
* Read the fragment of the URL the server redirected to: `#code=...` on
|
|
33
|
+
* success, `#error=...` on failure. Works for `https://` and custom schemes
|
|
34
|
+
* (`poodleck-console://auth/callback#code=...`). Returns `null` when the URL
|
|
35
|
+
* carries neither. If both are present, `error` wins.
|
|
36
|
+
*/
|
|
37
|
+
export declare function parseOAuthReturn(url: string): OAuthReturn | null;
|
package/dist/oauth.js
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// OAuth browser-redirect sign-in helpers (PKCE, RFC 7636). Pure and runtime
|
|
3
|
+
// agnostic: no Node `crypto`, no Buffer, no TextEncoder, no URLSearchParams, so
|
|
4
|
+
// it runs on Hermes / React Native, browsers and Node >= 19.
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.CODE_VERIFIER_LENGTH = void 0;
|
|
7
|
+
exports.resolveCrypto = resolveCrypto;
|
|
8
|
+
exports.generateCodeVerifier = generateCodeVerifier;
|
|
9
|
+
exports.base64UrlEncode = base64UrlEncode;
|
|
10
|
+
exports.codeChallengeS256 = codeChallengeS256;
|
|
11
|
+
exports.parseOAuthReturn = parseOAuthReturn;
|
|
12
|
+
const UNRESERVED = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~';
|
|
13
|
+
const B64URL = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_';
|
|
14
|
+
/** Largest multiple of 66 below 256: bytes at or above it are rejected (no modulo bias). */
|
|
15
|
+
const REJECT_FROM = 198;
|
|
16
|
+
exports.CODE_VERIFIER_LENGTH = 64;
|
|
17
|
+
/** Resolve the adapter, falling back to the global WebCrypto. Throws if neither exists. */
|
|
18
|
+
function resolveCrypto(adapter) {
|
|
19
|
+
if (adapter)
|
|
20
|
+
return adapter;
|
|
21
|
+
const g = globalThis.crypto;
|
|
22
|
+
if (g && typeof g.getRandomValues === 'function' && g.subtle && typeof g.subtle.digest === 'function') {
|
|
23
|
+
return {
|
|
24
|
+
getRandomValues: (bytes) => g.getRandomValues(bytes),
|
|
25
|
+
sha256: async (data) => new Uint8Array(await g.subtle.digest('SHA-256', data)),
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
throw new Error('No crypto available for OAuth PKCE: pass a `crypto` adapter ({ getRandomValues, sha256 }) in the client options');
|
|
29
|
+
}
|
|
30
|
+
function randomBytes(c, length) {
|
|
31
|
+
const bytes = new Uint8Array(length);
|
|
32
|
+
return c.getRandomValues(bytes) ?? bytes;
|
|
33
|
+
}
|
|
34
|
+
/** RFC 7636 verifier: 64 chars of the unreserved set. */
|
|
35
|
+
function generateCodeVerifier(c) {
|
|
36
|
+
let out = '';
|
|
37
|
+
while (out.length < exports.CODE_VERIFIER_LENGTH) {
|
|
38
|
+
const bytes = randomBytes(c, exports.CODE_VERIFIER_LENGTH * 2);
|
|
39
|
+
for (let i = 0; i < bytes.length && out.length < exports.CODE_VERIFIER_LENGTH; i++) {
|
|
40
|
+
if (bytes[i] < REJECT_FROM)
|
|
41
|
+
out += UNRESERVED[bytes[i] % UNRESERVED.length];
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return out;
|
|
45
|
+
}
|
|
46
|
+
function base64UrlEncode(bytes) {
|
|
47
|
+
let out = '';
|
|
48
|
+
for (let i = 0; i < bytes.length; i += 3) {
|
|
49
|
+
const n = (bytes[i] << 16) | ((bytes[i + 1] ?? 0) << 8) | (bytes[i + 2] ?? 0);
|
|
50
|
+
out += B64URL[(n >> 18) & 63] + B64URL[(n >> 12) & 63];
|
|
51
|
+
if (i + 1 < bytes.length)
|
|
52
|
+
out += B64URL[(n >> 6) & 63];
|
|
53
|
+
if (i + 2 < bytes.length)
|
|
54
|
+
out += B64URL[n & 63];
|
|
55
|
+
}
|
|
56
|
+
return out;
|
|
57
|
+
}
|
|
58
|
+
/** S256 challenge: base64url(SHA-256(ASCII(verifier))), no padding. */
|
|
59
|
+
async function codeChallengeS256(c, verifier) {
|
|
60
|
+
const data = new Uint8Array(verifier.length);
|
|
61
|
+
for (let i = 0; i < verifier.length; i++)
|
|
62
|
+
data[i] = verifier.charCodeAt(i) & 0xff;
|
|
63
|
+
const digest = await c.sha256(data);
|
|
64
|
+
return base64UrlEncode(digest);
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Read the fragment of the URL the server redirected to: `#code=...` on
|
|
68
|
+
* success, `#error=...` on failure. Works for `https://` and custom schemes
|
|
69
|
+
* (`poodleck-console://auth/callback#code=...`). Returns `null` when the URL
|
|
70
|
+
* carries neither. If both are present, `error` wins.
|
|
71
|
+
*/
|
|
72
|
+
function parseOAuthReturn(url) {
|
|
73
|
+
if (typeof url !== 'string')
|
|
74
|
+
return null;
|
|
75
|
+
const hash = url.indexOf('#');
|
|
76
|
+
if (hash < 0)
|
|
77
|
+
return null;
|
|
78
|
+
const params = {};
|
|
79
|
+
for (const pair of url.slice(hash + 1).split('&')) {
|
|
80
|
+
if (!pair)
|
|
81
|
+
continue;
|
|
82
|
+
const eq = pair.indexOf('=');
|
|
83
|
+
const rawKey = eq < 0 ? pair : pair.slice(0, eq);
|
|
84
|
+
const rawVal = eq < 0 ? '' : pair.slice(eq + 1);
|
|
85
|
+
try {
|
|
86
|
+
params[decodeURIComponent(rawKey.replace(/\+/g, ' '))] = decodeURIComponent(rawVal.replace(/\+/g, ' '));
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
89
|
+
// malformed escape: skip this pair
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
if (params.error)
|
|
93
|
+
return { error: params.error };
|
|
94
|
+
if (params.code)
|
|
95
|
+
return { code: params.code };
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { HttpCore } from '../http';
|
|
2
|
+
import type { Agent, AgentInput, AgentList, AgentParentTeam, ChatToken, ChatTokenInput, ListAgentsInput } from '../types';
|
|
3
|
+
export declare class AgentsResource {
|
|
4
|
+
private readonly http;
|
|
5
|
+
private readonly tenantId;
|
|
6
|
+
/** Public chat-widget tokens of an agent. */
|
|
7
|
+
readonly chatTokens: ChatTokensResource;
|
|
8
|
+
constructor(http: HttpCore, tenantId: string);
|
|
9
|
+
private get base();
|
|
10
|
+
/** `GET /admin/tenants/:tenantId/agents` */
|
|
11
|
+
list(input?: ListAgentsInput): Promise<AgentList>;
|
|
12
|
+
/** `POST .../agents` */
|
|
13
|
+
create(input: AgentInput): Promise<Agent>;
|
|
14
|
+
/** `GET .../agents/:agentId` */
|
|
15
|
+
get(agentId: string): Promise<Agent>;
|
|
16
|
+
/** `PATCH .../agents/:agentId` */
|
|
17
|
+
update(agentId: string, patch: Partial<AgentInput>): Promise<Agent>;
|
|
18
|
+
/** `DELETE .../agents/:agentId` (deactivates). */
|
|
19
|
+
delete(agentId: string): Promise<void>;
|
|
20
|
+
/** `GET .../agents/:agentId/parent-team` */
|
|
21
|
+
parentTeam(agentId: string): Promise<AgentParentTeam>;
|
|
22
|
+
}
|
|
23
|
+
export declare class ChatTokensResource {
|
|
24
|
+
private readonly http;
|
|
25
|
+
private readonly tenantId;
|
|
26
|
+
constructor(http: HttpCore, tenantId: string);
|
|
27
|
+
private base;
|
|
28
|
+
/** `GET .../agents/:agentId/chat-tokens` */
|
|
29
|
+
list(agentId: string): Promise<{
|
|
30
|
+
items: ChatToken[];
|
|
31
|
+
}>;
|
|
32
|
+
/** `POST .../agents/:agentId/chat-tokens` */
|
|
33
|
+
create(agentId: string, input: ChatTokenInput): Promise<ChatToken>;
|
|
34
|
+
/** `PATCH .../chat-tokens/:token` */
|
|
35
|
+
update(agentId: string, token: string, patch: Partial<ChatTokenInput> & {
|
|
36
|
+
isActive?: boolean;
|
|
37
|
+
}): Promise<void>;
|
|
38
|
+
/** `DELETE .../chat-tokens/:token` */
|
|
39
|
+
revoke(agentId: string, token: string): Promise<void>;
|
|
40
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// agents of one workspace (`forTenant(id).agents`). `delete` deactivates
|
|
3
|
+
// (`isActive: false`); history is kept.
|
|
4
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
5
|
+
exports.ChatTokensResource = exports.AgentsResource = void 0;
|
|
6
|
+
const http_1 = require("../http");
|
|
7
|
+
class AgentsResource {
|
|
8
|
+
constructor(http, tenantId) {
|
|
9
|
+
this.http = http;
|
|
10
|
+
this.tenantId = tenantId;
|
|
11
|
+
this.chatTokens = new ChatTokensResource(http, tenantId);
|
|
12
|
+
}
|
|
13
|
+
get base() {
|
|
14
|
+
return `/admin/tenants/${(0, http_1.seg)(this.tenantId)}/agents`;
|
|
15
|
+
}
|
|
16
|
+
/** `GET /admin/tenants/:tenantId/agents` */
|
|
17
|
+
async list(input = {}) {
|
|
18
|
+
return this.http.request('GET', this.base, { query: { ...input } });
|
|
19
|
+
}
|
|
20
|
+
/** `POST .../agents` */
|
|
21
|
+
async create(input) {
|
|
22
|
+
return this.http.request('POST', this.base, { body: input });
|
|
23
|
+
}
|
|
24
|
+
/** `GET .../agents/:agentId` */
|
|
25
|
+
async get(agentId) {
|
|
26
|
+
return this.http.request('GET', `${this.base}/${(0, http_1.seg)(agentId)}`);
|
|
27
|
+
}
|
|
28
|
+
/** `PATCH .../agents/:agentId` */
|
|
29
|
+
async update(agentId, patch) {
|
|
30
|
+
return this.http.request('PATCH', `${this.base}/${(0, http_1.seg)(agentId)}`, { body: patch });
|
|
31
|
+
}
|
|
32
|
+
/** `DELETE .../agents/:agentId` (deactivates). */
|
|
33
|
+
async delete(agentId) {
|
|
34
|
+
await this.http.request('DELETE', `${this.base}/${(0, http_1.seg)(agentId)}`);
|
|
35
|
+
}
|
|
36
|
+
/** `GET .../agents/:agentId/parent-team` */
|
|
37
|
+
async parentTeam(agentId) {
|
|
38
|
+
return this.http.request('GET', `${this.base}/${(0, http_1.seg)(agentId)}/parent-team`);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
exports.AgentsResource = AgentsResource;
|
|
42
|
+
class ChatTokensResource {
|
|
43
|
+
constructor(http, tenantId) {
|
|
44
|
+
this.http = http;
|
|
45
|
+
this.tenantId = tenantId;
|
|
46
|
+
}
|
|
47
|
+
base(agentId) {
|
|
48
|
+
return `/admin/tenants/${(0, http_1.seg)(this.tenantId)}/agents/${(0, http_1.seg)(agentId)}/chat-tokens`;
|
|
49
|
+
}
|
|
50
|
+
/** `GET .../agents/:agentId/chat-tokens` */
|
|
51
|
+
async list(agentId) {
|
|
52
|
+
return this.http.request('GET', this.base(agentId));
|
|
53
|
+
}
|
|
54
|
+
/** `POST .../agents/:agentId/chat-tokens` */
|
|
55
|
+
async create(agentId, input) {
|
|
56
|
+
return this.http.request('POST', this.base(agentId), { body: input });
|
|
57
|
+
}
|
|
58
|
+
/** `PATCH .../chat-tokens/:token` */
|
|
59
|
+
async update(agentId, token, patch) {
|
|
60
|
+
await this.http.request('PATCH', `${this.base(agentId)}/${(0, http_1.seg)(token)}`, { body: patch });
|
|
61
|
+
}
|
|
62
|
+
/** `DELETE .../chat-tokens/:token` */
|
|
63
|
+
async revoke(agentId, token) {
|
|
64
|
+
await this.http.request('DELETE', `${this.base(agentId)}/${(0, http_1.seg)(token)}`);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
exports.ChatTokensResource = ChatTokensResource;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { HttpCore } from '../http';
|
|
2
|
+
import type { AppSummary, UpdateAppInput } from '../types';
|
|
3
|
+
export declare class AppsResource {
|
|
4
|
+
private readonly http;
|
|
5
|
+
private readonly tenantId;
|
|
6
|
+
constructor(http: HttpCore, tenantId: string);
|
|
7
|
+
/** `GET /apps?tenantId=` */
|
|
8
|
+
list(): Promise<{
|
|
9
|
+
items: AppSummary[];
|
|
10
|
+
}>;
|
|
11
|
+
/** `GET /apps/:id?tenantId=` */
|
|
12
|
+
get(appId: string): Promise<AppSummary>;
|
|
13
|
+
/** `PATCH /apps/:id?tenantId=`: branding fields (workspace admin) and `templatePolicy`
|
|
14
|
+
* (workspace owner only; null clears; 400 `invalid_template_policy` if malformed). */
|
|
15
|
+
update(appId: string, patch: UpdateAppInput): Promise<AppSummary>;
|
|
16
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// apps: the platform Apps (publishers) a workspace owns. Read + edit.
|
|
3
|
+
// `forTenant(id).apps`.
|
|
4
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
5
|
+
exports.AppsResource = void 0;
|
|
6
|
+
const http_1 = require("../http");
|
|
7
|
+
class AppsResource {
|
|
8
|
+
constructor(http, tenantId) {
|
|
9
|
+
this.http = http;
|
|
10
|
+
this.tenantId = tenantId;
|
|
11
|
+
}
|
|
12
|
+
/** `GET /apps?tenantId=` */
|
|
13
|
+
async list() {
|
|
14
|
+
return this.http.request('GET', '/apps', { query: { tenantId: this.tenantId } });
|
|
15
|
+
}
|
|
16
|
+
/** `GET /apps/:id?tenantId=` */
|
|
17
|
+
async get(appId) {
|
|
18
|
+
return this.http.request('GET', `/apps/${(0, http_1.seg)(appId)}`, { query: { tenantId: this.tenantId } });
|
|
19
|
+
}
|
|
20
|
+
/** `PATCH /apps/:id?tenantId=`: branding fields (workspace admin) and `templatePolicy`
|
|
21
|
+
* (workspace owner only; null clears; 400 `invalid_template_policy` if malformed). */
|
|
22
|
+
async update(appId, patch) {
|
|
23
|
+
return this.http.request('PATCH', `/apps/${(0, http_1.seg)(appId)}`, { query: { tenantId: this.tenantId }, body: patch });
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
exports.AppsResource = AppsResource;
|