@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/index.cjs
CHANGED
|
@@ -32,19 +32,26 @@ var src_exports = {};
|
|
|
32
32
|
__export(src_exports, {
|
|
33
33
|
SinasAuthError: () => SinasAuthError,
|
|
34
34
|
SinasClient: () => SinasClient,
|
|
35
|
+
SinasClientAuth: () => SinasClientAuth,
|
|
35
36
|
SinasError: () => SinasError,
|
|
36
37
|
SinasNotFoundError: () => SinasNotFoundError,
|
|
37
38
|
SinasPermissionError: () => SinasPermissionError,
|
|
38
39
|
SinasProvider: () => SinasProvider,
|
|
39
40
|
SinasTimeoutError: () => SinasTimeoutError,
|
|
40
41
|
approveToolCall: () => approveToolCall,
|
|
42
|
+
checkPermissions: () => checkPermissions,
|
|
41
43
|
createChat: () => createChat,
|
|
42
44
|
executeFunction: () => executeFunction,
|
|
43
45
|
executeQuery: () => executeQuery,
|
|
44
46
|
generateFileUrl: () => generateFileUrl,
|
|
45
47
|
getChat: () => getChat,
|
|
48
|
+
getInfo: () => getInfo,
|
|
49
|
+
getMe: () => getMe,
|
|
46
50
|
listCollections: () => listCollections,
|
|
47
51
|
listFiles: () => listFiles,
|
|
52
|
+
login: () => login,
|
|
53
|
+
logout: () => logout,
|
|
54
|
+
refresh: () => refresh,
|
|
48
55
|
sendMessage: () => sendMessage,
|
|
49
56
|
stateDelete: () => stateDelete,
|
|
50
57
|
stateGet: () => stateGet,
|
|
@@ -53,12 +60,14 @@ __export(src_exports, {
|
|
|
53
60
|
streamFromChannel: () => streamFromChannel,
|
|
54
61
|
streamMessage: () => streamMessage,
|
|
55
62
|
uploadFile: () => uploadFile,
|
|
63
|
+
useAuth: () => useAuth,
|
|
56
64
|
useChat: () => useChat,
|
|
57
65
|
useClient: () => useClient,
|
|
58
66
|
useExecute: () => useExecute,
|
|
59
67
|
useQuery: () => useQuery,
|
|
60
68
|
useSinas: () => useSinas,
|
|
61
|
-
useStateStore: () => useStateStore
|
|
69
|
+
useStateStore: () => useStateStore,
|
|
70
|
+
verifyOTP: () => verifyOTP
|
|
62
71
|
});
|
|
63
72
|
module.exports = __toCommonJS(src_exports);
|
|
64
73
|
|
|
@@ -96,16 +105,131 @@ var SinasTimeoutError = class extends SinasError {
|
|
|
96
105
|
}
|
|
97
106
|
};
|
|
98
107
|
|
|
108
|
+
// src/client/auth.ts
|
|
109
|
+
function trimBase(baseUrl) {
|
|
110
|
+
return baseUrl.replace(/\/+$/, "");
|
|
111
|
+
}
|
|
112
|
+
async function authFetch(baseUrl, path, init = {}, accessToken) {
|
|
113
|
+
const headers = new Headers(init.headers);
|
|
114
|
+
if (!headers.has("Content-Type") && init.body !== void 0) {
|
|
115
|
+
headers.set("Content-Type", "application/json");
|
|
116
|
+
}
|
|
117
|
+
if (accessToken) headers.set("Authorization", `Bearer ${accessToken}`);
|
|
118
|
+
const res = await fetch(`${trimBase(baseUrl)}${path}`, { ...init, headers });
|
|
119
|
+
if (res.ok || res.status === 204) return res;
|
|
120
|
+
let detail = res.statusText;
|
|
121
|
+
try {
|
|
122
|
+
const body = await res.clone().json();
|
|
123
|
+
detail = body.detail || body.message || detail;
|
|
124
|
+
} catch {
|
|
125
|
+
}
|
|
126
|
+
if (res.status === 401) throw new SinasAuthError(detail);
|
|
127
|
+
if (res.status === 403) throw new SinasPermissionError(detail);
|
|
128
|
+
if (res.status === 404) throw new SinasNotFoundError(detail);
|
|
129
|
+
throw new SinasError(detail, res.status, detail);
|
|
130
|
+
}
|
|
131
|
+
async function getInfo(baseUrl) {
|
|
132
|
+
const res = await authFetch(baseUrl, "/info");
|
|
133
|
+
return await res.json();
|
|
134
|
+
}
|
|
135
|
+
async function login(baseUrl, request) {
|
|
136
|
+
const res = await authFetch(baseUrl, "/auth/login", {
|
|
137
|
+
method: "POST",
|
|
138
|
+
body: JSON.stringify(request)
|
|
139
|
+
});
|
|
140
|
+
return await res.json();
|
|
141
|
+
}
|
|
142
|
+
async function verifyOTP(baseUrl, request) {
|
|
143
|
+
const res = await authFetch(baseUrl, "/auth/verify-otp", {
|
|
144
|
+
method: "POST",
|
|
145
|
+
body: JSON.stringify(request)
|
|
146
|
+
});
|
|
147
|
+
return await res.json();
|
|
148
|
+
}
|
|
149
|
+
async function refresh(baseUrl, refreshToken) {
|
|
150
|
+
const res = await authFetch(baseUrl, "/auth/refresh", {
|
|
151
|
+
method: "POST",
|
|
152
|
+
body: JSON.stringify({ refresh_token: refreshToken })
|
|
153
|
+
});
|
|
154
|
+
return await res.json();
|
|
155
|
+
}
|
|
156
|
+
async function logout(baseUrl, refreshToken) {
|
|
157
|
+
try {
|
|
158
|
+
await authFetch(baseUrl, "/auth/logout", {
|
|
159
|
+
method: "POST",
|
|
160
|
+
body: JSON.stringify({ refresh_token: refreshToken })
|
|
161
|
+
});
|
|
162
|
+
} catch (e) {
|
|
163
|
+
if (e instanceof SinasNotFoundError) return;
|
|
164
|
+
throw e;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
async function getMe(baseUrl, accessToken) {
|
|
168
|
+
const res = await authFetch(baseUrl, "/auth/me", { method: "GET" }, accessToken);
|
|
169
|
+
return await res.json();
|
|
170
|
+
}
|
|
171
|
+
async function checkPermissions(baseUrl, accessToken, request) {
|
|
172
|
+
const res = await authFetch(
|
|
173
|
+
baseUrl,
|
|
174
|
+
"/auth/check-permissions",
|
|
175
|
+
{
|
|
176
|
+
method: "POST",
|
|
177
|
+
body: JSON.stringify({ logic: "AND", ...request })
|
|
178
|
+
},
|
|
179
|
+
accessToken
|
|
180
|
+
);
|
|
181
|
+
return await res.json();
|
|
182
|
+
}
|
|
183
|
+
|
|
99
184
|
// src/client/SinasClient.ts
|
|
185
|
+
function hasWindow() {
|
|
186
|
+
return typeof window !== "undefined";
|
|
187
|
+
}
|
|
188
|
+
function trim(url) {
|
|
189
|
+
return url.replace(/\/+$/, "");
|
|
190
|
+
}
|
|
191
|
+
var SinasClientAuth = class {
|
|
192
|
+
constructor(client) {
|
|
193
|
+
this.client = client;
|
|
194
|
+
}
|
|
195
|
+
getInfo() {
|
|
196
|
+
return getInfo(this.client.requireBaseUrl());
|
|
197
|
+
}
|
|
198
|
+
login(request) {
|
|
199
|
+
return login(this.client.requireBaseUrl(), request);
|
|
200
|
+
}
|
|
201
|
+
verifyOTP(request) {
|
|
202
|
+
return verifyOTP(this.client.requireBaseUrl(), request);
|
|
203
|
+
}
|
|
204
|
+
refresh(refreshToken) {
|
|
205
|
+
return refresh(this.client.requireBaseUrl(), refreshToken);
|
|
206
|
+
}
|
|
207
|
+
logout(refreshToken) {
|
|
208
|
+
return logout(this.client.requireBaseUrl(), refreshToken);
|
|
209
|
+
}
|
|
210
|
+
getMe() {
|
|
211
|
+
const token = this.client.getAccessToken();
|
|
212
|
+
if (!token) throw new SinasAuthError("No access token available");
|
|
213
|
+
return getMe(this.client.requireBaseUrl(), token);
|
|
214
|
+
}
|
|
215
|
+
checkPermissions(request) {
|
|
216
|
+
const token = this.client.getAccessToken();
|
|
217
|
+
if (!token) throw new SinasAuthError("No access token available");
|
|
218
|
+
return checkPermissions(this.client.requireBaseUrl(), token, request);
|
|
219
|
+
}
|
|
220
|
+
};
|
|
100
221
|
var SinasClient = class {
|
|
101
|
-
constructor(
|
|
102
|
-
this.
|
|
222
|
+
constructor(options = {}) {
|
|
223
|
+
this.refreshInFlight = null;
|
|
224
|
+
this.options = options;
|
|
225
|
+
this.auth = new SinasClientAuth(this);
|
|
103
226
|
}
|
|
227
|
+
// ─── Config / base URL ───
|
|
104
228
|
getConfig() {
|
|
105
|
-
const config = this.
|
|
229
|
+
const config = this.options.config || (hasWindow() ? window.__SINAS_CONFIG__ : void 0);
|
|
106
230
|
if (!config) {
|
|
107
231
|
return {
|
|
108
|
-
apiBase: "",
|
|
232
|
+
apiBase: this.options.baseUrl ? `${trim(this.options.baseUrl)}/api/v1` : "",
|
|
109
233
|
component: { namespace: "", name: "", version: "" },
|
|
110
234
|
resources: {
|
|
111
235
|
enabledAgents: [],
|
|
@@ -122,12 +246,56 @@ var SinasClient = class {
|
|
|
122
246
|
getInput() {
|
|
123
247
|
return this.getConfig().input || {};
|
|
124
248
|
}
|
|
249
|
+
/** Workspace base URL (root of the Sinas instance). Throws if unset. */
|
|
250
|
+
requireBaseUrl() {
|
|
251
|
+
const url = this.getBaseUrl();
|
|
252
|
+
if (!url) {
|
|
253
|
+
throw new SinasError(
|
|
254
|
+
"No baseUrl configured. Construct with `new SinasClient({ baseUrl })` for standalone use.",
|
|
255
|
+
0
|
|
256
|
+
);
|
|
257
|
+
}
|
|
258
|
+
return url;
|
|
259
|
+
}
|
|
260
|
+
getBaseUrl() {
|
|
261
|
+
if (this.options.baseUrl) return trim(this.options.baseUrl);
|
|
262
|
+
const apiBase = this.getConfig().apiBase;
|
|
263
|
+
if (!apiBase) return null;
|
|
264
|
+
return trim(apiBase).replace(/\/api\/v1$/, "");
|
|
265
|
+
}
|
|
266
|
+
// ─── Tokens ───
|
|
267
|
+
getAccessToken() {
|
|
268
|
+
if (this.options.getAccessToken) return this.options.getAccessToken();
|
|
269
|
+
if (hasWindow()) return window.__SINAS_AUTH_TOKEN__ ?? null;
|
|
270
|
+
return null;
|
|
271
|
+
}
|
|
272
|
+
getRefreshToken() {
|
|
273
|
+
return this.options.getRefreshToken ? this.options.getRefreshToken() : null;
|
|
274
|
+
}
|
|
275
|
+
async tryRefresh() {
|
|
276
|
+
const refreshToken = this.getRefreshToken();
|
|
277
|
+
if (!refreshToken) return null;
|
|
278
|
+
if (this.refreshInFlight) return this.refreshInFlight;
|
|
279
|
+
const baseUrl = this.getBaseUrl();
|
|
280
|
+
if (!baseUrl) return null;
|
|
281
|
+
this.refreshInFlight = (async () => {
|
|
282
|
+
try {
|
|
283
|
+
const resp = await refresh(baseUrl, refreshToken);
|
|
284
|
+
this.options.onTokenRefresh?.(resp.access_token);
|
|
285
|
+
return resp.access_token;
|
|
286
|
+
} catch {
|
|
287
|
+
return null;
|
|
288
|
+
} finally {
|
|
289
|
+
this.refreshInFlight = null;
|
|
290
|
+
}
|
|
291
|
+
})();
|
|
292
|
+
return this.refreshInFlight;
|
|
293
|
+
}
|
|
294
|
+
// ─── Headers / proxy ───
|
|
125
295
|
getAuthHeaders() {
|
|
126
|
-
const token =
|
|
296
|
+
const token = this.getAccessToken();
|
|
127
297
|
const headers = { "Content-Type": "application/json" };
|
|
128
|
-
if (token) {
|
|
129
|
-
headers["Authorization"] = `Bearer ${token}`;
|
|
130
|
-
}
|
|
298
|
+
if (token) headers["Authorization"] = `Bearer ${token}`;
|
|
131
299
|
return headers;
|
|
132
300
|
}
|
|
133
301
|
getProxyBase() {
|
|
@@ -135,7 +303,8 @@ var SinasClient = class {
|
|
|
135
303
|
const comp = config.component;
|
|
136
304
|
return `${config.apiBase}/components/${comp.namespace}/${comp.name}/proxy`;
|
|
137
305
|
}
|
|
138
|
-
|
|
306
|
+
// ─── Fetch with refresh-on-401 ───
|
|
307
|
+
async fetch(url, options = {}, _retried = false) {
|
|
139
308
|
const res = await fetch(url, {
|
|
140
309
|
...options,
|
|
141
310
|
headers: {
|
|
@@ -143,15 +312,18 @@ var SinasClient = class {
|
|
|
143
312
|
...options.headers || {}
|
|
144
313
|
}
|
|
145
314
|
});
|
|
146
|
-
if (
|
|
147
|
-
|
|
148
|
-
const
|
|
149
|
-
if (
|
|
150
|
-
|
|
151
|
-
if (res.status === 404) throw new SinasNotFoundError(message);
|
|
152
|
-
throw new SinasError(message, res.status);
|
|
315
|
+
if (res.ok || res.status === 204) return res;
|
|
316
|
+
if (res.status === 401 && !_retried) {
|
|
317
|
+
const newToken = await this.tryRefresh();
|
|
318
|
+
if (newToken) return this.fetch(url, options, true);
|
|
319
|
+
this.options.onUnauthenticated?.();
|
|
153
320
|
}
|
|
154
|
-
|
|
321
|
+
const body = await res.json().catch(() => ({ detail: res.statusText }));
|
|
322
|
+
const message = body.detail || body.message || res.statusText;
|
|
323
|
+
if (res.status === 401) throw new SinasAuthError(message);
|
|
324
|
+
if (res.status === 403) throw new SinasPermissionError(message);
|
|
325
|
+
if (res.status === 404) throw new SinasNotFoundError(message);
|
|
326
|
+
throw new SinasError(message, res.status);
|
|
155
327
|
}
|
|
156
328
|
};
|
|
157
329
|
|
|
@@ -475,8 +647,14 @@ var import_react2 = __toESM(require("react"), 1);
|
|
|
475
647
|
// src/hooks/context.ts
|
|
476
648
|
var import_react = __toESM(require("react"), 1);
|
|
477
649
|
var SinasContext = import_react.default.createContext(null);
|
|
478
|
-
function SinasProvider({ client, children }) {
|
|
479
|
-
const value = import_react.default.useMemo(
|
|
650
|
+
function SinasProvider({ client, options, children }) {
|
|
651
|
+
const value = import_react.default.useMemo(
|
|
652
|
+
() => client || new SinasClient(options),
|
|
653
|
+
// We intentionally only re-create when `client` reference changes; `options`
|
|
654
|
+
// is expected to be a stable object (callbacks reading from localStorage).
|
|
655
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
656
|
+
[client]
|
|
657
|
+
);
|
|
480
658
|
return import_react.default.createElement(SinasContext.Provider, { value }, children);
|
|
481
659
|
}
|
|
482
660
|
function useClient() {
|
|
@@ -951,23 +1129,105 @@ function useSinas() {
|
|
|
951
1129
|
useChat
|
|
952
1130
|
};
|
|
953
1131
|
}
|
|
1132
|
+
|
|
1133
|
+
// src/hooks/useAuth.ts
|
|
1134
|
+
var import_react6 = __toESM(require("react"), 1);
|
|
1135
|
+
function useAuth(options = {}) {
|
|
1136
|
+
const { autoRefresh = true } = options;
|
|
1137
|
+
const client = useClient();
|
|
1138
|
+
const [status, setStatus] = import_react6.default.useState("loading");
|
|
1139
|
+
const [user, setUser] = import_react6.default.useState(null);
|
|
1140
|
+
const [info, setInfo] = import_react6.default.useState(null);
|
|
1141
|
+
const [error, setError] = import_react6.default.useState(null);
|
|
1142
|
+
const refresh2 = import_react6.default.useCallback(async () => {
|
|
1143
|
+
setError(null);
|
|
1144
|
+
try {
|
|
1145
|
+
const [infoResp, meResp] = await Promise.all([
|
|
1146
|
+
client.auth.getInfo().catch(() => null),
|
|
1147
|
+
client.getAccessToken() ? client.auth.getMe().catch(() => null) : Promise.resolve(null)
|
|
1148
|
+
]);
|
|
1149
|
+
if (infoResp) setInfo(infoResp);
|
|
1150
|
+
if (meResp) {
|
|
1151
|
+
setUser(meResp);
|
|
1152
|
+
setStatus("authenticated");
|
|
1153
|
+
} else {
|
|
1154
|
+
setUser(null);
|
|
1155
|
+
setStatus("unauthenticated");
|
|
1156
|
+
}
|
|
1157
|
+
} catch (e) {
|
|
1158
|
+
setError(e instanceof Error ? e.message : String(e));
|
|
1159
|
+
setStatus("unauthenticated");
|
|
1160
|
+
}
|
|
1161
|
+
}, [client]);
|
|
1162
|
+
import_react6.default.useEffect(() => {
|
|
1163
|
+
if (autoRefresh) void refresh2();
|
|
1164
|
+
}, [autoRefresh, refresh2]);
|
|
1165
|
+
const login2 = import_react6.default.useCallback(
|
|
1166
|
+
async (email, password) => {
|
|
1167
|
+
setError(null);
|
|
1168
|
+
try {
|
|
1169
|
+
return await client.auth.login({ email, password });
|
|
1170
|
+
} catch (e) {
|
|
1171
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
1172
|
+
setError(msg);
|
|
1173
|
+
throw e;
|
|
1174
|
+
}
|
|
1175
|
+
},
|
|
1176
|
+
[client]
|
|
1177
|
+
);
|
|
1178
|
+
const verifyOTP2 = import_react6.default.useCallback(
|
|
1179
|
+
async (sessionId, otpCode) => {
|
|
1180
|
+
setError(null);
|
|
1181
|
+
try {
|
|
1182
|
+
return await client.auth.verifyOTP({ session_id: sessionId, otp_code: otpCode });
|
|
1183
|
+
} catch (e) {
|
|
1184
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
1185
|
+
setError(msg);
|
|
1186
|
+
throw e;
|
|
1187
|
+
}
|
|
1188
|
+
},
|
|
1189
|
+
[client]
|
|
1190
|
+
);
|
|
1191
|
+
const logout2 = import_react6.default.useCallback(
|
|
1192
|
+
async (refreshToken) => {
|
|
1193
|
+
if (refreshToken) {
|
|
1194
|
+
try {
|
|
1195
|
+
await client.auth.logout(refreshToken);
|
|
1196
|
+
} catch (e) {
|
|
1197
|
+
if (!(e instanceof SinasAuthError)) throw e;
|
|
1198
|
+
}
|
|
1199
|
+
}
|
|
1200
|
+
setUser(null);
|
|
1201
|
+
setStatus("unauthenticated");
|
|
1202
|
+
},
|
|
1203
|
+
[client]
|
|
1204
|
+
);
|
|
1205
|
+
return { status, user, info, error, refresh: refresh2, login: login2, verifyOTP: verifyOTP2, logout: logout2 };
|
|
1206
|
+
}
|
|
954
1207
|
// Annotate the CommonJS export names for ESM import in node:
|
|
955
1208
|
0 && (module.exports = {
|
|
956
1209
|
SinasAuthError,
|
|
957
1210
|
SinasClient,
|
|
1211
|
+
SinasClientAuth,
|
|
958
1212
|
SinasError,
|
|
959
1213
|
SinasNotFoundError,
|
|
960
1214
|
SinasPermissionError,
|
|
961
1215
|
SinasProvider,
|
|
962
1216
|
SinasTimeoutError,
|
|
963
1217
|
approveToolCall,
|
|
1218
|
+
checkPermissions,
|
|
964
1219
|
createChat,
|
|
965
1220
|
executeFunction,
|
|
966
1221
|
executeQuery,
|
|
967
1222
|
generateFileUrl,
|
|
968
1223
|
getChat,
|
|
1224
|
+
getInfo,
|
|
1225
|
+
getMe,
|
|
969
1226
|
listCollections,
|
|
970
1227
|
listFiles,
|
|
1228
|
+
login,
|
|
1229
|
+
logout,
|
|
1230
|
+
refresh,
|
|
971
1231
|
sendMessage,
|
|
972
1232
|
stateDelete,
|
|
973
1233
|
stateGet,
|
|
@@ -976,11 +1236,13 @@ function useSinas() {
|
|
|
976
1236
|
streamFromChannel,
|
|
977
1237
|
streamMessage,
|
|
978
1238
|
uploadFile,
|
|
1239
|
+
useAuth,
|
|
979
1240
|
useChat,
|
|
980
1241
|
useClient,
|
|
981
1242
|
useExecute,
|
|
982
1243
|
useQuery,
|
|
983
1244
|
useSinas,
|
|
984
|
-
useStateStore
|
|
1245
|
+
useStateStore,
|
|
1246
|
+
verifyOTP
|
|
985
1247
|
});
|
|
986
1248
|
//# sourceMappingURL=index.cjs.map
|