@sinas/sdk 0.4.0 → 0.5.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/{chunk-Z6NVKMMG.js → chunk-IABVF56L.js} +189 -18
- package/dist/chunk-IABVF56L.js.map +1 -0
- package/dist/client.cjs +198 -19
- package/dist/client.cjs.map +1 -1
- package/dist/client.d.cts +116 -4
- package/dist/client.d.ts +116 -4
- package/dist/client.js +19 -3
- package/dist/index.cjs +283 -21
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +41 -4
- package/dist/index.d.ts +41 -4
- package/dist/index.js +103 -5
- package/dist/index.js.map +1 -1
- package/dist/sinas-sdk.umd.js +273 -20
- package/dist/sinas-sdk.umd.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-Z6NVKMMG.js.map +0 -1
package/dist/sinas-sdk.umd.js
CHANGED
|
@@ -44,19 +44,26 @@ var SinasSDK = (() => {
|
|
|
44
44
|
__export(src_exports, {
|
|
45
45
|
SinasAuthError: () => SinasAuthError,
|
|
46
46
|
SinasClient: () => SinasClient,
|
|
47
|
+
SinasClientAuth: () => SinasClientAuth,
|
|
47
48
|
SinasError: () => SinasError,
|
|
48
49
|
SinasNotFoundError: () => SinasNotFoundError,
|
|
49
50
|
SinasPermissionError: () => SinasPermissionError,
|
|
50
51
|
SinasProvider: () => SinasProvider,
|
|
51
52
|
SinasTimeoutError: () => SinasTimeoutError,
|
|
52
53
|
approveToolCall: () => approveToolCall,
|
|
54
|
+
checkPermissions: () => checkPermissions,
|
|
53
55
|
createChat: () => createChat,
|
|
54
56
|
executeFunction: () => executeFunction,
|
|
55
57
|
executeQuery: () => executeQuery,
|
|
56
58
|
generateFileUrl: () => generateFileUrl,
|
|
57
59
|
getChat: () => getChat,
|
|
60
|
+
getInfo: () => getInfo,
|
|
61
|
+
getMe: () => getMe,
|
|
58
62
|
listCollections: () => listCollections,
|
|
59
63
|
listFiles: () => listFiles,
|
|
64
|
+
login: () => login,
|
|
65
|
+
logout: () => logout,
|
|
66
|
+
refresh: () => refresh,
|
|
60
67
|
sendMessage: () => sendMessage,
|
|
61
68
|
stateDelete: () => stateDelete,
|
|
62
69
|
stateGet: () => stateGet,
|
|
@@ -65,12 +72,14 @@ var SinasSDK = (() => {
|
|
|
65
72
|
streamFromChannel: () => streamFromChannel,
|
|
66
73
|
streamMessage: () => streamMessage,
|
|
67
74
|
uploadFile: () => uploadFile,
|
|
75
|
+
useAuth: () => useAuth,
|
|
68
76
|
useChat: () => useChat,
|
|
69
77
|
useClient: () => useClient,
|
|
70
78
|
useExecute: () => useExecute,
|
|
71
79
|
useQuery: () => useQuery,
|
|
72
80
|
useSinas: () => useSinas,
|
|
73
|
-
useStateStore: () => useStateStore
|
|
81
|
+
useStateStore: () => useStateStore,
|
|
82
|
+
verifyOTP: () => verifyOTP
|
|
74
83
|
});
|
|
75
84
|
|
|
76
85
|
// src/client/errors.ts
|
|
@@ -107,16 +116,131 @@ var SinasSDK = (() => {
|
|
|
107
116
|
}
|
|
108
117
|
};
|
|
109
118
|
|
|
119
|
+
// src/client/auth.ts
|
|
120
|
+
function trimBase(baseUrl) {
|
|
121
|
+
return baseUrl.replace(/\/+$/, "");
|
|
122
|
+
}
|
|
123
|
+
async function authFetch(baseUrl, path, init = {}, accessToken) {
|
|
124
|
+
const headers = new Headers(init.headers);
|
|
125
|
+
if (!headers.has("Content-Type") && init.body !== void 0) {
|
|
126
|
+
headers.set("Content-Type", "application/json");
|
|
127
|
+
}
|
|
128
|
+
if (accessToken) headers.set("Authorization", `Bearer ${accessToken}`);
|
|
129
|
+
const res = await fetch(`${trimBase(baseUrl)}${path}`, { ...init, headers });
|
|
130
|
+
if (res.ok || res.status === 204) return res;
|
|
131
|
+
let detail = res.statusText;
|
|
132
|
+
try {
|
|
133
|
+
const body = await res.clone().json();
|
|
134
|
+
detail = body.detail || body.message || detail;
|
|
135
|
+
} catch {
|
|
136
|
+
}
|
|
137
|
+
if (res.status === 401) throw new SinasAuthError(detail);
|
|
138
|
+
if (res.status === 403) throw new SinasPermissionError(detail);
|
|
139
|
+
if (res.status === 404) throw new SinasNotFoundError(detail);
|
|
140
|
+
throw new SinasError(detail, res.status, detail);
|
|
141
|
+
}
|
|
142
|
+
async function getInfo(baseUrl) {
|
|
143
|
+
const res = await authFetch(baseUrl, "/info");
|
|
144
|
+
return await res.json();
|
|
145
|
+
}
|
|
146
|
+
async function login(baseUrl, request) {
|
|
147
|
+
const res = await authFetch(baseUrl, "/auth/login", {
|
|
148
|
+
method: "POST",
|
|
149
|
+
body: JSON.stringify(request)
|
|
150
|
+
});
|
|
151
|
+
return await res.json();
|
|
152
|
+
}
|
|
153
|
+
async function verifyOTP(baseUrl, request) {
|
|
154
|
+
const res = await authFetch(baseUrl, "/auth/verify-otp", {
|
|
155
|
+
method: "POST",
|
|
156
|
+
body: JSON.stringify(request)
|
|
157
|
+
});
|
|
158
|
+
return await res.json();
|
|
159
|
+
}
|
|
160
|
+
async function refresh(baseUrl, refreshToken) {
|
|
161
|
+
const res = await authFetch(baseUrl, "/auth/refresh", {
|
|
162
|
+
method: "POST",
|
|
163
|
+
body: JSON.stringify({ refresh_token: refreshToken })
|
|
164
|
+
});
|
|
165
|
+
return await res.json();
|
|
166
|
+
}
|
|
167
|
+
async function logout(baseUrl, refreshToken) {
|
|
168
|
+
try {
|
|
169
|
+
await authFetch(baseUrl, "/auth/logout", {
|
|
170
|
+
method: "POST",
|
|
171
|
+
body: JSON.stringify({ refresh_token: refreshToken })
|
|
172
|
+
});
|
|
173
|
+
} catch (e) {
|
|
174
|
+
if (e instanceof SinasNotFoundError) return;
|
|
175
|
+
throw e;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
async function getMe(baseUrl, accessToken) {
|
|
179
|
+
const res = await authFetch(baseUrl, "/auth/me", { method: "GET" }, accessToken);
|
|
180
|
+
return await res.json();
|
|
181
|
+
}
|
|
182
|
+
async function checkPermissions(baseUrl, accessToken, request) {
|
|
183
|
+
const res = await authFetch(
|
|
184
|
+
baseUrl,
|
|
185
|
+
"/auth/check-permissions",
|
|
186
|
+
{
|
|
187
|
+
method: "POST",
|
|
188
|
+
body: JSON.stringify({ logic: "AND", ...request })
|
|
189
|
+
},
|
|
190
|
+
accessToken
|
|
191
|
+
);
|
|
192
|
+
return await res.json();
|
|
193
|
+
}
|
|
194
|
+
|
|
110
195
|
// src/client/SinasClient.ts
|
|
196
|
+
function hasWindow() {
|
|
197
|
+
return typeof window !== "undefined";
|
|
198
|
+
}
|
|
199
|
+
function trim(url) {
|
|
200
|
+
return url.replace(/\/+$/, "");
|
|
201
|
+
}
|
|
202
|
+
var SinasClientAuth = class {
|
|
203
|
+
constructor(client) {
|
|
204
|
+
this.client = client;
|
|
205
|
+
}
|
|
206
|
+
getInfo() {
|
|
207
|
+
return getInfo(this.client.requireBaseUrl());
|
|
208
|
+
}
|
|
209
|
+
login(request) {
|
|
210
|
+
return login(this.client.requireBaseUrl(), request);
|
|
211
|
+
}
|
|
212
|
+
verifyOTP(request) {
|
|
213
|
+
return verifyOTP(this.client.requireBaseUrl(), request);
|
|
214
|
+
}
|
|
215
|
+
refresh(refreshToken) {
|
|
216
|
+
return refresh(this.client.requireBaseUrl(), refreshToken);
|
|
217
|
+
}
|
|
218
|
+
logout(refreshToken) {
|
|
219
|
+
return logout(this.client.requireBaseUrl(), refreshToken);
|
|
220
|
+
}
|
|
221
|
+
getMe() {
|
|
222
|
+
const token = this.client.getAccessToken();
|
|
223
|
+
if (!token) throw new SinasAuthError("No access token available");
|
|
224
|
+
return getMe(this.client.requireBaseUrl(), token);
|
|
225
|
+
}
|
|
226
|
+
checkPermissions(request) {
|
|
227
|
+
const token = this.client.getAccessToken();
|
|
228
|
+
if (!token) throw new SinasAuthError("No access token available");
|
|
229
|
+
return checkPermissions(this.client.requireBaseUrl(), token, request);
|
|
230
|
+
}
|
|
231
|
+
};
|
|
111
232
|
var SinasClient = class {
|
|
112
|
-
constructor(
|
|
113
|
-
this.
|
|
233
|
+
constructor(options = {}) {
|
|
234
|
+
this.refreshInFlight = null;
|
|
235
|
+
this.options = options;
|
|
236
|
+
this.auth = new SinasClientAuth(this);
|
|
114
237
|
}
|
|
238
|
+
// ─── Config / base URL ───
|
|
115
239
|
getConfig() {
|
|
116
|
-
const config = this.
|
|
240
|
+
const config = this.options.config || (hasWindow() ? window.__SINAS_CONFIG__ : void 0);
|
|
117
241
|
if (!config) {
|
|
118
242
|
return {
|
|
119
|
-
apiBase: "",
|
|
243
|
+
apiBase: this.options.baseUrl ? `${trim(this.options.baseUrl)}/api/v1` : "",
|
|
120
244
|
component: { namespace: "", name: "", version: "" },
|
|
121
245
|
resources: {
|
|
122
246
|
enabledAgents: [],
|
|
@@ -133,12 +257,56 @@ var SinasSDK = (() => {
|
|
|
133
257
|
getInput() {
|
|
134
258
|
return this.getConfig().input || {};
|
|
135
259
|
}
|
|
260
|
+
/** Workspace base URL (root of the Sinas instance). Throws if unset. */
|
|
261
|
+
requireBaseUrl() {
|
|
262
|
+
const url = this.getBaseUrl();
|
|
263
|
+
if (!url) {
|
|
264
|
+
throw new SinasError(
|
|
265
|
+
"No baseUrl configured. Construct with `new SinasClient({ baseUrl })` for standalone use.",
|
|
266
|
+
0
|
|
267
|
+
);
|
|
268
|
+
}
|
|
269
|
+
return url;
|
|
270
|
+
}
|
|
271
|
+
getBaseUrl() {
|
|
272
|
+
if (this.options.baseUrl) return trim(this.options.baseUrl);
|
|
273
|
+
const apiBase = this.getConfig().apiBase;
|
|
274
|
+
if (!apiBase) return null;
|
|
275
|
+
return trim(apiBase).replace(/\/api\/v1$/, "");
|
|
276
|
+
}
|
|
277
|
+
// ─── Tokens ───
|
|
278
|
+
getAccessToken() {
|
|
279
|
+
if (this.options.getAccessToken) return this.options.getAccessToken();
|
|
280
|
+
if (hasWindow()) return window.__SINAS_AUTH_TOKEN__ ?? null;
|
|
281
|
+
return null;
|
|
282
|
+
}
|
|
283
|
+
getRefreshToken() {
|
|
284
|
+
return this.options.getRefreshToken ? this.options.getRefreshToken() : null;
|
|
285
|
+
}
|
|
286
|
+
async tryRefresh() {
|
|
287
|
+
const refreshToken = this.getRefreshToken();
|
|
288
|
+
if (!refreshToken) return null;
|
|
289
|
+
if (this.refreshInFlight) return this.refreshInFlight;
|
|
290
|
+
const baseUrl = this.getBaseUrl();
|
|
291
|
+
if (!baseUrl) return null;
|
|
292
|
+
this.refreshInFlight = (async () => {
|
|
293
|
+
try {
|
|
294
|
+
const resp = await refresh(baseUrl, refreshToken);
|
|
295
|
+
this.options.onTokenRefresh?.(resp.access_token);
|
|
296
|
+
return resp.access_token;
|
|
297
|
+
} catch {
|
|
298
|
+
return null;
|
|
299
|
+
} finally {
|
|
300
|
+
this.refreshInFlight = null;
|
|
301
|
+
}
|
|
302
|
+
})();
|
|
303
|
+
return this.refreshInFlight;
|
|
304
|
+
}
|
|
305
|
+
// ─── Headers / proxy ───
|
|
136
306
|
getAuthHeaders() {
|
|
137
|
-
const token =
|
|
307
|
+
const token = this.getAccessToken();
|
|
138
308
|
const headers = { "Content-Type": "application/json" };
|
|
139
|
-
if (token) {
|
|
140
|
-
headers["Authorization"] = `Bearer ${token}`;
|
|
141
|
-
}
|
|
309
|
+
if (token) headers["Authorization"] = `Bearer ${token}`;
|
|
142
310
|
return headers;
|
|
143
311
|
}
|
|
144
312
|
getProxyBase() {
|
|
@@ -146,7 +314,8 @@ var SinasSDK = (() => {
|
|
|
146
314
|
const comp = config.component;
|
|
147
315
|
return `${config.apiBase}/components/${comp.namespace}/${comp.name}/proxy`;
|
|
148
316
|
}
|
|
149
|
-
|
|
317
|
+
// ─── Fetch with refresh-on-401 ───
|
|
318
|
+
async fetch(url, options = {}, _retried = false) {
|
|
150
319
|
const res = await fetch(url, {
|
|
151
320
|
...options,
|
|
152
321
|
headers: {
|
|
@@ -154,15 +323,18 @@ var SinasSDK = (() => {
|
|
|
154
323
|
...options.headers || {}
|
|
155
324
|
}
|
|
156
325
|
});
|
|
157
|
-
if (
|
|
158
|
-
|
|
159
|
-
const
|
|
160
|
-
if (
|
|
161
|
-
|
|
162
|
-
if (res.status === 404) throw new SinasNotFoundError(message);
|
|
163
|
-
throw new SinasError(message, res.status);
|
|
326
|
+
if (res.ok || res.status === 204) return res;
|
|
327
|
+
if (res.status === 401 && !_retried) {
|
|
328
|
+
const newToken = await this.tryRefresh();
|
|
329
|
+
if (newToken) return this.fetch(url, options, true);
|
|
330
|
+
this.options.onUnauthenticated?.();
|
|
164
331
|
}
|
|
165
|
-
|
|
332
|
+
const body = await res.json().catch(() => ({ detail: res.statusText }));
|
|
333
|
+
const message = body.detail || body.message || res.statusText;
|
|
334
|
+
if (res.status === 401) throw new SinasAuthError(message);
|
|
335
|
+
if (res.status === 403) throw new SinasPermissionError(message);
|
|
336
|
+
if (res.status === 404) throw new SinasNotFoundError(message);
|
|
337
|
+
throw new SinasError(message, res.status);
|
|
166
338
|
}
|
|
167
339
|
};
|
|
168
340
|
|
|
@@ -486,8 +658,14 @@ var SinasSDK = (() => {
|
|
|
486
658
|
// src/hooks/context.ts
|
|
487
659
|
var import_react = __toESM(require_react(), 1);
|
|
488
660
|
var SinasContext = import_react.default.createContext(null);
|
|
489
|
-
function SinasProvider({ client, children }) {
|
|
490
|
-
const value = import_react.default.useMemo(
|
|
661
|
+
function SinasProvider({ client, options, children }) {
|
|
662
|
+
const value = import_react.default.useMemo(
|
|
663
|
+
() => client || new SinasClient(options),
|
|
664
|
+
// We intentionally only re-create when `client` reference changes; `options`
|
|
665
|
+
// is expected to be a stable object (callbacks reading from localStorage).
|
|
666
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
667
|
+
[client]
|
|
668
|
+
);
|
|
491
669
|
return import_react.default.createElement(SinasContext.Provider, { value }, children);
|
|
492
670
|
}
|
|
493
671
|
function useClient() {
|
|
@@ -962,6 +1140,81 @@ var SinasSDK = (() => {
|
|
|
962
1140
|
useChat
|
|
963
1141
|
};
|
|
964
1142
|
}
|
|
1143
|
+
|
|
1144
|
+
// src/hooks/useAuth.ts
|
|
1145
|
+
var import_react6 = __toESM(require_react(), 1);
|
|
1146
|
+
function useAuth(options = {}) {
|
|
1147
|
+
const { autoRefresh = true } = options;
|
|
1148
|
+
const client = useClient();
|
|
1149
|
+
const [status, setStatus] = import_react6.default.useState("loading");
|
|
1150
|
+
const [user, setUser] = import_react6.default.useState(null);
|
|
1151
|
+
const [info, setInfo] = import_react6.default.useState(null);
|
|
1152
|
+
const [error, setError] = import_react6.default.useState(null);
|
|
1153
|
+
const refresh2 = import_react6.default.useCallback(async () => {
|
|
1154
|
+
setError(null);
|
|
1155
|
+
try {
|
|
1156
|
+
const [infoResp, meResp] = await Promise.all([
|
|
1157
|
+
client.auth.getInfo().catch(() => null),
|
|
1158
|
+
client.getAccessToken() ? client.auth.getMe().catch(() => null) : Promise.resolve(null)
|
|
1159
|
+
]);
|
|
1160
|
+
if (infoResp) setInfo(infoResp);
|
|
1161
|
+
if (meResp) {
|
|
1162
|
+
setUser(meResp);
|
|
1163
|
+
setStatus("authenticated");
|
|
1164
|
+
} else {
|
|
1165
|
+
setUser(null);
|
|
1166
|
+
setStatus("unauthenticated");
|
|
1167
|
+
}
|
|
1168
|
+
} catch (e) {
|
|
1169
|
+
setError(e instanceof Error ? e.message : String(e));
|
|
1170
|
+
setStatus("unauthenticated");
|
|
1171
|
+
}
|
|
1172
|
+
}, [client]);
|
|
1173
|
+
import_react6.default.useEffect(() => {
|
|
1174
|
+
if (autoRefresh) void refresh2();
|
|
1175
|
+
}, [autoRefresh, refresh2]);
|
|
1176
|
+
const login2 = import_react6.default.useCallback(
|
|
1177
|
+
async (email, password) => {
|
|
1178
|
+
setError(null);
|
|
1179
|
+
try {
|
|
1180
|
+
return await client.auth.login({ email, password });
|
|
1181
|
+
} catch (e) {
|
|
1182
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
1183
|
+
setError(msg);
|
|
1184
|
+
throw e;
|
|
1185
|
+
}
|
|
1186
|
+
},
|
|
1187
|
+
[client]
|
|
1188
|
+
);
|
|
1189
|
+
const verifyOTP2 = import_react6.default.useCallback(
|
|
1190
|
+
async (sessionId, otpCode) => {
|
|
1191
|
+
setError(null);
|
|
1192
|
+
try {
|
|
1193
|
+
return await client.auth.verifyOTP({ session_id: sessionId, otp_code: otpCode });
|
|
1194
|
+
} catch (e) {
|
|
1195
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
1196
|
+
setError(msg);
|
|
1197
|
+
throw e;
|
|
1198
|
+
}
|
|
1199
|
+
},
|
|
1200
|
+
[client]
|
|
1201
|
+
);
|
|
1202
|
+
const logout2 = import_react6.default.useCallback(
|
|
1203
|
+
async (refreshToken) => {
|
|
1204
|
+
if (refreshToken) {
|
|
1205
|
+
try {
|
|
1206
|
+
await client.auth.logout(refreshToken);
|
|
1207
|
+
} catch (e) {
|
|
1208
|
+
if (!(e instanceof SinasAuthError)) throw e;
|
|
1209
|
+
}
|
|
1210
|
+
}
|
|
1211
|
+
setUser(null);
|
|
1212
|
+
setStatus("unauthenticated");
|
|
1213
|
+
},
|
|
1214
|
+
[client]
|
|
1215
|
+
);
|
|
1216
|
+
return { status, user, info, error, refresh: refresh2, login: login2, verifyOTP: verifyOTP2, logout: logout2 };
|
|
1217
|
+
}
|
|
965
1218
|
return __toCommonJS(src_exports);
|
|
966
1219
|
})();
|
|
967
1220
|
//# sourceMappingURL=sinas-sdk.umd.js.map
|